diff --git a/Cargo.lock b/Cargo.lock --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "async-trait" version = "0.1.79" @@ -158,6 +164,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.65.1" @@ -179,6 +194,22 @@ "syn 2.0.72", ] +[[package]] +name = "bitcoin-io" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitcoinsuite-core" version = "0.1.0" @@ -480,6 +511,16 @@ "unicode-width", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "convert_case" version = "0.6.0" @@ -585,13 +626,30 @@ version = "0.1.0" dependencies = [ "abc-rust-lint", + "ecash-secp256k1", "ripemd", - "secp256k1-abc", "sha2", "thiserror", "wasm-bindgen", ] +[[package]] +name = "ecash-secp256k1" +version = "0.30.0" +dependencies = [ + "bincode", + "bitcoin_hashes", + "ecash-secp256k1-sys", + "getrandom", + "hex_lit", + "rand 0.8.5", + "rand_core 0.6.4", + "serde", + "serde_cbor", + "serde_test", + "wasm-bindgen-test", +] + [[package]] name = "ecash-secp256k1-sys" version = "0.10.0" @@ -777,8 +835,10 @@ checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -793,6 +853,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + [[package]] name = "hash32" version = "0.2.1" @@ -846,12 +912,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + [[package]] name = "home" version = "0.5.9" @@ -1007,6 +1088,15 @@ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1604,6 +1694,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -1622,22 +1718,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" -[[package]] -name = "secp256k1-abc" -version = "0.20.3" -source = "git+https://github.com/raipay/secp256k1-abc?rev=b23e742#b23e74219f5c425eada0f53a52f9b51fdb3b23b2" -dependencies = [ - "secp256k1-sys-abc", -] - -[[package]] -name = "secp256k1-sys-abc" -version = "0.4.1" -source = "git+https://github.com/raipay/secp256k1-abc?rev=b23e742#b23e74219f5c425eada0f53a52f9b51fdb3b23b2" -dependencies = [ - "cc", -] - [[package]] name = "semver" version = "1.0.22" @@ -1653,6 +1733,17 @@ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7081ed758ec726a6ed8ee7e92f5d3f6e6f8c3901b1f972e3a4a2f2599fad14f" +dependencies = [ + "byteorder", + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -1694,6 +1785,15 @@ "serde", ] +[[package]] +name = "serde_test" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2169,6 +2269,18 @@ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -2198,6 +2310,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ "chronik/chronik-proto", "chronik/chronik-util", "modules/ecash-lib-wasm", + "modules/ecash-secp256k1", "modules/ecash-secp256k1/ecash-secp256k1-sys", ] diff --git a/modules/ecash-lib-wasm/Cargo.toml b/modules/ecash-lib-wasm/Cargo.toml --- a/modules/ecash-lib-wasm/Cargo.toml +++ b/modules/ecash-lib-wasm/Cargo.toml @@ -26,9 +26,8 @@ # Implementation of SHA-256 etc. cryptographic hash functions sha2 = "0.10" -[dependencies.secp256k1-abc] +[dependencies.ecash-secp256k1] # libsecp256k1 with support for BCH/XEC/XPI Schnorr signatures -git = "https://github.com/raipay/secp256k1-abc" -rev = "b23e742" +path = "../ecash-secp256k1" default-features = false features = ["alloc"] diff --git a/modules/ecash-lib-wasm/src/ecc.rs b/modules/ecash-lib-wasm/src/ecc.rs --- a/modules/ecash-lib-wasm/src/ecc.rs +++ b/modules/ecash-lib-wasm/src/ecc.rs @@ -4,7 +4,7 @@ //! Module for [`Ecc`] for signing secp256k1 signatures. -use secp256k1_abc::{ +use ecash_secp256k1::{ constants::SECRET_KEY_SIZE, All, Message, PublicKey, Secp256k1, SecretKey, }; use thiserror::Error; @@ -51,7 +51,9 @@ } fn parse_msg(msg: &[u8]) -> Result<Message, String> { - Ok(Message::from_slice(msg).map_err(|_| InvalidMessageSize(msg.len()))?) + Ok(Message::from_digest( + msg.try_into().map_err(|_| InvalidMessageSize(msg.len()))?, + )) } #[wasm_bindgen] @@ -82,7 +84,7 @@ ) -> Result<Vec<u8>, String> { let seckey = parse_secret_key(seckey)?; let msg = parse_msg(msg)?; - let sig = self.curve.sign(&msg, &seckey); + let sig = self.curve.sign_ecdsa(&msg, &seckey); Ok(sig.serialize_der().to_vec()) } @@ -95,7 +97,7 @@ ) -> Result<Vec<u8>, String> { let seckey = parse_secret_key(seckey)?; let msg = parse_msg(msg)?; - let sig = self.curve.schnorrabc_sign_no_aux_rand(&msg, &seckey); + let sig = self.curve.sign_schnorrabc_no_aux_rand(&msg, &seckey); Ok(sig.as_ref().to_vec()) } } diff --git a/modules/ecash-secp256k1/.gitignore b/modules/ecash-secp256k1/.gitignore new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/modules/ecash-secp256k1/Cargo.toml b/modules/ecash-secp256k1/Cargo.toml new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "ecash-secp256k1" +version = "0.30.0" +authors = [ + "Dawid Ciężarkiewicz <dpc@ucore.info>", + "Andrew Poelstra <apoelstra@wpsoftware.net>", + "Tobias Ruck <tobias.ruck@be.cash>", +] +license = "CC0-1.0" +homepage = "https://github.com/rust-bitcoin/rust-secp256k1/" +repository = "https://github.com/rust-bitcoin/rust-secp256k1/" +documentation = "https://docs.rs/secp256k1/" +description = "Rust wrapper library for Pieter Wuille's `libsecp256k1`. Implements ECDSA, eCash Schnorr and BIP 340 signatures for the SECG elliptic curve group secp256k1 and related utilities." +keywords = [ "crypto", "ECDSA", "secp256k1", "libsecp256k1", "bitcoin" ] +readme = "README.md" +edition = "2021" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[features] +default = ["std"] +std = ["alloc", "secp256k1-sys/std", "rand?/std", "rand?/std_rng", "hashes?/std"] +# allow use of Secp256k1::new and related API that requires an allocator +alloc = ["secp256k1-sys/alloc"] +recovery = ["secp256k1-sys/recovery"] +lowmemory = ["secp256k1-sys/lowmemory"] +global-context = ["std"] +# disable re-randomization of the global context, which provides some +# defense-in-depth against sidechannel attacks. You should only use +# this feature if you expect the `rand` crate's thread_rng to panic. +# (If you are sure the `rand` and `std` features will not be enabled, e.g. +# if you are doing a no-std build, then this feature does nothing +# and is not necessary.) +global-context-less-secure = ["global-context"] + +[dependencies] +secp256k1-sys = { version = "0.10.0", default-features = false, path = "./ecash-secp256k1-sys", package = "ecash-secp256k1-sys" } + +hashes = { package = "bitcoin_hashes", version = "0.14", default-features = false, optional = true } +rand = { version = "0.8", default-features = false, optional = true } +serde = { version = "1.0.103", default-features = false, optional = true } + +[dev-dependencies] +rand_core = "0.6" +serde_cbor = "0.10.0" +serde_test = "1.0.19" +bincode = "1.3.3" +hex_lit = "0.1.1" + +[target.wasm32-unknown-unknown.dev-dependencies] +wasm-bindgen-test = "0.3" +getrandom = { version = "0.2", features = ["js"] } + +[lints.rust] +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(secp256k1_fuzz)', 'cfg(rust_secp_no_symbol_renaming)'] } diff --git a/modules/ecash-secp256k1/LICENSE b/modules/ecash-secp256k1/LICENSE new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/LICENSE @@ -0,0 +1,122 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + diff --git a/modules/ecash-secp256k1/README.md b/modules/ecash-secp256k1/README.md new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/README.md @@ -0,0 +1,15 @@ +<div align="center"> + <h1>Rust Secp256k1</h1> +</div> + +`ecash-secp256k1` is a wrapper around [libsecp256k1](https://github.com/bitcoin-abc/secp256k1), a C +library implementing various cryptographic functions using the [SECG](https://www.secg.org/) curve +[secp256k1](https://en.bitcoin.it/wiki/Secp256k1). + +This library: + +- exposes type-safe Rust bindings for all `libsecp256k1` functions +- implements key generation +- implements deterministic nonce generation via RFC6979 +- implements many unit tests, adding to those already present in `libsecp256k1` +- makes no allocations (except in unit tests) for efficiency and use in freestanding implementations diff --git a/modules/ecash-secp256k1/contrib/test.sh b/modules/ecash-secp256k1/contrib/test.sh --- a/modules/ecash-secp256k1/contrib/test.sh +++ b/modules/ecash-secp256k1/contrib/test.sh @@ -15,6 +15,17 @@ main() { ecash_secp256k1_sys + ecash_secp256k1 +} + +ecash_secp256k1() { + FEATURES_WITH_STD="hashes global-context global-context-less-secure lowmemory rand recovery serde" + FEATURES_WITHOUT_STD="hashes global-context global-context-less-secure lowmemory rand recovery serde alloc" + + # Navigate to ecash-secp256k1-sys + pushd "$SCRIPT_DIR/../" + run_tests + popd } ecash_secp256k1_sys() { @@ -23,8 +34,8 @@ # Navigate to ecash-secp256k1-sys pushd "$SCRIPT_DIR/../ecash-secp256k1-sys" - run_tests + popd } run_tests() { @@ -48,8 +59,6 @@ build_docs build_wasm - - popd } # Build with each feature as well as all combinations of two features. diff --git a/modules/ecash-secp256k1/ecash-secp256k1-sys/build.rs b/modules/ecash-secp256k1/ecash-secp256k1-sys/build.rs --- a/modules/ecash-secp256k1/ecash-secp256k1-sys/build.rs +++ b/modules/ecash-secp256k1/ecash-secp256k1-sys/build.rs @@ -34,6 +34,7 @@ .define("ENABLE_MODULE_SCHNORRSIG", Some("1")) .define("ENABLE_MODULE_SCHNORR", Some("1")) .define("ENABLE_MODULE_EXTRAKEYS", Some("1")) + .define("ENABLE_NO_ALLOC", Some("1")) // upstream sometimes introduces calls to printf, which we cannot // compile with WASM due to its lack of libc. printf is never // necessary and we can just #define it away. diff --git a/modules/ecash-secp256k1/ecash-secp256k1-sys/src/lib.rs b/modules/ecash-secp256k1/ecash-secp256k1-sys/src/lib.rs --- a/modules/ecash-secp256k1/ecash-secp256k1-sys/src/lib.rs +++ b/modules/ecash-secp256k1/ecash-secp256k1-sys/src/lib.rs @@ -1145,11 +1145,14 @@ use crate::*; extern "C" { + #[link_name = "secp256k1_context_preallocated_size"] fn ecash_secp256k1_context_preallocated_size(flags: c_uint) -> size_t; + #[link_name = "secp256k1_context_preallocated_create"] fn ecash_secp256k1_context_preallocated_create( prealloc: NonNull<c_void>, flags: c_uint, ) -> NonNull<Context>; + #[link_name = "secp256k1_context_preallocated_clone"] fn ecash_secp256k1_context_preallocated_clone( cx: *const Context, prealloc: NonNull<c_void>, @@ -1603,6 +1606,49 @@ secp256k1_schnorrsig_sign(cx, sig, msg, keypair, ptr::null()) } + /// Schnorr ABC Signatures: + /// Sets sig to msghash32||pk[..32] + pub unsafe fn secp256k1_schnorr_sign( + cx: *const Context, + sig64: *mut c_uchar, + msghash32: *const c_uchar, + seckey: *const c_uchar, + _noncefn: NonceFn, + _noncedata: *const c_void, + ) -> c_int { + check_context_flags(cx, SECP256K1_START_SIGN); + // Check context is built for signing (and compute pk) + let mut new_pk = PublicKey::new(); + if secp256k1_ec_pubkey_create(cx, &mut new_pk, seckey) != 1 { + return 0; + } + // Sign + let sig_sl = slice::from_raw_parts_mut(sig64 as *mut u8, 64); + let msg_sl = slice::from_raw_parts(msghash32 as *const u8, 32); + sig_sl[..32].copy_from_slice(msg_sl); + sig_sl[32..].copy_from_slice(&new_pk.0[..32]); + 1 + } + + /// Schnorr ABC Signatures: + /// Verifies that sig is msghash32||pk[32..] + pub unsafe fn secp256k1_schnorr_verify( + cx: *const Context, + sig64: *const c_uchar, + msghash32: *const c_uchar, + pubkey: *const PublicKey, + ) -> c_int { + check_context_flags(cx, SECP256K1_START_VERIFY); + // Actually verify + let sig_sl = slice::from_raw_parts(sig64 as *const u8, 64); + let msg_sl = slice::from_raw_parts(msghash32 as *const u8, 32); + if &sig_sl[..32] == msg_sl && sig_sl[32..] == (*pubkey).0[0..32] { + 1 + } else { + 0 + } + } + // Extra keys pub unsafe fn secp256k1_keypair_create( cx: *const Context, diff --git a/modules/ecash-secp256k1/src/constants.rs b/modules/ecash-secp256k1/src/constants.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/constants.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Constants related to the API and the underlying curve. + +/// The size (in bytes) of a message. +pub const MESSAGE_SIZE: usize = 32; + +/// The size (in bytes) of a secret key. +pub const SECRET_KEY_SIZE: usize = 32; + +/// The size (in bytes) of a serialized public key. +pub const PUBLIC_KEY_SIZE: usize = 33; + +/// The size (in bytes) of an serialized uncompressed public key. +pub const UNCOMPRESSED_PUBLIC_KEY_SIZE: usize = 65; + +/// The maximum size of a signature. +pub const MAX_SIGNATURE_SIZE: usize = 72; + +/// The maximum size of a compact signature. +pub const COMPACT_SIGNATURE_SIZE: usize = 64; + +/// The size of a schnorr signature. +pub const SCHNORR_SIGNATURE_SIZE: usize = 64; + +/// The size of a schnorr public key. +pub const SCHNORR_PUBLIC_KEY_SIZE: usize = 32; + +/// The size of a key pair. +pub const KEY_PAIR_SIZE: usize = 96; + +/// The size of a full ElligatorSwift encoding. +pub const ELLSWIFT_ENCODING_SIZE: usize = 64; + +/// The Prime for the secp256k1 field element. +#[rustfmt::skip] +pub const FIELD_SIZE: [u8; 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, 0x2f +]; + +/// The order of the secp256k1 curve. +#[rustfmt::skip] +pub const CURVE_ORDER: [u8; 32] = [ + 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 +]; + +/// The X coordinate of the generator. +#[rustfmt::skip] +pub const GENERATOR_X: [u8; 32] = [ + 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, + 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, + 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, + 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98 +]; + +/// The Y coordinate of the generator. +#[rustfmt::skip] +pub const GENERATOR_Y: [u8; 32] = [ + 0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, + 0x5d, 0xa4, 0xfb, 0xfc, 0x0e, 0x11, 0x08, 0xa8, + 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19, + 0x9c, 0x47, 0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8 +]; + +/// The value zero as an array of bytes. +pub const ZERO: [u8; 32] = [0; 32]; + +/// The value one as big-endian array of bytes. +pub const ONE: [u8; 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, 1, +]; diff --git a/modules/ecash-secp256k1/src/context.rs b/modules/ecash-secp256k1/src/context.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/context.rs @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::marker::PhantomData; +use core::mem::ManuallyDrop; +use core::ptr::NonNull; + +#[cfg(feature = "alloc")] +pub use self::alloc_only::{All, SignOnly, VerifyOnly}; +use crate::ffi::types::{c_uint, c_void, AlignedType}; +use crate::ffi::{self, CPtr}; +use crate::{Error, Secp256k1}; + +#[cfg(all(feature = "global-context", feature = "std"))] +/// Module implementing a singleton pattern for a global `Secp256k1` context. +pub mod global { + + use std::ops::Deref; + use std::sync::Once; + + use crate::{All, Secp256k1}; + + /// Proxy struct for global `SECP256K1` context. + #[derive(Debug, Copy, Clone)] + pub struct GlobalContext { + __private: (), + } + + /// A global static context to avoid repeatedly creating contexts. + /// + /// If `rand` and `std` feature is enabled, context will have been + /// randomized using `thread_rng`. + /// + /// ``` + /// # #[cfg(all(feature = "global-context", feature = "rand", + /// # feature = "std"))] { + /// use ecash_secp256k1::{PublicKey, SECP256K1}; + /// let _ = SECP256K1.generate_keypair(&mut rand::thread_rng()); + /// # } + /// ``` + pub static SECP256K1: &GlobalContext = &GlobalContext { __private: () }; + + impl Deref for GlobalContext { + type Target = Secp256k1<All>; + + #[allow(unused_mut)] // Unused when `rand` + `std` is not enabled. + fn deref(&self) -> &Self::Target { + static ONCE: Once = Once::new(); + static mut CONTEXT: Option<Secp256k1<All>> = None; + ONCE.call_once(|| unsafe { + let mut ctx = Secp256k1::new(); + #[cfg(all( + not(target_arch = "wasm32"), + feature = "rand", + feature = "std", + not(feature = "global-context-less-secure") + ))] + { + ctx.randomize(&mut rand::thread_rng()); + } + CONTEXT = Some(ctx); + }); + unsafe { CONTEXT.as_ref().unwrap() } + } + } +} + +/// A trait for all kinds of contexts that lets you define the exact flags and a +/// function to deallocate memory. It isn't possible to implement this for types +/// outside this crate. +/// +/// # Safety +/// +/// This trait is marked unsafe to allow unsafe implementations of `deallocate`. +pub unsafe trait Context: private::Sealed { + /// Flags for the ffi. + const FLAGS: c_uint; + /// A constant description of the context. + const DESCRIPTION: &'static str; + /// A function to deallocate the memory when the context is dropped. + /// + /// # Safety + /// + /// `ptr` must be valid. Further safety constraints may be imposed by + /// [`std::alloc::dealloc`]. + unsafe fn deallocate(ptr: *mut u8, size: usize); +} + +/// Marker trait for indicating that an instance of [`Secp256k1`] can be used +/// for signing. +pub trait Signing: Context {} + +/// Marker trait for indicating that an instance of [`Secp256k1`] can be used +/// for verification. +pub trait Verification: Context {} + +/// Represents the set of capabilities needed for signing (preallocated memory). +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SignOnlyPreallocated<'buf> { + phantom: PhantomData<&'buf ()>, +} + +/// Represents the set of capabilities needed for verification (preallocated +/// memory). +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VerifyOnlyPreallocated<'buf> { + phantom: PhantomData<&'buf ()>, +} + +/// Represents the set of all capabilities (preallocated memory). +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct AllPreallocated<'buf> { + phantom: PhantomData<&'buf ()>, +} + +mod private { + use super::*; + pub trait Sealed {} + + impl<'buf> Sealed for AllPreallocated<'buf> {} + impl<'buf> Sealed for VerifyOnlyPreallocated<'buf> {} + impl<'buf> Sealed for SignOnlyPreallocated<'buf> {} +} + +#[cfg(feature = "alloc")] +mod alloc_only { + use core::marker::PhantomData; + use core::ptr::NonNull; + + use super::private; + use crate::alloc::alloc; + use crate::ffi::types::{c_uint, c_void}; + use crate::ffi::{self}; + use crate::{AlignedType, Context, Secp256k1, Signing, Verification}; + + impl private::Sealed for SignOnly {} + impl private::Sealed for All {} + impl private::Sealed for VerifyOnly {} + + const ALIGN_TO: usize = core::mem::align_of::<AlignedType>(); + + /// Represents the set of capabilities needed for signing. + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum SignOnly {} + + /// Represents the set of capabilities needed for verification. + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum VerifyOnly {} + + /// Represents the set of all capabilities. + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum All {} + + impl Signing for SignOnly {} + impl Signing for All {} + + impl Verification for VerifyOnly {} + impl Verification for All {} + + unsafe impl Context for SignOnly { + const DESCRIPTION: &'static str = "signing only"; + const FLAGS: c_uint = ffi::SECP256K1_START_SIGN; + + unsafe fn deallocate(ptr: *mut u8, size: usize) { + let layout = + alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + alloc::dealloc(ptr, layout); + } + } + + unsafe impl Context for VerifyOnly { + const DESCRIPTION: &'static str = "verification only"; + const FLAGS: c_uint = ffi::SECP256K1_START_VERIFY; + + unsafe fn deallocate(ptr: *mut u8, size: usize) { + let layout = + alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + alloc::dealloc(ptr, layout); + } + } + + unsafe impl Context for All { + const DESCRIPTION: &'static str = "all capabilities"; + const FLAGS: c_uint = VerifyOnly::FLAGS | SignOnly::FLAGS; + + unsafe fn deallocate(ptr: *mut u8, size: usize) { + let layout = + alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + alloc::dealloc(ptr, layout); + } + } + + impl<C: Context> Secp256k1<C> { + /// Lets you create a context in a generic manner (sign/verify/all). + /// + /// If `rand` and `std` feature is enabled, context will have been + /// randomized using `thread_rng`. + /// If `rand` or `std` feature is not enabled please consider + /// randomizing the context as follows: + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// # use ecash_secp256k1::Secp256k1; + /// # use ecash_secp256k1::rand::{thread_rng, RngCore}; + /// let mut ctx = Secp256k1::new(); + /// # let mut rng = thread_rng(); + /// # let mut seed = [0u8; 32]; + /// # rng.fill_bytes(&mut seed); + /// // let seed = <32 bytes of random data> + /// ctx.seeded_randomize(&seed); + /// # } + /// ``` + #[cfg_attr( + not(all(feature = "rand", feature = "std")), + allow(clippy::let_and_return, unused_mut) + )] + pub fn gen_new() -> Secp256k1<C> { + #[cfg(target_arch = "wasm32")] + ffi::types::sanity_checks_for_wasm(); + + let size = + unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) }; + let layout = + alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + let ptr = unsafe { alloc::alloc(layout) }; + let ptr = NonNull::new(ptr as *mut c_void) + .unwrap_or_else(|| alloc::handle_alloc_error(layout)); + + #[allow(unused_mut)] + // ctx is not mutated under some feature combinations. + let mut ctx: Secp256k1<C> = Secp256k1 { + ctx: unsafe { + ffi::secp256k1_context_preallocated_create(ptr, C::FLAGS) + }, + phantom: PhantomData, + }; + + #[cfg(all( + not(target_arch = "wasm32"), + feature = "rand", + feature = "std", + not(feature = "global-context-less-secure") + ))] + { + ctx.randomize(&mut rand::thread_rng()); + } + + #[allow(clippy::let_and_return)] // as for unusted_mut + ctx + } + } + + impl Secp256k1<All> { + /// Creates a new Secp256k1 context with all capabilities. + /// + /// If `rand` and `std` feature is enabled, context will have been + /// randomized using `thread_rng`. + /// If `rand` or `std` feature is not enabled please consider + /// randomizing the context (see + /// docs for `Secp256k1::gen_new()`). + pub fn new() -> Secp256k1<All> { + Secp256k1::gen_new() + } + } + + impl Secp256k1<SignOnly> { + /// Creates a new Secp256k1 context that can only be used for signing. + /// + /// If `rand` and `std` feature is enabled, context will have been + /// randomized using `thread_rng`. + /// If `rand` or `std` feature is not enabled please consider + /// randomizing the context (see + /// docs for `Secp256k1::gen_new()`). + pub fn signing_only() -> Secp256k1<SignOnly> { + Secp256k1::gen_new() + } + } + + impl Secp256k1<VerifyOnly> { + /// Creates a new Secp256k1 context that can only be used for + /// verification. + /// + /// If `rand` and `std` feature is enabled, context will have been + /// randomized using `thread_rng`. + /// If `rand` or `std` feature is not enabled please consider + /// randomizing the context (see + /// docs for `Secp256k1::gen_new()`). + pub fn verification_only() -> Secp256k1<VerifyOnly> { + Secp256k1::gen_new() + } + } + + impl Default for Secp256k1<All> { + fn default() -> Self { + Self::new() + } + } + + impl<C: Context> Clone for Secp256k1<C> { + fn clone(&self) -> Secp256k1<C> { + let size = unsafe { + ffi::secp256k1_context_preallocated_clone_size( + self.ctx.as_ptr(), + ) + }; + let layout = + alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + let ptr = unsafe { alloc::alloc(layout) }; + let ptr = NonNull::new(ptr as *mut c_void) + .unwrap_or_else(|| alloc::handle_alloc_error(layout)); + + Secp256k1 { + ctx: unsafe { + ffi::secp256k1_context_preallocated_clone( + self.ctx.as_ptr(), + ptr, + ) + }, + phantom: PhantomData, + } + } + } +} + +impl<'buf> Signing for SignOnlyPreallocated<'buf> {} +impl<'buf> Signing for AllPreallocated<'buf> {} + +impl<'buf> Verification for VerifyOnlyPreallocated<'buf> {} +impl<'buf> Verification for AllPreallocated<'buf> {} + +unsafe impl<'buf> Context for SignOnlyPreallocated<'buf> { + const DESCRIPTION: &'static str = "signing only"; + const FLAGS: c_uint = ffi::SECP256K1_START_SIGN; + + unsafe fn deallocate(_ptr: *mut u8, _size: usize) { + // Allocated by the user + } +} + +unsafe impl<'buf> Context for VerifyOnlyPreallocated<'buf> { + const DESCRIPTION: &'static str = "verification only"; + const FLAGS: c_uint = ffi::SECP256K1_START_VERIFY; + + unsafe fn deallocate(_ptr: *mut u8, _size: usize) { + // Allocated by the user. + } +} + +unsafe impl<'buf> Context for AllPreallocated<'buf> { + const DESCRIPTION: &'static str = "all capabilities"; + const FLAGS: c_uint = + SignOnlyPreallocated::FLAGS | VerifyOnlyPreallocated::FLAGS; + + unsafe fn deallocate(_ptr: *mut u8, _size: usize) { + // Allocated by the user. + } +} + +/// Trait marking that a particular context object internally points to +/// memory that must outlive `'a` +/// +/// # Safety +/// +/// This trait is used internally to gate which context markers can safely +/// be used with the `preallocated_gen_new` function. Do not implement it +/// on your own structures. +pub unsafe trait PreallocatedContext<'a> {} + +unsafe impl<'buf> PreallocatedContext<'buf> for AllPreallocated<'buf> {} +unsafe impl<'buf> PreallocatedContext<'buf> for SignOnlyPreallocated<'buf> {} +unsafe impl<'buf> PreallocatedContext<'buf> for VerifyOnlyPreallocated<'buf> {} + +impl<'buf, C: Context + PreallocatedContext<'buf>> Secp256k1<C> { + /// Lets you create a context with a preallocated buffer in a generic manner + /// (sign/verify/all). + pub fn preallocated_gen_new( + buf: &'buf mut [AlignedType], + ) -> Result<Secp256k1<C>, Error> { + #[cfg(target_arch = "wasm32")] + ffi::types::sanity_checks_for_wasm(); + + if buf.len() < Self::preallocate_size_gen() { + return Err(Error::NotEnoughMemory); + } + // Safe because buf is not null since it is not empty. + let buf = unsafe { + NonNull::new_unchecked(buf.as_mut_c_ptr() as *mut c_void) + }; + + Ok(Secp256k1 { + ctx: unsafe { + ffi::secp256k1_context_preallocated_create( + buf, + AllPreallocated::FLAGS, + ) + }, + phantom: PhantomData, + }) + } +} + +impl<'buf> Secp256k1<AllPreallocated<'buf>> { + /// Creates a new Secp256k1 context with all capabilities. + pub fn preallocated_new( + buf: &'buf mut [AlignedType], + ) -> Result<Secp256k1<AllPreallocated<'buf>>, Error> { + Secp256k1::preallocated_gen_new(buf) + } + + /// Uses the ffi `secp256k1_context_preallocated_size` to check the memory + /// size needed for a context. + pub fn preallocate_size() -> usize { + Self::preallocate_size_gen() + } + + /// Creates a context from a raw context. + /// + /// The returned [`core::mem::ManuallyDrop`] context will never deallocate + /// the memory pointed to by `raw_ctx` nor destroy the context. This may + /// lead to memory leaks. `ManuallyDrop::drop` + /// (or [`core::ptr::drop_in_place`]) will only destroy the context; the + /// caller is required to free the memory. + /// + /// # Safety + /// + /// This is highly unsafe due to a number of conditions that aren't checked, + /// specifically: + /// + /// * `raw_ctx` must be a valid pointer (live, aligned...) to memory that + /// was initialized by `secp256k1_context_preallocated_create` (either + /// called directly or from this library by one of the context creation + /// methods - all of which call it internally). + /// * The version of `libsecp256k1` used to create `raw_ctx` must be + /// **exactly the one linked into this library**. + /// * The lifetime of the `raw_ctx` pointer must outlive `'buf`. + /// * `raw_ctx` must point to writable memory (cannot be + /// `ffi::secp256k1_context_no_precomp`). + pub unsafe fn from_raw_all( + raw_ctx: NonNull<ffi::Context>, + ) -> ManuallyDrop<Secp256k1<AllPreallocated<'buf>>> { + ManuallyDrop::new(Secp256k1 { + ctx: raw_ctx, + phantom: PhantomData, + }) + } +} + +impl<'buf> Secp256k1<SignOnlyPreallocated<'buf>> { + /// Creates a new Secp256k1 context that can only be used for signing. + pub fn preallocated_signing_only( + buf: &'buf mut [AlignedType], + ) -> Result<Secp256k1<SignOnlyPreallocated<'buf>>, Error> { + Secp256k1::preallocated_gen_new(buf) + } + + /// Uses the ffi `secp256k1_context_preallocated_size` to check the memory + /// size needed for the context. + #[inline] + pub fn preallocate_signing_size() -> usize { + Self::preallocate_size_gen() + } + + /// Creates a context from a raw context that can only be used for signing. + /// + /// # Safety + /// + /// Please see [`Secp256k1::from_raw_all`] for full documentation and safety + /// requirements. + pub unsafe fn from_raw_signing_only( + raw_ctx: NonNull<ffi::Context>, + ) -> ManuallyDrop<Secp256k1<SignOnlyPreallocated<'buf>>> { + ManuallyDrop::new(Secp256k1 { + ctx: raw_ctx, + phantom: PhantomData, + }) + } +} + +impl<'buf> Secp256k1<VerifyOnlyPreallocated<'buf>> { + /// Creates a new Secp256k1 context that can only be used for verification + pub fn preallocated_verification_only( + buf: &'buf mut [AlignedType], + ) -> Result<Secp256k1<VerifyOnlyPreallocated<'buf>>, Error> { + Secp256k1::preallocated_gen_new(buf) + } + + /// Uses the ffi `secp256k1_context_preallocated_size` to check the memory + /// size needed for the context. + #[inline] + pub fn preallocate_verification_size() -> usize { + Self::preallocate_size_gen() + } + + /// Creates a context from a raw context that can only be used for + /// verification. + /// + /// # Safety + /// + /// Please see [`Secp256k1::from_raw_all`] for full documentation and safety + /// requirements. + pub unsafe fn from_raw_verification_only( + raw_ctx: NonNull<ffi::Context>, + ) -> ManuallyDrop<Secp256k1<VerifyOnlyPreallocated<'buf>>> { + ManuallyDrop::new(Secp256k1 { + ctx: raw_ctx, + phantom: PhantomData, + }) + } +} diff --git a/modules/ecash-secp256k1/src/ecdh.rs b/modules/ecash-secp256k1/src/ecdh.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/ecdh.rs @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Support for shared secret computations. + +use core::borrow::Borrow; +use core::{ptr, str}; + +use secp256k1_sys::types::{c_int, c_uchar, c_void}; + +use crate::ffi::{self, CPtr}; +use crate::key::{PublicKey, SecretKey}; +use crate::{constants, Error}; + +// The logic for displaying shared secrets relies on this (see `secret.rs`). +const SHARED_SECRET_SIZE: usize = constants::SECRET_KEY_SIZE; + +/// Enables two parties to create a shared secret without revealing their own +/// secrets. +/// +/// # Examples +/// +/// ``` +/// # #[cfg(all(feature = "rand", feature = "std"))] { +/// # use ecash_secp256k1::{rand, Secp256k1}; +/// # use ecash_secp256k1::ecdh::SharedSecret; +/// let s = Secp256k1::new(); +/// let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng()); +/// let (sk2, pk2) = s.generate_keypair(&mut rand::thread_rng()); +/// let sec1 = SharedSecret::new(&pk2, &sk1); +/// let sec2 = SharedSecret::new(&pk1, &sk2); +/// assert_eq!(sec1, sec2); +/// # } +// ``` +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SharedSecret([u8; SHARED_SECRET_SIZE]); +impl_display_secret!(SharedSecret); +impl_non_secure_erase!(SharedSecret, 0, [0u8; SHARED_SECRET_SIZE]); + +impl SharedSecret { + /// Creates a new shared secret from a pubkey and secret key. + #[inline] + pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret { + let mut buf = [0u8; SHARED_SECRET_SIZE]; + let res = unsafe { + ffi::secp256k1_ecdh( + ffi::secp256k1_context_no_precomp, + buf.as_mut_ptr(), + point.as_c_ptr(), + scalar.as_c_ptr(), + ffi::secp256k1_ecdh_hash_function_default, + ptr::null_mut(), + ) + }; + debug_assert_eq!(res, 1); + SharedSecret(buf) + } + + /// Returns the shared secret as a byte value. + #[inline] + pub fn secret_bytes(&self) -> [u8; SHARED_SECRET_SIZE] { + self.0 + } + + /// Creates a shared secret from `bytes` array. + #[inline] + pub fn from_bytes(bytes: [u8; SHARED_SECRET_SIZE]) -> SharedSecret { + SharedSecret(bytes) + } + + /// Creates a shared secret from `bytes` slice. + #[deprecated(since = "TBD", note = "Use `from_bytes` instead.")] + #[inline] + pub fn from_slice(bytes: &[u8]) -> Result<SharedSecret, Error> { + match bytes.len() { + SHARED_SECRET_SIZE => { + let mut ret = [0u8; SHARED_SECRET_SIZE]; + ret[..].copy_from_slice(bytes); + Ok(SharedSecret(ret)) + } + _ => Err(Error::InvalidSharedSecret), + } + } +} + +impl str::FromStr for SharedSecret { + type Err = Error; + + fn from_str(s: &str) -> Result<SharedSecret, Error> { + let mut res = [0u8; SHARED_SECRET_SIZE]; + match crate::from_hex(s, &mut res) { + Ok(SHARED_SECRET_SIZE) => Ok(SharedSecret::from_bytes(res)), + _ => Err(Error::InvalidSharedSecret), + } + } +} + +impl Borrow<[u8]> for SharedSecret { + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for SharedSecret { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// Creates a shared point from public key and secret key. +/// +/// **Important: use of a strong cryptographic hash function may be critical to +/// security! Do NOT use unless you understand cryptographical implications.** +/// If not, use SharedSecret instead. +/// +/// Can be used like `SharedSecret` but caller is responsible for then hashing +/// the returned buffer. This allows for the use of a custom hash function since +/// `SharedSecret` uses SHA256. +/// +/// # Returns +/// +/// 64 bytes representing the (x,y) co-ordinates of a point on the curve (32 +/// bytes each). +/// +/// # Examples +/// ``` +/// # #[cfg(all(feature = "hashes", feature = "rand", feature = "std"))] { +/// # use ecash_secp256k1::{ecdh, rand, Secp256k1, PublicKey, SecretKey}; +/// # use ecash_secp256k1::hashes::{Hash, sha512}; +/// +/// let s = Secp256k1::new(); +/// let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng()); +/// let (sk2, pk2) = s.generate_keypair(&mut rand::thread_rng()); +/// +/// let point1 = ecdh::shared_secret_point(&pk2, &sk1); +/// let secret1 = sha512::Hash::hash(&point1); +/// let point2 = ecdh::shared_secret_point(&pk1, &sk2); +/// let secret2 = sha512::Hash::hash(&point2); +/// assert_eq!(secret1, secret2) +/// # } +/// ``` +pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] { + let mut xy = [0u8; 64]; + + let res = unsafe { + ffi::secp256k1_ecdh( + ffi::secp256k1_context_no_precomp, + xy.as_mut_ptr(), + point.as_c_ptr(), + scalar.as_c_ptr(), + Some(c_callback), + ptr::null_mut(), + ) + }; + // Our callback *always* returns 1. + // The scalar was verified to be valid (0 > scalar > group_order) via the + // type system. + debug_assert_eq!(res, 1); + xy +} + +unsafe extern "C" fn c_callback( + output: *mut c_uchar, + x: *const c_uchar, + y: *const c_uchar, + _data: *mut c_void, +) -> c_int { + ptr::copy_nonoverlapping(x, output, 32); + ptr::copy_nonoverlapping(y, output.offset(32), 32); + 1 +} + +#[cfg(feature = "serde")] +impl ::serde::Serialize for SharedSecret { + fn serialize<S: ::serde::Serializer>( + &self, + s: S, + ) -> Result<S::Ok, S::Error> { + if s.is_human_readable() { + let mut buf = [0u8; SHARED_SECRET_SIZE * 2]; + s.serialize_str( + crate::to_hex(&self.0, &mut buf) + .expect("fixed-size hex serialization"), + ) + } else { + s.serialize_bytes(self.as_ref()) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> ::serde::Deserialize<'de> for SharedSecret { + fn deserialize<D: ::serde::Deserializer<'de>>( + d: D, + ) -> Result<Self, D::Error> { + if d.is_human_readable() { + d.deserialize_str(super::serde_util::FromStrVisitor::new( + "a hex string representing 32 byte SharedSecret", + )) + } else { + d.deserialize_bytes(super::serde_util::BytesVisitor::new( + "raw 32 bytes SharedSecret", + SharedSecret::from_slice, + )) + } + } +} + +#[cfg(test)] +#[allow(unused_imports)] +mod tests { + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + use super::SharedSecret; + use crate::Secp256k1; + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn ecdh() { + let s = Secp256k1::signing_only(); + let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng()); + let (sk2, pk2) = s.generate_keypair(&mut rand::thread_rng()); + + let sec1 = SharedSecret::new(&pk2, &sk1); + let sec2 = SharedSecret::new(&pk1, &sk2); + let sec_odd = SharedSecret::new(&pk1, &sk1); + assert_eq!(sec1, sec2); + assert!(sec_odd != sec2); + } + + #[test] + fn test_c_callback() { + let x = [5u8; 32]; + let y = [7u8; 32]; + let mut output = [0u8; 64]; + let res = unsafe { + super::c_callback( + output.as_mut_ptr(), + x.as_ptr(), + y.as_ptr(), + core::ptr::null_mut(), + ) + }; + assert_eq!(res, 1); + let mut new_x = [0u8; 32]; + let mut new_y = [0u8; 32]; + new_x.copy_from_slice(&output[..32]); + new_y.copy_from_slice(&output[32..]); + assert_eq!(x, new_x); + assert_eq!(y, new_y); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] + #[cfg(all(feature = "hashes", feature = "rand", feature = "std"))] + fn hashes_and_sys_generate_same_secret() { + use hashes::{sha256, Hash, HashEngine}; + + use crate::ecdh::shared_secret_point; + + let s = Secp256k1::signing_only(); + let (sk1, _) = s.generate_keypair(&mut rand::thread_rng()); + let (_, pk2) = s.generate_keypair(&mut rand::thread_rng()); + + let secret_sys = SharedSecret::new(&pk2, &sk1); + + let xy = shared_secret_point(&pk2, &sk1); + + // Mimics logic in `bitcoin-core/secp256k1/src/module/main_impl.h` + let version = (xy[63] & 0x01) | 0x02; + let mut engine = sha256::HashEngine::default(); + engine.input(&[version]); + engine.input(&xy.as_ref()[..32]); + let secret_bh = sha256::Hash::from_engine(engine); + + assert_eq!(secret_bh.as_byte_array(), secret_sys.as_ref()); + } + + #[test] + #[cfg(all(feature = "serde", feature = "alloc"))] + fn serde() { + use serde_test::{assert_tokens, Configure, Token}; + #[rustfmt::skip] + static BYTES: [u8; 32] = [ + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 3, 4, 5, 6, 7, + 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, + 99, 99, 99, 99, 99, 99, 99, 99 + ]; + static STR: &str = + "01010101010101010001020304050607ffff0000ffff00006363636363636363"; + + let secret = SharedSecret::from_slice(&BYTES).unwrap(); + + assert_tokens(&secret.compact(), &[Token::BorrowedBytes(&BYTES[..])]); + assert_tokens(&secret.compact(), &[Token::Bytes(&BYTES)]); + assert_tokens(&secret.compact(), &[Token::ByteBuf(&BYTES)]); + + assert_tokens(&secret.readable(), &[Token::BorrowedStr(STR)]); + assert_tokens(&secret.readable(), &[Token::Str(STR)]); + assert_tokens(&secret.readable(), &[Token::String(STR)]); + } +} + +#[cfg(bench)] +// Currently only a single bench that requires "rand" + "std". +#[cfg(all(feature = "rand", feature = "std"))] +mod benches { + use test::{black_box, Bencher}; + + use super::SharedSecret; + use crate::Secp256k1; + + #[bench] + pub fn bench_ecdh(bh: &mut Bencher) { + let s = Secp256k1::signing_only(); + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + + bh.iter(|| { + let res = SharedSecret::new(&pk, &sk); + black_box(res); + }); + } +} diff --git a/modules/ecash-secp256k1/src/ecdsa/mod.rs b/modules/ecash-secp256k1/src/ecdsa/mod.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/ecdsa/mod.rs @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Structs and functionality related to the ECDSA signature algorithm. + +#[cfg(feature = "recovery")] +mod recovery; +pub mod serialized_signature; + +use core::{fmt, ptr, str}; + +#[cfg(feature = "recovery")] +pub use self::recovery::{RecoverableSignature, RecoveryId}; +pub use self::serialized_signature::SerializedSignature; +use crate::ffi::CPtr; +#[cfg(feature = "global-context")] +use crate::SECP256K1; +use crate::{ + ffi, from_hex, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, + Verification, +}; + +/// An ECDSA signature +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct Signature(pub(crate) ffi::Signature); +impl_fast_comparisons!(Signature); + +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let sig = self.serialize_der(); + sig.fmt(f) + } +} + +impl str::FromStr for Signature { + type Err = Error; + + fn from_str(s: &str) -> Result<Signature, Error> { + let mut res = [0u8; 72]; + match from_hex(s, &mut res) { + Ok(x) => Signature::from_der(&res[0..x]), + _ => Err(Error::InvalidSignature), + } + } +} + +impl Signature { + #[inline] + /// Converts a DER-encoded byte slice to a signature + pub fn from_der(data: &[u8]) -> Result<Signature, Error> { + if data.is_empty() { + return Err(Error::InvalidSignature); + } + + unsafe { + let mut ret = ffi::Signature::new(); + if ffi::secp256k1_ecdsa_signature_parse_der( + ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_c_ptr(), + data.len(), + ) == 1 + { + Ok(Signature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Converts a 64-byte compact-encoded byte slice to a signature + pub fn from_compact(data: &[u8]) -> Result<Signature, Error> { + if data.len() != 64 { + return Err(Error::InvalidSignature); + } + + unsafe { + let mut ret = ffi::Signature::new(); + if ffi::secp256k1_ecdsa_signature_parse_compact( + ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_c_ptr(), + ) == 1 + { + Ok(Signature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Converts a "lax DER"-encoded byte slice to a signature. This is + /// basically only useful for validating signatures in the Bitcoin + /// blockchain from before 2016. It should never be used in new + /// applications. This library does not support serializing to this + /// "format" + pub fn from_der_lax(data: &[u8]) -> Result<Signature, Error> { + if data.is_empty() { + return Err(Error::InvalidSignature); + } + + unsafe { + let mut ret = ffi::Signature::new(); + if ffi::ecdsa_signature_parse_der_lax( + ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_c_ptr(), + data.len(), + ) == 1 + { + Ok(Signature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Normalizes a signature to a "low S" form. In ECDSA, signatures are + /// of the form (r, s) where r and s are numbers lying in some finite + /// field. The verification equation will pass for (r, s) iff it passes + /// for (r, -s), so it is possible to ``modify'' signatures in transit + /// by flipping the sign of s. This does not constitute a forgery since + /// the signed message still cannot be changed, but for some applications, + /// changing even the signature itself can be a problem. Such applications + /// require a "strong signature". It is believed that ECDSA is a strong + /// signature except for this ambiguity in the sign of s, so to accommodate + /// these applications libsecp256k1 considers signatures for which s is in + /// the upper half of the field range invalid. This eliminates the + /// ambiguity. + /// + /// However, for some systems, signatures with high s-values are considered + /// valid. (For example, parsing the historic Bitcoin blockchain requires + /// this.) For these applications we provide this normalization function, + /// which ensures that the s value lies in the lower half of its range. + pub fn normalize_s(&mut self) { + unsafe { + // Ignore return value, which indicates whether the sig + // was already normalized. We don't care. + ffi::secp256k1_ecdsa_signature_normalize( + ffi::secp256k1_context_no_precomp, + self.as_mut_c_ptr(), + self.as_c_ptr(), + ); + } + } + + /// Obtains a raw pointer suitable for use with FFI functions + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_c_ptr if you need to access the FFI layer" + )] + pub fn as_ptr(&self) -> *const ffi::Signature { + self.as_c_ptr() + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" + )] + pub fn as_mut_ptr(&mut self) -> *mut ffi::Signature { + self.as_mut_c_ptr() + } + + #[inline] + /// Serializes the signature in DER format + pub fn serialize_der(&self) -> SerializedSignature { + let mut data = [0u8; serialized_signature::MAX_LEN]; + let mut len: usize = serialized_signature::MAX_LEN; + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_der( + ffi::secp256k1_context_no_precomp, + data.as_mut_ptr(), + &mut len, + self.as_c_ptr(), + ); + debug_assert!(err == 1); + SerializedSignature::from_raw_parts(data, len) + } + } + + #[inline] + /// Serializes the signature in compact format + pub fn serialize_compact(&self) -> [u8; 64] { + let mut ret = [0u8; 64]; + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_compact( + ffi::secp256k1_context_no_precomp, + ret.as_mut_c_ptr(), + self.as_c_ptr(), + ); + debug_assert!(err == 1); + } + ret + } + + /// Verifies an ECDSA signature for `msg` using `pk` and the global + /// [`SECP256K1`] context. The signature must be normalized or + /// verification will fail (see [`Signature::normalize_s`]). + #[inline] + #[cfg(feature = "global-context")] + pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), Error> { + SECP256K1.verify_ecdsa(msg, self, pk) + } +} + +impl CPtr for Signature { + type Target = ffi::Signature; + + fn as_c_ptr(&self) -> *const Self::Target { + &self.0 + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + &mut self.0 + } +} + +/// Creates a new signature from a FFI signature +impl From<ffi::Signature> for Signature { + #[inline] + fn from(sig: ffi::Signature) -> Signature { + Signature(sig) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Signature { + fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { + if s.is_human_readable() { + s.collect_str(self) + } else { + s.serialize_bytes(&self.serialize_der()) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for Signature { + fn deserialize<D: serde::Deserializer<'de>>( + d: D, + ) -> Result<Self, D::Error> { + if d.is_human_readable() { + d.deserialize_str(crate::serde_util::FromStrVisitor::new( + "a hex string representing a DER encoded Signature", + )) + } else { + d.deserialize_bytes(crate::serde_util::BytesVisitor::new( + "raw byte stream, that represents a DER encoded Signature", + Signature::from_der, + )) + } + } +} + +impl<C: Signing> Secp256k1<C> { + fn sign_ecdsa_with_noncedata_pointer( + &self, + msg: &Message, + sk: &SecretKey, + noncedata: Option<&[u8; 32]>, + ) -> Signature { + unsafe { + let mut ret = ffi::Signature::new(); + let noncedata_ptr = match noncedata { + Some(arr) => arr.as_c_ptr() as *const _, + None => ptr::null(), + }; + // We can assume the return value because it's not possible to + // construct an invalid signature from a valid `Message` + // and `SecretKey` + assert_eq!( + ffi::secp256k1_ecdsa_sign( + self.ctx.as_ptr(), + &mut ret, + msg.as_c_ptr(), + sk.as_c_ptr(), + ffi::secp256k1_nonce_function_rfc6979, + noncedata_ptr + ), + 1 + ); + Signature::from(ret) + } + } + + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 + /// nonce Requires a signing-capable context. + pub fn sign_ecdsa(&self, msg: &Message, sk: &SecretKey) -> Signature { + self.sign_ecdsa_with_noncedata_pointer(msg, sk, None) + } + + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 + /// nonce and includes 32 bytes of noncedata in the nonce generation via + /// inclusion in one of the hash operations during nonce generation. + /// This is useful when multiple signatures are needed for the same + /// Message and SecretKey while still using RFC6979. Requires a + /// signing-capable context. + pub fn sign_ecdsa_with_noncedata( + &self, + msg: &Message, + sk: &SecretKey, + noncedata: &[u8; 32], + ) -> Signature { + self.sign_ecdsa_with_noncedata_pointer(msg, sk, Some(noncedata)) + } + + fn sign_grind_with_check( + &self, + msg: &Message, + sk: &SecretKey, + check: impl Fn(&ffi::Signature) -> bool, + ) -> Signature { + let mut entropy_p: *const ffi::types::c_void = ptr::null(); + let mut counter: u32 = 0; + let mut extra_entropy = [0u8; 32]; + loop { + unsafe { + let mut ret = ffi::Signature::new(); + // We can assume the return value because it's not possible to + // construct an invalid signature from a valid + // `Message` and `SecretKey` + assert_eq!( + ffi::secp256k1_ecdsa_sign( + self.ctx.as_ptr(), + &mut ret, + msg.as_c_ptr(), + sk.as_c_ptr(), + ffi::secp256k1_nonce_function_rfc6979, + entropy_p + ), + 1 + ); + if check(&ret) { + return Signature::from(ret); + } + + counter += 1; + extra_entropy[..4].copy_from_slice(&counter.to_le_bytes()); + entropy_p = + extra_entropy.as_c_ptr().cast::<ffi::types::c_void>(); + + // When fuzzing, these checks will usually spinloop forever, so + // just short-circuit them. + #[cfg(secp256k1_fuzz)] + return Signature::from(ret); + } + } + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 + /// nonce and "grinds" the nonce by passing extra entropy if necessary + /// to produce a signature that is less than 71 - `bytes_to_grind` + /// bytes. The number of signing operation performed by this function is + /// exponential in the number of bytes grinded. + /// Requires a signing capable context. + pub fn sign_ecdsa_grind_r( + &self, + msg: &Message, + sk: &SecretKey, + bytes_to_grind: usize, + ) -> Signature { + let len_check = + |s: &ffi::Signature| der_length_check(s, 71 - bytes_to_grind); + self.sign_grind_with_check(msg, sk, len_check) + } + + /// Constructs a signature for `msg` using the secret key `sk`, RFC6979 + /// nonce and "grinds" the nonce by passing extra entropy if necessary + /// to produce a signature that is less than 71 bytes and compatible + /// with the low r signature implementation of bitcoin core. In average, + /// this function will perform two signing operations. + /// Requires a signing capable context. + pub fn sign_ecdsa_low_r(&self, msg: &Message, sk: &SecretKey) -> Signature { + self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit) + } +} + +impl<C: Verification> Secp256k1<C> { + /// Checks that `sig` is a valid ECDSA signature for `msg` using the public + /// key `pubkey`. Returns `Ok(())` on success. Note that this function + /// cannot be used for Bitcoin consensus checking since there may exist + /// signatures which OpenSSL would verify but not libsecp256k1, or + /// vice-versa. Requires a verify-capable context. + /// + /// ```rust + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// # use ecash_secp256k1::{rand, Secp256k1, Message, Error}; + /// # + /// # let secp = Secp256k1::new(); + /// # let (secret_key, public_key) = + /// # secp.generate_keypair(&mut rand::thread_rng()); + /// # + /// let message = Message::from_digest_slice(&[0xab; 32]).expect("32bytes"); + /// let sig = secp.sign_ecdsa(&message, &secret_key); + /// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(())); + /// + /// let message = Message::from_digest_slice(&[0xcd; 32]).expect("32bytes"); + /// assert_eq!( + /// secp.verify_ecdsa(&message, &sig, &public_key), + /// Err(Error::IncorrectSignature) + /// ); + /// # } + /// ``` + #[inline] + pub fn verify_ecdsa( + &self, + msg: &Message, + sig: &Signature, + pk: &PublicKey, + ) -> Result<(), Error> { + unsafe { + if ffi::secp256k1_ecdsa_verify( + self.ctx.as_ptr(), + sig.as_c_ptr(), + msg.as_c_ptr(), + pk.as_c_ptr(), + ) == 0 + { + Err(Error::IncorrectSignature) + } else { + Ok(()) + } + } + } +} + +pub(crate) fn compact_sig_has_zero_first_bit(sig: &ffi::Signature) -> bool { + let mut compact = [0u8; 64]; + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_compact( + ffi::secp256k1_context_no_precomp, + compact.as_mut_c_ptr(), + sig, + ); + debug_assert!(err == 1); + } + compact[0] < 0x80 +} + +pub(crate) fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool { + let mut ser_ret = [0u8; 72]; + let mut len: usize = ser_ret.len(); + unsafe { + let err = ffi::secp256k1_ecdsa_signature_serialize_der( + ffi::secp256k1_context_no_precomp, + ser_ret.as_mut_c_ptr(), + &mut len, + sig, + ); + debug_assert!(err == 1); + } + len <= max_len +} + +#[cfg(test)] +mod tests { + use crate::ecdsa::Signature; + use crate::Scalar; + + #[test] + fn test_from_compact_min_r_and_min_s() { + // From libsecp256k1: "The signature must consist of a 32-byte big + // endian R value, followed by a 32-byte big endian S value. If + // R or S fall outside of [0..order-1], the encoding is invalid. + // R and S with value 0 are allowed in the encoding." + let r = Scalar::ZERO; + let s = Scalar::ZERO; + let mut bytes: [u8; 64] = [0; 64]; + bytes[..32].copy_from_slice(&r.to_be_bytes()); + bytes[32..].copy_from_slice(&s.to_be_bytes()); + + assert!(Signature::from_compact(&bytes).is_ok()) + } + + #[test] + fn test_from_compact_max_r_and_max_s() { + let r = Scalar::MAX; + let s = Scalar::MAX; + let mut bytes: [u8; 64] = [0; 64]; + bytes[..32].copy_from_slice(&r.to_be_bytes()); + bytes[32..].copy_from_slice(&s.to_be_bytes()); + + assert!(Signature::from_compact(&bytes).is_ok()) + } + + #[test] + fn test_from_compact_invalid_r() { + let r = Scalar::MAX; + let s = Scalar::MAX; + let mut bytes: [u8; 64] = [0; 64]; + bytes[..32].copy_from_slice(&r.to_be_bytes()); + bytes[32..].copy_from_slice(&s.to_be_bytes()); + bytes[31] += 1; + + assert!(Signature::from_compact(&bytes).is_err()) + } + + #[test] + fn test_from_compact_invalid_s() { + let r = Scalar::MAX; + let s = Scalar::MAX; + let mut bytes: [u8; 64] = [0; 64]; + bytes[..32].copy_from_slice(&r.to_be_bytes()); + bytes[32..].copy_from_slice(&s.to_be_bytes()); + bytes[63] += 1; + + assert!(Signature::from_compact(&bytes).is_err()) + } +} diff --git a/modules/ecash-secp256k1/src/ecdsa/recovery.rs b/modules/ecash-secp256k1/src/ecdsa/recovery.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/ecdsa/recovery.rs @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Provides a signing function that allows recovering the public key from the +//! signature. + +use core::ptr; + +use self::super_ffi::CPtr; +use super::ffi as super_ffi; +use crate::ecdsa::Signature; +use crate::ffi::recovery as ffi; +use crate::{key, Error, Message, Secp256k1, Signing, Verification}; + +/// A tag used for recovering the public key from a compact signature. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum RecoveryId { + /// Signature recovery ID 0 + Zero, + /// Signature recovery ID 1 + One, + /// Signature recovery ID 2 + Two, + /// Signature recovery ID 3 + Three, +} + +impl TryFrom<i32> for RecoveryId { + type Error = Error; + + #[inline] + fn try_from(id: i32) -> Result<RecoveryId, Error> { + match id { + 0 => Ok(RecoveryId::Zero), + 1 => Ok(RecoveryId::One), + 2 => Ok(RecoveryId::Two), + 3 => Ok(RecoveryId::Three), + _ => Err(Error::InvalidRecoveryId), + } + } +} + +impl From<RecoveryId> for i32 { + #[inline] + fn from(val: RecoveryId) -> Self { + match val { + RecoveryId::Zero => 0, + RecoveryId::One => 1, + RecoveryId::Two => 2, + RecoveryId::Three => 3, + } + } +} + +/// An ECDSA signature with a recovery ID for pubkey recovery. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)] +pub struct RecoverableSignature(ffi::RecoverableSignature); + +impl RecoverableSignature { + #[inline] + /// Converts a compact-encoded byte slice to a signature. This + /// representation is nonstandard and defined by the libsecp256k1 library. + pub fn from_compact( + data: &[u8], + recid: RecoveryId, + ) -> Result<RecoverableSignature, Error> { + if data.is_empty() { + return Err(Error::InvalidSignature); + } + + let mut ret = ffi::RecoverableSignature::new(); + + unsafe { + if data.len() != 64 { + Err(Error::InvalidSignature) + } else if ffi::secp256k1_ecdsa_recoverable_signature_parse_compact( + super_ffi::secp256k1_context_no_precomp, + &mut ret, + data.as_c_ptr(), + recid.into(), + ) == 1 + { + Ok(RecoverableSignature(ret)) + } else { + Err(Error::InvalidSignature) + } + } + } + + /// Obtains a raw pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_c_ptr if you need to access the FFI layer" + )] + pub fn as_ptr(&self) -> *const ffi::RecoverableSignature { + self.as_c_ptr() + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" + )] + pub fn as_mut_ptr(&mut self) -> *mut ffi::RecoverableSignature { + self.as_mut_c_ptr() + } + + #[inline] + /// Serializes the recoverable signature in compact format. + pub fn serialize_compact(&self) -> (RecoveryId, [u8; 64]) { + let mut ret = [0u8; 64]; + let mut recid = RecoveryId::Zero.into(); + unsafe { + let err = + ffi::secp256k1_ecdsa_recoverable_signature_serialize_compact( + super_ffi::secp256k1_context_no_precomp, + ret.as_mut_c_ptr(), + &mut recid, + self.as_c_ptr(), + ); + assert!(err == 1); + } + ( + recid.try_into().expect("ffi returned invalid RecoveryId!"), + ret, + ) + } + + /// Converts a recoverable signature to a non-recoverable one (this is + /// needed for verification). + #[inline] + pub fn to_standard(&self) -> Signature { + unsafe { + let mut ret = super_ffi::Signature::new(); + let err = ffi::secp256k1_ecdsa_recoverable_signature_convert( + super_ffi::secp256k1_context_no_precomp, + &mut ret, + self.as_c_ptr(), + ); + assert!(err == 1); + Signature(ret) + } + } + + /// Determines the public key for which this [`Signature`] is valid for + /// `msg`. Requires a verify-capable context. + #[inline] + #[cfg(feature = "global-context")] + pub fn recover(&self, msg: &Message) -> Result<key::PublicKey, Error> { + crate::SECP256K1.recover_ecdsa(msg, self) + } +} + +impl CPtr for RecoverableSignature { + type Target = ffi::RecoverableSignature; + + fn as_c_ptr(&self) -> *const Self::Target { + &self.0 + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + &mut self.0 + } +} + +/// Creates a new recoverable signature from a FFI one. +impl From<ffi::RecoverableSignature> for RecoverableSignature { + #[inline] + fn from(sig: ffi::RecoverableSignature) -> RecoverableSignature { + RecoverableSignature(sig) + } +} + +impl<C: Signing> Secp256k1<C> { + fn sign_ecdsa_recoverable_with_noncedata_pointer( + &self, + msg: &Message, + sk: &key::SecretKey, + noncedata_ptr: *const super_ffi::types::c_void, + ) -> RecoverableSignature { + let mut ret = ffi::RecoverableSignature::new(); + unsafe { + // We can assume the return value because it's not possible to + // construct an invalid signature from a valid `Message` + // and `SecretKey` + assert_eq!( + ffi::secp256k1_ecdsa_sign_recoverable( + self.ctx.as_ptr(), + &mut ret, + msg.as_c_ptr(), + sk.as_c_ptr(), + super_ffi::secp256k1_nonce_function_rfc6979, + noncedata_ptr + ), + 1 + ); + } + + RecoverableSignature::from(ret) + } + + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 + /// nonce Requires a signing-capable context. + pub fn sign_ecdsa_recoverable( + &self, + msg: &Message, + sk: &key::SecretKey, + ) -> RecoverableSignature { + self.sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, ptr::null()) + } + + /// Constructs a signature for `msg` using the secret key `sk` and RFC6979 + /// nonce and includes 32 bytes of noncedata in the nonce generation via + /// inclusion in one of the hash operations during nonce generation. + /// This is useful when multiple signatures are needed for the same + /// Message and SecretKey while still using RFC6979. Requires a + /// signing-capable context. + pub fn sign_ecdsa_recoverable_with_noncedata( + &self, + msg: &Message, + sk: &key::SecretKey, + noncedata: &[u8; 32], + ) -> RecoverableSignature { + let noncedata_ptr = + noncedata.as_ptr() as *const super_ffi::types::c_void; + self.sign_ecdsa_recoverable_with_noncedata_pointer( + msg, + sk, + noncedata_ptr, + ) + } +} + +impl<C: Verification> Secp256k1<C> { + /// Determines the public key for which `sig` is a valid signature for + /// `msg`. Requires a verify-capable context. + pub fn recover_ecdsa( + &self, + msg: &Message, + sig: &RecoverableSignature, + ) -> Result<key::PublicKey, Error> { + unsafe { + let mut pk = super_ffi::PublicKey::new(); + if ffi::secp256k1_ecdsa_recover( + self.ctx.as_ptr(), + &mut pk, + sig.as_c_ptr(), + msg.as_c_ptr(), + ) != 1 + { + return Err(Error::InvalidSignature); + } + Ok(key::PublicKey::from(pk)) + } + } +} + +#[cfg(test)] +#[allow(unused_imports)] +mod tests { + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + use super::{RecoverableSignature, RecoveryId}; + use crate::constants::ONE; + use crate::{Error, Message, Secp256k1, SecretKey}; + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn capabilities() { + let sign = Secp256k1::signing_only(); + let vrfy = Secp256k1::verification_only(); + let full = Secp256k1::new(); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest_slice(&msg).unwrap(); + + // Try key generation + let (sk, pk) = full.generate_keypair(&mut rand::thread_rng()); + + // Try signing + assert_eq!( + sign.sign_ecdsa_recoverable(&msg, &sk), + full.sign_ecdsa_recoverable(&msg, &sk) + ); + let sigr = full.sign_ecdsa_recoverable(&msg, &sk); + + // Try pk recovery + assert!(vrfy.recover_ecdsa(&msg, &sigr).is_ok()); + assert!(full.recover_ecdsa(&msg, &sigr).is_ok()); + + assert_eq!( + vrfy.recover_ecdsa(&msg, &sigr), + full.recover_ecdsa(&msg, &sigr) + ); + assert_eq!(full.recover_ecdsa(&msg, &sigr), Ok(pk)); + } + + #[test] + fn recid_sanity_check() { + let one = RecoveryId::One; + assert_eq!(one, one.clone()); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(all(feature = "rand", feature = "std"))] + #[rustfmt::skip] + fn sign() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let sk = SecretKey::from_slice(&ONE).unwrap(); + let msg = Message::from_digest_slice(&ONE).unwrap(); + + let sig = s.sign_ecdsa_recoverable(&msg, &sk); + + assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[ + 0x5c, 0xbb, 0x18, 0xc8, 0x0e, 0x78, 0x6a, 0x4d, + 0x07, 0xdd, 0x67, 0xcb, 0x76, 0x79, 0xe7, 0x16, + 0xde, 0xbe, 0x94, 0xf5, 0xe0, 0x7a, 0x42, 0x46, + 0x48, 0x4b, 0x2e, 0x0d, 0x81, 0x94, 0x1c, 0xab, + 0x4f, 0x1f, 0xbc, 0x18, 0x79, 0x14, 0x48, 0xa9, + 0xb9, 0x87, 0xc4, 0x7c, 0x78, 0x73, 0x4c, 0xe5, + 0x9b, 0x90, 0x07, 0x86, 0x06, 0x3b, 0x67, 0x97, + 0x8f, 0x06, 0x86, 0x45, 0xb0, 0x86, 0x9a, 0xd8], + RecoveryId::One)) + } + + #[test] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(all(feature = "rand", feature = "std"))] + #[rustfmt::skip] + fn sign_with_noncedata() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let sk = SecretKey::from_slice(&ONE).unwrap(); + let msg = Message::from_digest_slice(&ONE).unwrap(); + let nonce = [42u8; 32]; + + let sig = s.sign_ecdsa_recoverable_with_noncedata(&msg, &sk, &nonce); + + assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[ + 0x83, 0xd7, 0x93, 0x94, 0x4c, 0x73, 0xba, 0xf1, + 0x9c, 0x32, 0xfa, 0x40, 0x7f, 0xa5, 0x15, 0xab, + 0x30, 0x2c, 0xd6, 0xe9, 0x76, 0xcc, 0x82, 0x0b, + 0x9d, 0x85, 0x37, 0x70, 0x9c, 0xc1, 0x44, 0xbb, + 0x1a, 0x30, 0x07, 0xe8, 0x8f, 0xf0, 0x10, 0x8a, + 0x5e, 0x9c, 0xf7, 0xab, 0x0b, 0x49, 0x94, 0x8e, + 0xc0, 0x2c, 0x7f, 0xdb, 0x47, 0x20, 0xd2, 0x21, + 0x9d, 0xe5, 0x6a, 0x98, 0x24, 0x8f, 0x10, 0xf7], + RecoveryId::One)) + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn sign_and_verify_fail() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest_slice(&msg).unwrap(); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + + let sigr = s.sign_ecdsa_recoverable(&msg, &sk); + let sig = sigr.to_standard(); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest_slice(&msg).unwrap(); + assert_eq!( + s.verify_ecdsa(&msg, &sig, &pk), + Err(Error::IncorrectSignature) + ); + + let recovered_key = s.recover_ecdsa(&msg, &sigr).unwrap(); + assert!(recovered_key != pk); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn sign_with_recovery() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest_slice(&msg).unwrap(); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + + let sig = s.sign_ecdsa_recoverable(&msg, &sk); + + assert_eq!(s.recover_ecdsa(&msg, &sig), Ok(pk)); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn sign_with_recovery_and_noncedata() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest_slice(&msg).unwrap(); + + let noncedata = [42u8; 32]; + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + + let sig = + s.sign_ecdsa_recoverable_with_noncedata(&msg, &sk, &noncedata); + + assert_eq!(s.recover_ecdsa(&msg, &sig), Ok(pk)); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn bad_recovery() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let msg = Message::from_digest_slice(&[0x55; 32]).unwrap(); + + // Zero is not a valid sig + let sig = + RecoverableSignature::from_compact(&[0; 64], RecoveryId::Zero) + .unwrap(); + assert_eq!(s.recover_ecdsa(&msg, &sig), Err(Error::InvalidSignature)); + // ...but 111..111 is + let sig = + RecoverableSignature::from_compact(&[1; 64], RecoveryId::Zero) + .unwrap(); + assert!(s.recover_ecdsa(&msg, &sig).is_ok()); + } + + #[test] + fn test_debug_output() { + #[rustfmt::skip] + let sig = RecoverableSignature::from_compact(&[ + 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, + 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, + 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, + 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, + 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, + 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, + 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, + 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89], + RecoveryId::One).unwrap(); + assert_eq!( + &format!("{:?}", sig), + "RecoverableSignature(6673ffad2147741f04772b6f921f0ba6af0c1e77fc439\ + e65c36dedf4092e88984c1a971652e0ada880120ef8025e709fff2080c4a39aae0\ + 68d12eed009b68c8901)", + ); + } + + #[test] + fn test_recov_sig_serialize_compact() { + let recid_in = RecoveryId::One; + #[rustfmt::skip] + let bytes_in = &[ + 0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f, + 0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6, + 0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65, + 0xc3, 0x6d, 0xed, 0xf4, 0x09, 0x2e, 0x88, 0x98, + 0x4c, 0x1a, 0x97, 0x16, 0x52, 0xe0, 0xad, 0xa8, + 0x80, 0x12, 0x0e, 0xf8, 0x02, 0x5e, 0x70, 0x9f, + 0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06, + 0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89]; + let sig = + RecoverableSignature::from_compact(bytes_in, recid_in).unwrap(); + let (recid_out, bytes_out) = sig.serialize_compact(); + assert_eq!(recid_in, recid_out); + assert_eq!(&bytes_in[..], &bytes_out[..]); + } + + #[test] + fn test_recov_id_conversion_between_i32() { + assert!(RecoveryId::try_from(-1i32).is_err()); + assert!(RecoveryId::try_from(0i32).is_ok()); + assert!(RecoveryId::try_from(1i32).is_ok()); + assert!(RecoveryId::try_from(2i32).is_ok()); + assert!(RecoveryId::try_from(3i32).is_ok()); + assert!(RecoveryId::try_from(4i32).is_err()); + let id0 = RecoveryId::Zero; + assert_eq!(Into::<i32>::into(id0), 0i32); + let id1 = RecoveryId::One; + assert_eq!(Into::<i32>::into(id1), 1i32); + } +} + +#[cfg(bench)] +// Currently only a single bench that requires "rand" + "std". +#[cfg(all(feature = "rand", feature = "std"))] +mod benches { + use test::{black_box, Bencher}; + + use super::{Message, Secp256k1}; + + #[bench] + pub fn bench_recover(bh: &mut Bencher) { + let s = Secp256k1::new(); + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest_slice(&msg).unwrap(); + let (sk, _) = s.generate_keypair(&mut rand::thread_rng()); + let sig = s.sign_ecdsa_recoverable(&msg, &sk); + + bh.iter(|| { + let res = s.recover_ecdsa(&msg, &sig).unwrap(); + black_box(res); + }); + } +} diff --git a/modules/ecash-secp256k1/src/ecdsa/serialized_signature.rs b/modules/ecash-secp256k1/src/ecdsa/serialized_signature.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/ecdsa/serialized_signature.rs @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Implements [`SerializedSignature`] and related types. +//! +//! DER-serialized signatures have the issue that they can have different +//! lengths. We want to avoid using `Vec` since that would require allocations +//! making the code slower and unable to run on platforms without allocator. We +//! implement a special type to encapsulate serialized signatures and since it's +//! a bit more complicated it has its own module. + +use core::borrow::Borrow; +use core::{fmt, ops}; + +pub use into_iter::IntoIter; + +use super::Signature; +use crate::Error; + +pub(crate) const MAX_LEN: usize = 72; + +/// A DER serialized Signature +#[derive(Copy, Clone)] +pub struct SerializedSignature { + data: [u8; MAX_LEN], + len: usize, +} + +impl fmt::Debug for SerializedSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for SerializedSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for v in self { + write!(f, "{:02x}", v)?; + } + Ok(()) + } +} + +impl PartialEq for SerializedSignature { + #[inline] + fn eq(&self, other: &SerializedSignature) -> bool { + **self == **other + } +} + +impl PartialEq<[u8]> for SerializedSignature { + #[inline] + fn eq(&self, other: &[u8]) -> bool { + **self == *other + } +} + +impl PartialEq<SerializedSignature> for [u8] { + #[inline] + fn eq(&self, other: &SerializedSignature) -> bool { + *self == **other + } +} + +impl PartialOrd for SerializedSignature { + fn partial_cmp( + &self, + other: &SerializedSignature, + ) -> Option<core::cmp::Ordering> { + Some((**self).cmp(&**other)) + } +} + +impl Ord for SerializedSignature { + fn cmp(&self, other: &SerializedSignature) -> core::cmp::Ordering { + (**self).cmp(&**other) + } +} + +impl PartialOrd<[u8]> for SerializedSignature { + fn partial_cmp(&self, other: &[u8]) -> Option<core::cmp::Ordering> { + (**self).partial_cmp(other) + } +} + +impl PartialOrd<SerializedSignature> for [u8] { + fn partial_cmp( + &self, + other: &SerializedSignature, + ) -> Option<core::cmp::Ordering> { + self.partial_cmp(&**other) + } +} + +impl core::hash::Hash for SerializedSignature { + fn hash<H: core::hash::Hasher>(&self, state: &mut H) { + (**self).hash(state) + } +} + +impl AsRef<[u8]> for SerializedSignature { + #[inline] + fn as_ref(&self) -> &[u8] { + self + } +} + +impl Borrow<[u8]> for SerializedSignature { + #[inline] + fn borrow(&self) -> &[u8] { + self + } +} + +impl ops::Deref for SerializedSignature { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.data[..self.len] + } +} + +impl Eq for SerializedSignature {} + +impl IntoIterator for SerializedSignature { + type IntoIter = IntoIter; + type Item = u8; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self) + } +} + +impl<'a> IntoIterator for &'a SerializedSignature { + type IntoIter = core::slice::Iter<'a, u8>; + type Item = &'a u8; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl From<Signature> for SerializedSignature { + fn from(value: Signature) -> Self { + Self::from_signature(&value) + } +} + +impl<'a> From<&'a Signature> for SerializedSignature { + fn from(value: &'a Signature) -> Self { + Self::from_signature(value) + } +} + +impl TryFrom<SerializedSignature> for Signature { + type Error = Error; + + fn try_from(value: SerializedSignature) -> Result<Self, Self::Error> { + value.to_signature() + } +} + +impl<'a> TryFrom<&'a SerializedSignature> for Signature { + type Error = Error; + + fn try_from(value: &'a SerializedSignature) -> Result<Self, Self::Error> { + value.to_signature() + } +} + +impl SerializedSignature { + /// Creates `SerializedSignature` from data and length. + /// + /// ## Panics + /// + /// If `len` > `MAX_LEN` + #[inline] + pub(crate) fn from_raw_parts(data: [u8; MAX_LEN], len: usize) -> Self { + assert!( + len <= MAX_LEN, + "attempt to set length to {} but the maximum is {}", + len, + MAX_LEN + ); + SerializedSignature { data, len } + } + + /// Get the capacity of the underlying data buffer. + #[deprecated = "This always returns 72"] + #[inline] + pub fn capacity(&self) -> usize { + self.data.len() + } + + /// Get the len of the used data. + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Set the length of the object. + #[inline] + pub(crate) fn set_len_unchecked(&mut self, len: usize) { + self.len = len; + } + + /// Convert the serialized signature into the Signature struct. + /// (This DER deserializes it) + #[inline] + pub fn to_signature(&self) -> Result<Signature, Error> { + Signature::from_der(self) + } + + /// Create a SerializedSignature from a Signature. + /// (this DER serializes it) + #[inline] + pub fn from_signature(sig: &Signature) -> SerializedSignature { + sig.serialize_der() + } + + /// Check if the space is zero. + #[deprecated = "This always returns false"] + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +/// Separate mod to prevent outside code accidentally breaking invariants. +mod into_iter { + use super::*; + + /// Owned iterator over the bytes of [`SerializedSignature`] + /// + /// Created by [`IntoIterator::into_iter`] method. + // allowed because of https://github.com/rust-lang/rust/issues/98348 + #[allow(missing_copy_implementations)] + #[derive(Debug, Clone)] + pub struct IntoIter { + signature: SerializedSignature, + // invariant: pos <= signature.len() + pos: usize, + } + + impl IntoIter { + #[inline] + pub(crate) fn new(signature: SerializedSignature) -> Self { + IntoIter { + signature, + // for all unsigned n: 0 <= n + pos: 0, + } + } + + /// Returns the remaining bytes as a slice. + /// + /// This method is analogous to [`core::slice::Iter::as_slice`]. + #[inline] + pub fn as_slice(&self) -> &[u8] { + &self.signature[self.pos..] + } + } + + impl Iterator for IntoIter { + type Item = u8; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + let byte = *self.signature.get(self.pos)?; + // can't overflow or break invariant because if pos is too large we + // return early + self.pos += 1; + Some(byte) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + // can't underlflow thanks to the invariant + let len = self.signature.len() - self.pos; + (len, Some(len)) + } + + // override for speed + #[inline] + fn nth(&mut self, n: usize) -> Option<Self::Item> { + if n >= self.len() { + // upholds invariant becasue the values will be equal + self.pos = self.signature.len(); + None + } else { + // if n < signtature.len() - self.pos then n + self.pos < + // signature.len() which neither overflows nor + // breaks the invariant + self.pos += n; + self.next() + } + } + } + + impl ExactSizeIterator for IntoIter {} + + impl core::iter::FusedIterator for IntoIter {} + + impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option<Self::Item> { + if self.pos == self.signature.len() { + return None; + } + + // if len is 0 then pos is also 0 thanks to the invariant so we + // would return before we reach this + let new_len = self.signature.len() - 1; + let byte = self.signature[new_len]; + self.signature.set_len_unchecked(new_len); + Some(byte) + } + } +} + +#[cfg(test)] +mod tests { + use super::{SerializedSignature, MAX_LEN}; + + #[test] + fn iterator_ops_are_homomorphic() { + let mut fake_signature_data = [0; MAX_LEN]; + for (i, byte) in fake_signature_data.iter_mut().enumerate() { + *byte = i as u8; // cast ok because MAX_LEN fits in u8. + } + + let fake_signature = SerializedSignature { + data: fake_signature_data, + len: MAX_LEN, + }; + + let mut iter1 = fake_signature.into_iter(); + let mut iter2 = fake_signature.iter(); + + // while let so we can compare size_hint and as_slice + while let (Some(a), Some(b)) = (iter1.next(), iter2.next()) { + assert_eq!(a, *b); + assert_eq!(iter1.size_hint(), iter2.size_hint()); + assert_eq!(iter1.as_slice(), iter2.as_slice()); + } + + let mut iter1 = fake_signature.into_iter(); + let mut iter2 = fake_signature.iter(); + + // manual next_back instead of rev() so that we can check as_slice() + // if next_back is implemented correctly then rev() is also correct - + // provided by `core` + while let (Some(a), Some(b)) = (iter1.next_back(), iter2.next_back()) { + assert_eq!(a, *b); + assert_eq!(iter1.size_hint(), iter2.size_hint()); + assert_eq!(iter1.as_slice(), iter2.as_slice()); + } + } +} diff --git a/modules/ecash-secp256k1/src/key.rs b/modules/ecash-secp256k1/src/key.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/key.rs @@ -0,0 +1,2934 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Public and secret keys. + +use core::ops::{self, BitXor}; +use core::{fmt, ptr, str}; + +#[cfg(feature = "serde")] +use serde::ser::SerializeTuple; + +use crate::ffi::types::c_uint; +use crate::ffi::{self, CPtr}; +use crate::Error::{ + self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey, +}; +#[cfg(feature = "hashes")] +#[allow(deprecated)] +use crate::ThirtyTwoByteHash; +#[cfg(feature = "global-context")] +use crate::SECP256K1; +use crate::{ + constants, ecdsa, from_hex, schnorr, Message, Scalar, Secp256k1, Signing, + Verification, +}; + +/// Secret key - a 256-bit key used to create ECDSA and Taproot signatures. +/// +/// This value should be generated using a [cryptographically secure +/// pseudorandom number generator]. +/// +/// # Side channel attacks +/// +/// We have attempted to reduce the side channel attack surface by implementing +/// a constant time `eq` method. For similar reasons we explicitly do not +/// implement `PartialOrd`, `Ord`, or `Hash` on `SecretKey`. If you really want +/// to order secrets keys then you can use `AsRef` to get at the underlying +/// bytes and compare them - however this is almost certainly a bad idea. +/// +/// # Serde support +/// +/// Implements de/serialization with the `serde` feature enabled. We treat the +/// byte value as a tuple of 32 `u8`s for non-human-readable formats. This +/// representation is optimal for for some formats (e.g. [`bincode`]) however +/// other formats may be less optimal (e.g. [`cbor`]). +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # #[cfg(all(feature = "rand", feature = "std"))] { +/// use ecash_secp256k1::{rand, Secp256k1, SecretKey}; +/// +/// let secp = Secp256k1::new(); +/// let secret_key = SecretKey::new(&mut rand::thread_rng()); +/// # } +/// ``` +/// [`bincode`]: https://docs.rs/bincode +/// [`cbor`]: https://docs.rs/cbor +/// [cryptographically secure pseudorandom number generator]: +/// https://en.wikipedia.org/wiki/\ +/// Cryptographically_secure_pseudorandom_number_generator +#[derive(Copy, Clone)] +pub struct SecretKey([u8; constants::SECRET_KEY_SIZE]); +impl_display_secret!(SecretKey); +impl_non_secure_erase!(SecretKey, 0, [1u8; constants::SECRET_KEY_SIZE]); + +impl PartialEq for SecretKey { + /// This implementation is designed to be constant time to help prevent side + /// channel attacks. + #[inline] + fn eq(&self, other: &Self) -> bool { + let accum = self + .0 + .iter() + .zip(&other.0) + .fold(0, |accum, (a, b)| accum | a ^ b); + unsafe { core::ptr::read_volatile(&accum) == 0 } + } +} + +impl Eq for SecretKey {} + +impl AsRef<[u8; constants::SECRET_KEY_SIZE]> for SecretKey { + /// Gets a reference to the underlying array. + /// + /// # Side channel attacks + /// + /// Using ordering functions (`PartialOrd`/`Ord`) on a reference to secret + /// keys leaks data because the implementations are not constant time. + /// Doing so will make your code vulnerable to side channel attacks. + /// [`SecretKey::eq`] is implemented using a constant time algorithm, + /// please consider using it to do comparisons of secret keys. + #[inline] + fn as_ref(&self) -> &[u8; constants::SECRET_KEY_SIZE] { + let SecretKey(dat) = self; + dat + } +} + +impl<I> ops::Index<I> for SecretKey +where + [u8]: ops::Index<I>, +{ + type Output = <[u8] as ops::Index<I>>::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + &self.0[index] + } +} + +impl ffi::CPtr for SecretKey { + type Target = u8; + + fn as_c_ptr(&self) -> *const Self::Target { + let SecretKey(dat) = self; + dat.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + let &mut SecretKey(ref mut dat) = self; + dat.as_mut_ptr() + } +} + +impl str::FromStr for SecretKey { + type Err = Error; + + fn from_str(s: &str) -> Result<SecretKey, Error> { + let mut res = [0u8; constants::SECRET_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SECRET_KEY_SIZE) => SecretKey::from_byte_array(&res), + _ => Err(Error::InvalidSecretKey), + } + } +} + +/// Public key - used to verify ECDSA signatures and to do Taproot tweaks. +/// +/// # Serde support +/// +/// Implements de/serialization with the `serde` feature enabled. We treat the +/// byte value as a tuple of 33 `u8`s for non-human-readable formats. This +/// representation is optimal for for some formats (e.g. [`bincode`]) however +/// other formats may be less optimal (e.g. [`cbor`]). +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # #[cfg(feature = "alloc")] { +/// use ecash_secp256k1::{PublicKey, Secp256k1, SecretKey}; +/// +/// let secp = Secp256k1::new(); +/// let secret_key = SecretKey::from_byte_array(&[0xcd; 32]) +/// .expect("32 bytes, within curve order"); +/// let public_key = PublicKey::from_secret_key(&secp, &secret_key); +/// # } +/// ``` +/// [`bincode`]: https://docs.rs/bincode +/// [`cbor`]: https://docs.rs/cbor +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct PublicKey(ffi::PublicKey); +impl_fast_comparisons!(PublicKey); + +impl fmt::LowerHex for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let ser = self.serialize(); + for ch in &ser[..] { + write!(f, "{:02x}", *ch)?; + } + Ok(()) + } +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for PublicKey { + type Err = Error; + + fn from_str(s: &str) -> Result<PublicKey, Error> { + let mut res = [0u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::PUBLIC_KEY_SIZE) => { + let bytes: [u8; constants::PUBLIC_KEY_SIZE] = + res[0..constants::PUBLIC_KEY_SIZE].try_into().unwrap(); + PublicKey::from_byte_array_compressed(&bytes) + } + Ok(constants::UNCOMPRESSED_PUBLIC_KEY_SIZE) => { + PublicKey::from_byte_array_uncompressed(&res) + } + _ => Err(Error::InvalidPublicKey), + } + } +} + +impl SecretKey { + /// Generates a new random secret key. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "std", feature = "rand"))] { + /// use ecash_secp256k1::{rand, SecretKey}; + /// let secret_key = SecretKey::new(&mut rand::thread_rng()); + /// # } + /// ``` + #[inline] + #[cfg(feature = "rand")] + pub fn new<R: rand::Rng + ?Sized>(rng: &mut R) -> SecretKey { + let mut data = crate::random_32_bytes(rng); + unsafe { + while ffi::secp256k1_ec_seckey_verify( + ffi::secp256k1_context_no_precomp, + data.as_c_ptr(), + ) == 0 + { + data = crate::random_32_bytes(rng); + } + } + SecretKey(data) + } + + /// Converts a 32-byte slice to a secret key. + /// + /// # Examples + /// + /// ``` + /// use ecash_secp256k1::SecretKey; + /// let sk = SecretKey::from_slice(&[0xcd; 32]) + /// .expect("32 bytes, within curve order"); + /// ``` + #[deprecated(since = "TBD", note = "Use `from_byte_array` instead.")] + #[inline] + pub fn from_slice(data: &[u8]) -> Result<SecretKey, Error> { + match <[u8; constants::SECRET_KEY_SIZE]>::try_from(data) { + Ok(data) => Self::from_byte_array(&data), + Err(_) => Err(InvalidSecretKey), + } + } + + /// Converts a 32-byte array to a secret key. + /// + /// # Examples + /// + /// ``` + /// use ecash_secp256k1::SecretKey; + /// let sk = SecretKey::from_byte_array(&[0xcd; 32]) + /// .expect("32 bytes, within curve order"); + /// ``` + #[inline] + pub fn from_byte_array( + data: &[u8; constants::SECRET_KEY_SIZE], + ) -> Result<SecretKey, Error> { + unsafe { + if ffi::secp256k1_ec_seckey_verify( + ffi::secp256k1_context_no_precomp, + data.as_c_ptr(), + ) == 0 + { + return Err(InvalidSecretKey); + } + } + Ok(SecretKey(*data)) + } + + /// Creates a new secret key using data from BIP-340 [`Keypair`]. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{rand, Keypair, Secp256k1, SecretKey}; + /// + /// let secp = Secp256k1::new(); + /// let keypair = Keypair::new(&secp, &mut rand::thread_rng()); + /// let secret_key = SecretKey::from_keypair(&keypair); + /// # } + /// ``` + #[inline] + pub fn from_keypair(keypair: &Keypair) -> Self { + let mut sk = [0u8; constants::SECRET_KEY_SIZE]; + unsafe { + let ret = ffi::secp256k1_keypair_sec( + ffi::secp256k1_context_no_precomp, + sk.as_mut_c_ptr(), + keypair.as_c_ptr(), + ); + debug_assert_eq!(ret, 1); + } + SecretKey(sk) + } + + /// Returns the secret key as a byte value. + #[inline] + pub fn secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] { + self.0 + } + + /// Negates the secret key. + #[inline] + #[must_use = "you forgot to use the negated secret key"] + pub fn negate(mut self) -> SecretKey { + unsafe { + let res = ffi::secp256k1_ec_seckey_negate( + ffi::secp256k1_context_no_precomp, + self.as_mut_c_ptr(), + ); + debug_assert_eq!(res, 1); + } + self + } + + /// Tweaks a [`SecretKey`] by adding `tweak` modulo the curve order. + /// + /// # Errors + /// + /// Returns an error if the resulting key would be invalid. + #[inline] + pub fn add_tweak(mut self, tweak: &Scalar) -> Result<SecretKey, Error> { + unsafe { + if ffi::secp256k1_ec_seckey_tweak_add( + ffi::secp256k1_context_no_precomp, + self.as_mut_c_ptr(), + tweak.as_c_ptr(), + ) != 1 + { + Err(Error::InvalidTweak) + } else { + Ok(self) + } + } + } + + /// Tweaks a [`SecretKey`] by multiplying by `tweak` modulo the curve order. + /// + /// # Errors + /// + /// Returns an error if the resulting key would be invalid. + #[inline] + pub fn mul_tweak(mut self, tweak: &Scalar) -> Result<SecretKey, Error> { + unsafe { + if ffi::secp256k1_ec_seckey_tweak_mul( + ffi::secp256k1_context_no_precomp, + self.as_mut_c_ptr(), + tweak.as_c_ptr(), + ) != 1 + { + Err(Error::InvalidTweak) + } else { + Ok(self) + } + } + } + + /// Constructs an ECDSA signature for `msg` using the global [`SECP256K1`] + /// context. + #[inline] + #[cfg(feature = "global-context")] + pub fn sign_ecdsa(&self, msg: Message) -> ecdsa::Signature { + SECP256K1.sign_ecdsa(&msg, self) + } + + /// Returns the [`Keypair`] for this [`SecretKey`]. + /// + /// This is equivalent to using [`Keypair::from_secret_key`]. + #[inline] + pub fn keypair<C: Signing>(&self, secp: &Secp256k1<C>) -> Keypair { + Keypair::from_secret_key(secp, self) + } + + /// Returns the [`PublicKey`] for this [`SecretKey`]. + /// + /// This is equivalent to using [`PublicKey::from_secret_key`]. + #[inline] + pub fn public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> PublicKey { + PublicKey::from_secret_key(secp, self) + } + + /// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this + /// [`SecretKey`]. + /// + /// This is equivalent to + /// `XOnlyPublicKey::from_keypair(self.keypair(secp))`. + #[inline] + pub fn x_only_public_key<C: Signing>( + &self, + secp: &Secp256k1<C>, + ) -> (XOnlyPublicKey, Parity) { + let kp = self.keypair(secp); + XOnlyPublicKey::from_keypair(&kp) + } +} + +#[cfg(feature = "hashes")] +#[allow(deprecated)] +impl<T: ThirtyTwoByteHash> From<T> for SecretKey { + /// Converts a 32-byte hash directly to a secret key without error paths. + fn from(t: T) -> SecretKey { + SecretKey::from_byte_array(&t.into_32()) + .expect("failed to create secret key") + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for SecretKey { + fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { + if s.is_human_readable() { + let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2]; + s.serialize_str( + crate::to_hex(&self.0, &mut buf) + .expect("fixed-size hex serialization"), + ) + } else { + let mut tuple = s.serialize_tuple(constants::SECRET_KEY_SIZE)?; + for byte in self.0.iter() { + tuple.serialize_element(byte)?; + } + tuple.end() + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for SecretKey { + fn deserialize<D: serde::Deserializer<'de>>( + d: D, + ) -> Result<Self, D::Error> { + if d.is_human_readable() { + d.deserialize_str(super::serde_util::FromStrVisitor::new( + "a hex string representing 32 byte SecretKey", + )) + } else { + let visitor = super::serde_util::Tuple32Visitor::new( + "raw 32 bytes SecretKey", + SecretKey::from_slice, + ); + d.deserialize_tuple(constants::SECRET_KEY_SIZE, visitor) + } + } +} + +impl PublicKey { + /// Obtains a raw const pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_c_ptr if you need to access the FFI layer" + )] + pub fn as_ptr(&self) -> *const ffi::PublicKey { + self.as_c_ptr() + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" + )] + pub fn as_mut_ptr(&mut self) -> *mut ffi::PublicKey { + self.as_mut_c_ptr() + } + + /// Creates a new public key from a [`SecretKey`]. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{rand, PublicKey, Secp256k1, SecretKey}; + /// + /// let secp = Secp256k1::new(); + /// let secret_key = SecretKey::new(&mut rand::thread_rng()); + /// let public_key = PublicKey::from_secret_key(&secp, &secret_key); + /// # } + /// ``` + #[inline] + pub fn from_secret_key<C: Signing>( + secp: &Secp256k1<C>, + sk: &SecretKey, + ) -> PublicKey { + unsafe { + let mut pk = ffi::PublicKey::new(); + // We can assume the return value because it's not possible to + // construct an invalid `SecretKey` without transmute + // trickery or something. + let res = ffi::secp256k1_ec_pubkey_create( + secp.ctx.as_ptr(), + &mut pk, + sk.as_c_ptr(), + ); + debug_assert_eq!(res, 1); + PublicKey(pk) + } + } + + /// Creates a new public key from a [`SecretKey`] and the global + /// [`SECP256K1`] context. + #[inline] + #[cfg(feature = "global-context")] + pub fn from_secret_key_global(sk: &SecretKey) -> PublicKey { + PublicKey::from_secret_key(SECP256K1, sk) + } + + /// Creates a public key directly from a slice. + #[inline] + pub fn from_slice(data: &[u8]) -> Result<PublicKey, Error> { + match data.len() { + constants::PUBLIC_KEY_SIZE => { + PublicKey::from_byte_array_compressed( + &<[u8; constants::PUBLIC_KEY_SIZE]>::try_from(data) + .unwrap(), + ) + } + constants::UNCOMPRESSED_PUBLIC_KEY_SIZE => { + PublicKey::from_byte_array_uncompressed( + &<[u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from( + data, + ) + .unwrap(), + ) + } + _ => Err(InvalidPublicKey), + } + } + + /// Creates a public key from a serialized array in compressed format. + #[inline] + pub fn from_byte_array_compressed( + data: &[u8; constants::PUBLIC_KEY_SIZE], + ) -> Result<PublicKey, Error> { + unsafe { + let mut pk = ffi::PublicKey::new(); + if ffi::secp256k1_ec_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + constants::PUBLIC_KEY_SIZE, + ) == 1 + { + Ok(PublicKey(pk)) + } else { + Err(InvalidPublicKey) + } + } + } + + /// Creates a public key from a serialized array in uncompressed format. + #[inline] + pub fn from_byte_array_uncompressed( + data: &[u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE], + ) -> Result<PublicKey, Error> { + unsafe { + let mut pk = ffi::PublicKey::new(); + if ffi::secp256k1_ec_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + constants::UNCOMPRESSED_PUBLIC_KEY_SIZE, + ) == 1 + { + Ok(PublicKey(pk)) + } else { + Err(InvalidPublicKey) + } + } + } + + /// Creates a new compressed public key using data from BIP-340 [`Keypair`]. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{rand, Keypair, PublicKey, Secp256k1}; + /// + /// let secp = Secp256k1::new(); + /// let keypair = Keypair::new(&secp, &mut rand::thread_rng()); + /// let public_key = PublicKey::from_keypair(&keypair); + /// # } + /// ``` + #[inline] + pub fn from_keypair(keypair: &Keypair) -> Self { + unsafe { + let mut pk = ffi::PublicKey::new(); + let ret = ffi::secp256k1_keypair_pub( + ffi::secp256k1_context_no_precomp, + &mut pk, + keypair.as_c_ptr(), + ); + debug_assert_eq!(ret, 1); + PublicKey(pk) + } + } + + /// Creates a [`PublicKey`] using the key material from `pk` combined with + /// the `parity`. + pub fn from_x_only_public_key( + pk: XOnlyPublicKey, + parity: Parity, + ) -> PublicKey { + let mut buf = [0u8; 33]; + + // First byte of a compressed key should be `0x02 AND parity`. + buf[0] = match parity { + Parity::Even => 0x02, + Parity::Odd => 0x03, + }; + buf[1..].clone_from_slice(&pk.serialize()); + + PublicKey::from_byte_array_compressed(&buf) + .expect("we know the buffer is valid") + } + + #[inline] + /// Serializes the key as a byte-encoded pair of values. In compressed form + /// the y-coordinate is represented by only a single bit, as x + /// determines it up to one bit. + pub fn serialize(&self) -> [u8; constants::PUBLIC_KEY_SIZE] { + let mut ret = [0u8; constants::PUBLIC_KEY_SIZE]; + self.serialize_internal(&mut ret, ffi::SECP256K1_SER_COMPRESSED); + ret + } + + #[inline] + /// Serializes the key as a byte-encoded pair of values, in uncompressed + /// form. + pub fn serialize_uncompressed( + &self, + ) -> [u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE] { + let mut ret = [0u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]; + self.serialize_internal(&mut ret, ffi::SECP256K1_SER_UNCOMPRESSED); + ret + } + + #[inline(always)] + fn serialize_internal(&self, ret: &mut [u8], flag: c_uint) { + let mut ret_len = ret.len(); + let res = unsafe { + ffi::secp256k1_ec_pubkey_serialize( + ffi::secp256k1_context_no_precomp, + ret.as_mut_c_ptr(), + &mut ret_len, + self.as_c_ptr(), + flag, + ) + }; + debug_assert_eq!(res, 1); + debug_assert_eq!(ret_len, ret.len()); + } + + /// Negates the public key. + #[inline] + #[must_use = "you forgot to use the negated public key"] + pub fn negate<C: Verification>(mut self, secp: &Secp256k1<C>) -> PublicKey { + unsafe { + let res = + ffi::secp256k1_ec_pubkey_negate(secp.ctx.as_ptr(), &mut self.0); + debug_assert_eq!(res, 1); + } + self + } + + /// Tweaks a [`PublicKey`] by adding `tweak * G` modulo the curve order. + /// + /// # Errors + /// + /// Returns an error if the resulting key would be invalid. + #[inline] + pub fn add_exp_tweak<C: Verification>( + mut self, + secp: &Secp256k1<C>, + tweak: &Scalar, + ) -> Result<PublicKey, Error> { + unsafe { + if ffi::secp256k1_ec_pubkey_tweak_add( + secp.ctx.as_ptr(), + &mut self.0, + tweak.as_c_ptr(), + ) == 1 + { + Ok(self) + } else { + Err(Error::InvalidTweak) + } + } + } + + /// Tweaks a [`PublicKey`] by multiplying by `tweak` modulo the curve order. + /// + /// # Errors + /// + /// Returns an error if the resulting key would be invalid. + #[inline] + pub fn mul_tweak<C: Verification>( + mut self, + secp: &Secp256k1<C>, + other: &Scalar, + ) -> Result<PublicKey, Error> { + unsafe { + if ffi::secp256k1_ec_pubkey_tweak_mul( + secp.ctx.as_ptr(), + &mut self.0, + other.as_c_ptr(), + ) == 1 + { + Ok(self) + } else { + Err(Error::InvalidTweak) + } + } + } + + /// Adds a second key to this one, returning the sum. + /// + /// # Errors + /// + /// If the result would be the point at infinity, i.e. adding this point to + /// its own negation. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{rand, Secp256k1}; + /// + /// let secp = Secp256k1::new(); + /// let mut rng = rand::thread_rng(); + /// let (_, pk1) = secp.generate_keypair(&mut rng); + /// let (_, pk2) = secp.generate_keypair(&mut rng); + /// let sum = pk1 + /// .combine(&pk2) + /// .expect("It's improbable to fail for 2 random public keys"); + /// # } + /// ``` + pub fn combine(&self, other: &PublicKey) -> Result<PublicKey, Error> { + PublicKey::combine_keys(&[self, other]) + } + + /// Adds the keys in the provided slice together, returning the sum. + /// + /// # Errors + /// + /// Errors under any of the following conditions: + /// - The result would be the point at infinity, i.e. adding a point to its + /// own negation. + /// - The provided slice is empty. + /// - The number of elements in the provided slice is greater than + /// `i32::MAX`. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{rand, PublicKey, Secp256k1}; + /// + /// let secp = Secp256k1::new(); + /// let mut rng = rand::thread_rng(); + /// let (_, pk1) = secp.generate_keypair(&mut rng); + /// let (_, pk2) = secp.generate_keypair(&mut rng); + /// let (_, pk3) = secp.generate_keypair(&mut rng); + /// let sum = PublicKey::combine_keys(&[&pk1, &pk2, &pk3]) + /// .expect("It's improbable to fail for 3 random public keys"); + /// # } + /// ``` + pub fn combine_keys(keys: &[&PublicKey]) -> Result<PublicKey, Error> { + use core::mem::transmute; + + if keys.is_empty() || keys.len() > i32::MAX as usize { + return Err(InvalidPublicKeySum); + } + + unsafe { + let mut ret = ffi::PublicKey::new(); + let ptrs: &[*const ffi::PublicKey] = + transmute::<&[&PublicKey], &[*const ffi::PublicKey]>(keys); + if ffi::secp256k1_ec_pubkey_combine( + ffi::secp256k1_context_no_precomp, + &mut ret, + ptrs.as_c_ptr(), + keys.len(), + ) == 1 + { + Ok(PublicKey(ret)) + } else { + Err(InvalidPublicKeySum) + } + } + } + + /// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this + /// [`PublicKey`]. + #[inline] + pub fn x_only_public_key(&self) -> (XOnlyPublicKey, Parity) { + let mut pk_parity = 0; + unsafe { + let mut xonly_pk = ffi::XOnlyPublicKey::new(); + let ret = ffi::secp256k1_xonly_pubkey_from_pubkey( + ffi::secp256k1_context_no_precomp, + &mut xonly_pk, + &mut pk_parity, + self.as_c_ptr(), + ); + debug_assert_eq!(ret, 1); + let parity = Parity::from_i32(pk_parity) + .expect("should not panic, pk_parity is 0 or 1"); + + (XOnlyPublicKey(xonly_pk), parity) + } + } + + /// Checks that `sig` is a valid ECDSA signature for `msg` using this public + /// key. + pub fn verify<C: Verification>( + &self, + secp: &Secp256k1<C>, + msg: &Message, + sig: &ecdsa::Signature, + ) -> Result<(), Error> { + secp.verify_ecdsa(msg, sig, self) + } +} + +/// This trait enables interaction with the FFI layer and even though it is part +/// of the public API normal users should never need to directly interact with +/// FFI types. +impl CPtr for PublicKey { + type Target = ffi::PublicKey; + + /// Obtains a const pointer suitable for use with FFI functions. + fn as_c_ptr(&self) -> *const Self::Target { + &self.0 + } + + /// Obtains a mutable pointer suitable for use with FFI functions. + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + &mut self.0 + } +} + +/// Creates a new public key from a FFI public key. +/// +/// Note, normal users should never need to interact directly with FFI types. +impl From<ffi::PublicKey> for PublicKey { + #[inline] + fn from(pk: ffi::PublicKey) -> PublicKey { + PublicKey(pk) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for PublicKey { + fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { + if s.is_human_readable() { + s.collect_str(self) + } else { + let mut tuple = s.serialize_tuple(constants::PUBLIC_KEY_SIZE)?; + // Serialize in compressed form. + for byte in self.serialize().iter() { + tuple.serialize_element(&byte)?; + } + tuple.end() + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for PublicKey { + fn deserialize<D: serde::Deserializer<'de>>( + d: D, + ) -> Result<PublicKey, D::Error> { + if d.is_human_readable() { + d.deserialize_str(super::serde_util::FromStrVisitor::new( + "an ASCII hex string representing a public key", + )) + } else { + let visitor = super::serde_util::Tuple33Visitor::new( + "33 bytes compressed public key", + PublicKey::from_slice, + ); + d.deserialize_tuple(constants::PUBLIC_KEY_SIZE, visitor) + } + } +} + +/// Opaque data structure that holds a keypair consisting of a secret and a +/// public key. +/// +/// # Serde support +/// +/// Implements de/serialization with the `serde` and_`global-context` features +/// enabled. Serializes the secret bytes only. We treat the byte value as a +/// tuple of 32 `u8`s for non-human-readable formats. This representation is +/// optimal for for some formats (e.g. [`bincode`]) however other formats may be +/// less optimal (e.g. [`cbor`]). For human-readable formats we use a hex +/// string. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # #[cfg(all(feature = "rand", feature = "std"))] { +/// use ecash_secp256k1::{rand, Keypair, Secp256k1}; +/// +/// let secp = Secp256k1::new(); +/// let (secret_key, public_key) = +/// secp.generate_keypair(&mut rand::thread_rng()); +/// let keypair = Keypair::from_secret_key(&secp, &secret_key); +/// # } +/// ``` +/// [`bincode`]: https://docs.rs/bincode +/// [`cbor`]: https://docs.rs/cbor +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct Keypair(ffi::Keypair); +impl_fast_comparisons!(Keypair); + +impl Keypair { + /// Obtains a raw const pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_c_ptr if you need to access the FFI layer" + )] + pub fn as_ptr(&self) -> *const ffi::Keypair { + self.as_c_ptr() + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" + )] + pub fn as_mut_ptr(&mut self) -> *mut ffi::Keypair { + self.as_mut_c_ptr() + } + + /// Creates a [`Keypair`] directly from a Secp256k1 secret key. + #[inline] + pub fn from_secret_key<C: Signing>( + secp: &Secp256k1<C>, + sk: &SecretKey, + ) -> Keypair { + unsafe { + let mut kp = ffi::Keypair::new(); + if ffi::secp256k1_keypair_create( + secp.ctx.as_ptr(), + &mut kp, + sk.as_c_ptr(), + ) == 1 + { + Keypair(kp) + } else { + panic!( + "the provided secret key is invalid: it is corrupted or \ + was not produced by Secp256k1 library" + ) + } + } + } + + /// Creates a [`Keypair`] directly from a secret key slice. + /// + /// # Errors + /// + /// [`Error::InvalidSecretKey`] if the provided data has an incorrect + /// length, exceeds Secp256k1 field `p` value or the corresponding + /// public key is not even. + #[inline] + pub fn from_seckey_slice<C: Signing>( + secp: &Secp256k1<C>, + data: &[u8], + ) -> Result<Keypair, Error> { + if data.is_empty() || data.len() != constants::SECRET_KEY_SIZE { + return Err(Error::InvalidSecretKey); + } + + unsafe { + let mut kp = ffi::Keypair::new(); + if ffi::secp256k1_keypair_create( + secp.ctx.as_ptr(), + &mut kp, + data.as_c_ptr(), + ) == 1 + { + Ok(Keypair(kp)) + } else { + Err(Error::InvalidSecretKey) + } + } + } + + /// Creates a [`Keypair`] directly from a secret key string. + /// + /// # Errors + /// + /// [`Error::InvalidSecretKey`] if corresponding public key for the provided + /// secret key is not even. + #[inline] + pub fn from_seckey_str<C: Signing>( + secp: &Secp256k1<C>, + s: &str, + ) -> Result<Keypair, Error> { + let mut res = [0u8; constants::SECRET_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SECRET_KEY_SIZE) => Keypair::from_seckey_slice( + secp, + &res[0..constants::SECRET_KEY_SIZE], + ), + _ => Err(Error::InvalidPublicKey), + } + } + + /// Creates a [`Keypair`] directly from a secret key string and the global + /// [`SECP256K1`] context. + /// + /// # Errors + /// + /// [`Error::InvalidSecretKey`] if corresponding public key for the provided + /// secret key is not even. + #[inline] + #[cfg(feature = "global-context")] + pub fn from_seckey_str_global(s: &str) -> Result<Keypair, Error> { + Keypair::from_seckey_str(SECP256K1, s) + } + + /// Generates a new random secret key. + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{rand, Keypair, Secp256k1, SecretKey}; + /// + /// let secp = Secp256k1::new(); + /// let keypair = Keypair::new(&secp, &mut rand::thread_rng()); + /// # } + /// ``` + #[inline] + #[cfg(feature = "rand")] + pub fn new<R: rand::Rng + ?Sized, C: Signing>( + secp: &Secp256k1<C>, + rng: &mut R, + ) -> Keypair { + let mut data = crate::random_32_bytes(rng); + unsafe { + let mut keypair = ffi::Keypair::new(); + while ffi::secp256k1_keypair_create( + secp.ctx.as_ptr(), + &mut keypair, + data.as_c_ptr(), + ) == 0 + { + data = crate::random_32_bytes(rng); + } + Keypair(keypair) + } + } + + /// Generates a new random secret key using the global [`SECP256K1`] + /// context. + #[inline] + #[cfg(all(feature = "global-context", feature = "rand"))] + pub fn new_global<R: ::rand::Rng + ?Sized>(rng: &mut R) -> Keypair { + Keypair::new(SECP256K1, rng) + } + + /// Returns the secret bytes for this key pair. + #[inline] + pub fn secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] { + *SecretKey::from_keypair(self).as_ref() + } + + /// Tweaks a keypair by first converting the public key to an xonly key and + /// tweaking it. + /// + /// # Errors + /// + /// Returns an error if the resulting key would be invalid. + /// + /// NB: Will not error if the tweaked public key has an odd value and can't + /// be used for BIP 340-342 purposes. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{Keypair, Scalar, Secp256k1}; + /// + /// let secp = Secp256k1::new(); + /// let tweak = Scalar::random(); + /// + /// let mut keypair = Keypair::new(&secp, &mut rand::thread_rng()); + /// let tweaked = keypair + /// .add_xonly_tweak(&secp, &tweak) + /// .expect("Improbable to fail with a randomly generated tweak"); + /// # } + /// ``` + // TODO: Add checked implementation + #[inline] + pub fn add_xonly_tweak<C: Verification>( + mut self, + secp: &Secp256k1<C>, + tweak: &Scalar, + ) -> Result<Keypair, Error> { + unsafe { + let err = ffi::secp256k1_keypair_xonly_tweak_add( + secp.ctx.as_ptr(), + &mut self.0, + tweak.as_c_ptr(), + ); + if err != 1 { + return Err(Error::InvalidTweak); + } + + Ok(self) + } + } + + /// Returns the [`SecretKey`] for this [`Keypair`]. + /// + /// This is equivalent to using [`SecretKey::from_keypair`]. + #[inline] + pub fn secret_key(&self) -> SecretKey { + SecretKey::from_keypair(self) + } + + /// Returns the [`PublicKey`] for this [`Keypair`]. + /// + /// This is equivalent to using [`PublicKey::from_keypair`]. + #[inline] + pub fn public_key(&self) -> PublicKey { + PublicKey::from_keypair(self) + } + + /// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this + /// [`Keypair`]. + /// + /// This is equivalent to using [`XOnlyPublicKey::from_keypair`]. + #[inline] + pub fn x_only_public_key(&self) -> (XOnlyPublicKey, Parity) { + XOnlyPublicKey::from_keypair(self) + } + + /// Constructs an schnorr signature for `msg` using the global [`SECP256K1`] + /// context. + #[inline] + #[cfg(all(feature = "global-context", feature = "rand", feature = "std"))] + pub fn sign_schnorr(&self, msg: &[u8]) -> schnorr::Signature { + SECP256K1.sign_schnorr(msg, self) + } + + /// Attempts to erase the secret within the underlying array. + /// + /// Note, however, that the compiler is allowed to freely copy or move the + /// contents of this array to other places in memory. Preventing this + /// behavior is very subtle. For more discussion on this, please see the + /// documentation of the [`zeroize`](https://docs.rs/zeroize) crate. + #[inline] + pub fn non_secure_erase(&mut self) { + self.0.non_secure_erase(); + } +} + +impl fmt::Debug for Keypair { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("Keypair") + .field("pubkey", &self.public_key()) + .field("secret", &"<hidden>") + .finish() + } +} + +impl From<Keypair> for SecretKey { + #[inline] + fn from(pair: Keypair) -> Self { + SecretKey::from_keypair(&pair) + } +} + +impl<'a> From<&'a Keypair> for SecretKey { + #[inline] + fn from(pair: &'a Keypair) -> Self { + SecretKey::from_keypair(pair) + } +} + +impl From<Keypair> for PublicKey { + #[inline] + fn from(pair: Keypair) -> Self { + PublicKey::from_keypair(&pair) + } +} + +impl<'a> From<&'a Keypair> for PublicKey { + #[inline] + fn from(pair: &'a Keypair) -> Self { + PublicKey::from_keypair(pair) + } +} + +#[cfg(any(feature = "global-context", feature = "alloc"))] +impl str::FromStr for Keypair { + type Err = Error; + + // When built with no default features. + #[allow(unused_variables, unreachable_code)] + fn from_str(s: &str) -> Result<Self, Self::Err> { + #[cfg(feature = "global-context")] + let ctx = SECP256K1; + + #[cfg(all(not(feature = "global-context"), feature = "alloc"))] + let ctx = Secp256k1::signing_only(); + + #[allow(clippy::needless_borrow)] + Keypair::from_seckey_str(&ctx, s) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Keypair { + fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { + if s.is_human_readable() { + let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2]; + s.serialize_str( + crate::to_hex(&self.secret_bytes(), &mut buf) + .expect("fixed-size hex serialization"), + ) + } else { + let mut tuple = s.serialize_tuple(constants::SECRET_KEY_SIZE)?; + for byte in self.secret_bytes().iter() { + tuple.serialize_element(&byte)?; + } + tuple.end() + } + } +} + +#[cfg(feature = "serde")] +// For `data` under some feature combinations (the unconditional panic below). +#[allow(unused_variables)] +#[cfg(all( + feature = "serde", + any(feature = "global-context", feature = "alloc") +))] +impl<'de> serde::Deserialize<'de> for Keypair { + fn deserialize<D: serde::Deserializer<'de>>( + d: D, + ) -> Result<Self, D::Error> { + if d.is_human_readable() { + d.deserialize_str(super::serde_util::FromStrVisitor::new( + "a hex string representing 32 byte Keypair", + )) + } else { + let visitor = super::serde_util::Tuple32Visitor::new( + "raw 32 bytes Keypair", + |data| { + #[cfg(feature = "global-context")] + let ctx = SECP256K1; + + #[cfg(all( + not(feature = "global-context"), + feature = "alloc" + ))] + let ctx = Secp256k1::signing_only(); + + #[allow(clippy::needless_borrow)] + Keypair::from_seckey_slice(&ctx, data) + }, + ); + d.deserialize_tuple(constants::SECRET_KEY_SIZE, visitor) + } + } +} + +impl CPtr for Keypair { + type Target = ffi::Keypair; + + fn as_c_ptr(&self) -> *const Self::Target { + &self.0 + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + &mut self.0 + } +} + +/// An x-only public key, used for verification of Taproot signatures and +/// serialized according to BIP-340. +/// +/// # Serde support +/// +/// Implements de/serialization with the `serde` feature enabled. We treat the +/// byte value as a tuple of 32 `u8`s for non-human-readable formats. This +/// representation is optimal for for some formats (e.g. [`bincode`]) however +/// other formats may be less optimal (e.g. [`cbor`]). +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// # #[cfg(all(feature = "rand", feature = "std"))] { +/// use ecash_secp256k1::{rand, Keypair, Secp256k1, XOnlyPublicKey}; +/// +/// let secp = Secp256k1::new(); +/// let keypair = Keypair::new(&secp, &mut rand::thread_rng()); +/// let xonly = XOnlyPublicKey::from_keypair(&keypair); +/// # } +/// ``` +/// [`bincode`]: https://docs.rs/bincode +/// [`cbor`]: https://docs.rs/cbor +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct XOnlyPublicKey(ffi::XOnlyPublicKey); +impl_fast_comparisons!(XOnlyPublicKey); + +impl fmt::LowerHex for XOnlyPublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let ser = self.serialize(); + for ch in &ser[..] { + write!(f, "{:02x}", *ch)?; + } + Ok(()) + } +} + +impl fmt::Display for XOnlyPublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl fmt::Debug for XOnlyPublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for XOnlyPublicKey { + type Err = Error; + + fn from_str(s: &str) -> Result<XOnlyPublicKey, Error> { + let mut res = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => { + XOnlyPublicKey::from_byte_array(&res) + } + _ => Err(Error::InvalidPublicKey), + } + } +} + +impl XOnlyPublicKey { + /// Obtains a raw const pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_c_ptr if you need to access the FFI layer" + )] + pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { + self.as_c_ptr() + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions. + #[inline] + #[deprecated( + since = "0.25.0", + note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" + )] + pub fn as_mut_ptr(&mut self) -> *mut ffi::XOnlyPublicKey { + self.as_mut_c_ptr() + } + + /// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for `keypair`. + #[inline] + pub fn from_keypair(keypair: &Keypair) -> (XOnlyPublicKey, Parity) { + let mut pk_parity = 0; + unsafe { + let mut xonly_pk = ffi::XOnlyPublicKey::new(); + let ret = ffi::secp256k1_keypair_xonly_pub( + ffi::secp256k1_context_no_precomp, + &mut xonly_pk, + &mut pk_parity, + keypair.as_c_ptr(), + ); + debug_assert_eq!(ret, 1); + let parity = Parity::from_i32(pk_parity) + .expect("should not panic, pk_parity is 0 or 1"); + + (XOnlyPublicKey(xonly_pk), parity) + } + } + + /// Creates a schnorr public key directly from a slice. + /// + /// # Errors + /// + /// Returns [`Error::InvalidPublicKey`] if the length of the data slice is + /// not 32 bytes or the slice does not represent a valid Secp256k1 point + /// x coordinate. + #[deprecated(since = "TBD", note = "Use `from_byte_array` instead.")] + #[inline] + pub fn from_slice(data: &[u8]) -> Result<XOnlyPublicKey, Error> { + match <[u8; constants::SCHNORR_PUBLIC_KEY_SIZE]>::try_from(data) { + Ok(data) => Self::from_byte_array(&data), + Err(_) => Err(InvalidPublicKey), + } + } + + /// Creates a schnorr public key directly from a byte array. + /// + /// # Errors + /// + /// Returns [`Error::InvalidPublicKey`] if the array does not represent a + /// valid Secp256k1 point x coordinate. + #[inline] + pub fn from_byte_array( + data: &[u8; constants::SCHNORR_PUBLIC_KEY_SIZE], + ) -> Result<XOnlyPublicKey, Error> { + unsafe { + let mut pk = ffi::XOnlyPublicKey::new(); + if ffi::secp256k1_xonly_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + ) == 1 + { + Ok(XOnlyPublicKey(pk)) + } else { + Err(Error::InvalidPublicKey) + } + } + } + + #[inline] + /// Serializes the key as a byte-encoded x coordinate value (32 bytes). + pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] { + let mut ret = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE]; + + unsafe { + let err = ffi::secp256k1_xonly_pubkey_serialize( + ffi::secp256k1_context_no_precomp, + ret.as_mut_c_ptr(), + self.as_c_ptr(), + ); + debug_assert_eq!(err, 1); + } + ret + } + + /// Tweaks an [`XOnlyPublicKey`] by adding the generator multiplied with the + /// given tweak to it. + /// + /// # Returns + /// + /// The newly tweaked key plus an opaque type representing the parity of the + /// tweaked key, this should be provided to `tweak_add_check` which can + /// be used to verify a tweak more efficiently than regenerating it and + /// checking equality. + /// + /// # Errors + /// + /// If the resulting key would be invalid. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{Keypair, Scalar, Secp256k1, XOnlyPublicKey}; + /// + /// let secp = Secp256k1::new(); + /// let tweak = Scalar::random(); + /// + /// let mut keypair = Keypair::new(&secp, &mut rand::thread_rng()); + /// let (xonly, _parity) = keypair.x_only_public_key(); + /// let tweaked = xonly + /// .add_tweak(&secp, &tweak) + /// .expect("Improbable to fail with a randomly generated tweak"); + /// # } + /// ``` + pub fn add_tweak<V: Verification>( + mut self, + secp: &Secp256k1<V>, + tweak: &Scalar, + ) -> Result<(XOnlyPublicKey, Parity), Error> { + let mut pk_parity = 0; + unsafe { + let mut pubkey = ffi::PublicKey::new(); + let mut err = ffi::secp256k1_xonly_pubkey_tweak_add( + secp.ctx.as_ptr(), + &mut pubkey, + self.as_c_ptr(), + tweak.as_c_ptr(), + ); + if err != 1 { + return Err(Error::InvalidTweak); + } + + err = ffi::secp256k1_xonly_pubkey_from_pubkey( + secp.ctx.as_ptr(), + &mut self.0, + &mut pk_parity, + &pubkey, + ); + if err == 0 { + return Err(Error::InvalidPublicKey); + } + + let parity = Parity::from_i32(pk_parity)?; + Ok((self, parity)) + } + } + + /// Verifies that a tweak produced by [`XOnlyPublicKey::add_tweak`] was + /// computed correctly. + /// + /// Should be called on the original untweaked key. Takes the tweaked key + /// and output parity from [`XOnlyPublicKey::add_tweak`] as input. + /// + /// Currently this is not much more efficient than just recomputing the + /// tweak and checking equality. However, in future this API will + /// support batch verification, which is significantly faster, so it is + /// wise to design protocols with this in mind. + /// + /// # Returns + /// + /// True if tweak and check is successful, false otherwise. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(all(feature = "rand", feature = "std"))] { + /// use ecash_secp256k1::{Keypair, Scalar, Secp256k1}; + /// + /// let secp = Secp256k1::new(); + /// let tweak = Scalar::random(); + /// + /// let mut keypair = Keypair::new(&secp, &mut rand::thread_rng()); + /// let (mut public_key, _) = keypair.x_only_public_key(); + /// let original = public_key; + /// let (tweaked, parity) = public_key + /// .add_tweak(&secp, &tweak) + /// .expect("Improbable to fail with a randomly generated tweak"); + /// assert!(original.tweak_add_check(&secp, &tweaked, parity, tweak)); + /// # } + /// ``` + pub fn tweak_add_check<V: Verification>( + &self, + secp: &Secp256k1<V>, + tweaked_key: &Self, + tweaked_parity: Parity, + tweak: Scalar, + ) -> bool { + let tweaked_ser = tweaked_key.serialize(); + unsafe { + let err = ffi::secp256k1_xonly_pubkey_tweak_add_check( + secp.ctx.as_ptr(), + tweaked_ser.as_c_ptr(), + tweaked_parity.to_i32(), + &self.0, + tweak.as_c_ptr(), + ); + + err == 1 + } + } + + /// Returns the [`PublicKey`] for this [`XOnlyPublicKey`]. + /// + /// This is equivalent to using [`PublicKey::from_xonly_and_parity(self, + /// parity)`]. + #[inline] + pub fn public_key(&self, parity: Parity) -> PublicKey { + PublicKey::from_x_only_public_key(*self, parity) + } + + /// Checks that `sig` is a valid schnorr signature for `msg` using this + /// public key. + pub fn verify<C: Verification>( + &self, + secp: &Secp256k1<C>, + msg: &[u8], + sig: &schnorr::Signature, + ) -> Result<(), Error> { + secp.verify_schnorr(sig, msg, self) + } +} + +/// Represents the parity passed between FFI function calls. +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] +pub enum Parity { + /// Even parity. + Even = 0, + /// Odd parity. + Odd = 1, +} + +impl Parity { + /// Converts parity into an integer (byte) value. + /// + /// This returns `0` for even parity and `1` for odd parity. + pub fn to_u8(self) -> u8 { + self as u8 + } + + /// Converts parity into an integer value. + /// + /// This returns `0` for even parity and `1` for odd parity. + pub fn to_i32(self) -> i32 { + self as i32 + } + + /// Constructs a [`Parity`] from a byte. + /// + /// The only allowed values are `0` meaning even parity and `1` meaning odd. + /// Other values result in error being returned. + pub fn from_u8(parity: u8) -> Result<Parity, InvalidParityValue> { + Parity::from_i32(parity.into()) + } + + /// Constructs a [`Parity`] from a signed integer. + /// + /// The only allowed values are `0` meaning even parity and `1` meaning odd. + /// Other values result in error being returned. + pub fn from_i32(parity: i32) -> Result<Parity, InvalidParityValue> { + match parity { + 0 => Ok(Parity::Even), + 1 => Ok(Parity::Odd), + _ => Err(InvalidParityValue(parity)), + } + } +} + +/// `Even` for `0`, `Odd` for `1`, error for anything else +impl TryFrom<i32> for Parity { + type Error = InvalidParityValue; + + fn try_from(parity: i32) -> Result<Self, Self::Error> { + Self::from_i32(parity) + } +} + +/// `Even` for `0`, `Odd` for `1`, error for anything else +impl TryFrom<u8> for Parity { + type Error = InvalidParityValue; + + fn try_from(parity: u8) -> Result<Self, Self::Error> { + Self::from_u8(parity) + } +} + +/// The conversion returns `0` for even parity and `1` for odd. +impl From<Parity> for i32 { + fn from(parity: Parity) -> i32 { + parity.to_i32() + } +} + +/// The conversion returns `0` for even parity and `1` for odd. +impl From<Parity> for u8 { + fn from(parity: Parity) -> u8 { + parity.to_u8() + } +} + +/// Returns even parity if the operands are equal, odd otherwise. +impl BitXor for Parity { + type Output = Parity; + + fn bitxor(self, rhs: Parity) -> Self::Output { + // This works because Parity has only two values (i.e. only 1 bit of + // information). + if self == rhs { + Parity::Even // 1^1==0 and 0^0==0 + } else { + Parity::Odd // 1^0==1 and 0^1==1 + } + } +} + +/// Error returned when conversion from an integer to `Parity` fails. +// +// Note that we don't allow inspecting the value because we may change the type. +// Yes, this comment is intentionally NOT doc comment. +// Too many derives for compatibility with current Error type. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct InvalidParityValue(i32); + +impl fmt::Display for InvalidParityValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "invalid value {} for Parity - must be 0 or 1", self.0) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidParityValue {} + +impl From<InvalidParityValue> for Error { + fn from(error: InvalidParityValue) -> Self { + Error::InvalidParityValue(error) + } +} + +/// The parity is serialized as `u8` - `0` for even, `1` for odd. +#[cfg(feature = "serde")] +impl serde::Serialize for Parity { + fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { + s.serialize_u8(self.to_u8()) + } +} + +/// The parity is deserialized as `u8` - `0` for even, `1` for odd. +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for Parity { + fn deserialize<D: serde::Deserializer<'de>>( + d: D, + ) -> Result<Self, D::Error> { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = Parity; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("8-bit integer (byte) with value 0 or 1") + } + + fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + use serde::de::Unexpected; + + Parity::from_u8(v).map_err(|_| { + E::invalid_value(Unexpected::Unsigned(v.into()), &"0 or 1") + }) + } + } + + d.deserialize_u8(Visitor) + } +} + +impl CPtr for XOnlyPublicKey { + type Target = ffi::XOnlyPublicKey; + + fn as_c_ptr(&self) -> *const Self::Target { + &self.0 + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + &mut self.0 + } +} + +/// Creates a new schnorr public key from a FFI x-only public key. +impl From<ffi::XOnlyPublicKey> for XOnlyPublicKey { + #[inline] + fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey { + XOnlyPublicKey(pk) + } +} + +impl From<PublicKey> for XOnlyPublicKey { + fn from(src: PublicKey) -> XOnlyPublicKey { + unsafe { + let mut pk = ffi::XOnlyPublicKey::new(); + assert_eq!( + 1, + ffi::secp256k1_xonly_pubkey_from_pubkey( + ffi::secp256k1_context_no_precomp, + &mut pk, + ptr::null_mut(), + src.as_c_ptr(), + ) + ); + XOnlyPublicKey(pk) + } + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for XOnlyPublicKey { + fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { + if s.is_human_readable() { + s.collect_str(self) + } else { + let mut tuple = + s.serialize_tuple(constants::SCHNORR_PUBLIC_KEY_SIZE)?; + for byte in self.serialize().iter() { + tuple.serialize_element(&byte)?; + } + tuple.end() + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for XOnlyPublicKey { + fn deserialize<D: serde::Deserializer<'de>>( + d: D, + ) -> Result<Self, D::Error> { + if d.is_human_readable() { + d.deserialize_str(super::serde_util::FromStrVisitor::new( + "a hex string representing 32 byte schnorr public key", + )) + } else { + let visitor = super::serde_util::Tuple32Visitor::new( + "raw 32 bytes schnorr public key", + XOnlyPublicKey::from_slice, + ); + d.deserialize_tuple(constants::SCHNORR_PUBLIC_KEY_SIZE, visitor) + } + } +} + +#[cfg(test)] +#[allow(unused_imports)] +mod test { + use core::str::FromStr; + + #[cfg(not(secp256k1_fuzz))] + use hex_lit::hex; + #[cfg(feature = "rand")] + use rand::{self, rngs::mock::StepRng, RngCore}; + use serde_test::{Configure, Token}; + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + use super::{ + Keypair, Parity, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey, *, + }; + use crate::Error::{InvalidPublicKey, InvalidSecretKey}; + use crate::{constants, from_hex, to_hex, Scalar}; + + #[test] + fn skey_from_slice() { + let sk = SecretKey::from_slice(&[1; 31]); + assert_eq!(sk, Err(InvalidSecretKey)); + + let sk = SecretKey::from_slice(&[1; 32]); + assert!(sk.is_ok()); + } + + #[test] + fn pubkey_from_slice() { + assert_eq!(PublicKey::from_slice(&[]), Err(InvalidPublicKey)); + assert_eq!(PublicKey::from_slice(&[1, 2, 3]), Err(InvalidPublicKey)); + + let uncompressed = PublicKey::from_slice(&[ + 4, 54, 57, 149, 239, 162, 148, 175, 246, 254, 239, 75, 154, 152, + 10, 82, 234, 224, 85, 220, 40, 100, 57, 121, 30, 162, 94, 156, 135, + 67, 74, 49, 179, 57, 236, 53, 162, 124, 149, 144, 168, 77, 74, 30, + 72, 211, 229, 110, 111, 55, 96, 193, 86, 227, 183, 152, 195, 155, + 51, 247, 123, 113, 60, 228, 188, + ]); + assert!(uncompressed.is_ok()); + + let compressed = PublicKey::from_slice(&[ + 3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, 248, + 140, 11, 3, 51, 41, 111, 180, 110, 143, 114, 134, 88, 73, 198, 174, + 52, 184, 78, + ]); + assert!(compressed.is_ok()); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn keypair_slice_round_trip() { + let s = Secp256k1::new(); + + let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng()); + assert_eq!(SecretKey::from_slice(&sk1[..]), Ok(sk1)); + assert_eq!(PublicKey::from_slice(&pk1.serialize()[..]), Ok(pk1)); + assert_eq!( + PublicKey::from_slice(&pk1.serialize_uncompressed()[..]), + Ok(pk1) + ); + } + + #[test] + #[cfg(all(feature = "std", not(secp256k1_fuzz)))] + fn erased_keypair_is_valid() { + let s = Secp256k1::new(); + let kp = + Keypair::from_seckey_slice(&s, &[1u8; constants::SECRET_KEY_SIZE]) + .expect("valid secret key"); + let mut kp2 = kp; + kp2.non_secure_erase(); + assert!(kp.eq_fast_unstable(&kp2)); + } + + #[test] + #[rustfmt::skip] + fn invalid_secret_key() { + // Zero + assert_eq!(SecretKey::from_slice(&[0; 32]), Err(InvalidSecretKey)); + assert_eq!( + SecretKey::from_str( + "00000000000000000000000000000000000000000000000000000000000000\ + 00" + ), + Err(InvalidSecretKey) + ); + // -1 + assert_eq!(SecretKey::from_slice(&[0xff; 32]), Err(InvalidSecretKey)); + // Top of range + assert!(SecretKey::from_slice(&[ + 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, 0x40, + ]).is_ok()); + // One past top of range + assert!(SecretKey::from_slice(&[ + 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, + ]).is_err()); + } + + #[test] + #[cfg(all(feature = "rand", feature = "alloc"))] + fn test_out_of_range() { + struct BadRng(u8); + impl RngCore for BadRng { + fn next_u32(&mut self) -> u32 { + unimplemented!() + } + + fn next_u64(&mut self) -> u64 { + unimplemented!() + } + + // This will set a secret key to a little over the + // group order, then decrement with repeated calls + // until it returns a valid key + fn fill_bytes(&mut self, data: &mut [u8]) { + #[rustfmt::skip] + let group_order: [u8; 32] = [ + 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]; + assert_eq!(data.len(), 32); + data.copy_from_slice(&group_order[..]); + data[31] = self.0; + self.0 -= 1; + } + + fn try_fill_bytes( + &mut self, + dest: &mut [u8], + ) -> Result<(), rand::Error> { + self.fill_bytes(dest); + Ok(()) + } + } + + let s = Secp256k1::new(); + s.generate_keypair(&mut BadRng(0xff)); + } + + #[test] + fn test_pubkey_from_bad_slice() { + // Bad sizes + assert_eq!( + PublicKey::from_slice(&[0; constants::PUBLIC_KEY_SIZE - 1]), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice(&[0; constants::PUBLIC_KEY_SIZE + 1]), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice( + &[0; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE - 1] + ), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice( + &[0; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE + 1] + ), + Err(InvalidPublicKey) + ); + + // Bad parse + assert_eq!( + PublicKey::from_slice( + &[0xff; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE] + ), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice(&[0x55; constants::PUBLIC_KEY_SIZE]), + Err(InvalidPublicKey) + ); + assert_eq!(PublicKey::from_slice(&[]), Err(InvalidPublicKey)); + } + + #[test] + fn test_seckey_from_bad_slice() { + // Bad sizes + assert_eq!( + SecretKey::from_slice(&[0; constants::SECRET_KEY_SIZE - 1]), + Err(InvalidSecretKey) + ); + assert_eq!( + SecretKey::from_slice(&[0; constants::SECRET_KEY_SIZE + 1]), + Err(InvalidSecretKey) + ); + // Bad parse + assert_eq!( + SecretKey::from_slice(&[0xff; constants::SECRET_KEY_SIZE]), + Err(InvalidSecretKey) + ); + assert_eq!( + SecretKey::from_slice(&[0x00; constants::SECRET_KEY_SIZE]), + Err(InvalidSecretKey) + ); + assert_eq!(SecretKey::from_slice(&[]), Err(InvalidSecretKey)); + } + + #[test] + #[cfg(all(feature = "rand", feature = "alloc", not(feature = "hashes")))] + fn test_debug_output() { + let s = Secp256k1::new(); + let (sk, _) = s.generate_keypair(&mut StepRng::new(1, 1)); + + assert_eq!( + &format!("{:?}", sk), + "<secret key; enable `hashes` feature of `secp256k1` to display \ + fingerprint>" + ); + + let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2]; + assert_eq!( + to_hex(&sk[..], &mut buf).unwrap(), + "0100000000000000020000000000000003000000000000000400000000000000" + ); + } + + #[test] + #[cfg(feature = "alloc")] + fn test_display_output() { + #[rustfmt::skip] + static SK_BYTES: [u8; 32] = [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + ]; + + #[cfg(not(secp256k1_fuzz))] + let s = Secp256k1::signing_only(); + let sk = SecretKey::from_slice(&SK_BYTES).expect("sk"); + + // In fuzzing mode secret->public key derivation is different, so + // hard-code the expected result. + #[cfg(not(secp256k1_fuzz))] + let pk = PublicKey::from_secret_key(&s, &sk); + #[cfg(secp256k1_fuzz)] + let pk = PublicKey::from_slice(&[ + 0x02, 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, 0x1c, 0x97, + 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, 0x06, 0x83, 0x7f, 0x30, 0xaa, + 0x0c, 0xd0, 0x54, 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66, + ]) + .expect("pk"); + + assert_eq!( + sk.display_secret().to_string(), + "01010101010101010001020304050607ffff0000ffff00006363636363636363" + ); + assert_eq!( + SecretKey::from_str( + "01010101010101010001020304050607ffff0000ffff000063636363636363\ + 63" + ) + .unwrap(), + sk + ); + assert_eq!( + pk.to_string(), + "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" + ); + assert_eq!( + PublicKey::from_str( + "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91dd\ + d166" + ) + .unwrap(), + pk + ); + assert_eq!( + PublicKey::from_str( + "0418845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91dd\ + d16684B84DB303A340CD7D6823EE88174747D12A67D2F8F2F9BA40846EE5EE\ + 7A44F6" + ) + .unwrap(), + pk + ); + + assert!(SecretKey::from_str( + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ) + .is_err()); + assert!(SecretKey::from_str( + "01010101010101010001020304050607ffff0000ffff0000636363636363636363" + ) + .is_err()); + assert!(SecretKey::from_str( + "01010101010101010001020304050607ffff0000ffff0000636363636363636" + ) + .is_err()); + assert!(SecretKey::from_str( + "01010101010101010001020304050607ffff0000ffff000063636363636363" + ) + .is_err()); + assert!(SecretKey::from_str( + "01010101010101010001020304050607ffff0000ffff000063636363636363xx" + ) + .is_err()); + assert!(PublicKey::from_str( + "03000000000000000000000000000000000000000000000000000000000000000 + 00" + ) + .is_err()); + assert!(PublicKey::from_str( + "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166\ + 01" + ) + .is_err()); + assert!(PublicKey::from_str( + "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16" + ) + .is_err()); + assert!(PublicKey::from_str( + "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + assert!(PublicKey::from_str( + "xx0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + + let long_str = "a".repeat(1024 * 1024); + assert!(SecretKey::from_str(&long_str).is_err()); + assert!(PublicKey::from_str(&long_str).is_err()); + } + + #[test] + // In fuzzing mode the Y coordinate is expected to match the X, so this + // test uses invalid public keys. + #[cfg(not(secp256k1_fuzz))] + #[cfg(all(feature = "alloc", feature = "rand"))] + fn test_pubkey_serialize() { + let s = Secp256k1::new(); + let (_, pk1) = s.generate_keypair(&mut StepRng::new(1, 1)); + assert_eq!( + &pk1.serialize_uncompressed()[..], + &[ + 4, 124, 121, 49, 14, 253, 63, 197, 50, 39, 194, 107, 17, 193, + 219, 108, 154, 126, 9, 181, 248, 2, 12, 149, 233, 198, 71, 149, + 134, 250, 184, 154, 229, 185, 28, 165, 110, 27, 3, 162, 126, + 238, 167, 157, 242, 221, 76, 251, 237, 34, 231, 72, 39, 245, 3, + 191, 64, 111, 170, 117, 103, 82, 28, 102, 163 + ][..] + ); + assert_eq!( + &pk1.serialize()[..], + &[ + 3, 124, 121, 49, 14, 253, 63, 197, 50, 39, 194, 107, 17, 193, + 219, 108, 154, 126, 9, 181, 248, 2, 12, 149, 233, 198, 71, 149, + 134, 250, 184, 154, 229 + ][..] + ); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn tweak_add_arbitrary_data() { + let s = Secp256k1::new(); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + assert_eq!(PublicKey::from_secret_key(&s, &sk), pk); // Sanity check. + + // TODO: This would be better tested with a _lot_ of different tweaks. + let tweak = Scalar::random(); + + let tweaked_sk = sk.add_tweak(&tweak).unwrap(); + assert_ne!(sk, tweaked_sk); // Make sure we did something. + let tweaked_pk = pk.add_exp_tweak(&s, &tweak).unwrap(); + assert_ne!(pk, tweaked_pk); + + assert_eq!(PublicKey::from_secret_key(&s, &tweaked_sk), tweaked_pk); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn tweak_add_zero() { + let s = Secp256k1::new(); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + + let tweak = Scalar::ZERO; + + let tweaked_sk = sk.add_tweak(&tweak).unwrap(); + assert_eq!(sk, tweaked_sk); // Tweak by zero does nothing. + let tweaked_pk = pk.add_exp_tweak(&s, &tweak).unwrap(); + assert_eq!(pk, tweaked_pk); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn tweak_mul_arbitrary_data() { + let s = Secp256k1::new(); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + assert_eq!(PublicKey::from_secret_key(&s, &sk), pk); // Sanity check. + + // TODO: This would be better tested with a _lot_ of different tweaks. + let tweak = Scalar::random(); + + let tweaked_sk = sk.mul_tweak(&tweak).unwrap(); + assert_ne!(sk, tweaked_sk); // Make sure we did something. + let tweaked_pk = pk.mul_tweak(&s, &tweak).unwrap(); + assert_ne!(pk, tweaked_pk); + + assert_eq!(PublicKey::from_secret_key(&s, &tweaked_sk), tweaked_pk); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn tweak_mul_zero() { + let s = Secp256k1::new(); + let (sk, _) = s.generate_keypair(&mut rand::thread_rng()); + + let tweak = Scalar::ZERO; + assert!(sk.mul_tweak(&tweak).is_err()) + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn test_negation() { + let s = Secp256k1::new(); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + + assert_eq!(PublicKey::from_secret_key(&s, &sk), pk); // Sanity check. + + let neg = sk.negate(); + assert_ne!(sk, neg); + let back_sk = neg.negate(); + assert_eq!(sk, back_sk); + + let neg = pk.negate(&s); + assert_ne!(pk, neg); + let back_pk = neg.negate(&s); + assert_eq!(pk, back_pk); + + assert_eq!(PublicKey::from_secret_key(&s, &back_sk), pk); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn pubkey_hash() { + use std::collections::hash_map::DefaultHasher; + use std::collections::HashSet; + use std::hash::{Hash, Hasher}; + + fn hash<T: Hash>(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() + } + + let s = Secp256k1::new(); + let mut set = HashSet::new(); + const COUNT: usize = 1024; + for _ in 0..COUNT { + let (_, pk) = s.generate_keypair(&mut rand::thread_rng()); + let hash = hash(&pk); + assert!(!set.contains(&hash)); + set.insert(hash); + } + assert_eq!(set.len(), COUNT); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] + fn pubkey_combine() { + let compressed1 = PublicKey::from_slice(&hex!( + "0241cc121c419921942add6db6482fb36243faf83317c866d2a28d8c6d7089f7ba" + )) + .unwrap(); + let compressed2 = PublicKey::from_slice(&hex!( + "02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443" + )) + .unwrap(); + let exp_sum = PublicKey::from_slice(&hex!( + "0384526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f07" + )) + .unwrap(); + + let sum1 = compressed1.combine(&compressed2); + assert!(sum1.is_ok()); + let sum2 = compressed2.combine(&compressed1); + assert!(sum2.is_ok()); + assert_eq!(sum1, sum2); + assert_eq!(sum1.unwrap(), exp_sum); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] + fn pubkey_combine_keys() { + let compressed1 = PublicKey::from_slice(&hex!( + "0241cc121c419921942add6db6482fb36243faf83317c866d2a28d8c6d7089f7ba" + )) + .unwrap(); + let compressed2 = PublicKey::from_slice(&hex!( + "02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443" + )) + .unwrap(); + let compressed3 = PublicKey::from_slice(&hex!( + "03e74897d8644eb3e5b391ca2ab257aec2080f4d1a95cad57e454e47f021168eb0" + )) + .unwrap(); + let exp_sum = PublicKey::from_slice(&hex!( + "0252d73a47f66cf341e5651542f0348f452b7c793af62a6d8bff75ade703a451ad" + )) + .unwrap(); + + let sum1 = PublicKey::combine_keys(&[ + &compressed1, + &compressed2, + &compressed3, + ]); + assert!(sum1.is_ok()); + let sum2 = PublicKey::combine_keys(&[ + &compressed1, + &compressed2, + &compressed3, + ]); + assert!(sum2.is_ok()); + assert_eq!(sum1, sum2); + assert_eq!(sum1.unwrap(), exp_sum); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] + fn pubkey_combine_keys_empty_slice() { + assert!(PublicKey::combine_keys(&[]).is_err()); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn create_pubkey_combine() { + let s = Secp256k1::new(); + + let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng()); + let (sk2, pk2) = s.generate_keypair(&mut rand::thread_rng()); + + let sum1 = pk1.combine(&pk2); + assert!(sum1.is_ok()); + let sum2 = pk2.combine(&pk1); + assert!(sum2.is_ok()); + assert_eq!(sum1, sum2); + + let tweaked = sk1.add_tweak(&Scalar::from(sk2)).unwrap(); + let sksum = PublicKey::from_secret_key(&s, &tweaked); + assert_eq!(Ok(sksum), sum1); + } + + #[cfg(not(secp256k1_fuzz))] + #[test] + #[allow(clippy::nonminimal_bool)] + fn pubkey_equal() { + let pk1 = PublicKey::from_slice(&hex!( + "0241cc121c419921942add6db6482fb36243faf83317c866d2a28d8c6d7089f7ba" + )) + .unwrap(); + let pk2 = pk1; + let pk3 = PublicKey::from_slice(&hex!( + "02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443" + )) + .unwrap(); + + assert_eq!(pk1, pk2); + assert!(pk1 <= pk2); + assert!(pk2 <= pk1); + assert!(!(pk2 < pk1)); + assert!(!(pk1 < pk2)); + + assert!(pk3 > pk1); + assert!(pk1 < pk3); + assert!(pk3 >= pk1); + assert!(pk1 <= pk3); + } + + #[test] + #[cfg(all(feature = "serde", feature = "alloc"))] + fn test_serde() { + use serde_test::{assert_tokens, Configure, Token}; + #[rustfmt::skip] + static SK_BYTES: [u8; 32] = [ + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 3, 4, 5, 6, 7, + 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, + 99, 99, 99, 99, 99, 99, 99, 99 + ]; + static SK_STR: &str = + "01010101010101010001020304050607ffff0000ffff00006363636363636363"; + + #[cfg(secp256k1_fuzz)] + #[rustfmt::skip] + static PK_BYTES: [u8; 33] = [ + 0x02, + 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, + 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, + 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, + 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66, + ]; + static PK_STR: &str = + "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166\ + "; + + #[cfg(not(secp256k1_fuzz))] + let s = Secp256k1::new(); + let sk = SecretKey::from_slice(&SK_BYTES).unwrap(); + + // In fuzzing mode secret->public key derivation is different, so + // hard-code the expected result. + #[cfg(not(secp256k1_fuzz))] + let pk = PublicKey::from_secret_key(&s, &sk); + #[cfg(secp256k1_fuzz)] + let pk = PublicKey::from_slice(&PK_BYTES).expect("pk"); + + assert_tokens( + &sk.compact(), + &[ + Token::Tuple { len: 32 }, + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(0), + Token::U8(1), + Token::U8(2), + Token::U8(3), + Token::U8(4), + Token::U8(5), + Token::U8(6), + Token::U8(7), + Token::U8(0xff), + Token::U8(0xff), + Token::U8(0), + Token::U8(0), + Token::U8(0xff), + Token::U8(0xff), + Token::U8(0), + Token::U8(0), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::TupleEnd, + ], + ); + + assert_tokens(&sk.readable(), &[Token::BorrowedStr(SK_STR)]); + assert_tokens(&sk.readable(), &[Token::Str(SK_STR)]); + assert_tokens(&sk.readable(), &[Token::String(SK_STR)]); + + assert_tokens( + &pk.compact(), + &[ + Token::Tuple { len: 33 }, + Token::U8(0x02), + Token::U8(0x18), + Token::U8(0x84), + Token::U8(0x57), + Token::U8(0x81), + Token::U8(0xf6), + Token::U8(0x31), + Token::U8(0xc4), + Token::U8(0x8f), + Token::U8(0x1c), + Token::U8(0x97), + Token::U8(0x09), + Token::U8(0xe2), + Token::U8(0x30), + Token::U8(0x92), + Token::U8(0x06), + Token::U8(0x7d), + Token::U8(0x06), + Token::U8(0x83), + Token::U8(0x7f), + Token::U8(0x30), + Token::U8(0xaa), + Token::U8(0x0c), + Token::U8(0xd0), + Token::U8(0x54), + Token::U8(0x4a), + Token::U8(0xc8), + Token::U8(0x87), + Token::U8(0xfe), + Token::U8(0x91), + Token::U8(0xdd), + Token::U8(0xd1), + Token::U8(0x66), + Token::TupleEnd, + ], + ); + + assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]); + assert_tokens(&pk.readable(), &[Token::Str(PK_STR)]); + assert_tokens(&pk.readable(), &[Token::String(PK_STR)]); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn test_tweak_add_then_tweak_add_check() { + let s = Secp256k1::new(); + + // TODO: 10 times is arbitrary, we should test this a _lot_ of times. + for _ in 0..10 { + let tweak = Scalar::random(); + + let kp = Keypair::new(&s, &mut rand::thread_rng()); + let (xonly, _) = XOnlyPublicKey::from_keypair(&kp); + + let tweaked_kp = kp + .add_xonly_tweak(&s, &tweak) + .expect("keypair tweak add failed"); + let (tweaked_xonly, parity) = xonly + .add_tweak(&s, &tweak) + .expect("xonly pubkey tweak failed"); + + let (want_tweaked_xonly, tweaked_kp_parity) = + XOnlyPublicKey::from_keypair(&tweaked_kp); + + assert_eq!(tweaked_xonly, want_tweaked_xonly); + assert_eq!(parity, tweaked_kp_parity); + + assert!(xonly.tweak_add_check(&s, &tweaked_xonly, parity, tweak)); + } + } + + #[test] + fn test_from_key_pubkey() { + let kpk1 = PublicKey::from_str( + "02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443\ + ", + ) + .unwrap(); + let kpk2 = PublicKey::from_str( + "0384526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f07\ + ", + ) + .unwrap(); + + let pk1 = XOnlyPublicKey::from(kpk1); + let pk2 = XOnlyPublicKey::from(kpk2); + + assert_eq!(pk1.serialize()[..], kpk1.serialize()[1..]); + assert_eq!(pk2.serialize()[..], kpk2.serialize()[1..]); + } + + #[test] + #[cfg(all(feature = "global-context", feature = "serde"))] + fn test_serde_keypair() { + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_test::{assert_tokens, Configure, Token}; + + use crate::key::Keypair; + use crate::SECP256K1; + + #[rustfmt::skip] + static SK_BYTES: [u8; 32] = [ + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 3, 4, 5, 6, 7, + 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, + 99, 99, 99, 99, 99, 99, 99, 99 + ]; + static SK_STR: &str = + "01010101010101010001020304050607ffff0000ffff00006363636363636363"; + + let sk = Keypair::from_seckey_slice(SECP256K1, &SK_BYTES).unwrap(); + + assert_tokens( + &sk.compact(), + &[ + Token::Tuple { len: 32 }, + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(1), + Token::U8(0), + Token::U8(1), + Token::U8(2), + Token::U8(3), + Token::U8(4), + Token::U8(5), + Token::U8(6), + Token::U8(7), + Token::U8(0xff), + Token::U8(0xff), + Token::U8(0), + Token::U8(0), + Token::U8(0xff), + Token::U8(0xff), + Token::U8(0), + Token::U8(0), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::U8(99), + Token::TupleEnd, + ], + ); + + assert_tokens(&sk.readable(), &[Token::BorrowedStr(SK_STR)]); + assert_tokens(&sk.readable(), &[Token::Str(SK_STR)]); + assert_tokens(&sk.readable(), &[Token::String(SK_STR)]); + } + + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn keys() -> (SecretKey, PublicKey, Keypair, XOnlyPublicKey) { + let secp = Secp256k1::new(); + + #[rustfmt::skip] + static SK_BYTES: [u8; 32] = [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + ]; + + #[rustfmt::skip] + static PK_BYTES: [u8; 32] = [ + 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, + 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, + 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, + 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66 + ]; + + let mut pk_bytes = [0u8; 33]; + pk_bytes[0] = 0x02; // Use positive Y co-ordinate. + pk_bytes[1..].clone_from_slice(&PK_BYTES); + + let sk = + SecretKey::from_slice(&SK_BYTES).expect("failed to parse sk bytes"); + let pk = PublicKey::from_slice(&pk_bytes) + .expect("failed to create pk from iterator"); + let kp = Keypair::from_secret_key(&secp, &sk); + let xonly = XOnlyPublicKey::from_slice(&PK_BYTES) + .expect("failed to get xonly from slice"); + + (sk, pk, kp, xonly) + } + + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn convert_public_key_to_xonly_public_key() { + let (_sk, pk, _kp, want) = keys(); + let (got, parity) = pk.x_only_public_key(); + + assert_eq!(parity, Parity::Even); + assert_eq!(got, want) + } + + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn convert_secret_key_to_public_key() { + let secp = Secp256k1::new(); + + let (sk, want, _kp, _xonly) = keys(); + let got = sk.public_key(&secp); + + assert_eq!(got, want) + } + + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn convert_secret_key_to_x_only_public_key() { + let secp = Secp256k1::new(); + + let (sk, _pk, _kp, want) = keys(); + let (got, parity) = sk.x_only_public_key(&secp); + + assert_eq!(parity, Parity::Even); + assert_eq!(got, want) + } + + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn convert_keypair_to_public_key() { + let (_sk, want, kp, _xonly) = keys(); + let got = kp.public_key(); + + assert_eq!(got, want) + } + + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn convert_keypair_to_x_only_public_key() { + let (_sk, _pk, kp, want) = keys(); + let (got, parity) = kp.x_only_public_key(); + + assert_eq!(parity, Parity::Even); + assert_eq!(got, want) + } + + // SecretKey -> Keypair -> SecretKey + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn roundtrip_secret_key_via_keypair() { + let secp = Secp256k1::new(); + let (sk, _pk, _kp, _xonly) = keys(); + + let kp = sk.keypair(&secp); + let back = kp.secret_key(); + + assert_eq!(back, sk) + } + + // Keypair -> SecretKey -> Keypair + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn roundtrip_keypair_via_secret_key() { + let secp = Secp256k1::new(); + let (_sk, _pk, kp, _xonly) = keys(); + + let sk = kp.secret_key(); + let back = sk.keypair(&secp); + + assert_eq!(back, kp) + } + + // XOnlyPublicKey -> PublicKey -> XOnlyPublicKey + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn roundtrip_x_only_public_key_via_public_key() { + let (_sk, _pk, _kp, xonly) = keys(); + + let pk = xonly.public_key(Parity::Even); + let (back, parity) = pk.x_only_public_key(); + + assert_eq!(parity, Parity::Even); + assert_eq!(back, xonly) + } + + // PublicKey -> XOnlyPublicKey -> PublicKey + #[test] + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + fn roundtrip_public_key_via_x_only_public_key() { + let (_sk, pk, _kp, _xonly) = keys(); + + let (xonly, parity) = pk.x_only_public_key(); + let back = xonly.public_key(parity); + + assert_eq!(back, pk) + } + + #[test] + fn public_key_from_x_only_public_key_and_odd_parity() { + let s = + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166"; + let mut want = String::from("03"); + want.push_str(s); + + let xonly = XOnlyPublicKey::from_str(s) + .expect("failed to parse xonly pubkey string"); + let pk = xonly.public_key(Parity::Odd); + let got = format!("{}", pk); + + assert_eq!(got, want) + } + + #[test] + #[cfg(not(secp256k1_fuzz))] + #[cfg(all(feature = "global-context", feature = "serde"))] + fn test_serde_x_only_pubkey() { + use serde_test::{assert_tokens, Configure, Token}; + + #[rustfmt::skip] + static SK_BYTES: [u8; 32] = [ + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 3, 4, 5, 6, 7, + 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, + 99, 99, 99, 99, 99, 99, 99, 99 + ]; + + static PK_STR: &str = + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166"; + + let kp = + Keypair::from_seckey_slice(crate::SECP256K1, &SK_BYTES).unwrap(); + let (pk, _parity) = XOnlyPublicKey::from_keypair(&kp); + + assert_tokens( + &pk.compact(), + &[ + Token::Tuple { len: 32 }, + Token::U8(0x18), + Token::U8(0x84), + Token::U8(0x57), + Token::U8(0x81), + Token::U8(0xf6), + Token::U8(0x31), + Token::U8(0xc4), + Token::U8(0x8f), + Token::U8(0x1c), + Token::U8(0x97), + Token::U8(0x09), + Token::U8(0xe2), + Token::U8(0x30), + Token::U8(0x92), + Token::U8(0x06), + Token::U8(0x7d), + Token::U8(0x06), + Token::U8(0x83), + Token::U8(0x7f), + Token::U8(0x30), + Token::U8(0xaa), + Token::U8(0x0c), + Token::U8(0xd0), + Token::U8(0x54), + Token::U8(0x4a), + Token::U8(0xc8), + Token::U8(0x87), + Token::U8(0xfe), + Token::U8(0x91), + Token::U8(0xdd), + Token::U8(0xd1), + Token::U8(0x66), + Token::TupleEnd, + ], + ); + + assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]); + assert_tokens(&pk.readable(), &[Token::Str(PK_STR)]); + assert_tokens(&pk.readable(), &[Token::String(PK_STR)]); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn test_keypair_from_str() { + let ctx = crate::Secp256k1::new(); + let keypair = Keypair::new(&ctx, &mut rand::thread_rng()); + let mut buf = [0_u8; constants::SECRET_KEY_SIZE * 2]; // for hex digits + let s = to_hex(&keypair.secret_key().secret_bytes(), &mut buf).unwrap(); + let parsed_key = Keypair::from_str(s).unwrap(); + assert_eq!(parsed_key, keypair); + } + + #[test] + #[cfg(all( + any(feature = "alloc", feature = "global-context"), + feature = "serde" + ))] + fn test_keypair_deserialize_serde() { + let ctx = crate::Secp256k1::new(); + let sec_key_str = + "4242424242424242424242424242424242424242424242424242424242424242"; + let keypair = Keypair::from_seckey_str(&ctx, sec_key_str).unwrap(); + + serde_test::assert_tokens( + &keypair.readable(), + &[Token::String(sec_key_str)], + ); + + let sec_key_bytes = keypair.secret_key().secret_bytes(); + let tokens = std::iter::once(Token::Tuple { len: 32 }) + .chain(sec_key_bytes.iter().copied().map(Token::U8)) + .chain(std::iter::once(Token::TupleEnd)) + .collect::<Vec<_>>(); + serde_test::assert_tokens(&keypair.compact(), &tokens); + } +} + +#[cfg(bench)] +mod benches { + use std::collections::BTreeSet; + + use test::Bencher; + + use crate::constants::GENERATOR_X; + use crate::PublicKey; + + #[bench] + fn bench_pk_ordering(b: &mut Bencher) { + let mut map = BTreeSet::new(); + let mut g_slice = [02u8; 33]; + g_slice[1..].copy_from_slice(&GENERATOR_X); + let g = PublicKey::from_slice(&g_slice).unwrap(); + let mut pk = g; + b.iter(|| { + map.insert(pk); + pk = pk.combine(&pk).unwrap(); + }) + } +} diff --git a/modules/ecash-secp256k1/src/lib.rs b/modules/ecash-secp256k1/src/lib.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/lib.rs @@ -0,0 +1,1218 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Rust bindings for Pieter Wuille's secp256k1 library, which is used for +//! fast and accurate manipulation of ECDSA and Schnorr signatures on the +//! secp256k1 curve. Such signatures are used extensively by the Bitcoin network +//! and its derivatives. +//! +//! To minimize dependencies, some functions are feature-gated. To generate +//! random keys or to re-randomize a context object, compile with the +//! `rand` and `std` features. If you are willing to use these features, we +//! have enabled an additional defense-in-depth sidechannel protection for +//! our context objects, which re-blinds certain operations on secret key +//! data. To de/serialize objects with serde, compile with "serde". +//! **Important**: `serde` encoding is **not** the same as consensus +//! encoding! +//! +//! Where possible, the bindings use the Rust type system to ensure that +//! API usage errors are impossible. For example, the library uses context +//! objects that contain precomputation tables which are created on object +//! construction. Since this is a slow operation (10+ milliseconds, vs ~50 +//! microseconds for typical crypto operations, on a 2.70 Ghz i7-6820HQ) +//! the tables are optional, giving a performance boost for users who only +//! care about signing, only care about verification, or only care about +//! parsing. In the upstream library, if you attempt to sign a message using +//! a context that does not support this, it will trigger an assertion +//! failure and terminate the program. In `rust-secp256k1`, this is caught +//! at compile-time; in fact, it is impossible to compile code that will +//! trigger any assertion failures in the upstream library. +//! +//! ```rust +//! # #[cfg(all(feature = "rand", feature = "hashes", feature = "std"))] { +//! use ecash_secp256k1::rand::rngs::OsRng; +//! use ecash_secp256k1::{Secp256k1, Message}; +//! use ecash_secp256k1::hashes::{sha256, Hash}; +//! +//! let secp = Secp256k1::new(); +//! let (secret_key, public_key) = secp.generate_keypair(&mut OsRng); +//! let digest = sha256::Hash::hash("Hello World!".as_bytes()); +//! let message = Message::from_digest(digest.to_byte_array()); +//! +//! let sig = secp.sign_ecdsa(&message, &secret_key); +//! assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); +//! # } +//! ``` +//! +//! If the "global-context" feature is enabled you have access to an alternate +//! API. +//! +//! ```rust +//! # #[cfg(all(feature = "global-context", +//! # feature = "hashes", +//! # feature = "rand", +//! # feature = "std"))] { +//! use ecash_secp256k1::hashes::{sha256, Hash}; +//! use ecash_secp256k1::{generate_keypair, Message}; +//! +//! let (secret_key, public_key) = generate_keypair(&mut rand::thread_rng()); +//! let digest = sha256::Hash::hash("Hello World!".as_bytes()); +//! let message = Message::from_digest(digest.to_byte_array()); +//! +//! let sig = secret_key.sign_ecdsa(message); +//! assert!(sig.verify(&message, &public_key).is_ok()); +//! # } +//! ``` +//! +//! The above code requires `rust-secp256k1` to be compiled with the `rand`, +//! `hashes`, and `std` feature enabled, to get access to +//! [`generate_keypair`](struct.Secp256k1.html#method.generate_keypair) +//! Alternately, keys and messages can be parsed from slices, like +//! +//! ```rust +//! # #[cfg(feature = "alloc")] { +//! use ecash_secp256k1::{Message, PublicKey, Secp256k1, SecretKey}; +//! # fn compute_hash(_: &[u8]) -> [u8; 32] { [0xab; 32] } +//! +//! let secp = Secp256k1::new(); +//! let secret_key = SecretKey::from_slice(&[0xcd; 32]) +//! .expect("32 bytes, within curve order"); +//! let public_key = PublicKey::from_secret_key(&secp, &secret_key); +//! // If the supplied byte slice was *not* the output of a cryptographic hash +//! // function this would be cryptographically broken. It has been trivially +//! // used in the past to execute attacks. +//! let message = Message::from_digest(compute_hash(b"CSW is not Satoshi")); +//! +//! let sig = secp.sign_ecdsa(&message, &secret_key); +//! assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); +//! # } +//! ``` +//! +//! Users who only want to verify signatures can use a cheaper context, like so: +//! +//! ```rust +//! # #[cfg(feature = "alloc")] { +//! use ecash_secp256k1::{ecdsa, Message, PublicKey, Secp256k1}; +//! +//! let secp = Secp256k1::verification_only(); +//! +//! let public_key = PublicKey::from_slice(&[ +//! 0x02, 0xc6, 0x6e, 0x7d, 0x89, 0x66, 0xb5, 0xc5, 0x55, 0xaf, 0x58, 0x05, +//! 0x98, 0x9d, 0xa9, 0xfb, 0xf8, 0xdb, 0x95, 0xe1, 0x56, 0x31, 0xce, 0x35, +//! 0x8c, 0x3a, 0x17, 0x10, 0xc9, 0x62, 0x67, 0x90, 0x63, +//! ]) +//! .expect( +//! "public keys must be 33 or 65 bytes, serialized according to SEC 2", +//! ); +//! +//! let message = Message::from_digest([ +//! 0xaa, 0xdf, 0x7d, 0xe7, 0x82, 0x03, 0x4f, 0xbe, 0x3d, 0x3d, 0xb2, 0xcb, +//! 0x13, 0xc0, 0xcd, 0x91, 0xbf, 0x41, 0xcb, 0x08, 0xfa, 0xc7, 0xbd, 0x61, +//! 0xd5, 0x44, 0x53, 0xcf, 0x6e, 0x82, 0xb4, 0x50, +//! ]); +//! +//! let sig = ecdsa::Signature::from_compact(&[ +//! 0xdc, 0x4d, 0xc2, 0x64, 0xa9, 0xfe, 0xf1, 0x7a, 0x3f, 0x25, 0x34, 0x49, +//! 0xcf, 0x8c, 0x39, 0x7a, 0xb6, 0xf1, 0x6f, 0xb3, 0xd6, 0x3d, 0x86, 0x94, +//! 0x0b, 0x55, 0x86, 0x82, 0x3d, 0xfd, 0x02, 0xae, 0x3b, 0x46, 0x1b, 0xb4, +//! 0x33, 0x6b, 0x5e, 0xcb, 0xae, 0xfd, 0x66, 0x27, 0xaa, 0x92, 0x2e, 0xfc, +//! 0x04, 0x8f, 0xec, 0x0c, 0x88, 0x1c, 0x10, 0xc4, 0xc9, 0x42, 0x8f, 0xca, +//! 0x69, 0xc1, 0x32, 0xa2, +//! ]) +//! .expect("compact signatures are 64 bytes; DER signatures are 68-72 bytes"); +//! +//! # #[cfg(not(secp256k1_fuzz))] +//! assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); +//! # } +//! ``` +//! +//! Observe that the same code using, say +//! [`signing_only`](struct.Secp256k1.html#method.signing_only) to generate a +//! context would simply not compile. +//! +//! ## Crate features/optional dependencies +//! +//! This crate provides the following opt-in Cargo features: +//! +//! * `std` - use standard Rust library, enabled by default. +//! * `alloc` - use the `alloc` standard Rust library to provide heap +//! allocations. +//! * `rand` - use `rand` library to provide random generator (e.g. to generate +//! keys). +//! * `hashes` - use the `hashes` library. +//! * `recovery` - enable functions that can compute the public key from +//! signature. +//! * `lowmemory` - optimize the library for low-memory environments. +//! * `global-context` - enable use of global secp256k1 context (implies `std`). +//! * `serde` - implements serialization and deserialization for types in this +//! crate using `serde`. **Important**: `serde` encoding is **not** the same +//! as consensus encoding! + +// Coding conventions +#![deny(non_upper_case_globals, non_camel_case_types, non_snake_case)] +#![warn( + missing_docs, + missing_copy_implementations, + missing_debug_implementations +)] +#![cfg_attr(all(not(test), not(feature = "std")), no_std)] +// Experimental features we need. +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(bench, feature(test))] + +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(any(test, feature = "std"))] +extern crate core; +#[cfg(bench)] +extern crate test; + +#[cfg(feature = "hashes")] +pub extern crate hashes; + +#[macro_use] +mod macros; +#[macro_use] +mod secret; +mod context; +mod key; + +pub mod constants; +pub mod ecdh; +pub mod ecdsa; +pub mod scalar; +pub mod schnorr; +pub mod schnorrabc; +#[cfg(feature = "serde")] +mod serde_util; + +use core::marker::PhantomData; +use core::ptr::NonNull; +use core::{fmt, mem, str}; + +#[cfg(all(feature = "global-context", feature = "std"))] +pub use context::global::{self, SECP256K1}; +#[cfg(feature = "rand")] +pub use rand; +pub use secp256k1_sys as ffi; +#[cfg(feature = "serde")] +pub use serde; + +#[cfg(feature = "alloc")] +pub use crate::context::{All, SignOnly, VerifyOnly}; +pub use crate::context::{ + AllPreallocated, Context, PreallocatedContext, SignOnlyPreallocated, + Signing, Verification, VerifyOnlyPreallocated, +}; +use crate::ffi::types::AlignedType; +use crate::ffi::CPtr; +pub use crate::key::{ + InvalidParityValue, Keypair, Parity, PublicKey, SecretKey, XOnlyPublicKey, +}; +pub use crate::scalar::Scalar; + +/// Trait describing something that promises to be a 32-byte uniformly random +/// number. +/// +/// In particular, anything implementing this trait must have neglibile +/// probability of being zero, overflowing the group order, or equalling any +/// specific value. +/// +/// Since version 0.29 this has been deprecated; users should instead implement +/// `Into<Message>` for types that satisfy these properties. +#[deprecated( + since = "0.29.0", + note = "Please see v0.29.0 rust-secp256k1/CHANGELOG.md for suggestion" +)] +pub trait ThirtyTwoByteHash { + /// Converts the object into a 32-byte array + fn into_32(self) -> [u8; 32]; +} + +/// A (hashed) message input to an ECDSA signature. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Message([u8; constants::MESSAGE_SIZE]); +impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE); +impl_pretty_debug!(Message); + +impl Message { + /// Creates a [`Message`] from a 32 byte slice `digest`. + /// + /// Converts a `MESSAGE_SIZE`-byte slice to a message object. **WARNING:** + /// the slice has to be a cryptographically secure hash of the actual + /// message that's going to be signed. Otherwise the result of signing + /// isn't a secure signature, see + /// <https://twitter.com/pwuille/status/1063582706288586752>. + #[inline] + #[deprecated(since = "0.28.0", note = "use from_digest instead")] + pub fn from_slice(digest: &[u8]) -> Result<Message, Error> { + #[allow(deprecated)] + Message::from_digest_slice(digest) + } + + /// Creates a [`Message`] from a `digest`. + /// + /// The `digest` array has to be a cryptographically secure hash of the + /// actual message that's going to be signed. Otherwise the result of + /// signing isn't a [secure signature]. + /// + /// [secure signature]: + /// <https://twitter.com/pwuille/status/1063582706288586752> + #[inline] + pub fn from_digest(digest: [u8; 32]) -> Message { + Message(digest) + } + + /// Creates a [`Message`] from a 32 byte slice `digest`. + /// + /// The slice has to be 32 bytes long and be a cryptographically secure hash + /// of the actual message that's going to be signed. Otherwise the + /// result of signing isn't a [secure signature]. + /// + /// This method is deprecated. It's best to use [`Message::from_digest`] + /// directly with an array. If your hash engine doesn't return an array + /// for some reason use `.try_into()` on its output. + /// + /// # Errors + /// + /// If `digest` is not exactly 32 bytes long. + /// + /// [secure signature]: + /// <https://twitter.com/pwuille/status/1063582706288586752> + #[inline] + #[deprecated(since = "TBD", note = "use from_digest instead")] + pub fn from_digest_slice(digest: &[u8]) -> Result<Message, Error> { + Ok(Message::from_digest( + digest.try_into().map_err(|_| Error::InvalidMessage)?, + )) + } +} + +#[allow(deprecated)] +impl<T: ThirtyTwoByteHash> From<T> for Message { + /// Converts a 32-byte hash directly to a message without error paths. + fn from(t: T) -> Message { + Message(t.into_32()) + } +} + +impl fmt::LowerHex for Message { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for byte in self.0.iter() { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } +} + +impl fmt::Display for Message { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +/// The main error type for this library. +#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)] +pub enum Error { + /// Signature failed verification. + IncorrectSignature, + /// Bad sized message ("messages" are actually fixed-sized digests + /// [`constants::MESSAGE_SIZE`]). + InvalidMessage, + /// Bad public key. + InvalidPublicKey, + /// Bad signature. + InvalidSignature, + /// Bad secret key. + InvalidSecretKey, + /// Bad shared secret. + InvalidSharedSecret, + /// Bad recovery id. + InvalidRecoveryId, + /// Tried to add/multiply by an invalid tweak. + InvalidTweak, + /// Didn't pass enough memory to context creation with preallocated memory. + NotEnoughMemory, + /// Bad set of public keys. + InvalidPublicKeySum, + /// The only valid parity values are 0 or 1. + InvalidParityValue(key::InvalidParityValue), + /// Bad EllSwift value + InvalidEllSwift, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + use Error::*; + + match *self { + IncorrectSignature => f.write_str("signature failed verification"), + InvalidMessage => { + f.write_str("message was not 32 bytes (do you need to hash?)") + } + InvalidPublicKey => f.write_str("malformed public key"), + InvalidSignature => f.write_str("malformed signature"), + InvalidSecretKey => { + f.write_str("malformed or out-of-range secret key") + } + InvalidSharedSecret => { + f.write_str("malformed or out-of-range shared secret") + } + InvalidRecoveryId => f.write_str("bad recovery id"), + InvalidTweak => f.write_str("bad tweak"), + NotEnoughMemory => f.write_str("not enough memory allocated"), + InvalidPublicKeySum => f.write_str( + "the sum of public keys was invalid or the input vector \ + lengths was less than 1", + ), + InvalidParityValue(e) => write_err!(f, "couldn't create parity"; e), + InvalidEllSwift => f.write_str("malformed EllSwift value"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::IncorrectSignature => None, + Error::InvalidMessage => None, + Error::InvalidPublicKey => None, + Error::InvalidSignature => None, + Error::InvalidSecretKey => None, + Error::InvalidSharedSecret => None, + Error::InvalidRecoveryId => None, + Error::InvalidTweak => None, + Error::NotEnoughMemory => None, + Error::InvalidPublicKeySum => None, + Error::InvalidParityValue(error) => Some(error), + Error::InvalidEllSwift => None, + } + } +} + +/// The secp256k1 engine, used to execute all signature operations. +pub struct Secp256k1<C: Context> { + ctx: NonNull<ffi::Context>, + phantom: PhantomData<C>, +} + +// The underlying secp context does not contain any references to memory it does +// not own. +unsafe impl<C: Context> Send for Secp256k1<C> {} +// The API does not permit any mutation of `Secp256k1` objects except through +// `&mut` references. +unsafe impl<C: Context> Sync for Secp256k1<C> {} + +impl<C: Context> PartialEq for Secp256k1<C> { + fn eq(&self, _other: &Secp256k1<C>) -> bool { + true + } +} + +impl<C: Context> Eq for Secp256k1<C> {} + +impl<C: Context> Drop for Secp256k1<C> { + fn drop(&mut self) { + unsafe { + let size = ffi::secp256k1_context_preallocated_clone_size( + self.ctx.as_ptr(), + ); + ffi::secp256k1_context_preallocated_destroy(self.ctx); + + C::deallocate(self.ctx.as_ptr() as _, size); + } + } +} + +impl<C: Context> fmt::Debug for Secp256k1<C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<secp256k1 context {:?}, {}>", self.ctx, C::DESCRIPTION) + } +} + +impl<C: Context> Secp256k1<C> { + /// Getter for the raw pointer to the underlying secp256k1 context. This + /// shouldn't be needed with normal usage of the library. It enables + /// extending the Secp256k1 with more cryptographic algorithms outside of + /// this crate. + pub fn ctx(&self) -> NonNull<ffi::Context> { + self.ctx + } + + /// Returns the required memory for a preallocated context buffer in a + /// generic manner(sign/verify/all). + pub fn preallocate_size_gen() -> usize { + let word_size = mem::size_of::<AlignedType>(); + let bytes = + unsafe { ffi::secp256k1_context_preallocated_size(C::FLAGS) }; + + (bytes + word_size - 1) / word_size + } + + /// (Re)randomizes the Secp256k1 context for extra sidechannel resistance. + /// + /// Requires compilation with "rand" feature. See comment by Gregory Maxwell + /// in [libsecp256k1](https://github.com/bitcoin-core/secp256k1) commit + /// d2275795ff22a6f4738869f5528fbbb61738aa48. + #[cfg(feature = "rand")] + pub fn randomize<R: rand::Rng + ?Sized>(&mut self, rng: &mut R) { + let mut seed = [0u8; 32]; + rng.fill_bytes(&mut seed); + self.seeded_randomize(&seed); + } + + /// (Re)randomizes the Secp256k1 context for extra sidechannel resistance + /// given 32 bytes of cryptographically-secure random data; + /// see comment in libsecp256k1 commit d2275795f by Gregory Maxwell. + pub fn seeded_randomize(&mut self, seed: &[u8; 32]) { + unsafe { + let err = + ffi::secp256k1_context_randomize(self.ctx, seed.as_c_ptr()); + // This function cannot fail; it has an error return for + // future-proofing. We do not expose this error since it + // is impossible to hit, and we have precedent for not + // exposing impossible errors (for example in + // `PublicKey::from_secret_key` where it is impossible to create an + // invalid secret key through the API.) + // However, if this DOES fail, the result is potentially weaker + // side-channel resistance, which is deadly and + // undetectable, so we take out the entire thread to be + // on the safe side. + assert_eq!(err, 1); + } + } +} + +impl<C: Signing> Secp256k1<C> { + /// Generates a random keypair. Convenience function for [`SecretKey::new`] + /// and [`PublicKey::from_secret_key`]. + #[inline] + #[cfg(feature = "rand")] + pub fn generate_keypair<R: rand::Rng + ?Sized>( + &self, + rng: &mut R, + ) -> (key::SecretKey, key::PublicKey) { + let sk = key::SecretKey::new(rng); + let pk = key::PublicKey::from_secret_key(self, &sk); + (sk, pk) + } +} + +/// Generates a random keypair using the global [`SECP256K1`] context. +#[inline] +#[cfg(all(feature = "global-context", feature = "rand"))] +pub fn generate_keypair<R: rand::Rng + ?Sized>( + rng: &mut R, +) -> (key::SecretKey, key::PublicKey) { + SECP256K1.generate_keypair(rng) +} + +/// Utility function used to parse hex into a target u8 buffer. Returns +/// the number of bytes converted or an error if it encounters an invalid +/// character or unexpected end of string. +fn from_hex(hex: &str, target: &mut [u8]) -> Result<usize, ()> { + if hex.len() % 2 == 1 || hex.len() > target.len() * 2 { + return Err(()); + } + + let mut b = 0; + let mut idx = 0; + for c in hex.bytes() { + b <<= 4; + match c { + b'A'..=b'F' => b |= c - b'A' + 10, + b'a'..=b'f' => b |= c - b'a' + 10, + b'0'..=b'9' => b |= c - b'0', + _ => return Err(()), + } + if (idx & 1) == 1 { + target[idx / 2] = b; + b = 0; + } + idx += 1; + } + Ok(idx / 2) +} + +/// Utility function used to encode hex into a target u8 buffer. Returns +/// a reference to the target buffer as an str. Returns an error if the target +/// buffer isn't big enough. +#[inline] +fn to_hex<'a>(src: &[u8], target: &'a mut [u8]) -> Result<&'a str, ()> { + let hex_len = src.len() * 2; + if target.len() < hex_len { + return Err(()); + } + const HEX_TABLE: [u8; 16] = *b"0123456789abcdef"; + + let mut i = 0; + for &b in src { + target[i] = HEX_TABLE[usize::from(b >> 4)]; + target[i + 1] = HEX_TABLE[usize::from(b & 0b00001111)]; + i += 2; + } + let result = &target[..hex_len]; + debug_assert!(str::from_utf8(result).is_ok()); + return unsafe { Ok(str::from_utf8_unchecked(result)) }; +} + +#[cfg(feature = "rand")] +pub(crate) fn random_32_bytes<R: rand::Rng + ?Sized>(rng: &mut R) -> [u8; 32] { + let mut ret = [0u8; 32]; + rng.fill(&mut ret); + ret +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use hex_lit::hex; + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + use super::*; + + #[ignore] + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + // In rustc 1.72 this Clippy lint was pulled out of clippy and into rustc, + // and was made deny-by-default, breaking compilation of this test. + // Aside from this breaking change, which there is no point in bugging, + // the rename was done so clumsily that you need four separate "allow"s + // to disable this wrong lint. + #[allow(unknown_lints)] + #[allow(renamed_and_removed_lints)] + #[allow(undropped_manually_drops)] + #[allow(clippy::unknown_manually_drops)] + fn test_raw_ctx() { + use std::mem::{forget, ManuallyDrop}; + + let ctx_full = Secp256k1::new(); + let ctx_sign = Secp256k1::signing_only(); + let ctx_vrfy = Secp256k1::verification_only(); + + let full = unsafe { Secp256k1::from_raw_all(ctx_full.ctx) }; + let sign = unsafe { Secp256k1::from_raw_signing_only(ctx_sign.ctx) }; + let mut vrfy = + unsafe { Secp256k1::from_raw_verification_only(ctx_vrfy.ctx) }; + + let (sk, pk) = full.generate_keypair(&mut rand::thread_rng()); + let msg = Message::from_digest([2u8; 32]); + // Try signing + assert_eq!(sign.sign_ecdsa(&msg, &sk), full.sign_ecdsa(&msg, &sk)); + let sig = full.sign_ecdsa(&msg, &sk); + + // Try verifying + assert!(vrfy.verify_ecdsa(&msg, &sig, &pk).is_ok()); + assert!(full.verify_ecdsa(&msg, &sig, &pk).is_ok()); + + // The following drop will have no effect; in fact, they will trigger a + // compiler error because manually dropping a `ManuallyDrop` is + // almost certainly incorrect. If you want to drop the inner + // object you should called `ManuallyDrop::drop`. + drop(full); + // This will actually drop the context, though it will leave `full` + // accessible and in an invalid state. However, this is almost + // certainly what you want to do. + drop(ctx_full); + unsafe { + // Need to compute the allocation size, and need to do so *before* + // dropping anything. + let sz = ffi::secp256k1_context_preallocated_clone_size( + ctx_sign.ctx.as_ptr(), + ); + // We can alternately drop the `ManuallyDrop` by unwrapping it and + // then letting it be dropped. This is actually a safe + // function, but it will destruct the underlying context + // without deallocating it... + ManuallyDrop::into_inner(sign); + // ...leaving us holding the bag to deallocate the context's memory + // without double-calling `secp256k1_context_destroy`, + // which cannot be done safely. + SignOnly::deallocate(ctx_sign.ctx.as_ptr() as *mut u8, sz); + forget(ctx_sign); + } + + unsafe { + // Finally, we can call `ManuallyDrop::drop`, which has the same + // effect, but + let sz = ffi::secp256k1_context_preallocated_clone_size( + ctx_vrfy.ctx.as_ptr(), + ); + // leaves the `ManuallyDrop` itself accessible. This is marked + // unsafe. + ManuallyDrop::drop(&mut vrfy); + VerifyOnly::deallocate(ctx_vrfy.ctx.as_ptr() as *mut u8, sz); + forget(ctx_vrfy); + } + } + + #[cfg(not(target_arch = "wasm32"))] + #[test] + #[ignore] + // Panicking from C may trap (SIGILL) intentionally, so we test this + // manually. + #[cfg(feature = "alloc")] + fn test_panic_raw_ctx_should_terminate_abnormally() { + // Trying to use an all-zeros public key should cause an ARG_CHECK to + // trigger. + let pk = PublicKey::from(unsafe { ffi::PublicKey::new() }); + pk.serialize(); + } + + #[test] + #[ignore] + #[cfg(all(feature = "rand", feature = "std"))] + fn test_preallocation() { + use crate::ffi::types::AlignedType; + + let mut buf_ful = + vec![AlignedType::zeroed(); Secp256k1::preallocate_size()]; + let mut buf_sign = + vec![AlignedType::zeroed(); Secp256k1::preallocate_signing_size()]; + let mut buf_vfy = vec![ + AlignedType::zeroed(); + Secp256k1::preallocate_verification_size() + ]; + + let full = Secp256k1::preallocated_new(&mut buf_ful).unwrap(); + let sign = Secp256k1::preallocated_signing_only(&mut buf_sign).unwrap(); + // Use after free? + let vrfy = + Secp256k1::preallocated_verification_only(&mut buf_vfy).unwrap(); + + let (sk, pk) = full.generate_keypair(&mut rand::thread_rng()); + let msg = Message::from_digest([2u8; 32]); + // Try signing + assert_eq!(sign.sign_ecdsa(&msg, &sk), full.sign_ecdsa(&msg, &sk)); + let sig = full.sign_ecdsa(&msg, &sk); + + // Try verifying + assert!(vrfy.verify_ecdsa(&msg, &sig, &pk).is_ok()); + assert!(full.verify_ecdsa(&msg, &sig, &pk).is_ok()); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn capabilities() { + let sign = Secp256k1::signing_only(); + let vrfy = Secp256k1::verification_only(); + let full = Secp256k1::new(); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest(msg); + + // Try key generation + let (sk, pk) = full.generate_keypair(&mut rand::thread_rng()); + + // Try signing + assert_eq!(sign.sign_ecdsa(&msg, &sk), full.sign_ecdsa(&msg, &sk)); + let sig = full.sign_ecdsa(&msg, &sk); + + // Try verifying + assert!(vrfy.verify_ecdsa(&msg, &sig, &pk).is_ok()); + assert!(full.verify_ecdsa(&msg, &sig, &pk).is_ok()); + + // Check that we can produce keys from slices with no precomputation + let (pk_slice, sk_slice) = (&pk.serialize(), &sk[..]); + let new_pk = PublicKey::from_slice(pk_slice).unwrap(); + let new_sk = SecretKey::from_slice(sk_slice).unwrap(); + assert_eq!(sk, new_sk); + assert_eq!(pk, new_pk); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn signature_serialize_roundtrip() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + for _ in 0..100 { + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest(msg); + + let (sk, _) = s.generate_keypair(&mut rand::thread_rng()); + let sig1 = s.sign_ecdsa(&msg, &sk); + let der = sig1.serialize_der(); + let sig2 = ecdsa::Signature::from_der(&der[..]).unwrap(); + assert_eq!(sig1, sig2); + + let compact = sig1.serialize_compact(); + let sig2 = ecdsa::Signature::from_compact(&compact[..]).unwrap(); + assert_eq!(sig1, sig2); + + assert!(ecdsa::Signature::from_compact(&der[..]).is_err()); + assert!(ecdsa::Signature::from_compact(&compact[0..4]).is_err()); + assert!(ecdsa::Signature::from_der(&compact[..]).is_err()); + assert!(ecdsa::Signature::from_der(&der[0..4]).is_err()); + } + } + + #[test] + fn signature_display() { + const HEX_STR: &str = + "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba3\ + 2d751c0f7acb21ac8a0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83\ + ee297c7a5587b2011c4fcc72eab45"; + let byte_str = hex!(HEX_STR); + + assert_eq!( + ecdsa::Signature::from_der(&byte_str).expect("byte str decode"), + ecdsa::Signature::from_str(HEX_STR).expect("byte str decode") + ); + + let sig = ecdsa::Signature::from_str(HEX_STR).expect("byte str decode"); + assert_eq!(&sig.to_string(), HEX_STR); + assert_eq!(&format!("{:?}", sig), HEX_STR); + + assert!(ecdsa::Signature::from_str( + "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a\ + 0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011\ + c4fcc72eab4" + ) + .is_err()); + assert!(ecdsa::Signature::from_str( + "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a\ + 0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011\ + c4fcc72eab" + ) + .is_err()); + assert!(ecdsa::Signature::from_str( + "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a\ + 0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011\ + c4fcc72eabxx" + ) + .is_err()); + assert!(ecdsa::Signature::from_str( + "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a\ + 0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011\ + c4fcc72eab4572022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a558\ + 7b2011c4fcc72eab4572022100e89bd46bb3a5a62adc679f659b7ce876d83ee297\ + c7a5587b2011c4fcc72eab4572022100e89bd46bb3a5a62adc679f659b7ce876d8\ + 3ee297c7a5587b2011c4fcc72eab4572022100e89bd46bb3a5a62adc679f659b7c\ + e876d83ee297c7a5587b2011c4fcc72eab45" + ) + .is_err()); + + // 71 byte signature + let hex_str = + "30450221009d0bad576719d32ae76bedb34c774866673cbde3f4e12951555c9408\ + e6ce774b02202876e7102f204f6bfee26c967c3926ce702cf97d4b010062e193f7\ + 63190f6776"; + let sig = ecdsa::Signature::from_str(hex_str).expect("byte str decode"); + assert_eq!(&format!("{}", sig), hex_str); + } + + #[test] + fn signature_lax_der() { + macro_rules! check_lax_sig( + ($hex:expr) => ({ + let sig = hex!($hex); + assert!(ecdsa::Signature::from_der_lax(&sig[..]).is_ok()); + }) + ); + + check_lax_sig!( + "304402204c2dd8a9b6f8d425fcd8ee9a20ac73b619906a6367eac6cb93e7037522\ + 5ec0160220356878eff111ff3663d7e6bf08947f94443845e0dcc54961664d922f\ + 7660b80c" + ); + check_lax_sig!( + "304402202ea9d51c7173b1d96d331bd41b3d1b4e78e66148e64ed5992abd6ca662\ + 90321c0220628c47517e049b3e41509e9d71e480a0cdc766f8cdec265ef0017711\ + c1b5336f" + ); + check_lax_sig!( + "3045022100bf8e050c85ffa1c313108ad8c482c4849027937916374617af3f2e9a\ + 881861c9022023f65814222cab09d5ec41032ce9c72ca96a5676020736614de7b7\ + 8a4e55325a" + ); + check_lax_sig!( + "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a\ + 0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011\ + c4fcc72eab45" + ); + check_lax_sig!( + "3046022100eaa5f90483eb20224616775891397d47efa64c68b969db1dacb1c30a\ + cdfc50aa022100cf9903bbefb1c8000cf482b0aeeb5af19287af20bd794de11d82\ + 716f9bae3db1" + ); + check_lax_sig!( + "3045022047d512bc85842ac463ca3b669b62666ab8672ee60725b6c06759e476ce\ + bdc6c102210083805e93bd941770109bcc797784a71db9e48913f702c56e60b1c3\ + e2ff379a60" + ); + check_lax_sig!( + "3044022023ee4e95151b2fbbb08a72f35babe02830d14d54bd7ed1320e4751751d\ + 1baa4802206235245254f58fd1be6ff19ca291817da76da65c2f6d81d654b5185d\ + d86b8acf" + ); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn sign_and_verify_ecdsa() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let noncedata = [42u8; 32]; + for _ in 0..100 { + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest(msg); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + let sig = s.sign_ecdsa(&msg, &sk); + assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Ok(())); + let noncedata_sig = + s.sign_ecdsa_with_noncedata(&msg, &sk, &noncedata); + assert_eq!(s.verify_ecdsa(&msg, &noncedata_sig, &pk), Ok(())); + let low_r_sig = s.sign_ecdsa_low_r(&msg, &sk); + assert_eq!(s.verify_ecdsa(&msg, &low_r_sig, &pk), Ok(())); + let grind_r_sig = s.sign_ecdsa_grind_r(&msg, &sk, 1); + assert_eq!(s.verify_ecdsa(&msg, &grind_r_sig, &pk), Ok(())); + let compact = sig.serialize_compact(); + if compact[0] < 0x80 { + assert_eq!(sig, low_r_sig); + } else { + // mocked sig generation doesn't produce low-R sigs + #[cfg(not(secp256k1_fuzz))] + assert_ne!(sig, low_r_sig); + } + // mocked sig generation doesn't produce low-R sigs + #[cfg(not(secp256k1_fuzz))] + assert!(ecdsa::compact_sig_has_zero_first_bit(&low_r_sig.0)); + // mocked sig generation doesn't produce low-R sigs + #[cfg(not(secp256k1_fuzz))] + assert!(ecdsa::der_length_check(&grind_r_sig.0, 70)); + } + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn sign_and_verify_extreme() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + // Wild keys: 1, CURVE_ORDER - 1 + // Wild msgs: 1, CURVE_ORDER - 1 + let mut wild_keys = [[0u8; 32]; 2]; + let mut wild_msgs = [[0u8; 32]; 2]; + + wild_keys[0][0] = 1; + wild_msgs[0][0] = 1; + + use constants; + wild_keys[1][..].copy_from_slice(&constants::CURVE_ORDER[..]); + wild_msgs[1][..].copy_from_slice(&constants::CURVE_ORDER[..]); + + wild_keys[1][0] -= 1; + wild_msgs[1][0] -= 1; + + for key in wild_keys + .iter() + .map(|k| SecretKey::from_slice(&k[..]).unwrap()) + { + for msg in wild_msgs.into_iter().map(Message::from_digest) { + let sig = s.sign_ecdsa(&msg, &key); + let low_r_sig = s.sign_ecdsa_low_r(&msg, &key); + let grind_r_sig = s.sign_ecdsa_grind_r(&msg, &key, 1); + let pk = PublicKey::from_secret_key(&s, &key); + assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Ok(())); + assert_eq!(s.verify_ecdsa(&msg, &low_r_sig, &pk), Ok(())); + assert_eq!(s.verify_ecdsa(&msg, &grind_r_sig, &pk), Ok(())); + } + } + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn sign_and_verify_fail() { + let mut s = Secp256k1::new(); + s.randomize(&mut rand::thread_rng()); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest(msg); + + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + + let sig = s.sign_ecdsa(&msg, &sk); + + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest(msg); + assert_eq!( + s.verify_ecdsa(&msg, &sig, &pk), + Err(Error::IncorrectSignature) + ); + } + + #[test] + #[allow(deprecated)] + fn test_bad_slice() { + assert_eq!( + ecdsa::Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE + 1]), + Err(Error::InvalidSignature) + ); + assert_eq!( + ecdsa::Signature::from_der(&[0; constants::MAX_SIGNATURE_SIZE]), + Err(Error::InvalidSignature) + ); + + assert_eq!( + Message::from_digest_slice(&[0; constants::MESSAGE_SIZE - 1]), + Err(Error::InvalidMessage) + ); + assert_eq!( + Message::from_digest_slice(&[0; constants::MESSAGE_SIZE + 1]), + Err(Error::InvalidMessage) + ); + assert!( + Message::from_digest_slice(&[0; constants::MESSAGE_SIZE]).is_ok() + ); + assert!( + Message::from_digest_slice(&[1; constants::MESSAGE_SIZE]).is_ok() + ); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn test_hex() { + use rand::RngCore; + + use super::to_hex; + + let mut rng = rand::thread_rng(); + const AMOUNT: usize = 1024; + for i in 0..AMOUNT { + // 255 isn't a valid utf8 character. + let mut hex_buf = [255u8; AMOUNT * 2]; + let mut src_buf = [0u8; AMOUNT]; + let mut result_buf = [0u8; AMOUNT]; + let src = &mut src_buf[0..i]; + rng.fill_bytes(src); + + let hex = to_hex(src, &mut hex_buf).unwrap(); + assert_eq!(from_hex(hex, &mut result_buf).unwrap(), i); + assert_eq!(src, &result_buf[..i]); + } + + assert!(to_hex(&[1; 2], &mut [0u8; 3]).is_err()); + assert!(to_hex(&[1; 2], &mut [0u8; 4]).is_ok()); + assert!(from_hex("deadbeaf", &mut [0u8; 3]).is_err()); + assert!(from_hex("deadbeaf", &mut [0u8; 4]).is_ok()); + assert!(from_hex("a", &mut [0u8; 4]).is_err()); + assert!(from_hex("ag", &mut [0u8; 4]).is_err()); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] // fuzz-sigs have fixed size/format + #[cfg(any(feature = "alloc", feature = "std"))] + fn test_noncedata() { + let secp = Secp256k1::new(); + let msg = hex!( + "887d04bb1cf1b1554f1b268dfe62d13064ca67ae45348d50d1392ce2d13418ac" + ); + let msg = Message::from_digest(msg); + let noncedata = [42u8; 32]; + let sk = SecretKey::from_str( + "57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead", + ) + .unwrap(); + let expected_sig = hex!( + "daaf6acb56826c1b98263d7b387e9577cf3bf5eeecdff9da78cb519582b3fa2c3d\ + 7154450694e7e3bcbe83bdf3647c3644fc068f4fce822b54102b2eede4dd5c" + ); + let expected_sig = + ecdsa::Signature::from_compact(&expected_sig).unwrap(); + + let sig = secp.sign_ecdsa_with_noncedata(&msg, &sk, &noncedata); + + assert_eq!(expected_sig, sig); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(any(feature = "alloc", feature = "std"))] + fn test_low_s() { + // nb this is a transaction on testnet + // txid 8ccc87b72d766ab3128f03176bb1c98293f2d1f85ebfaf07b82cc81ea6891fa9 + // input number 3 + let sig = hex!( + "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a\ + 0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011\ + c4fcc72eab45" + ); + let pk = hex!( + "031ee99d2b786ab3b0991325f2de8489246a6a3fdb700f6d0511b1d80cf5f4cd43" + ); + let msg = hex!( + "a4965ca63b7d8562736ceec36dfa5a11bf426eb65be8ea3f7a49ae363032da0d" + ); + + let secp = Secp256k1::new(); + let mut sig = ecdsa::Signature::from_der(&sig[..]).unwrap(); + let pk = PublicKey::from_slice(&pk[..]).unwrap(); + let msg = Message::from_digest(msg); + + // without normalization we expect this will fail + assert_eq!( + secp.verify_ecdsa(&msg, &sig, &pk), + Err(Error::IncorrectSignature) + ); + // after normalization it should pass + sig.normalize_s(); + assert_eq!(secp.verify_ecdsa(&msg, &sig, &pk), Ok(())); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] // fuzz-sigs have fixed size/format + #[cfg(any(feature = "alloc", feature = "std"))] + fn test_low_r() { + let secp = Secp256k1::new(); + let msg = hex!( + "887d04bb1cf1b1554f1b268dfe62d13064ca67ae45348d50d1392ce2d13418ac" + ); + let msg = Message::from_digest(msg); + let sk = SecretKey::from_str( + "57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead", + ) + .unwrap(); + let expected_sig = hex!( + "02bdb3b9178d424b7f3b81f3c4e3f8feb699caac940eca8bb4ed8ce6abc9523244\ + 358e88e70b8e78a1ffe75288440a2ad84c3b79b56606faaea598f0d5ca6f75" + ); + let expected_sig = + ecdsa::Signature::from_compact(&expected_sig).unwrap(); + + let sig = secp.sign_ecdsa_low_r(&msg, &sk); + + assert_eq!(expected_sig, sig); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] // fuzz-sigs have fixed size/format + #[cfg(any(feature = "alloc", feature = "std"))] + fn test_grind_r() { + let secp = Secp256k1::new(); + let msg = hex!( + "ef2d5b9a7c61865a95941d0f04285420560df7e9d76890ac1b8867b12ce43167" + ); + let msg = Message::from_digest(msg); + let sk = SecretKey::from_str( + "848355d75fe1c354cf05539bb29b2015f1863065bcb6766b44d399ab95c3fa0b", + ) + .unwrap(); + let expected_sig = ecdsa::Signature::from_str( + "3043021f4193abeb40007545ccd4c5213a2b4d7ee16af7941ae9a8f47562679601\ + f6a7022014059a75d9cf3fd0b278b796adc7c4560d5216cf40a9a018b84c535ebf\ + 2246d2" + ) + .unwrap(); + + let sig = secp.sign_ecdsa_grind_r(&msg, &sk, 2); + + assert_eq!(expected_sig, sig); + } + + #[cfg(feature = "serde")] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(any(feature = "alloc", feature = "std"))] + #[test] + fn test_serde() { + use serde_test::{assert_tokens, Configure, Token}; + + let s = Secp256k1::new(); + + let msg = Message::from_digest([1; 32]); + let sk = SecretKey::from_slice(&[2; 32]).unwrap(); + let sig = s.sign_ecdsa(&msg, &sk); + static SIG_BYTES: [u8; 71] = [ + 48, 69, 2, 33, 0, 165, 112, 165, 56, 166, 96, 143, 117, 5, 155, 80, + 102, 225, 52, 1, 13, 209, 246, 209, 110, 88, 168, 209, 53, 173, 32, + 177, 155, 197, 72, 135, 167, 2, 32, 77, 17, 27, 229, 166, 73, 176, + 244, 166, 254, 22, 163, 208, 183, 156, 83, 196, 176, 110, 99, 45, + 172, 20, 195, 193, 218, 96, 179, 212, 63, 113, 198, + ]; + static SIG_STR: &str = "\ + 3045022100a570a538a6608f75059b5066e134010dd1f6d16e58a8d135ad20b19b\ + c54887a702204d111be5a649b0f4a6fe16a3d0b79c53c4b06e632dac14c3c1da60\ + b3d43f71c6\ + "; + + assert_tokens(&sig.compact(), &[Token::BorrowedBytes(&SIG_BYTES[..])]); + assert_tokens(&sig.compact(), &[Token::Bytes(&SIG_BYTES)]); + assert_tokens(&sig.compact(), &[Token::ByteBuf(&SIG_BYTES)]); + + assert_tokens(&sig.readable(), &[Token::BorrowedStr(SIG_STR)]); + assert_tokens(&sig.readable(), &[Token::Str(SIG_STR)]); + assert_tokens(&sig.readable(), &[Token::String(SIG_STR)]); + } + + #[cfg(feature = "global-context")] + #[test] + fn test_global_context() { + use crate::SECP256K1; + let sk_data = hex!( + "e6dd32f8761625f105c39a39f19370b3521d845a12456d60ce44debd0a362641" + ); + let sk = SecretKey::from_slice(&sk_data).unwrap(); + let msg_data = hex!( + "a4965ca63b7d8562736ceec36dfa5a11bf426eb65be8ea3f7a49ae363032da0d" + ); + let msg = Message::from_digest(msg_data); + + // Check usage as explicit parameter + let pk = PublicKey::from_secret_key(SECP256K1, &sk); + + // Check usage as self + let sig = SECP256K1.sign_ecdsa(&msg, &sk); + assert!(SECP256K1.verify_ecdsa(&msg, &sig, &pk).is_ok()); + } +} + +#[cfg(bench)] +#[cfg(all(feature = "rand", feature = "std"))] +mod benches { + use rand::rngs::mock::StepRng; + use test::{black_box, Bencher}; + + use super::{Message, Secp256k1}; + + #[bench] + pub fn generate(bh: &mut Bencher) { + let s = Secp256k1::new(); + let mut r = StepRng::new(1, 1); + bh.iter(|| { + let (sk, pk) = s.generate_keypair(&mut r); + black_box(sk); + black_box(pk); + }); + } + + #[bench] + pub fn bench_sign_ecdsa(bh: &mut Bencher) { + let s = Secp256k1::new(); + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest(msg); + let (sk, _) = s.generate_keypair(&mut rand::thread_rng()); + + bh.iter(|| { + let sig = s.sign_ecdsa(&msg, &sk); + black_box(sig); + }); + } + + #[bench] + pub fn bench_verify_ecdsa(bh: &mut Bencher) { + let s = Secp256k1::new(); + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + let msg = Message::from_digest(msg); + let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); + let sig = s.sign_ecdsa(&msg, &sk); + + bh.iter(|| { + let res = s.verify_ecdsa(&msg, &sig, &pk).unwrap(); + black_box(res); + }); + } +} diff --git a/modules/ecash-secp256k1/src/macros.rs b/modules/ecash-secp256k1/src/macros.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/macros.rs @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: CC0-1.0 + +/// Implement methods and traits for types that contain an inner array. +#[macro_export] +macro_rules! impl_array_newtype { + ($thing:ident, $ty:ty, $len:expr) => { + impl AsRef<[$ty; $len]> for $thing { + #[inline] + /// Gets a reference to the underlying array + fn as_ref(&self) -> &[$ty; $len] { + let &$thing(ref dat) = self; + dat + } + } + + impl<I> core::ops::Index<I> for $thing + where + [$ty]: core::ops::Index<I>, + { + type Output = <[$ty] as core::ops::Index<I>>::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + &self.0[index] + } + } + + impl $crate::ffi::CPtr for $thing { + type Target = $ty; + + fn as_c_ptr(&self) -> *const Self::Target { + let &$thing(ref dat) = self; + dat.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + let &mut $thing(ref mut dat) = self; + dat.as_mut_ptr() + } + } + }; +} + +macro_rules! impl_pretty_debug { + ($thing:ident) => { + impl core::fmt::Debug for $thing { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{}(", stringify!($thing))?; + for i in &self[..] { + write!(f, "{:02x}", i)?; + } + f.write_str(")") + } + } + }; +} + +macro_rules! impl_non_secure_erase { + ($thing:ident, $target:tt, $value:expr) => { + impl $thing { + /// Attempts to erase the contents of the underlying array. + /// + /// Note, however, that the compiler is allowed to freely copy or + /// move the contents of this array to other places in memory. + /// Preventing this behavior is very subtle. For more discussion + /// on this, please see the documentation of the + /// [`zeroize`](https://docs.rs/zeroize) crate. + #[inline] + pub fn non_secure_erase(&mut self) { + secp256k1_sys::non_secure_erase_impl(&mut self.$target, $value); + } + } + }; +} + +/// Formats error. If `std` feature is OFF appends error source (delimited by `: +/// `). We do this because `e.source()` is only available in std builds, without +/// this macro the error source is lost for no-std builds. +macro_rules! write_err { + ($writer:expr, $string:literal $(, $args:expr),*; $source:expr) => { + { + #[cfg(feature = "std")] + { + let _ = &$source; // Prevents clippy warnings. + write!($writer, $string $(, $args)*) + } + #[cfg(not(feature = "std"))] + { + write!($writer, concat!($string, ": {}") $(, $args)*, $source) + } + } + } +} + +/// Implements fast unstable comparison methods for `$ty`. +macro_rules! impl_fast_comparisons { + ($ty:ident) => { + impl $ty { + /// Like `cmp::Cmp` but faster and with no guarantees across library + /// versions. + /// + /// The `Cmp` implementation for FFI types is stable but slow + /// because it first serializes `self` and `other` before + /// comparing them. This function provides a faster comparison + /// if you know that your types come from the same library version. + pub fn cmp_fast_unstable( + &self, + other: &Self, + ) -> core::cmp::Ordering { + self.0.cmp_fast_unstable(&other.0) + } + + /// Like `cmp::Eq` but faster and with no guarantees across library + /// versions. + /// + /// The `Eq` implementation for FFI types is stable but slow because + /// it first serializes `self` and `other` before comparing + /// them. This function provides a faster equality check if you + /// know that your types come from the same library version. + pub fn eq_fast_unstable(&self, other: &Self) -> bool { + self.0.eq_fast_unstable(&other.0) + } + } + }; +} diff --git a/modules/ecash-secp256k1/src/scalar.rs b/modules/ecash-secp256k1/src/scalar.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/scalar.rs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Provides [`Scalar`] and related types. +//! +//! In elliptic curve cryptography scalars are non-point values that can be used +//! to multiply points. The most common type of scalars are private keys. +//! However not all scalars are private keys. They can even be public *values*. +//! To make handling them safer and easier this module provides the `Scalar` +//! type and related. + +use core::{fmt, ops}; + +use crate::constants; + +/// Positive 256-bit integer guaranteed to be less than the secp256k1 curve +/// order. +/// +/// The difference between `SecretKey` and `Scalar` is that `Scalar` doesn't +/// guarantee being securely usable as a private key. +/// +/// **Warning: the operations on this type are NOT constant time!** +/// Using this with secret values is not advised. +// Internal represenation is big endian to match what `libsecp256k1` uses. +// Also easier to implement comparison. +// Debug impl omitted for now, the bytes may be secret +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Scalar([u8; 32]); +impl_pretty_debug!(Scalar); +impl_non_secure_erase!(Scalar, 0, [0u8; 32]); + +const MAX_RAW: [u8; 32] = [ + 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, 0x40, +]; + +impl Scalar { + /// Maximum valid value: `curve_order - 1` + pub const MAX: Scalar = Scalar(MAX_RAW); + /// Scalar representing `1` + pub const ONE: Scalar = Scalar(constants::ONE); + /// Scalar representing `0` + pub const ZERO: Scalar = Scalar(constants::ZERO); + + /// Generates a random scalar + #[cfg(all(feature = "rand", feature = "std"))] + pub fn random() -> Self { + Self::random_custom(rand::thread_rng()) + } + + /// Generates a random scalar using supplied RNG + #[cfg(feature = "rand")] + pub fn random_custom<R: rand::Rng>(mut rng: R) -> Self { + let mut bytes = [0u8; 32]; + loop { + rng.fill_bytes(&mut bytes); + // unlikely to go past MAX + if let Ok(scalar) = Scalar::from_be_bytes(bytes) { + break scalar; + } + } + } + + /// Tries to deserialize from big endian bytes + /// + /// **Security warning:** this function is not constant time! + /// Passing secret data is not recommended. + /// + /// # Errors + /// + /// Returns error when the value is above the curve order. + pub fn from_be_bytes(value: [u8; 32]) -> Result<Self, OutOfRangeError> { + // Lexicographic ordering of arrays of the same length is same as + // ordering of BE numbers + if value <= MAX_RAW { + Ok(Scalar(value)) + } else { + Err(OutOfRangeError {}) + } + } + + /// Tries to deserialize from little endian bytes + /// + /// **Security warning:** this function is not constant time! + /// Passing secret data is not recommended. + /// + /// # Errors + /// + /// Returns error when the value is above the curve order. + pub fn from_le_bytes(mut value: [u8; 32]) -> Result<Self, OutOfRangeError> { + value.reverse(); + Self::from_be_bytes(value) + } + + /// Serializes to big endian bytes + pub fn to_be_bytes(self) -> [u8; 32] { + self.0 + } + + /// Serializes to little endian bytes + pub fn to_le_bytes(self) -> [u8; 32] { + let mut res = self.0; + res.reverse(); + res + } + + // returns a reference to internal bytes + // non-public to not leak the internal representation + pub(crate) fn as_be_bytes(&self) -> &[u8; 32] { + &self.0 + } + + pub(crate) fn as_c_ptr(&self) -> *const u8 { + use secp256k1_sys::CPtr; + + self.as_be_bytes().as_c_ptr() + } +} + +impl<I> ops::Index<I> for Scalar +where + [u8]: ops::Index<I>, +{ + type Output = <[u8] as ops::Index<I>>::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + &self.0[index] + } +} + +impl From<crate::SecretKey> for Scalar { + fn from(value: crate::SecretKey) -> Self { + Scalar(value.secret_bytes()) + } +} + +/// Error returned when the value of scalar is invalid - larger than the curve +/// order. +// Intentionally doesn't implement `Copy` to improve forward compatibility. +// Same reason for `non_exhaustive`. +#[allow(missing_copy_implementations)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[non_exhaustive] +pub struct OutOfRangeError {} + +impl fmt::Display for OutOfRangeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt("the value is not a member of secp256k1 field", f) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for OutOfRangeError {} diff --git a/modules/ecash-secp256k1/src/schnorr.rs b/modules/ecash-secp256k1/src/schnorr.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/schnorr.rs @@ -0,0 +1,1069 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Support for schnorr signatures. + +use core::{fmt, ptr, str}; + +#[cfg(feature = "rand")] +use rand::{CryptoRng, Rng}; +use secp256k1_sys::SchnorrSigExtraParams; + +use crate::ffi::{self, CPtr}; +use crate::key::{Keypair, XOnlyPublicKey}; +#[cfg(feature = "global-context")] +use crate::SECP256K1; +use crate::{constants, from_hex, Error, Secp256k1, Signing, Verification}; + +/// Represents a schnorr signature. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Signature([u8; constants::SCHNORR_SIGNATURE_SIZE]); +impl_array_newtype!(Signature, u8, constants::SCHNORR_SIGNATURE_SIZE); +impl_pretty_debug!(Signature); + +#[cfg(feature = "serde")] +impl serde::Serialize for Signature { + fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { + if s.is_human_readable() { + s.collect_str(self) + } else { + s.serialize_bytes(&self[..]) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for Signature { + fn deserialize<D: serde::Deserializer<'de>>( + d: D, + ) -> Result<Self, D::Error> { + if d.is_human_readable() { + d.deserialize_str(super::serde_util::FromStrVisitor::new( + "a hex string representing 64 byte schnorr signature", + )) + } else { + d.deserialize_bytes(super::serde_util::BytesVisitor::new( + "raw 64 bytes schnorr signature", + Signature::from_slice, + )) + } + } +} + +impl fmt::LowerHex for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in &self.0[..] { + write!(f, "{:02x}", ch)?; + } + Ok(()) + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for Signature { + type Err = Error; + + fn from_str(s: &str) -> Result<Signature, Error> { + let mut res = [0u8; constants::SCHNORR_SIGNATURE_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SCHNORR_SIGNATURE_SIZE) => { + Ok(Signature::from_byte_array(res)) + } + _ => Err(Error::InvalidSignature), + } + } +} + +impl Signature { + /// Construct a `Signature` from a 64 bytes array. + #[inline] + pub fn from_byte_array( + sig: [u8; constants::SCHNORR_SIGNATURE_SIZE], + ) -> Self { + Self(sig) + } + + /// Creates a `Signature` directly from a slice. + #[deprecated(since = "TBD", note = "Use `from_byte_array` instead.")] + #[inline] + pub fn from_slice(data: &[u8]) -> Result<Signature, Error> { + match data.len() { + constants::SCHNORR_SIGNATURE_SIZE => { + let mut ret = [0u8; constants::SCHNORR_SIGNATURE_SIZE]; + ret[..].copy_from_slice(data); + Ok(Signature(ret)) + } + _ => Err(Error::InvalidSignature), + } + } + + /// Returns a signature as a byte array. + #[deprecated(since = "0.30.0", note = "Use `to_byte_array` instead.")] + pub fn serialize(&self) -> [u8; constants::SCHNORR_SIGNATURE_SIZE] { + self.0 + } + + /// Returns a signature as a byte array. + #[inline] + pub fn to_byte_array(self) -> [u8; constants::SCHNORR_SIGNATURE_SIZE] { + self.0 + } + + /// Returns a signature as a byte array. + #[inline] + pub fn as_byte_array(&self) -> &[u8; constants::SCHNORR_SIGNATURE_SIZE] { + &self.0 + } + + /// Verifies a schnorr signature for `msg` using `pk` and the global + /// [`SECP256K1`] context. + #[inline] + #[cfg(feature = "global-context")] + pub fn verify(&self, msg: &[u8], pk: &XOnlyPublicKey) -> Result<(), Error> { + SECP256K1.verify_schnorr(self, msg, pk) + } +} + +impl<C: Signing> Secp256k1<C> { + fn sign_schnorr_helper( + &self, + msg: &[u8], + keypair: &Keypair, + nonce_data: *const ffi::types::c_uchar, + ) -> Signature { + unsafe { + let mut sig = [0u8; constants::SCHNORR_SIGNATURE_SIZE]; + let extra = SchnorrSigExtraParams::new(None, nonce_data.cast()); + assert_eq!( + 1, + ffi::secp256k1_schnorrsig_sign_custom( + self.ctx.as_ptr(), + sig.as_mut_c_ptr(), + msg.as_c_ptr(), + msg.len(), + keypair.as_c_ptr(), + &extra, + ) + ); + + Signature(sig) + } + } + + /// Creates a schnorr signature internally using the + /// [`rand::rngs::ThreadRng`] random number generator to generate the + /// auxiliary random data. + #[cfg(all(feature = "rand", feature = "std"))] + pub fn sign_schnorr(&self, msg: &[u8], keypair: &Keypair) -> Signature { + self.sign_schnorr_with_rng(msg, keypair, &mut rand::thread_rng()) + } + + /// Creates a schnorr signature without using any auxiliary random data. + pub fn sign_schnorr_no_aux_rand( + &self, + msg: &[u8], + keypair: &Keypair, + ) -> Signature { + self.sign_schnorr_helper(msg, keypair, ptr::null()) + } + + /// Creates a schnorr signature using the given auxiliary random data. + pub fn sign_schnorr_with_aux_rand( + &self, + msg: &[u8], + keypair: &Keypair, + aux_rand: &[u8; 32], + ) -> Signature { + self.sign_schnorr_helper( + msg, + keypair, + aux_rand.as_c_ptr() as *const ffi::types::c_uchar, + ) + } + + /// Creates a schnorr signature using the given random number generator to + /// generate the auxiliary random data. + #[cfg(feature = "rand")] + pub fn sign_schnorr_with_rng<R: Rng + CryptoRng>( + &self, + msg: &[u8], + keypair: &Keypair, + rng: &mut R, + ) -> Signature { + let mut aux = [0u8; 32]; + rng.fill_bytes(&mut aux); + self.sign_schnorr_helper( + msg, + keypair, + aux.as_c_ptr() as *const ffi::types::c_uchar, + ) + } +} + +impl<C: Verification> Secp256k1<C> { + /// Verifies a schnorr signature. + pub fn verify_schnorr( + &self, + sig: &Signature, + msg: &[u8], + pubkey: &XOnlyPublicKey, + ) -> Result<(), Error> { + unsafe { + let ret = ffi::secp256k1_schnorrsig_verify( + self.ctx.as_ptr(), + sig.as_c_ptr(), + msg.as_c_ptr(), + msg.len(), + pubkey.as_c_ptr(), + ); + + if ret == 1 { + Ok(()) + } else { + Err(Error::IncorrectSignature) + } + } + } +} + +#[cfg(test)] +#[allow(unused_imports)] +mod tests { + use core::str::FromStr; + + #[cfg(all(feature = "rand", feature = "std"))] + use rand::rngs::ThreadRng; + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + use super::*; + use crate::schnorr::{Keypair, Signature, XOnlyPublicKey}; + use crate::Error::InvalidPublicKey; + use crate::{constants, from_hex, Message, Secp256k1, SecretKey}; + + #[cfg(all(not(secp256k1_fuzz), feature = "alloc"))] + macro_rules! hex_32 { + ($hex:expr) => {{ + let mut result = [0u8; 32]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }}; + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn schnorr_sign_with_aux_rand_verify() { + sign_helper(|secp, msg, seckey, rng| { + let aux_rand = crate::random_32_bytes(rng); + secp.sign_schnorr_with_aux_rand(msg, seckey, &aux_rand) + }) + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn schnor_sign_with_rng_verify() { + sign_helper(|secp, msg, seckey, rng| { + secp.sign_schnorr_with_rng(msg, seckey, rng) + }) + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn schnorr_sign_verify() { + sign_helper(|secp, msg, seckey, _| secp.sign_schnorr(msg, seckey)) + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn schnorr_sign_no_aux_rand_verify() { + sign_helper(|secp, msg, seckey, _| { + secp.sign_schnorr_no_aux_rand(msg, seckey) + }) + } + + #[cfg(all(feature = "rand", feature = "std"))] + fn sign_helper( + sign: fn( + &Secp256k1<crate::All>, + &[u8], + &Keypair, + &mut ThreadRng, + ) -> Signature, + ) { + let secp = Secp256k1::new(); + + let mut rng = rand::thread_rng(); + let kp = Keypair::new(&secp, &mut rng); + let (pk, _parity) = kp.x_only_public_key(); + + for _ in 0..100 { + let msg = crate::random_32_bytes(&mut rand::thread_rng()); + + let sig = sign(&secp, &msg, &kp, &mut rng); + + assert!(secp.verify_schnorr(&sig, &msg, &pk).is_ok()); + } + } + + #[test] + #[cfg(feature = "alloc")] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + fn schnorr_sign() { + let secp = Secp256k1::new(); + + let msg = hex_32!( + "E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614" + ); + let sk = Keypair::from_seckey_str( + &secp, + "688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF", + ) + .unwrap(); + let aux_rand: [u8; 32] = hex_32!( + "02CCE08E913F22A36C5648D6405A2C7C50106E7AA2F1649E381C7F09D16B80AB" + ); + let expected_sig = Signature::from_str( + "6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE50\ + 77C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8" + ) + .unwrap(); + + let sig = secp.sign_schnorr_with_aux_rand(&msg, &sk, &aux_rand); + + assert_eq!(expected_sig, sig); + } + + #[test] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + #[cfg(feature = "alloc")] + fn schnorr_verify() { + let secp = Secp256k1::new(); + + let msg = hex_32!( + "E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614" + ); + let sig = Signature::from_str( + "6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE50\ + 77C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8" + ) + .unwrap(); + let pubkey = XOnlyPublicKey::from_str( + "B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390", + ) + .unwrap(); + + assert!(secp.verify_schnorr(&sig, &msg, &pubkey).is_ok()); + } + + #[test] + fn test_serialize() { + let sig = Signature::from_str( + "6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE50\ + 77C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8" + ) + .unwrap(); + let sig_bytes = sig.to_byte_array(); + let bytes = [ + 100, 112, 253, 19, 3, 221, 164, 253, 167, 23, 185, 131, 113, 83, + 194, 74, 110, 171, 55, 113, 131, 252, 67, 143, 147, 158, 14, 210, + 182, 32, 233, 238, 80, 119, 196, 168, 184, 220, 162, 137, 99, 215, + 114, 169, 79, 95, 13, 223, 89, 142, 28, 71, 193, 55, 249, 25, 51, + 39, 76, 124, 62, 218, 220, 232, + ]; + assert_eq!(sig_bytes, bytes); + } + + #[test] + fn test_pubkey_from_slice() { + assert_eq!(XOnlyPublicKey::from_slice(&[]), Err(InvalidPublicKey)); + assert_eq!( + XOnlyPublicKey::from_slice(&[1, 2, 3]), + Err(InvalidPublicKey) + ); + let pk = XOnlyPublicKey::from_slice(&[ + 0xB3, 0x3C, 0xC9, 0xED, 0xC0, 0x96, 0xD0, 0xA8, 0x34, 0x16, 0x96, + 0x4B, 0xD3, 0xC6, 0x24, 0x7B, 0x8F, 0xEC, 0xD2, 0x56, 0xE4, 0xEF, + 0xA7, 0x87, 0x0D, 0x2C, 0x85, 0x4B, 0xDE, 0xB3, 0x33, 0x90, + ]); + assert!(pk.is_ok()); + } + + #[test] + #[cfg(all(feature = "rand", feature = "std"))] + fn test_pubkey_serialize_roundtrip() { + let secp = Secp256k1::new(); + let kp = Keypair::new(&secp, &mut rand::thread_rng()); + let (pk, _parity) = kp.x_only_public_key(); + + let ser = pk.serialize(); + let pubkey2 = XOnlyPublicKey::from_slice(&ser).unwrap(); + assert_eq!(pk, pubkey2); + } + + #[test] + #[cfg(feature = "alloc")] + fn test_xonly_key_extraction() { + let secp = Secp256k1::new(); + let sk_str = + "688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF"; + let keypair = Keypair::from_seckey_str(&secp, sk_str).unwrap(); + let sk = SecretKey::from_keypair(&keypair); + assert_eq!(SecretKey::from_str(sk_str).unwrap(), sk); + let pk = crate::key::PublicKey::from_keypair(&keypair); + assert_eq!(crate::key::PublicKey::from_secret_key(&secp, &sk), pk); + let (xpk, _parity) = keypair.x_only_public_key(); + assert_eq!(XOnlyPublicKey::from(pk), xpk); + } + + #[test] + fn test_pubkey_from_bad_slice() { + // Bad sizes + assert_eq!( + XOnlyPublicKey::from_slice( + &[0; constants::SCHNORR_PUBLIC_KEY_SIZE - 1] + ), + Err(InvalidPublicKey) + ); + assert_eq!( + XOnlyPublicKey::from_slice( + &[0; constants::SCHNORR_PUBLIC_KEY_SIZE + 1] + ), + Err(InvalidPublicKey) + ); + + // Bad parse + assert_eq!( + XOnlyPublicKey::from_slice( + &[0xff; constants::SCHNORR_PUBLIC_KEY_SIZE] + ), + Err(InvalidPublicKey) + ); + // In fuzzing mode restrictions on public key validity are much more + // relaxed, thus the invalid check below is expected to fail. + #[cfg(not(secp256k1_fuzz))] + assert_eq!( + XOnlyPublicKey::from_slice( + &[0x55; constants::SCHNORR_PUBLIC_KEY_SIZE] + ), + Err(InvalidPublicKey) + ); + assert_eq!(XOnlyPublicKey::from_slice(&[]), Err(InvalidPublicKey)); + } + + #[test] + #[cfg(feature = "std")] + fn test_pubkey_display_output() { + #[cfg(not(secp256k1_fuzz))] + let pk = { + let secp = Secp256k1::new(); + static SK_BYTES: [u8; 32] = [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, + ]; + + let kp = Keypair::from_seckey_slice(&secp, &SK_BYTES).expect("sk"); + + // In fuzzing mode secret->public key derivation is different, so + // hard-code the expected result. + let (pk, _parity) = kp.x_only_public_key(); + pk + }; + #[cfg(secp256k1_fuzz)] + let pk = XOnlyPublicKey::from_slice(&[ + 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, 0x1c, 0x97, 0x09, + 0xe2, 0x30, 0x92, 0x06, 0x7d, 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, + 0xd0, 0x54, 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66, + ]) + .expect("pk"); + + assert_eq!( + pk.to_string(), + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" + ); + assert_eq!( + XOnlyPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1\ + 66" + ) + .unwrap(), + pk + ); + + /*assert!(XOnlyPublicKey::from_str( + "00000000000000000000000000000000000000000000000000000000000000000" + ) + .is_err()); + assert!(XOnlyPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16601" + ) + .is_err()); + assert!(XOnlyPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16" + ) + .is_err()); + assert!(XOnlyPublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + assert!(XOnlyPublicKey::from_str( + "xx18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + + let long_str: String = "a".repeat(1024 * 1024); + assert!(XOnlyPublicKey::from_str(&long_str).is_err());*/ + } + + #[test] + // In fuzzing mode secret->public key derivation is different, so + // this test will never correctly derive the static pubkey. + #[cfg(not(secp256k1_fuzz))] + #[cfg(all(feature = "rand", feature = "alloc"))] + fn test_pubkey_serialize() { + use rand::rngs::mock::StepRng; + let secp = Secp256k1::new(); + let kp = Keypair::new(&secp, &mut StepRng::new(1, 1)); + let (pk, _parity) = kp.x_only_public_key(); + assert_eq!( + &pk.serialize()[..], + &[ + 124, 121, 49, 14, 253, 63, 197, 50, 39, 194, 107, 17, 193, 219, + 108, 154, 126, 9, 181, 248, 2, 12, 149, 233, 198, 71, 149, 134, + 250, 184, 154, 229 + ][..] + ); + } + + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + #[test] + #[cfg(all(feature = "serde", feature = "alloc"))] + fn test_serde() { + use serde_test::{assert_tokens, Configure, Token}; + + let s = Secp256k1::new(); + + let msg = [1; 32]; + let keypair = Keypair::from_seckey_slice(&s, &[2; 32]).unwrap(); + let aux = [3u8; 32]; + let sig = s.sign_schnorr_with_aux_rand(&msg, &keypair, &aux); + static SIG_BYTES: [u8; constants::SCHNORR_SIGNATURE_SIZE] = [ + 0x14, 0xd0, 0xbf, 0x1a, 0x89, 0x53, 0x50, 0x6f, 0xb4, 0x60, 0xf5, + 0x8b, 0xe1, 0x41, 0xaf, 0x76, 0x7f, 0xd1, 0x12, 0x53, 0x5f, 0xb3, + 0x92, 0x2e, 0xf2, 0x17, 0x30, 0x8e, 0x2c, 0x26, 0x70, 0x6f, 0x1e, + 0xeb, 0x43, 0x2b, 0x3d, 0xba, 0x9a, 0x01, 0x08, 0x2f, 0x9e, 0x4d, + 0x4e, 0xf5, 0x67, 0x8a, 0xd0, 0xd9, 0xd5, 0x32, 0xc0, 0xdf, 0xa9, + 0x07, 0xb5, 0x68, 0x72, 0x2d, 0x0b, 0x01, 0x19, 0xba, + ]; + static SIG_STR: &str = "\ + 14d0bf1a8953506fb460f58be141af767fd112535fb3922ef217308e2c26706f1ee\ + b432b3dba9a01082f9e4d4ef5678ad0d9d532c0dfa907b568722d0b0119ba\ + "; + + static PK_BYTES: [u8; 32] = [ + 24, 132, 87, 129, 246, 49, 196, 143, 28, 151, 9, 226, 48, 146, 6, + 125, 6, 131, 127, 48, 170, 12, 208, 84, 74, 200, 135, 254, 145, + 221, 209, 102, + ]; + static PK_STR: &str = + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166"; + let pk = XOnlyPublicKey::from_slice(&PK_BYTES).unwrap(); + + assert_tokens(&sig.compact(), &[Token::BorrowedBytes(&SIG_BYTES[..])]); + assert_tokens(&sig.compact(), &[Token::Bytes(&SIG_BYTES[..])]); + assert_tokens(&sig.compact(), &[Token::ByteBuf(&SIG_BYTES[..])]); + + assert_tokens(&sig.readable(), &[Token::BorrowedStr(SIG_STR)]); + assert_tokens(&sig.readable(), &[Token::Str(SIG_STR)]); + assert_tokens(&sig.readable(), &[Token::String(SIG_STR)]); + + assert_tokens( + &pk.compact(), + &[ + Token::Tuple { len: 32 }, + Token::U8(24), + Token::U8(132), + Token::U8(87), + Token::U8(129), + Token::U8(246), + Token::U8(49), + Token::U8(196), + Token::U8(143), + Token::U8(28), + Token::U8(151), + Token::U8(9), + Token::U8(226), + Token::U8(48), + Token::U8(146), + Token::U8(6), + Token::U8(125), + Token::U8(6), + Token::U8(131), + Token::U8(127), + Token::U8(48), + Token::U8(170), + Token::U8(12), + Token::U8(208), + Token::U8(84), + Token::U8(74), + Token::U8(200), + Token::U8(135), + Token::U8(254), + Token::U8(145), + Token::U8(221), + Token::U8(209), + Token::U8(102), + Token::TupleEnd, + ], + ); + + assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]); + assert_tokens(&pk.readable(), &[Token::Str(PK_STR)]); + assert_tokens(&pk.readable(), &[Token::String(PK_STR)]); + } + + #[test] + #[cfg(feature = "alloc")] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + fn bip340_test_vectors() { + struct TestVector { + secret_key: Option<[u8; 32]>, + public_key: [u8; 32], + aux_rand: Option<[u8; 32]>, + message: Vec<u8>, + signature: [u8; 64], + should_fail_verify: bool, + } + fn hex_arr<T: From<[u8; N]>, const N: usize>(s: &str) -> T { + let mut out = [0; N]; + from_hex(s, &mut out).unwrap(); + out.into() + } + let hex_vec = |s: &str| { + let mut v = vec![0u8; s.len() / 2]; + from_hex(s, v.as_mut_slice()).unwrap(); + v + }; + + let vectors = [ + TestVector { + secret_key: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000003" + ), + public_key: hex_arr( + "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BC\ + E036F9" + ), + aux_rand: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000000" + ), + message: hex_vec( + "0000000000000000000000000000000000000000000000000000000000\ + 000000" + ), + signature: hex_arr( + "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2D\ + CA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4\ + 900D310536C0" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr( + "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D90451\ + 90CFEF" + ), + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000001" + ), + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89"), + signature: hex_arr( + "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78\ + DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95\ + F6DE339E4B0A" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr( + "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B\ + 14E5C9" + ), + public_key: hex_arr( + "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969\ + 774EB8" + ), + aux_rand: hex_arr( + "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798\ + E6D906" + ), + message: hex_vec( + "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A5\ + 08B75C" + ), + signature: hex_arr( + "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD\ + 313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E\ + 03674A6F3FB7" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr( + "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2\ + 401710" + ), + public_key: hex_arr( + "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160\ + D8F517" + ), + aux_rand: hex_arr( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\ + FFFFFF" + ), + message: hex_vec( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\ + FFFFFF" + ), + signature: hex_arr( + "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC96\ + 37D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC\ + 5922EFC66EA3" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D2\ + 2DC7B9" + ), + aux_rand: None, + message: hex_vec( + "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9\ + 696703" + ), + signature: hex_arr( + "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D679\ + 5F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B\ + 07D28308D7F4" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A\ + 2D4A34" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E\ + 17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D\ + 5F7FC407D39B" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A1460\ + 2975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAF\ + A34B1AC553E2" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0\ + E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA\ + 5134FCCDB2BD" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E\ + 177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834\ + FF0D0C2E6DA6" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C\ + 6425BD186051" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 0000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780\ + D5A1837CF197" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F82204\ + 29BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D\ + 5F7FC407D39B" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFF\ + FFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D\ + 5F7FC407D39B" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B50\ + 2BA659" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E\ + 177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD2\ + 5E8CD0364141" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D\ + 6FD117" + ), + aux_rand: None, + message: hex_vec( + "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC\ + 4E6C89" + ), + signature: hex_arr( + "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E\ + 17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D\ + 5F7FC407D39B" + ), + should_fail_verify: true, + }, + TestVector { + secret_key: hex_arr( + "0340034003400340034003400340034003400340034003400340034003\ + 400340" + ), + public_key: hex_arr( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D\ + 6FD117" + ), + aux_rand: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000000" + ), + message: hex_vec(""), + signature: hex_arr( + "71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A5589\ + 5464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF\ + 0574E427AB63" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr( + "0340034003400340034003400340034003400340034003400340034003\ + 400340" + ), + public_key: hex_arr( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D\ + 6FD117" + ), + aux_rand: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000000" + ), + message: hex_vec("11"), + signature: hex_arr( + "08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134\ + C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14\ + 564CEC2BACBF" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr( + "0340034003400340034003400340034003400340034003400340034003\ + 400340" + ), + public_key: hex_arr( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D\ + 6FD117" + ), + aux_rand: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000000" + ), + message: hex_vec("0102030405060708090A0B0C0D0E0F1011"), + signature: hex_arr( + "5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B5\ + 0AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C\ + 5566A97EA5A5" + ), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr( + "0340034003400340034003400340034003400340034003400340034003\ + 400340" + ), + public_key: hex_arr( + "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D\ + 6FD117" + ), + aux_rand: hex_arr( + "0000000000000000000000000000000000000000000000000000000000\ + 000000" + ), + message: hex_vec( + "9999999999999999999999999999999999999999999999999999999999\ + 9999999999999999999999999999999999999999999999999999999999\ + 9999999999999999999999999999999999999999999999999999999999\ + 99999999999999999999999999" + ), + signature: hex_arr( + "403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163E\ + CA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894\ + CFD249F22367" + ), + should_fail_verify: false, + }, + ]; + let secp = Secp256k1::new(); + + for TestVector { + secret_key, + public_key, + aux_rand, + message, + signature, + should_fail_verify, + } in vectors + { + if let (Some(secret_key), Some(aux_rand)) = (secret_key, aux_rand) { + let keypair = + Keypair::from_seckey_slice(&secp, &secret_key).unwrap(); + assert_eq!( + keypair.x_only_public_key().0.serialize(), + public_key + ); + let sig = secp + .sign_schnorr_with_aux_rand(&message, &keypair, &aux_rand); + assert_eq!(sig.to_byte_array(), signature); + } + let sig = Signature::from_byte_array(signature); + let is_verified = + if let Ok(pubkey) = XOnlyPublicKey::from_slice(&public_key) { + secp.verify_schnorr(&sig, &message, &pubkey).is_ok() + } else { + false + }; + assert_eq!(is_verified, !should_fail_verify); + } + } +} diff --git a/modules/ecash-secp256k1/src/schnorrabc.rs b/modules/ecash-secp256k1/src/schnorrabc.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/schnorrabc.rs @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Support for schnorr signatures. + +use core::ptr; + +#[cfg(feature = "rand")] +use rand::{CryptoRng, Rng}; + +use crate::ffi::{self, CPtr}; +use crate::schnorr::Signature; +use crate::{ + constants, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, + Verification, +}; + +impl<C: Signing> Secp256k1<C> { + fn sign_schnorrabc_helper( + &self, + msg: &Message, + seckey: &SecretKey, + nonce_data: *const ffi::types::c_void, + ) -> Signature { + unsafe { + let mut sig = [0u8; constants::SCHNORR_SIGNATURE_SIZE]; + assert_eq!( + 1, + ffi::secp256k1_schnorr_sign( + self.ctx.as_ptr(), + sig.as_mut_c_ptr(), + msg.as_c_ptr(), + seckey.as_c_ptr(), + ffi::secp256k1_nonce_function_default, + nonce_data + ) + ); + + Signature::from_byte_array(sig) + } + } + + /// Creates a schnorr signature internally using the + /// [`rand::rngs::ThreadRng`] random number generator to generate the + /// auxiliary random data. + #[cfg(all(feature = "rand", feature = "std"))] + pub fn sign_schnorrabc( + &self, + msg: &Message, + seckey: &SecretKey, + ) -> Signature { + let mut rng = rand::thread_rng(); + self.sign_schnorrabc_with_rng(msg, seckey, &mut rng) + } + + /// Create a schnorr signature without using any auxiliary random data. + pub fn sign_schnorrabc_no_aux_rand( + &self, + msg: &Message, + seckey: &SecretKey, + ) -> Signature { + self.sign_schnorrabc_helper(msg, seckey, ptr::null()) + } + + /// Creates a schnorr signature using the given auxiliary random data. + pub fn sign_schnorrabc_with_aux_rand( + &self, + msg: &Message, + seckey: &SecretKey, + aux_rand: &[u8; 32], + ) -> Signature { + self.sign_schnorrabc_helper( + msg, + seckey, + aux_rand.as_c_ptr() as *const ffi::types::c_void, + ) + } + + /// Creates a schnorr signature using the given random number generator to + /// generate the auxiliary random data. + #[cfg(feature = "rand")] + pub fn sign_schnorrabc_with_rng<R: Rng + CryptoRng>( + &self, + msg: &Message, + seckey: &SecretKey, + rng: &mut R, + ) -> Signature { + let mut aux = [0u8; 32]; + rng.fill_bytes(&mut aux); + self.sign_schnorrabc_helper( + msg, + seckey, + aux.as_c_ptr() as *const ffi::types::c_void, + ) + } +} + +impl<C: Verification> Secp256k1<C> { + /// Verifies a schnorr signature. + pub fn verify_schnorrabc( + &self, + sig: &Signature, + msg: &Message, + pubkey: &PublicKey, + ) -> Result<(), Error> { + unsafe { + let ret = ffi::secp256k1_schnorr_verify( + self.ctx.as_ptr(), + sig.as_c_ptr(), + msg.as_c_ptr(), + pubkey.as_c_ptr(), + ); + + if ret == 1 { + Ok(()) + } else { + Err(Error::IncorrectSignature) + } + } + } +} + +#[cfg(feature = "std")] +#[cfg(test)] +mod tests { + #[cfg(not(secp256k1_fuzz))] + macro_rules! hex_32 { + ($hex:expr) => {{ + let mut result = [0; 32]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }}; + } + + #[cfg(feature = "rand")] + fn test_sign_schnorrabc_helper( + sign: fn( + &crate::Secp256k1<crate::All>, + &crate::Message, + &crate::SecretKey, + &mut rand::rngs::ThreadRng, + ) -> crate::schnorr::Signature, + ) { + use rand::{thread_rng, RngCore}; + + use crate::{All, Message, PublicKey, Secp256k1, SecretKey}; + let secp = Secp256k1::<All>::new(); + + let mut rng = thread_rng(); + let seckey = SecretKey::new(&mut rng); + let pubkey = PublicKey::from_secret_key(&secp, &seckey); + let mut msg = [0; 32]; + + for _ in 0..100 { + rng.fill_bytes(&mut msg); + let msg = Message::from_digest(msg); + + let sig = sign(&secp, &msg, &seckey, &mut rng); + + assert!(secp.verify_schnorrabc(&sig, &msg, &pubkey).is_ok()); + } + } + + #[cfg(feature = "rand")] + #[test] + fn test_sign_schnorrabc_with_aux_rand_verify() { + test_sign_schnorrabc_helper(|secp, msg, seckey, rng| { + use rand::RngCore; + let mut aux_rand = [0; 32]; + rng.fill_bytes(&mut aux_rand); + secp.sign_schnorrabc_with_aux_rand(msg, seckey, &aux_rand) + }) + } + + #[cfg(feature = "rand")] + #[test] + fn test_sign_schnorrabc_with_rng_verify() { + test_sign_schnorrabc_helper(|secp, msg, seckey, mut rng| { + secp.sign_schnorrabc_with_rng(msg, seckey, &mut rng) + }) + } + + #[cfg(feature = "rand")] + #[test] + fn test_sign_schnorrabc_verify() { + test_sign_schnorrabc_helper(|secp, msg, seckey, _| { + secp.sign_schnorrabc(msg, seckey) + }) + } + + #[cfg(feature = "rand")] + #[test] + fn test_sign_schnorrabc_no_aux_rand_verify() { + test_sign_schnorrabc_helper(|secp, msg, seckey, _| { + secp.sign_schnorrabc_no_aux_rand(msg, seckey) + }) + } + + #[cfg(not(secp256k1_fuzz))] + #[test] + fn test_schnorrabc_sign() { + use std::str::FromStr; + + use crate::{ + from_hex, schnorr::Signature, Message, Secp256k1, SecretKey, + }; + let secp = Secp256k1::<crate::All>::new(); + + let hex_msg = hex_32!( + "E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614" + ); + let msg = Message::from_digest(hex_msg); + let seckey: SecretKey = + "688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF" + .parse() + .unwrap(); + let aux_rand: [u8; 32] = hex_32!( + "02CCE08E913F22A36C5648D6405A2C7C50106E7AA2F1649E381C7F09D16B80AB" + ); + let expected_sig = Signature::from_str( + "EDA588A9DDA57D3003E7DC9FEE6637B963016D4E425202C47EA1F72408AC6EEDEF\ + 8E96112AEE39AA242AEB93D0D479FA0EABAA8C5606E7B72346C701B71B1210" + ).unwrap(); + + let sig = secp.sign_schnorrabc_with_aux_rand(&msg, &seckey, &aux_rand); + + assert_eq!(expected_sig, sig); + } + + #[cfg(not(secp256k1_fuzz))] + #[test] + fn test_schnorrabc_verify() { + use std::str::FromStr; + + use crate::{ + from_hex, schnorr::Signature, Message, PublicKey, Secp256k1, + }; + + let secp = Secp256k1::<crate::All>::new(); + + let hex_msg = hex_32!( + "E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614" + ); + let msg = Message::from_digest(hex_msg); + let sig = Signature::from_str( + "EDA588A9DDA57D3003E7DC9FEE6637B963016D4E425202C47EA1F72408AC6EEDEF\ + 8E96112AEE39AA242AEB93D0D479FA0EABAA8C5606E7B72346C701B71B1210" + ) + .unwrap(); + let pubkey: PublicKey = + "03B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390\ + ".parse().unwrap(); + + assert!(secp.verify_schnorrabc(&sig, &msg, &pubkey).is_ok()); + } +} diff --git a/modules/ecash-secp256k1/src/secret.rs b/modules/ecash-secp256k1/src/secret.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/secret.rs @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Helpers for displaying secret values + +use core::fmt; + +use crate::constants::SECRET_KEY_SIZE; +use crate::ecdh::SharedSecret; +use crate::key::{Keypair, SecretKey}; +use crate::to_hex; +macro_rules! impl_display_secret { + // Default hasher exists only in standard library and not alloc + ($thing:ident) => { + #[cfg(feature = "hashes")] + impl ::core::fmt::Debug for $thing { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter, + ) -> ::core::fmt::Result { + use hashes::{sha256, Hash, HashEngine}; + + let tag = "rust-secp256k1DEBUG"; + + let mut engine = sha256::Hash::engine(); + let tag_hash = sha256::Hash::hash(tag.as_bytes()); + engine.input(&tag_hash.as_ref()); + engine.input(&tag_hash.as_ref()); + engine.input(&self.secret_bytes()); + let hash = sha256::Hash::from_engine(engine); + + f.debug_tuple(stringify!($thing)) + .field(&format_args!("#{:.16}", hash)) + .finish() + } + } + + #[cfg(not(feature = "hashes"))] + impl ::core::fmt::Debug for $thing { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter, + ) -> ::core::fmt::Result { + write!( + f, + "<secret key; enable `hashes` feature of `secp256k1` to \ + display fingerprint>" + ) + } + } + }; +} + +/// Helper struct for safely printing secrets (like [`SecretKey`] value). +/// Formats the explicit byte value of the secret kept inside the type as a +/// little-endian hexadecimal string using the provided formatter. +/// +/// Secrets should not implement neither [`Debug`] and [`Display`] traits +/// directly, and instead provide `fn display_secret<'a>(&'a self) -> +/// DisplaySecret<'a>` function to be used in different display contexts (see +/// "examples" below). +/// +/// [`Display`]: fmt::Display +/// [`Debug`]: fmt::Debug +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DisplaySecret { + secret: [u8; SECRET_KEY_SIZE], +} +impl_non_secure_erase!(DisplaySecret, secret, [0u8; SECRET_KEY_SIZE]); + +impl fmt::Debug for DisplaySecret { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut slice = [0u8; SECRET_KEY_SIZE * 2]; + let hex = to_hex(&self.secret, &mut slice) + .expect("fixed-size hex serializer failed"); + f.debug_tuple("DisplaySecret").field(&hex).finish() + } +} + +impl fmt::Display for DisplaySecret { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in &self.secret { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } +} + +impl SecretKey { + /// Formats the explicit byte value of the secret key kept inside the type + /// as a little-endian hexadecimal string using the provided formatter. + /// + /// This is the only method that outputs the actual secret key value, and, + /// thus, should be used with extreme caution. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(feature = "std")] { + /// # use std::str::FromStr; + /// use ecash_secp256k1::SecretKey; + /// let key = SecretKey::from_str( + /// "0000000000000000000000000000000000000000000000000000000000000001", + /// ) + /// .unwrap(); + /// + /// // Normal debug hides value (`Display` not implemented for `SecretKey`). + /// // E.g., `format!("{:?}", key)` prints "SecretKey(#2518682f7819fb2d)". + /// + /// // Here we explicitly display the secret value: + /// assert_eq!( + /// "0000000000000000000000000000000000000000000000000000000000000001", + /// format!("{}", key.display_secret()) + /// ); + /// // Also, we can explicitly display with `Debug`: + /// assert_eq!( + /// format!("{:?}", key.display_secret()), + /// format!("DisplaySecret(\"{}\")", key.display_secret()) + /// ); + /// # } + /// ``` + #[inline] + pub fn display_secret(&self) -> DisplaySecret { + DisplaySecret { + secret: self.secret_bytes(), + } + } +} + +impl Keypair { + /// Formats the explicit byte value of the secret key kept inside the type + /// as a little-endian hexadecimal string using the provided formatter. + /// + /// This is the only method that outputs the actual secret key value, and, + /// thus, should be used with extreme precaution. + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "std")] { + /// # use std::str::FromStr; + /// use ecash_secp256k1::{Keypair, Secp256k1, SecretKey}; + /// + /// let secp = Secp256k1::new(); + /// let key = SecretKey::from_str( + /// "0000000000000000000000000000000000000000000000000000000000000001", + /// ) + /// .unwrap(); + /// let key = Keypair::from_secret_key(&secp, &key); + /// // Here we explicitly display the secret value: + /// assert_eq!( + /// "0000000000000000000000000000000000000000000000000000000000000001", + /// format!("{}", key.display_secret()) + /// ); + /// // Also, we can explicitly display with `Debug`: + /// assert_eq!( + /// format!("{:?}", key.display_secret()), + /// format!("DisplaySecret(\"{}\")", key.display_secret()) + /// ); + /// # } + /// ``` + #[inline] + pub fn display_secret(&self) -> DisplaySecret { + DisplaySecret { + secret: self.secret_bytes(), + } + } +} + +impl SharedSecret { + /// Formats the explicit byte value of the shared secret kept inside the + /// type as a little-endian hexadecimal string using the provided + /// formatter. + /// + /// This is the only method that outputs the actual shared secret value, + /// and, thus, should be used with extreme caution. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(not(secp256k1_fuzz))] + /// # #[cfg(feature = "std")] { + /// # use std::str::FromStr; + /// use ecash_secp256k1::{SecretKey, PublicKey}; + /// use ecash_secp256k1::ecdh::SharedSecret; + /// + /// # let pk = PublicKey::from_slice( + /// # &[3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, + /// # 248, 140, 11, 3, 51, 41, 111, 180, 110, 143, 114, 134, 88, 73, + /// # 198, 174, 52, 184, 78] + /// # ).expect("hard coded slice should parse correctly"); + /// # let sk = SecretKey::from_str( + /// # "57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead" + /// # ).unwrap(); + /// + /// let secret = SharedSecret::new(&pk, &sk); + /// // Here we explicitly display the secret value: + /// assert_eq!( + /// format!("{}", secret.display_secret()), + /// "cf05ae7da039ddce6d56dd57d3000c6dd91c6f1695eae47e05389f11e2467043" + /// ); + /// // Also, we can explicitly display with `Debug`: + /// assert_eq!( + /// format!("{:?}", secret.display_secret()), + /// format!("DisplaySecret(\"{}\")", secret.display_secret()) + /// ); + /// # } + /// ``` + #[inline] + pub fn display_secret(&self) -> DisplaySecret { + DisplaySecret { + secret: self.secret_bytes(), + } + } +} diff --git a/modules/ecash-secp256k1/src/serde_util.rs b/modules/ecash-secp256k1/src/serde_util.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/src/serde_util.rs @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: CC0-1.0 + +use core::fmt; +use core::marker::PhantomData; +use core::str::{self, FromStr}; + +use serde::de; + +/// A serde visitor that works for `T`s implementing `FromStr`. +pub struct FromStrVisitor<T> { + expectation: &'static str, + _pd: PhantomData<T>, +} + +impl<T> FromStrVisitor<T> { + pub fn new(expectation: &'static str) -> Self { + FromStrVisitor { + expectation, + _pd: PhantomData, + } + } +} + +impl<'de, T> de::Visitor<'de> for FromStrVisitor<T> +where + T: FromStr, + <T as FromStr>::Err: fmt::Display, +{ + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(self.expectation) + } + + fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> { + FromStr::from_str(v).map_err(E::custom) + } +} + +pub struct BytesVisitor<F> { + expectation: &'static str, + parse_fn: F, +} + +impl<F, T, Err> BytesVisitor<F> +where + F: FnOnce(&[u8]) -> Result<T, Err>, + Err: fmt::Display, +{ + pub fn new(expectation: &'static str, parse_fn: F) -> Self { + BytesVisitor { + expectation, + parse_fn, + } + } +} + +impl<'de, F, T, Err> de::Visitor<'de> for BytesVisitor<F> +where + F: FnOnce(&[u8]) -> Result<T, Err>, + Err: fmt::Display, +{ + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(self.expectation) + } + + fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> { + (self.parse_fn)(v).map_err(E::custom) + } +} + +macro_rules! impl_tuple_visitor { + ($thing:ident, $len:expr) => { + pub(crate) struct $thing<F> { + expectation: &'static str, + parse_fn: F, + } + + impl<F, T, E> $thing<F> + where + F: FnOnce(&[u8]) -> Result<T, E>, + E: fmt::Display, + { + pub fn new(expectation: &'static str, parse_fn: F) -> Self { + $thing { + expectation, + parse_fn, + } + } + } + + impl<'de, F, T, E> de::Visitor<'de> for $thing<F> + where + F: FnOnce(&[u8]) -> Result<T, E>, + E: fmt::Display, + { + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(self.expectation) + } + + fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error> + where + V: de::SeqAccess<'de>, + { + let mut bytes = [0u8; $len]; + + for (i, byte) in bytes.iter_mut().enumerate() { + if let Some(value) = seq.next_element()? { + *byte = value; + } else { + return Err(de::Error::invalid_length(i, &self)); + } + } + (self.parse_fn)(&bytes).map_err(de::Error::custom) + } + } + }; +} + +impl_tuple_visitor!(Tuple32Visitor, 32); +impl_tuple_visitor!(Tuple33Visitor, 33); diff --git a/modules/ecash-secp256k1/tests/serde.rs b/modules/ecash-secp256k1/tests/serde.rs new file mode 100644 --- /dev/null +++ b/modules/ecash-secp256k1/tests/serde.rs @@ -0,0 +1,90 @@ +#![cfg(feature = "serde")] + +extern crate bincode; +extern crate ecash_secp256k1 as secp256k1; +extern crate serde_cbor; + +#[cfg(feature = "global-context")] +use secp256k1::{Keypair, Secp256k1}; +use secp256k1::{PublicKey, SecretKey, XOnlyPublicKey}; + +// Arbitrary key data. + +#[rustfmt::skip] +static SK_BYTES: [u8; 32] = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, + 0x0f, 0x10, 0x1f, 0xa0, 0xa9, 0xaa, 0xaf, 0xff, +]; + +#[rustfmt::skip] +static PK_BYTES: [u8; 33] = [ + 0x02, + 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, + 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, + 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, + 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66, +]; + +#[rustfmt::skip] +static XONLY_PK_BYTES: [u8; 32] = [ + 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, + 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, + 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, + 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66, +]; + +fn secret_key() -> SecretKey { + SecretKey::from_slice(&SK_BYTES).expect("failed to create sk from slice") +} + +// Our current serde serialization implementation is only guaranteed to be fixed +// width for bincode. https://docs.rs/bincode/latest/bincode/index.html +#[test] +fn bincode_secret_key() { + let sk = secret_key(); + let ser = bincode::serialize(&sk).unwrap(); + + assert_eq!(ser, SK_BYTES); +} + +#[test] +fn bincode_public_key() { + let pk = PublicKey::from_slice(&PK_BYTES) + .expect("failed to create pk from slice"); + let ser = bincode::serialize(&pk).unwrap(); + + assert_eq!(ser, &PK_BYTES as &[u8]) +} + +#[test] +#[cfg(feature = "global-context")] +fn bincode_keypair() { + let secp = Secp256k1::new(); + let kp = Keypair::from_seckey_slice(&secp, &SK_BYTES) + .expect("failed to create keypair"); + let ser = bincode::serialize(&kp).unwrap(); + + assert_eq!(ser, SK_BYTES); +} + +#[test] +fn bincode_x_only_public_key() { + let pk = XOnlyPublicKey::from_slice(&XONLY_PK_BYTES) + .expect("failed to create xonly pk from slice"); + let ser = bincode::serialize(&pk).unwrap(); + + assert_eq!(ser, XONLY_PK_BYTES); +} + +#[test] +fn cbor() { + let sk = secret_key(); + let e = serde_cbor::to_vec(&sk).unwrap(); + // Secret key is 32 bytes. CBOR adds a byte of metadata for 20 of these + // bytes, (Apparently, any byte whose value is <24 gets an extra byte.) + // It also adds a 1-byte length prefix and a byte of metadata for the whole + // vector. + assert_eq!(e.len(), 54); +}