diff --git a/Cargo.toml b/Cargo.toml index a1958d945..d357c8a6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["std"] -std = ["alloc", "secp256k1-sys/std"] +default = ["sys-std"] +std = ["alloc"] +alloc = [] +sys-std = ["std", "sys-alloc", "secp256k1-sys/std"] # allow use of Secp256k1::new and related API that requires an allocator -alloc = ["secp256k1-sys/alloc"] +sys-alloc = ["secp256k1-sys/alloc"] hashes-std = ["std", "hashes/std"] rand-std = ["std", "rand", "rand/std", "rand/std_rng"] recovery = ["secp256k1-sys/recovery"] @@ -36,7 +38,7 @@ global-context = ["std"] global-context-less-secure = ["global-context"] [dependencies] -secp256k1-sys = { version = "0.10.0", default-features = false, path = "./secp256k1-sys" } +secp256k1-sys = { version = "0.10.0", default-features = false, path = "./secp256k1-sys", optional = true } serde = { version = "1.0.103", default-features = false, optional = true } # You likely only want to enable these if you explicitly do not want to use "std", otherwise enable @@ -57,15 +59,15 @@ getrandom = { version = "0.2", features = ["js"] } [[example]] name = "sign_verify_recovery" -required-features = ["recovery", "hashes-std"] +required-features = ["recovery", "hashes-std", "secp256k1-sys"] [[example]] name = "sign_verify" -required-features = ["hashes-std"] +required-features = ["hashes-std", "secp256k1-sys"] [[example]] name = "generate_keys" -required-features = ["rand-std"] +required-features = ["rand-std", "secp256k1-sys"] [workspace] members = ["secp256k1-sys"] diff --git a/contrib/_test.sh b/contrib/_test.sh index e18b23146..3a75ebe24 100755 --- a/contrib/_test.sh +++ b/contrib/_test.sh @@ -3,7 +3,7 @@ set -ex REPO_DIR=$(git rev-parse --show-toplevel) -FEATURES="hashes global-context lowmemory rand recovery serde std alloc hashes-std rand-std" +FEATURES="hashes global-context lowmemory rand recovery serde std alloc hashes-std rand-std secp256k1-sys" cargo --version rustc --version @@ -62,17 +62,17 @@ if [ "$DO_FEATURE_MATRIX" = true ]; then fi # Examples - cargo run --locked --example sign_verify --features=hashes-std - cargo run --locked --example sign_verify_recovery --features=recovery,hashes-std - cargo run --locked --example generate_keys --features=rand-std + cargo run --locked --example sign_verify --features=hashes-std,secp256k1-sys + cargo run --locked --example sign_verify_recovery --features=recovery,hashes-std,secp256k1-sys + cargo run --locked --example generate_keys --features=rand-std,secp256k1-sys fi if [ "$DO_LINT" = true ] then cargo clippy --locked --all-features --all-targets -- -D warnings - cargo clippy --locked --example sign_verify --features=hashes-std -- -D warnings - cargo clippy --locked --example sign_verify_recovery --features=recovery,hashes-std -- -D warnings - cargo clippy --locked --example generate_keys --features=rand-std -- -D warnings + cargo clippy --locked --example sign_verify --features=hashes-std,secp256k1-sys -- -D warnings + cargo clippy --locked --example sign_verify_recovery --features=recovery,hashes-std,secp256k1-sys -- -D warnings + cargo clippy --locked --example generate_keys --features=rand-std,secp256k1-sys -- -D warnings fi # Build the docs if told to (this only works with the nightly toolchain) diff --git a/src/context.rs b/src/context.rs index 7383e6274..07c1c120c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,13 +1,18 @@ // SPDX-License-Identifier: CC0-1.0 use core::marker::PhantomData; +#[cfg(feature = "secp256k1-sys")] use core::mem::ManuallyDrop; +#[cfg(feature = "secp256k1-sys")] use core::ptr::NonNull; #[cfg(feature = "alloc")] pub use self::alloc_only::{All, SignOnly, VerifyOnly}; +#[cfg(feature = "secp256k1-sys")] use crate::ffi::types::{c_uint, c_void, AlignedType}; +#[cfg(feature = "secp256k1-sys")] use crate::ffi::{self, CPtr}; +#[cfg(feature = "secp256k1-sys")] use crate::{Error, Secp256k1}; #[cfg(all(feature = "global-context", feature = "std"))] @@ -15,6 +20,7 @@ use crate::{Error, Secp256k1}; pub mod global { use std::ops::Deref; + #[cfg(feature = "secp256k1-sys")] use std::sync::Once; use crate::{All, Secp256k1}; @@ -41,6 +47,7 @@ pub mod global { type Target = Secp256k1; #[allow(unused_mut)] // Unused when `rand-std` is not enabled. + #[cfg(feature = "secp256k1-sys")] fn deref(&self) -> &Self::Target { static ONCE: Once = Once::new(); static mut CONTEXT: Option> = None; @@ -58,6 +65,11 @@ pub mod global { }); unsafe { CONTEXT.as_ref().unwrap() } } + + #[cfg(not(feature = "secp256k1-sys"))] + fn deref(&self) -> &Self::Target { + &Secp256k1 { phantom: core::marker::PhantomData } + } } } @@ -69,6 +81,7 @@ pub mod global { /// This trait is marked unsafe to allow unsafe implementations of `deallocate`. pub unsafe trait Context: private::Sealed { /// Flags for the ffi. + #[cfg(feature = "secp256k1-sys")] const FLAGS: c_uint; /// A constant description of the context. const DESCRIPTION: &'static str; @@ -116,18 +129,25 @@ mod private { #[cfg(feature = "alloc")] mod alloc_only { use core::marker::PhantomData; + #[cfg(feature = "secp256k1-sys")] use core::ptr::NonNull; use super::private; + #[cfg(feature = "secp256k1-sys")] use crate::alloc::alloc; + #[cfg(feature = "secp256k1-sys")] use crate::ffi::types::{c_uint, c_void}; + #[cfg(feature = "secp256k1-sys")] use crate::ffi::{self}; - use crate::{AlignedType, Context, Secp256k1, Signing, Verification}; + use crate::{Context, Secp256k1, Signing, Verification}; + #[cfg(feature = "secp256k1-sys")] + use crate::AlignedType; impl private::Sealed for SignOnly {} impl private::Sealed for All {} impl private::Sealed for VerifyOnly {} + #[cfg(feature = "secp256k1-sys")] const ALIGN_TO: usize = core::mem::align_of::(); /// Represents the set of capabilities needed for signing. @@ -149,32 +169,50 @@ mod alloc_only { impl Verification for All {} unsafe impl Context for SignOnly { + #[cfg(feature = "secp256k1-sys")] const FLAGS: c_uint = ffi::SECP256K1_START_SIGN; const DESCRIPTION: &'static str = "signing only"; unsafe fn deallocate(ptr: *mut u8, size: usize) { - let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); - alloc::dealloc(ptr, layout); + #[cfg(feature = "secp256k1-sys")] + { + let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + alloc::dealloc(ptr, layout); + } + #[cfg(not(feature = "secp256k1-sys"))] + let _ = (ptr, size); } } unsafe impl Context for VerifyOnly { + #[cfg(feature = "secp256k1-sys")] const FLAGS: c_uint = ffi::SECP256K1_START_VERIFY; const DESCRIPTION: &'static str = "verification only"; unsafe fn deallocate(ptr: *mut u8, size: usize) { - let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); - alloc::dealloc(ptr, layout); + #[cfg(feature = "secp256k1-sys")] + { + let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + alloc::dealloc(ptr, layout); + } + #[cfg(not(feature = "secp256k1-sys"))] + let _ = (ptr, size); } } unsafe impl Context for All { + #[cfg(feature = "secp256k1-sys")] const FLAGS: c_uint = VerifyOnly::FLAGS | SignOnly::FLAGS; const DESCRIPTION: &'static str = "all capabilities"; unsafe fn deallocate(ptr: *mut u8, size: usize) { - let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); - alloc::dealloc(ptr, layout); + #[cfg(feature = "secp256k1-sys")] + { + let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); + alloc::dealloc(ptr, layout); + } + #[cfg(not(feature = "secp256k1-sys"))] + let _ = (ptr, size); } } @@ -184,7 +222,7 @@ mod alloc_only { /// If `rand-std` feature is enabled, context will have been randomized using `thread_rng`. /// If `rand-std` feature is not enabled please consider randomizing the context as follows: /// ``` - /// # #[cfg(feature = "rand-std")] { + /// # #[cfg(all(feature = "rand-std", feature = "secp256k1-sys"))] { /// # use secp256k1::Secp256k1; /// # use secp256k1::rand::{thread_rng, RngCore}; /// let mut ctx = Secp256k1::new(); @@ -197,32 +235,39 @@ mod alloc_only { /// ``` #[cfg_attr(not(feature = "rand-std"), allow(clippy::let_and_return, unused_mut))] pub fn gen_new() -> Secp256k1 { - #[cfg(target_arch = "wasm32")] - ffi::types::sanity_checks_for_wasm(); + #[cfg(feature = "secp256k1-sys")] + { + #[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)); + 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 { - ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr, C::FLAGS) }, - phantom: PhantomData, - }; + #[allow(unused_mut)] // ctx is not mutated under some feature combinations. + let mut ctx = Secp256k1 { + ctx: unsafe { ffi::secp256k1_context_preallocated_create(ptr, C::FLAGS) }, + phantom: PhantomData, + }; + + #[cfg(all( + not(target_arch = "wasm32"), + feature = "rand-std", + not(feature = "global-context-less-secure") + ))] + { + ctx.randomize(&mut rand::thread_rng()); + } - #[cfg(all( - not(target_arch = "wasm32"), - feature = "rand-std", - not(feature = "global-context-less-secure") - ))] + #[allow(clippy::let_and_return)] // as for unusted_mut + ctx + } + #[cfg(not(feature = "secp256k1-sys"))] { - ctx.randomize(&mut rand::thread_rng()); + Secp256k1 { phantom: PhantomData } } - - #[allow(clippy::let_and_return)] // as for unusted_mut - ctx } } @@ -258,6 +303,7 @@ mod alloc_only { } impl Clone for Secp256k1 { + #[cfg(feature = "secp256k1-sys")] fn clone(&self) -> Secp256k1 { let size = unsafe { ffi::secp256k1_context_preallocated_clone_size(self.ctx.as_ptr()) }; let layout = alloc::Layout::from_size_align(size, ALIGN_TO).unwrap(); @@ -270,39 +316,10 @@ mod alloc_only { 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 FLAGS: c_uint = ffi::SECP256K1_START_SIGN; - const DESCRIPTION: &'static str = "signing only"; - - unsafe fn deallocate(_ptr: *mut u8, _size: usize) { - // Allocated by the user - } -} - -unsafe impl<'buf> Context for VerifyOnlyPreallocated<'buf> { - const FLAGS: c_uint = ffi::SECP256K1_START_VERIFY; - const DESCRIPTION: &'static str = "verification only"; - - unsafe fn deallocate(_ptr: *mut u8, _size: usize) { - // Allocated by the user. - } -} - -unsafe impl<'buf> Context for AllPreallocated<'buf> { - const FLAGS: c_uint = SignOnlyPreallocated::FLAGS | VerifyOnlyPreallocated::FLAGS; - const DESCRIPTION: &'static str = "all capabilities"; - - unsafe fn deallocate(_ptr: *mut u8, _size: usize) { - // Allocated by the user. + #[cfg(not(feature = "secp256k1-sys"))] + fn clone(&self) -> Secp256k1 { + Secp256k1::gen_new() + } } } @@ -316,108 +333,147 @@ unsafe impl<'buf> Context for AllPreallocated<'buf> { /// 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> {} +#[cfg(feature = "secp256k1-sys")] +mod preallocated { + use super::*; -impl<'buf, C: Context + PreallocatedContext<'buf>> Secp256k1 { - /// 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, Error> { - #[cfg(target_arch = "wasm32")] - ffi::types::sanity_checks_for_wasm(); + impl<'buf> Signing for SignOnlyPreallocated<'buf> {} + impl<'buf> Signing for AllPreallocated<'buf> {} - 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) }; + impl<'buf> Verification for VerifyOnlyPreallocated<'buf> {} + impl<'buf> Verification for AllPreallocated<'buf> {} + + unsafe impl<'buf> Context for SignOnlyPreallocated<'buf> { + #[cfg(feature = "secp256k1-sys")] + const FLAGS: c_uint = ffi::SECP256K1_START_SIGN; + const DESCRIPTION: &'static str = "signing only"; - Ok(Secp256k1 { - ctx: unsafe { ffi::secp256k1_context_preallocated_create(buf, AllPreallocated::FLAGS) }, - phantom: PhantomData, - }) + unsafe fn deallocate(_ptr: *mut u8, _size: usize) { + // Allocated by the user + } } -} -impl<'buf> Secp256k1> { - /// Creates a new Secp256k1 context with all capabilities. - pub fn preallocated_new( - buf: &'buf mut [AlignedType], - ) -> Result>, Error> { - Secp256k1::preallocated_gen_new(buf) + unsafe impl<'buf> Context for VerifyOnlyPreallocated<'buf> { + #[cfg(feature = "secp256k1-sys")] + const FLAGS: c_uint = ffi::SECP256K1_START_VERIFY; + const DESCRIPTION: &'static str = "verification only"; + + unsafe fn deallocate(_ptr: *mut u8, _size: usize) { + // Allocated by the user. + } } - /// 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, - ) -> ManuallyDrop>> { - ManuallyDrop::new(Secp256k1 { ctx: raw_ctx, phantom: PhantomData }) + unsafe impl<'buf> Context for AllPreallocated<'buf> { + const FLAGS: c_uint = SignOnlyPreallocated::FLAGS | VerifyOnlyPreallocated::FLAGS; + const DESCRIPTION: &'static str = "all capabilities"; + + unsafe fn deallocate(_ptr: *mut u8, _size: usize) { + // Allocated by the user. + } } -} + 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 { + /// 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, 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) }; -impl<'buf> Secp256k1> { - /// Creates a new Secp256k1 context that can only be used for signing. - pub fn preallocated_signing_only( - buf: &'buf mut [AlignedType], - ) -> Result>, Error> { - Secp256k1::preallocated_gen_new(buf) + Ok(Secp256k1 { + ctx: unsafe { ffi::secp256k1_context_preallocated_create(buf, AllPreallocated::FLAGS) }, + phantom: PhantomData, + }) + } } - /// 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() } + impl<'buf> Secp256k1> { + /// Creates a new Secp256k1 context with all capabilities. + pub fn preallocated_new( + buf: &'buf mut [AlignedType], + ) -> Result>, 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 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, - ) -> ManuallyDrop>> { - ManuallyDrop::new(Secp256k1 { ctx: raw_ctx, phantom: PhantomData }) + /// 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, + ) -> ManuallyDrop>> { + ManuallyDrop::new(Secp256k1 { ctx: raw_ctx, phantom: PhantomData }) + } } -} -impl<'buf> Secp256k1> { - /// Creates a new Secp256k1 context that can only be used for verification - pub fn preallocated_verification_only( - buf: &'buf mut [AlignedType], - ) -> Result>, Error> { - Secp256k1::preallocated_gen_new(buf) + impl<'buf> Secp256k1> { + /// Creates a new Secp256k1 context that can only be used for signing. + pub fn preallocated_signing_only( + buf: &'buf mut [AlignedType], + ) -> Result>, 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, + ) -> ManuallyDrop>> { + ManuallyDrop::new(Secp256k1 { ctx: raw_ctx, phantom: PhantomData }) + } } - /// 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() } + impl<'buf> Secp256k1> { + /// Creates a new Secp256k1 context that can only be used for verification + pub fn preallocated_verification_only( + buf: &'buf mut [AlignedType], + ) -> Result>, Error> { + Secp256k1::preallocated_gen_new(buf) + } - /// 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, - ) -> ManuallyDrop>> { - ManuallyDrop::new(Secp256k1 { ctx: raw_ctx, phantom: PhantomData }) + /// 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, + ) -> ManuallyDrop>> { + ManuallyDrop::new(Secp256k1 { ctx: raw_ctx, phantom: PhantomData }) + } } } diff --git a/src/ecdh.rs b/src/ecdh.rs index e53dd79dd..f452415a5 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -4,11 +4,16 @@ //! use core::borrow::Borrow; -use core::{ptr, str}; +use core::str; +#[cfg(feature = "secp256k1-sys")] +use core::ptr; +#[cfg(feature = "secp256k1-sys")] use secp256k1_sys::types::{c_int, c_uchar, c_void}; +#[cfg(feature = "secp256k1-sys")] use crate::ffi::{self, CPtr}; +#[cfg(feature = "secp256k1-sys")] use crate::key::{PublicKey, SecretKey}; use crate::{constants, Error}; @@ -20,7 +25,7 @@ const SHARED_SECRET_SIZE: usize = constants::SECRET_KEY_SIZE; /// # Examples /// /// ``` -/// # #[cfg(feature = "rand-std")] { +/// # #[cfg(all(feature = "rand-std", feature = "secp256k1-sys"))] { /// # use secp256k1::{rand, Secp256k1}; /// # use secp256k1::ecdh::SharedSecret; /// let s = Secp256k1::new(); @@ -39,6 +44,7 @@ impl_non_secure_erase!(SharedSecret, 0, [0u8; SHARED_SECRET_SIZE]); impl SharedSecret { /// Creates a new shared secret from a pubkey and secret key. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret { let mut buf = [0u8; SHARED_SECRET_SIZE]; let res = unsafe { @@ -125,6 +131,7 @@ impl AsRef<[u8]> for SharedSecret { /// assert_eq!(secret1, secret2) /// # } /// ``` +#[cfg(feature = "secp256k1-sys")] pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] { let mut xy = [0u8; 64]; @@ -144,6 +151,7 @@ pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] { xy } +#[cfg(feature = "secp256k1-sys")] unsafe extern "C" fn c_callback( output: *mut c_uchar, x: *const c_uchar, @@ -184,6 +192,7 @@ impl<'de> ::serde::Deserialize<'de> for SharedSecret { } #[cfg(test)] +#[cfg(feature = "secp256k1-sys")] #[allow(unused_imports)] mod tests { #[cfg(target_arch = "wasm32")] @@ -194,6 +203,7 @@ mod tests { #[test] #[cfg(feature = "rand-std")] + #[cfg(feature = "secp256k1-sys")] fn ecdh() { let s = Secp256k1::signing_only(); let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng()); @@ -207,6 +217,7 @@ mod tests { } #[test] + #[cfg(feature = "secp256k1-sys")] fn test_c_callback() { let x = [5u8; 32]; let y = [7u8; 32]; @@ -226,6 +237,7 @@ mod tests { #[test] #[cfg(not(secp256k1_fuzz))] #[cfg(all(feature = "hashes-std", feature = "rand-std"))] + #[cfg(feature = "secp256k1-sys")] fn hashes_and_sys_generate_same_secret() { use hashes::{sha256, Hash, HashEngine}; @@ -248,7 +260,10 @@ mod tests { assert_eq!(secret_bh.as_byte_array(), secret_sys.as_ref()); } +} +#[cfg(test)] +mod non_crypto_tests { #[test] #[cfg(all(feature = "serde", feature = "alloc"))] fn serde() { @@ -262,7 +277,7 @@ mod tests { ]; static STR: &str = "01010101010101010001020304050607ffff0000ffff00006363636363636363"; - let secret = SharedSecret::from_slice(&BYTES).unwrap(); + let secret = super::SharedSecret::from_slice(&BYTES).unwrap(); assert_tokens(&secret.compact(), &[Token::BorrowedBytes(&BYTES[..])]); assert_tokens(&secret.compact(), &[Token::Bytes(&BYTES)]); @@ -283,6 +298,7 @@ mod benches { use crate::Secp256k1; #[bench] + #[cfg(feature = "secp256k1-sys")] pub fn bench_ecdh(bh: &mut Bencher) { let s = Secp256k1::signing_only(); let (sk, pk) = s.generate_keypair(&mut rand::thread_rng()); diff --git a/src/key.rs b/src/key.rs index 012f09d36..cf2dda0c1 100644 --- a/src/key.rs +++ b/src/key.rs @@ -4,22 +4,34 @@ //! use core::ops::{self, BitXor}; -use core::{fmt, ptr, str}; +use core::{fmt, str}; + +#[cfg(not(feature = "secp256k1-sys"))] +mod non_c; #[cfg(feature = "serde")] use serde::ser::SerializeTuple; +#[cfg(feature = "secp256k1-sys")] use crate::ellswift::ElligatorSwift; +#[cfg(feature = "secp256k1-sys")] use crate::ffi::types::c_uint; +#[cfg(feature = "secp256k1-sys")] use crate::ffi::{self, CPtr}; -use crate::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey}; +use crate::Error::{self, InvalidPublicKey, InvalidSecretKey}; +#[cfg(feature = "secp256k1-sys")] +use crate::Error::InvalidPublicKeySum; #[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, + constants, from_hex, Secp256k1, Signing +}; +#[cfg(feature = "secp256k1-sys")] +use crate::{ + ecdsa, Message, Scalar, schnorr, Verification, }; /// Secret key - a 256-bit key used to create ECDSA and Taproot signatures. @@ -44,7 +56,7 @@ use crate::{ /// Basic usage: /// /// ``` -/// # #[cfg(feature = "rand-std")] { +/// # #[cfg(all(feature = "rand-std", feature = "secp256k1-sys"))] { /// use secp256k1::{rand, Secp256k1, SecretKey}; /// /// let secp = Secp256k1::new(); @@ -96,6 +108,7 @@ where fn index(&self, index: I) -> &Self::Output { &self.0[index] } } +#[cfg(feature = "secp256k1-sys")] impl ffi::CPtr for SecretKey { type Target = u8; @@ -121,6 +134,12 @@ impl str::FromStr for SecretKey { } } +#[cfg(feature = "secp256k1-sys")] +type PublicKeyRepr = ffi::PublicKey; + +#[cfg(not(feature = "secp256k1-sys"))] +type PublicKeyRepr = non_c::PublicKey; + /// Public key - used to verify ECDSA signatures and to do Taproot tweaks. /// /// # Serde support @@ -134,7 +153,7 @@ impl str::FromStr for SecretKey { /// Basic usage: /// /// ``` -/// # #[cfg(feature = "alloc")] { +/// # #[cfg(all(feature = "alloc", feature = "secp256k1-sys"))] { /// use secp256k1::{SecretKey, Secp256k1, PublicKey}; /// /// let secp = Secp256k1::new(); @@ -145,8 +164,8 @@ impl str::FromStr for SecretKey { /// [`bincode`]: https://docs.rs/bincode /// [`cbor`]: https://docs.rs/cbor #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] -#[repr(transparent)] -pub struct PublicKey(ffi::PublicKey); +#[cfg_attr(feature = "secp256k1", repr(transparent))] +pub struct PublicKey(PublicKeyRepr); impl_fast_comparisons!(PublicKey); impl fmt::LowerHex for PublicKey { @@ -188,7 +207,7 @@ impl SecretKey { /// # } /// ``` #[inline] - #[cfg(feature = "rand")] + #[cfg(all(feature = "rand", feature = "secp256k1-sys"))] pub fn new(rng: &mut R) -> SecretKey { let mut data = crate::random_32_bytes(rng); unsafe { @@ -214,6 +233,7 @@ impl SecretKey { #[inline] pub fn from_slice(data: &[u8]) -> Result { match <[u8; constants::SECRET_KEY_SIZE]>::try_from(data) { + #[cfg(feature = "secp256k1-sys")] Ok(data) => { unsafe { if ffi::secp256k1_ec_seckey_verify( @@ -226,7 +246,9 @@ impl SecretKey { } Ok(SecretKey(data)) } - Err(_) => Err(InvalidSecretKey), + #[cfg(not(feature = "secp256k1-sys"))] + Ok(data) if non_c::is_seckey_valid(&data) => Ok(SecretKey(data)), + _ => Err(InvalidSecretKey), } } @@ -235,7 +257,7 @@ impl SecretKey { /// # Examples /// /// ``` - /// # #[cfg(feature = "rand-std")] { + /// # #[cfg(all(feature = "rand-std", feature = "secp256k1-sys"))] { /// use secp256k1::{rand, Secp256k1, SecretKey, Keypair}; /// /// let secp = Secp256k1::new(); @@ -245,16 +267,23 @@ impl SecretKey { /// ``` #[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); + #[cfg(feature = "secp256k1-sys")] + { + 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) + } + #[cfg(not(feature = "secp256k1-sys"))] + { + keypair.0 } - SecretKey(sk) } /// Returns the secret key as a byte value. @@ -263,6 +292,7 @@ impl SecretKey { /// Negates the secret key. #[inline] + #[cfg(feature = "secp256k1-sys")] #[must_use = "you forgot to use the negated secret key"] pub fn negate(mut self) -> SecretKey { unsafe { @@ -281,6 +311,7 @@ impl SecretKey { /// /// Returns an error if the resulting key would be invalid. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn add_tweak(mut self, tweak: &Scalar) -> Result { unsafe { if ffi::secp256k1_ec_seckey_tweak_add( @@ -302,6 +333,7 @@ impl SecretKey { /// /// Returns an error if the resulting key would be invalid. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn mul_tweak(mut self, tweak: &Scalar) -> Result { unsafe { if ffi::secp256k1_ec_seckey_tweak_mul( @@ -319,6 +351,7 @@ impl SecretKey { /// Constructs an ECDSA signature for `msg` using the global [`SECP256K1`] context. #[inline] + #[cfg(feature = "secp256k1-sys")] #[cfg(feature = "global-context")] pub fn sign_ecdsa(&self, msg: Message) -> ecdsa::Signature { SECP256K1.sign_ecdsa(&msg, self) } @@ -326,6 +359,7 @@ impl SecretKey { /// /// This is equivalent to using [`Keypair::from_secret_key`]. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn keypair(&self, secp: &Secp256k1) -> Keypair { Keypair::from_secret_key(secp, self) } @@ -334,6 +368,7 @@ impl SecretKey { /// /// This is equivalent to using [`PublicKey::from_secret_key`]. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn public_key(&self, secp: &Secp256k1) -> PublicKey { PublicKey::from_secret_key(secp, self) } @@ -342,6 +377,7 @@ impl SecretKey { /// /// This is equivalent to `XOnlyPublicKey::from_keypair(self.keypair(secp))`. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn x_only_public_key(&self, secp: &Secp256k1) -> (XOnlyPublicKey, Parity) { let kp = self.keypair(secp); XOnlyPublicKey::from_keypair(&kp) @@ -374,6 +410,7 @@ impl serde::Serialize for SecretKey { } #[cfg(feature = "serde")] +#[cfg(feature = "secp256k1-sys")] impl<'de> serde::Deserialize<'de> for SecretKey { fn deserialize>(d: D) -> Result { if d.is_human_readable() { @@ -394,6 +431,7 @@ 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")] + #[cfg(feature = "secp256k1-sys")] pub fn as_ptr(&self) -> *const ffi::PublicKey { self.as_c_ptr() } /// Obtains a raw mutable pointer suitable for use with FFI functions. @@ -402,6 +440,7 @@ impl PublicKey { since = "0.25.0", note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" )] + #[cfg(feature = "secp256k1-sys")] pub fn as_mut_ptr(&mut self) -> *mut ffi::PublicKey { self.as_mut_c_ptr() } /// Creates a new public key from a [`SecretKey`]. @@ -418,6 +457,7 @@ impl PublicKey { /// # } /// ``` #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn from_secret_key(secp: &Secp256k1, sk: &SecretKey) -> PublicKey { unsafe { let mut pk = ffi::PublicKey::new(); @@ -430,11 +470,12 @@ impl PublicKey { } /// Creates a new public key from an [`ElligatorSwift`]. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn from_ellswift(ellswift: ElligatorSwift) -> PublicKey { ElligatorSwift::decode(ellswift) } /// Creates a new public key from a [`SecretKey`] and the global [`SECP256K1`] context. #[inline] - #[cfg(feature = "global-context")] + #[cfg(all(feature = "global-context", feature = "secp256k1-sys"))] pub fn from_secret_key_global(sk: &SecretKey) -> PublicKey { PublicKey::from_secret_key(SECP256K1, sk) } @@ -442,24 +483,31 @@ impl PublicKey { /// Creates a public key directly from a slice. #[inline] pub fn from_slice(data: &[u8]) -> Result { - if data.is_empty() { - return Err(Error::InvalidPublicKey); - } + #[cfg(feature = "secp256k1-sys")] + { + if data.is_empty() { + return Err(Error::InvalidPublicKey); + } - unsafe { - let mut pk = ffi::PublicKey::new(); - if ffi::secp256k1_ec_pubkey_parse( - ffi::secp256k1_context_no_precomp, - &mut pk, - data.as_c_ptr(), - data.len(), - ) == 1 - { - Ok(PublicKey(pk)) - } else { - Err(InvalidPublicKey) + unsafe { + let mut pk = ffi::PublicKey::new(); + if ffi::secp256k1_ec_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + data.len(), + ) == 1 + { + Ok(PublicKey(pk)) + } else { + Err(InvalidPublicKey) + } } } + #[cfg(not(feature = "secp256k1-sys"))] + { + Ok(PublicKey(PublicKeyRepr::decode(data).ok_or(InvalidPublicKey)?)) + } } /// Creates a new compressed public key using data from BIP-340 [`Keypair`]. @@ -476,6 +524,7 @@ impl PublicKey { /// # } /// ``` #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn from_keypair(keypair: &Keypair) -> Self { unsafe { let mut pk = ffi::PublicKey::new(); @@ -507,20 +556,35 @@ impl PublicKey { /// 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 + #[cfg(feature = "secp256k1-sys")] + { + let mut ret = [0u8; constants::PUBLIC_KEY_SIZE]; + self.serialize_internal(&mut ret, ffi::SECP256K1_SER_COMPRESSED); + ret + } + #[cfg(not(feature = "secp256k1-sys"))] + { + self.0.serialize_compressed() + } } #[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 + #[cfg(feature = "secp256k1-sys")] + { + let mut ret = [0u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]; + self.serialize_internal(&mut ret, ffi::SECP256K1_SER_UNCOMPRESSED); + ret + } + #[cfg(not(feature = "secp256k1-sys"))] + { + self.0.serialize_uncompressed() + } } #[inline(always)] + #[cfg(feature = "secp256k1-sys")] fn serialize_internal(&self, ret: &mut [u8], flag: c_uint) { let mut ret_len = ret.len(); let res = unsafe { @@ -539,6 +603,7 @@ impl PublicKey { /// Negates the public key. #[inline] #[must_use = "you forgot to use the negated public key"] + #[cfg(feature = "secp256k1-sys")] pub fn negate(mut self, secp: &Secp256k1) -> PublicKey { unsafe { let res = ffi::secp256k1_ec_pubkey_negate(secp.ctx.as_ptr(), &mut self.0); @@ -553,6 +618,7 @@ impl PublicKey { /// /// Returns an error if the resulting key would be invalid. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn add_exp_tweak( mut self, secp: &Secp256k1, @@ -575,6 +641,7 @@ impl PublicKey { /// /// Returns an error if the resulting key would be invalid. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn mul_tweak( mut self, secp: &Secp256k1, @@ -610,6 +677,7 @@ impl PublicKey { /// let sum = pk1.combine(&pk2).expect("It's improbable to fail for 2 random public keys"); /// # } /// ``` + #[cfg(feature = "secp256k1-sys")] pub fn combine(&self, other: &PublicKey) -> Result { PublicKey::combine_keys(&[self, other]) } @@ -637,6 +705,7 @@ impl PublicKey { /// let sum = PublicKey::combine_keys(&[&pk1, &pk2, &pk3]).expect("It's improbable to fail for 3 random public keys"); /// # } /// ``` + #[cfg(feature = "secp256k1-sys")] pub fn combine_keys(keys: &[&PublicKey]) -> Result { use core::i32::MAX; use core::mem::transmute; @@ -666,24 +735,33 @@ impl PublicKey { /// 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) + #[cfg(feature = "secp256k1-sys")] + { + 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) + } + } + #[cfg(not(feature = "secp256k1-sys"))] + { + let (key, parity) = self.0.to_xonly(); + (XOnlyPublicKey(key), parity) } } /// Checks that `sig` is a valid ECDSA signature for `msg` using this public key. + #[cfg(feature = "secp256k1-sys")] pub fn verify( &self, secp: &Secp256k1, @@ -696,6 +774,7 @@ impl PublicKey { /// 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. +#[cfg(feature = "secp256k1-sys")] impl CPtr for PublicKey { type Target = ffi::PublicKey; @@ -709,6 +788,7 @@ impl CPtr for PublicKey { /// Creates a new public key from a FFI public key. /// /// Note, normal users should never need to interact directly with FFI types. +#[cfg(feature = "secp256k1-sys")] impl From for PublicKey { #[inline] fn from(pk: ffi::PublicKey) -> PublicKey { PublicKey(pk) } @@ -747,6 +827,11 @@ impl<'de> serde::Deserialize<'de> for PublicKey { } } +#[cfg(feature = "secp256k1-sys")] +type KeypairRepr = ffi::Keypair; +#[cfg(not(feature = "secp256k1-sys"))] +type KeypairRepr = SecretKey; + /// Opaque data structure that holds a keypair consisting of a secret and a public key. /// /// # Serde support @@ -761,7 +846,7 @@ impl<'de> serde::Deserialize<'de> for PublicKey { /// Basic usage: /// /// ``` -/// # #[cfg(feature = "rand-std")] { +/// # #[cfg(all(feature = "rand-std", feature = "secp256k1-sys"))] { /// use secp256k1::{rand, Keypair, Secp256k1}; /// /// let secp = Secp256k1::new(); @@ -771,15 +856,18 @@ impl<'de> serde::Deserialize<'de> for PublicKey { /// ``` /// [`bincode`]: https://docs.rs/bincode /// [`cbor`]: https://docs.rs/cbor -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct Keypair(ffi::Keypair); +#[derive(Copy, Clone)] +#[cfg_attr(feature = "secp256k1-sys", derive(PartialOrd, Ord, PartialEq, Eq, Hash))] +pub struct Keypair(KeypairRepr); impl_display_secret!(Keypair); +#[cfg(feature = "secp256k1-sys")] 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")] + #[cfg(feature = "secp256k1-sys")] pub fn as_ptr(&self) -> *const ffi::Keypair { self.as_c_ptr() } /// Obtains a raw mutable pointer suitable for use with FFI functions. @@ -788,11 +876,13 @@ impl Keypair { since = "0.25.0", note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" )] + #[cfg(feature = "secp256k1-sys")] 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(secp: &Secp256k1, sk: &SecretKey) -> Keypair { + #[cfg(feature = "secp256k1-sys")] unsafe { let mut kp = ffi::Keypair::new(); if ffi::secp256k1_keypair_create(secp.ctx.as_ptr(), &mut kp, sk.as_c_ptr()) == 1 { @@ -801,6 +891,11 @@ impl Keypair { panic!("the provided secret key is invalid: it is corrupted or was not produced by Secp256k1 library") } } + #[cfg(not(feature = "secp256k1-sys"))] + { + let _ = secp; + Keypair(*sk) + } } /// Creates a [`Keypair`] directly from a secret key slice. @@ -814,18 +909,25 @@ impl Keypair { secp: &Secp256k1, data: &[u8], ) -> Result { - if data.is_empty() || data.len() != constants::SECRET_KEY_SIZE { - return Err(Error::InvalidSecretKey); - } + #[cfg(feature = "secp256k1-sys")] + { + 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) + 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) + } } } + #[cfg(not(feature = "secp256k1-sys"))] + { + Ok(Keypair::from_secret_key(secp, &SecretKey::from_slice(data)?)) + } } /// Creates a [`Keypair`] directly from a secret key string. @@ -849,7 +951,7 @@ impl Keypair { /// /// [`Error::InvalidSecretKey`] if corresponding public key for the provided secret key is not even. #[inline] - #[cfg(feature = "global-context")] + #[cfg(all(feature = "global-context", feature = "secp256k1-sys"))] pub fn from_seckey_str_global(s: &str) -> Result { Keypair::from_seckey_str(SECP256K1, s) } @@ -866,7 +968,7 @@ impl Keypair { /// # } /// ``` #[inline] - #[cfg(feature = "rand")] + #[cfg(all(feature = "rand", feature = "secp256k1-sys"))] pub fn new(secp: &Secp256k1, rng: &mut R) -> Keypair { let mut data = crate::random_32_bytes(rng); unsafe { @@ -882,7 +984,7 @@ impl Keypair { /// Generates a new random secret key using the global [`SECP256K1`] context. #[inline] - #[cfg(all(feature = "global-context", feature = "rand"))] + #[cfg(all(feature = "global-context", feature = "rand", feature = "secp256k1-sys"))] pub fn new_global(rng: &mut R) -> Keypair { Keypair::new(SECP256K1, rng) } @@ -917,6 +1019,7 @@ impl Keypair { /// ``` // TODO: Add checked implementation #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn add_xonly_tweak( mut self, secp: &Secp256k1, @@ -946,12 +1049,14 @@ impl Keypair { /// /// This is equivalent to using [`PublicKey::from_keypair`]. #[inline] + #[cfg(feature = "secp256k1-sys")] 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] + #[cfg(feature = "secp256k1-sys")] pub fn x_only_public_key(&self) -> (XOnlyPublicKey, Parity) { XOnlyPublicKey::from_keypair(self) } @@ -959,6 +1064,7 @@ impl Keypair { /// Constructs an schnorr signature for `msg` using the global [`SECP256K1`] context. #[inline] #[cfg(all(feature = "global-context", feature = "rand-std"))] + #[cfg(feature = "secp256k1-sys")] pub fn sign_schnorr(&self, msg: Message) -> schnorr::Signature { SECP256K1.sign_schnorr(&msg, self) } @@ -970,6 +1076,7 @@ impl Keypair { /// For more discussion on this, please see the documentation of the /// [`zeroize`](https://docs.rs/zeroize) crate. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn non_secure_erase(&mut self) { self.0.non_secure_erase(); } } @@ -983,30 +1090,30 @@ impl<'a> From<&'a Keypair> for SecretKey { fn from(pair: &'a Keypair) -> Self { SecretKey::from_keypair(pair) } } +#[cfg(feature = "secp256k1-sys")] impl From for PublicKey { #[inline] fn from(pair: Keypair) -> Self { PublicKey::from_keypair(&pair) } } +#[cfg(feature = "secp256k1-sys")] 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; #[allow(unused_variables, unreachable_code)] // When built with no default features. fn from_str(s: &str) -> Result { #[cfg(feature = "global-context")] - let ctx = SECP256K1; + let ctx = &**SECP256K1; #[cfg(all(not(feature = "global-context"), feature = "alloc"))] let ctx = Secp256k1::signing_only(); - #[cfg(not(any(feature = "global-context", feature = "alloc")))] - let ctx: Secp256k1 = panic!("The previous implementation was panicking too, please enable the global-context feature of rust-secp256k1"); - #[allow(clippy::needless_borrow)] Keypair::from_seckey_str(&ctx, s) } @@ -1031,7 +1138,7 @@ impl serde::Serialize for Keypair { } } -#[cfg(feature = "serde")] +#[cfg(all(feature = "serde", any(feature = "global-context", feature = "alloc")))] #[allow(unused_variables)] // For `data` under some feature combinations (the unconditional panic below). #[allow(unreachable_code)] // For `Keypair::from_seckey_slice` after unconditional panic. impl<'de> serde::Deserialize<'de> for Keypair { @@ -1048,9 +1155,6 @@ impl<'de> serde::Deserialize<'de> for Keypair { #[cfg(all(not(feature = "global-context"), feature = "alloc"))] let ctx = Secp256k1::signing_only(); - #[cfg(not(any(feature = "global-context", feature = "alloc")))] - let ctx: Secp256k1 = panic!("cannot deserialize key pair without a context (please enable either the global-context or alloc feature)"); - #[allow(clippy::needless_borrow)] Keypair::from_seckey_slice(&ctx, data) }); @@ -1059,6 +1163,7 @@ impl<'de> serde::Deserialize<'de> for Keypair { } } +#[cfg(feature = "secp256k1-sys")] impl CPtr for Keypair { type Target = ffi::Keypair; fn as_c_ptr(&self) -> *const Self::Target { &self.0 } @@ -1066,6 +1171,12 @@ impl CPtr for Keypair { fn as_mut_c_ptr(&mut self) -> *mut Self::Target { &mut self.0 } } +#[cfg(feature = "secp256k1-sys")] +type XOnlyPublicKeyRepr = ffi::XOnlyPublicKey; + +#[cfg(not(feature = "secp256k1-sys"))] +type XOnlyPublicKeyRepr = non_c::XOnlyPublicKey; + /// An x-only public key, used for verification of Taproot signatures and serialized according to BIP-340. /// /// # Serde support @@ -1079,7 +1190,7 @@ impl CPtr for Keypair { /// Basic usage: /// /// ``` -/// # #[cfg(feature = "rand-std")] { +/// # #[cfg(all(feature = "rand-std", feature = "secp256k1-sys"))] { /// use secp256k1::{rand, Secp256k1, Keypair, XOnlyPublicKey}; /// /// let secp = Secp256k1::new(); @@ -1090,7 +1201,7 @@ impl CPtr for Keypair { /// [`bincode`]: https://docs.rs/bincode /// [`cbor`]: https://docs.rs/cbor #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct XOnlyPublicKey(ffi::XOnlyPublicKey); +pub struct XOnlyPublicKey(XOnlyPublicKeyRepr); impl_fast_comparisons!(XOnlyPublicKey); impl fmt::LowerHex for XOnlyPublicKey { @@ -1123,6 +1234,7 @@ 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")] + #[cfg(feature = "secp256k1-sys")] pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { self.as_c_ptr() } /// Obtains a raw mutable pointer suitable for use with FFI functions. @@ -1131,10 +1243,12 @@ impl XOnlyPublicKey { since = "0.25.0", note = "Use Self::as_mut_c_ptr if you need to access the FFI layer" )] + #[cfg(feature = "secp256k1-sys")] 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] + #[cfg(feature = "secp256k1-sys")] pub fn from_keypair(keypair: &Keypair) -> (XOnlyPublicKey, Parity) { let mut pk_parity = 0; unsafe { @@ -1161,39 +1275,52 @@ impl XOnlyPublicKey { /// slice does not represent a valid Secp256k1 point x coordinate. #[inline] pub fn from_slice(data: &[u8]) -> Result { - if data.is_empty() || data.len() != constants::SCHNORR_PUBLIC_KEY_SIZE { - return Err(Error::InvalidPublicKey); - } - - 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) + let data: &[u8; constants::SCHNORR_PUBLIC_KEY_SIZE] = data.try_into() + .map_err(|_| Error::InvalidPublicKey)?; + + #[cfg(feature = "secp256k1-sys")] + { + 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) + } } } + #[cfg(not(feature = "secp256k1-sys"))] + { + Ok(XOnlyPublicKey(XOnlyPublicKeyRepr::from_bytes(&data).ok_or(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]; + #[cfg(feature = "secp256k1-sys")] + { + 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); + 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 + } + #[cfg(not(feature = "secp256k1-sys"))] + { + self.0.serialize() } - ret } /// Tweaks an [`XOnlyPublicKey`] by adding the generator multiplied with the given tweak to it. @@ -1222,6 +1349,7 @@ impl XOnlyPublicKey { /// let tweaked = xonly.add_tweak(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak"); /// # } /// ``` + #[cfg(feature = "secp256k1-sys")] pub fn add_tweak( mut self, secp: &Secp256k1, @@ -1284,6 +1412,7 @@ impl XOnlyPublicKey { /// assert!(original.tweak_add_check(&secp, &tweaked, parity, tweak)); /// # } /// ``` + #[cfg(feature = "secp256k1-sys")] pub fn tweak_add_check( &self, secp: &Secp256k1, @@ -1314,6 +1443,7 @@ impl XOnlyPublicKey { } /// Checks that `sig` is a valid schnorr signature for `msg` using this public key. + #[cfg(feature = "secp256k1-sys")] pub fn verify( &self, secp: &Secp256k1, @@ -1363,6 +1493,16 @@ impl Parity { _ => Err(InvalidParityValue(parity)), } } + + /// Returns true if `self` is `Parity::Even. + pub fn is_even(self) -> bool { + self == Parity::Even + } + + /// Returns true if `self` is `Parity::Odd. + pub fn is_odd(self) -> bool { + self == Parity::Odd + } } /// `Even` for `0`, `Odd` for `1`, error for anything else @@ -1460,6 +1600,7 @@ impl<'de> serde::Deserialize<'de> for Parity { } } +#[cfg(feature = "secp256k1-sys")] impl CPtr for XOnlyPublicKey { type Target = ffi::XOnlyPublicKey; fn as_c_ptr(&self) -> *const Self::Target { &self.0 } @@ -1468,6 +1609,7 @@ impl CPtr for XOnlyPublicKey { } /// Creates a new schnorr public key from a FFI x-only public key. +#[cfg(feature = "secp256k1-sys")] impl From for XOnlyPublicKey { #[inline] fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey { XOnlyPublicKey(pk) } @@ -1475,19 +1617,7 @@ impl From for XOnlyPublicKey { impl From 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) - } + src.x_only_public_key().0 } } @@ -1525,27 +1655,10 @@ impl<'de> serde::Deserialize<'de> for XOnlyPublicKey { #[cfg(test)] #[allow(unused_imports)] -mod test { - use core::str::FromStr; - - #[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}; +mod non_crypto_test { + use super::{Parity, PublicKey, SecretKey, XOnlyPublicKey}; use crate::{constants, from_hex, to_hex, Scalar}; - - #[cfg(not(secp256k1_fuzz))] - macro_rules! hex { - ($hex:expr) => {{ - let mut result = vec![0; $hex.len() / 2]; - from_hex($hex, &mut result).expect("valid hex string"); - result - }}; - } + use crate::Error::{InvalidSecretKey, InvalidPublicKey}; #[test] fn skey_from_slice() { @@ -1558,6 +1671,7 @@ mod test { #[test] fn pubkey_from_slice() { + "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8".parse::().unwrap(); assert_eq!(PublicKey::from_slice(&[]), Err(InvalidPublicKey)); assert_eq!(PublicKey::from_slice(&[1, 2, 3]), Err(InvalidPublicKey)); @@ -1576,35 +1690,13 @@ mod test { assert!(compressed.is_ok()); } - #[test] - #[cfg(feature = "rand-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("0000000000000000000000000000000000000000000000000000000000000000"), + "0000000000000000000000000000000000000000000000000000000000000000".parse::(), Err(InvalidSecretKey) ); // -1 @@ -1625,38 +1717,6 @@ mod test { ]).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 @@ -1711,6 +1771,86 @@ mod test { ); assert_eq!(SecretKey::from_slice(&[]), Err(InvalidSecretKey)); } +} + +#[cfg(test)] +#[allow(unused_imports)] +#[cfg(feature = "secp256k1-sys")] +mod test { + use core::str::FromStr; + + #[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}; + + #[cfg(not(secp256k1_fuzz))] + macro_rules! hex { + ($hex:expr) => {{ + let mut result = vec![0; $hex.len() / 2]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }}; + } + + #[test] + #[cfg(feature = "rand-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] + #[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] #[cfg(all(feature = "rand", feature = "alloc"))] @@ -2420,14 +2560,6 @@ mod test { .collect::>(); serde_test::assert_tokens(&keypair.compact(), &tokens); } - - #[test] - #[should_panic(expected = "The previous implementation was panicking too")] - #[cfg(not(any(feature = "alloc", feature = "global-context")))] - fn test_parse_keypair_no_alloc_panic() { - let key_hex = "4242424242424242424242424242424242424242424242424242424242424242"; - let _: Keypair = key_hex.parse().expect("We shouldn't even get this far"); - } } #[cfg(bench)] diff --git a/src/key/non_c/mod.rs b/src/key/non_c/mod.rs new file mode 100644 index 000000000..37da93772 --- /dev/null +++ b/src/key/non_c/mod.rs @@ -0,0 +1,130 @@ +//! Pure Rust implementation of basic secp256k1-related checks. +//! +//! It can be useful to have the key types provided by this library without full cryptography and +//! implied compilation/linking of C. However for compatibility, we must check the keys when +//! deserializing. Therefore we need at least some basic secp256k1 math to do that. +//! +//! We explicitly do **not** implement point operations or similarly advanced features. + +pub(crate) mod u256; +pub(crate) mod zp; + +use zp::Zp; +use crate::Parity; + +fn is_point_on_curve(x: Zp, y: Zp) -> bool { + y * y == x * x * x + Zp::wrapping_from(u256::U256::from(7u128)) +} + +fn compute_y_coord(x: Zp, parity: Parity) -> Option { + (x * x * x + Zp::wrapping_from(u256::U256::from(7u128))).sqrt(parity) +} + +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub(crate) struct PublicKey { + x: Zp, + y: Zp, +} + +impl PublicKey { + pub(crate) fn decode(key: &[u8]) -> Option { + match *key.get(0)? { + 0x04 if key.len() == 65 => { + let x = Zp::from_be_bytes(key[1..33].try_into().expect("static len"))?; + let y = Zp::from_be_bytes(key[33..].try_into().expect("static len"))?; + if is_point_on_curve(x, y) { + Some(PublicKey { x, y }) + } else { + None + } + }, + parity @ (0x02 | 0x03) if key.len() == 33 => { + let x = Zp::from_be_bytes(key[1..33].try_into().expect("static len"))?; + let y = compute_y_coord(x, if parity == 0x02 { Parity::Even } else { Parity::Odd})?; + Some(PublicKey { x, y }) + }, + _ => None, + } + } + + pub(crate) fn serialize_compressed(&self) -> [u8; 33] { + let mut buf = [0; 33]; + buf[0] = if self.y.is_even() { + 0x02 + } else { + 0x03 + }; + buf[1..].copy_from_slice(&self.x.to_be_bytes()); + buf + } + + pub(crate) fn serialize_uncompressed(&self) -> [u8; 65] { + let mut buf = [0; 65]; + buf[0] = 0x04; + buf[1..33].copy_from_slice(&self.x.to_be_bytes()); + buf[33..].copy_from_slice(&self.y.to_be_bytes()); + buf + } + + pub(crate) fn to_xonly(&self) -> (XOnlyPublicKey, Parity) { + let parity = if self.y.is_even() { + Parity::Even + } else { + Parity::Odd + }; + (XOnlyPublicKey { x: self.x }, parity) + } +} + +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub(crate) struct XOnlyPublicKey { + x: Zp, +} + +impl XOnlyPublicKey { + pub(crate) fn from_bytes(bytes: &[u8; 32]) -> Option { + let x = Zp::from_be_bytes(bytes)?; + compute_y_coord(x, Parity::Even)?; + Some(XOnlyPublicKey { x }) + } + + pub(crate) fn serialize(&self) -> [u8; 32] { + self.x.to_be_bytes() + } +} + +pub(crate) fn is_seckey_valid(scalar: &[u8; 32]) -> bool { + let sum = scalar.iter().copied().fold(0u8, u8::wrapping_add); + if launder_u8(sum) == 0 { + return false; + } + + // Translated from the C version + const N: [u32; 8] = [0xD0364141, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF]; + let mut buf = [0; 8]; + for (chunk, dst) in scalar.chunks_exact(4).rev().zip(&mut buf) { + *dst = u32::from_be_bytes(chunk.try_into().expect("chunk_exact returns 4-byte slices")); + } + let scalar = buf; + let mut yes = 0u8; + let mut no = 0u8; + no |= u8::from(scalar[7] < N[7]); /* No need for a > check. */ + no |= u8::from(scalar[6] < N[6]); /* No need for a > check. */ + no |= u8::from(scalar[5] < N[5]); /* No need for a > check. */ + no |= u8::from(scalar[4] < N[4]); + yes |= u8::from(scalar[4] > N[4]) & !no; + no |= u8::from(scalar[3] < N[3]) & !yes; + yes |= u8::from(scalar[3] > N[3]) & !no; + no |= u8::from(scalar[2] < N[2]) & !yes; + yes |= u8::from(scalar[2] > N[2]) & !no; + no |= u8::from(scalar[1] < N[1]) & !yes; + yes |= u8::from(scalar[1] > N[1]) & !no; + yes |= u8::from(scalar[0] >= N[0]) & !no; + launder_u8(yes) == 0 +} + +fn launder_u8(val: u8) -> u8 { + unsafe { + core::ptr::read_volatile(&val) + } +} diff --git a/src/key/non_c/u256.rs b/src/key/non_c/u256.rs new file mode 100644 index 000000000..646712bc3 --- /dev/null +++ b/src/key/non_c/u256.rs @@ -0,0 +1,414 @@ +#![allow(dead_code)] +/// Implementation of unsigned 256-bit integer, copied from `bitcoin`. +// Probably better than constant refactoring + +use core::ops::{Add, Sub, Mul, Div, Rem, Not, Shl, Shr, BitAnd}; +use core::fmt; + +/// Big-endian 256 bit integer type. +// (high, low): u.0 contains the high bits, u.1 contains the low bits. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub(crate) struct U256(u128, u128); + +impl U256 { + const MAX: U256 = + U256(0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff, 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff); + + pub(crate) const ZERO: U256 = U256(0, 0); + + pub(crate) const ONE: U256 = U256(0, 1); + + /// Creates `U256` from a big-endian array of `u8`s. + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) const fn from_be_bytes(a: [u8; 32]) -> U256 { + let (high, low) = split_in_half(a); + let big = u128::from_be_bytes(high); + let little = u128::from_be_bytes(low); + U256(big, little) + } + + /// Creates a `U256` from a little-endian array of `u8`s. + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn from_le_bytes(a: [u8; 32]) -> U256 { + let (high, low) = split_in_half(a); + let little = u128::from_le_bytes(high); + let big = u128::from_le_bytes(low); + U256(big, little) + } + + /// Converts `U256` to a big-endian array of `u8`s. + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn to_be_bytes(self) -> [u8; 32] { + let mut out = [0; 32]; + out[..16].copy_from_slice(&self.0.to_be_bytes()); + out[16..].copy_from_slice(&self.1.to_be_bytes()); + out + } + + /// Converts `U256` to a little-endian array of `u8`s. + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn to_le_bytes(self) -> [u8; 32] { + let mut out = [0; 32]; + out[..16].copy_from_slice(&self.1.to_le_bytes()); + out[16..].copy_from_slice(&self.0.to_le_bytes()); + out + } + + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn is_zero(&self) -> bool { self.0 == 0 && self.1 == 0 } + + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn is_one(&self) -> bool { self.0 == 0 && self.1 == 1 } + + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn is_max(&self) -> bool { self.0 == u128::MAX && self.1 == u128::MAX } + + /// Returns the low 32 bits. + pub(crate) fn low_u32(&self) -> u32 { self.low_u128() as u32 } + + /// Returns the low 64 bits. + pub(crate) fn low_u64(&self) -> u64 { self.low_u128() as u64 } + + /// Returns the low 128 bits. + pub(crate) fn low_u128(&self) -> u128 { self.1 } + + /// Returns this `U256` as a `u128` saturating to `u128::MAX` if `self` is too big. + // Matagen gives false positive because >= and > both return u128::MAX + pub(crate) fn saturating_to_u128(&self) -> u128 { + if *self > U256::from(u128::MAX) { + u128::MAX + } else { + self.low_u128() + } + } + + /// Returns the least number of bits needed to represent the number. + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn bits(&self) -> u32 { + if self.0 > 0 { + 256 - self.0.leading_zeros() + } else { + 128 - self.1.leading_zeros() + } + } + + /// Wrapping multiplication by `u64`. + /// + /// # Returns + /// + /// The multiplication result along with a boolean indicating whether an arithmetic overflow + /// occurred. If an overflow occurred then the wrapped value is returned. + // mutagen false pos mul_u64: replace `|` with `^` (XOR is same as OR when combined with <<) + // mutagen false pos mul_u64: replace `|` with `^` + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn mul_u64(self, rhs: u64) -> (U256, bool) { + let mut carry: u128 = 0; + let mut split_le = + [self.1 as u64, (self.1 >> 64) as u64, self.0 as u64, (self.0 >> 64) as u64]; + + for word in &mut split_le { + // This will not overflow, for proof see https://github.com/rust-bitcoin/rust-bitcoin/pull/1496#issuecomment-1365938572 + let n = carry + u128::from(rhs) * u128::from(*word); + + *word = n as u64; // Intentional truncation, save the low bits + carry = n >> 64; // and carry the high bits. + } + + let low = u128::from(split_le[0]) | u128::from(split_le[1]) << 64; + let high = u128::from(split_le[2]) | u128::from(split_le[3]) << 64; + (Self(high, low), carry != 0) + } + + /// Calculates quotient and remainder. + /// + /// # Returns + /// + /// (quotient, remainder) + /// + /// # Panics + /// + /// If `rhs` is zero. + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn div_rem(self, rhs: Self) -> (Self, Self) { + let mut sub_copy = self; + let mut shift_copy = rhs; + let mut ret = [0u128; 2]; + + let my_bits = self.bits(); + let your_bits = rhs.bits(); + + // Check for division by 0 + assert!(your_bits != 0, "attempted to divide {:?} by zero", self); + + // Early return in case we are dividing by a larger number than us + if my_bits < your_bits { + return (U256::ZERO, sub_copy); + } + + // Bitwise long division + let mut shift = my_bits - your_bits; + shift_copy = shift_copy << shift; + loop { + if sub_copy >= shift_copy { + ret[1 - (shift / 128) as usize] |= 1 << (shift % 128); + sub_copy = sub_copy.wrapping_sub(shift_copy); + } + shift_copy = shift_copy >> 1; + if shift == 0 { + break; + } + shift -= 1; + } + + (U256(ret[0], ret[1]), sub_copy) + } + + /// Calculates `self` + `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating whether an arithmetic + /// overflow would occur. If an overflow would have occurred then the wrapped value is returned. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let mut ret = U256::ZERO; + let mut ret_overflow = false; + + let (high, overflow) = self.0.overflowing_add(rhs.0); + ret.0 = high; + ret_overflow |= overflow; + + let (low, overflow) = self.1.overflowing_add(rhs.1); + ret.1 = low; + if overflow { + let (high, overflow) = ret.0.overflowing_add(1); + ret.0 = high; + ret_overflow |= overflow; + } + + (ret, ret_overflow) + } + + /// Calculates `self` - `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic + /// overflow would occur. If an overflow would have occurred then the wrapped value is returned. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let ret = self.wrapping_add(!rhs).wrapping_add(Self::ONE); + let overflow = rhs > self; + (ret, overflow) + } + + /// Calculates the multiplication of `self` and `rhs`. + /// + /// Returns a tuple of the multiplication along with a boolean + /// indicating whether an arithmetic overflow would occur. If an + /// overflow would have occurred then the wrapped value is returned. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn overflowing_mul(self, rhs: Self) -> (Self, bool) { + let mut ret = U256::ZERO; + let mut ret_overflow = false; + + for i in 0..3 { + let to_mul = (rhs >> (64 * i)).low_u64(); + let (mul_res, _) = self.mul_u64(to_mul); + ret = ret.wrapping_add(mul_res << (64 * i)); + } + + let to_mul = (rhs >> 192).low_u64(); + let (mul_res, overflow) = self.mul_u64(to_mul); + ret_overflow |= overflow; + let (sum, overflow) = ret.overflowing_add(mul_res); + ret = sum; + ret_overflow |= overflow; + + (ret, ret_overflow) + } + + /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the boundary of the + /// type. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub(crate) fn wrapping_add(self, rhs: Self) -> Self { + let (ret, _overflow) = self.overflowing_add(rhs); + ret + } + + /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the boundary of + /// the type. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub(crate) fn wrapping_sub(self, rhs: Self) -> Self { + let (ret, _overflow) = self.overflowing_sub(rhs); + ret + } + + /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at the boundary of + /// the type. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg(test)] + pub(crate) fn wrapping_mul(self, rhs: Self) -> Self { + let (ret, _overflow) = self.overflowing_mul(rhs); + ret + } + + /// Returns `self` incremented by 1 wrapping around at the boundary of the type. + #[must_use = "this returns the result of the increment, without modifying the original"] + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn wrapping_inc(&self) -> U256 { + let mut ret = U256::ZERO; + + ret.1 = self.1.wrapping_add(1); + if ret.1 == 0 { + ret.0 = self.0.wrapping_add(1); + } else { + ret.0 = self.0; + } + ret + } + + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes any + /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is + /// restricted to the range of the type, rather than the bits shifted out of the LHS being + /// returned to the other end. We do not currently support `rotate_left`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn wrapping_shl(self, rhs: u32) -> Self { + let shift = rhs & 0x000000ff; + + let mut ret = U256::ZERO; + let word_shift = shift >= 128; + let bit_shift = shift % 128; + + if word_shift { + ret.0 = self.1 << bit_shift + } else { + ret.0 = self.0 << bit_shift; + if bit_shift > 0 { + ret.0 += self.1.wrapping_shr(128 - bit_shift); + } + ret.1 = self.1 << bit_shift; + } + ret + } + + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` removes any + /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is + /// restricted to the range of the type, rather than the bits shifted out of the LHS being + /// returned to the other end. We do not currently support `rotate_right`. + #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(all(test, mutate), mutate)] + pub(crate) fn wrapping_shr(self, rhs: u32) -> Self { + let shift = rhs & 0x000000ff; + + let mut ret = U256::ZERO; + let word_shift = shift >= 128; + let bit_shift = shift % 128; + + if word_shift { + ret.1 = self.0 >> bit_shift + } else { + ret.0 = self.0 >> bit_shift; + ret.1 = self.1 >> bit_shift; + if bit_shift > 0 { + ret.1 += self.0.wrapping_shl(128 - bit_shift); + } + } + ret + } +} + +impl> From for U256 { + fn from(x: T) -> Self { U256(0, x.into()) } +} + +impl Add for U256 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + let (res, overflow) = self.overflowing_add(rhs); + debug_assert!(!overflow, "Addition of U256 values overflowed"); + res + } +} + +impl Sub for U256 { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + let (res, overflow) = self.overflowing_sub(rhs); + debug_assert!(!overflow, "Subtraction of U256 values overflowed"); + res + } +} + +impl Mul for U256 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + let (res, overflow) = self.overflowing_mul(rhs); + debug_assert!(!overflow, "Multiplication of U256 values overflowed"); + res + } +} + +impl Div for U256 { + type Output = Self; + fn div(self, rhs: Self) -> Self { self.div_rem(rhs).0 } +} + +impl Rem for U256 { + type Output = Self; + fn rem(self, rhs: Self) -> Self { self.div_rem(rhs).1 } +} + +impl Not for U256 { + type Output = Self; + + fn not(self) -> Self { U256(!self.0, !self.1) } +} + +impl BitAnd for U256 { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self { U256(self.0 & rhs.0, self.1 & rhs.1) } +} + +impl Shl for U256 { + type Output = Self; + fn shl(self, shift: u32) -> U256 { self.wrapping_shl(shift) } +} + +impl Shr for U256 { + type Output = Self; + fn shr(self, shift: u32) -> U256 { self.wrapping_shr(shift) } +} + +impl fmt::Debug for U256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for x in &self.to_be_bytes() { + write!(f, "{:02x}", x)?; + } + Ok(()) + } +} + +/// Splits a 32 byte array into two 16 byte arrays. +const fn split_in_half(a: [u8; 32]) -> ([u8; 16], [u8; 16]) { + let mut high = [0_u8; 16]; + let mut low = [0_u8; 16]; + let mut i = 0; + while i < 16 { + high[i] = a[i]; + i += 1; + } + i = 0; + while i < 16 { + low[i] = a[i + 16]; + i += 1; + } + + (high, low) +} + diff --git a/src/key/non_c/zp.rs b/src/key/non_c/zp.rs new file mode 100644 index 000000000..07a4cbf82 --- /dev/null +++ b/src/key/non_c/zp.rs @@ -0,0 +1,229 @@ +/// Implementation of field of ZP, where P is the secp256k1 paramter. + +use super::u256::U256; +use core::ops::{Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign, Div, DivAssign}; + +const P: U256 = U256::from_be_bytes([ + 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, +]); + +/// Implementation of `Z_p` cyclic group where `p` is the size of the field used in secp256k1 - se +/// the `P` constant in this library. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub(crate) struct Zp(U256); + +impl Zp { + pub(crate) const ZERO: Self = Zp(U256::ZERO); + pub(crate) const ONE: Self = Zp(U256::ONE); + + /// Converts the value % P to Self + pub(crate) fn wrapping_from(value: U256) -> Self { + if value >= P { + Zp(value.wrapping_sub(P)) + } else { + Zp(value) + } + } + + pub(crate) fn checked_from(value: U256) -> Option { + if value >= P { + None + } else { + Some(Zp(value)) + } + } + + pub fn to_be_bytes(&self) -> [u8; 32] { + self.0.to_be_bytes() + } + + pub fn from_be_bytes(bytes: &[u8; 32]) -> Option { + Self::checked_from(U256::from_be_bytes(*bytes)) + } + + pub(crate) fn is_zero(&self) -> bool { + self.0.is_zero() + } + + pub(crate) fn is_even(&self) -> bool { + self.0 < P / 2u128.into() + } + + pub(crate) fn parity(&self) -> crate::Parity { + if self.is_even() { crate::Parity::Even } else { crate::Parity::Odd } + } + + pub(crate) fn pow(mut self, mut exp: U256) -> Self { + let mut res = Zp::ONE; + while exp != U256::ZERO { + if exp & U256::ONE == U256::ONE { + res = res * self; + } + self = self * self; + exp = exp >> 1; + } + res + } + + pub fn multiplicative_inverse(self) -> Self { + // refactored from + // https://github.com/paritytech/bigint/blob/master/src/uint.rs + let mut mn = (P, self.0); + let mut xy = (Zp::ZERO, Zp::ONE); + + while mn.1 != U256::ZERO { + let sb = xy.1 * (mn.0 / mn.1) ; + xy = (xy.1, xy.0 - sb); + mn = (mn.1, mn.0 % mn.1); + } + + xy.0 + } + + pub(crate) fn sqrt(&self, parity: crate::Parity) -> Option { + // Copied from https://github.com/KanoczTomas/ecc-generic + // and simplified + if self.is_zero() { + return Some(*self); + } + if !self.is_quadratic_residue() { + return None; + } + let res = self.pow((P + U256::ONE) / 4u128.into()); + if parity == res.parity() { + Some(res) + } else { + Some(-res) + } + } + + pub fn is_quadratic_residue(self) -> bool { + // Copied from https://github.com/KanoczTomas/ecc-generic + //if self % p == 0 + //As Zp is already mod p, we just have to check if it is 0 + match self.is_zero() { + true => true, + false => self.pow((P - U256::ONE) / 2u128.into()) == Zp::ONE + } + + } +} + +// We use simple subtraction instead of modulo as it should be more efficient +impl Add for Zp { + type Output = Self; + + fn add(self, rhs: Zp) -> Self::Output { + let (res, overflow) = self.0.overflowing_add(rhs.0); + Zp(if overflow || res >= P { + res.wrapping_sub(P) + } else { + res + }) + } +} + +impl AddAssign for Zp { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sub for Zp { + type Output = Self; + + fn sub(self, rhs: Zp) -> Self::Output { + let (res, overflow) = self.0.overflowing_sub(rhs.0); + Zp(if overflow || res >= P { + res.wrapping_add(P) + } else { + res + }) + } +} + +impl SubAssign for Zp { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Mul for Zp { + type Output = Zp; + + /// Double-and-add algorithm + fn mul(self, mut rhs: U256) -> Self::Output { + let mut res = Zp::ZERO; + let high = U256::ONE << 255; + + for _ in 0..256 { + // Can't use *= 2 - that would cause infinite recursion. + // Don't ask how I know. + res += res; + if rhs & high != U256::ZERO { + res += self; + } + rhs = rhs.wrapping_shl(1); + } + + res + } +} + +impl Mul for Zp { + type Output = Zp; + + fn mul(self, rhs: u64) -> Self::Output { + self * U256::from(rhs) + } +} + +impl MulAssign for Zp { + fn mul_assign(&mut self, rhs: u64) { + *self = *self * rhs; + } +} + +impl Mul for Zp { + type Output = Zp; + + fn mul(self, rhs: Zp) -> Self::Output { + self * rhs.0 + } +} + +impl MulAssign for Zp { + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Div for Zp { + type Output = Zp; + + fn div(self, rhs: Zp) -> Self::Output { + self * rhs.multiplicative_inverse() + } +} + +impl DivAssign for Zp { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs; + } +} + +impl Neg for Zp { + type Output = Zp; + + fn neg(self) -> Self::Output { + if self.is_zero() { + self + } else { + Zp(P - self.0) + } + } +} + diff --git a/src/lib.rs b/src/lib.rs index 20ff5d44e..47cd935fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ //! Alternately, keys and messages can be parsed from slices, like //! //! ```rust -//! # #[cfg(feature = "alloc")] { +//! # #[cfg(all(feature = "alloc", feature = "secp256k1-sys"))] { //! use secp256k1::{Secp256k1, Message, SecretKey, PublicKey}; //! //! let secp = Secp256k1::new(); @@ -82,7 +82,7 @@ //! Users who only want to verify signatures can use a cheaper context, like so: //! //! ```rust -//! # #[cfg(feature = "alloc")] { +//! # #[cfg(all(feature = "alloc", feature = "secp256k1-sys"))] { //! use secp256k1::{Secp256k1, Message, ecdsa, PublicKey}; //! //! let secp = Secp256k1::verification_only(); @@ -165,32 +165,42 @@ mod key; pub mod constants; pub mod ecdh; +#[cfg(feature = "secp256k1-sys")] pub mod ecdsa; +#[cfg(feature = "secp256k1-sys")] pub mod ellswift; pub mod scalar; +#[cfg(feature = "secp256k1-sys")] pub mod schnorr; #[cfg(feature = "serde")] mod serde_util; use core::marker::PhantomData; +#[cfg(feature = "secp256k1-sys")] use core::ptr::NonNull; -use core::{fmt, mem, str}; +use core::{fmt, str}; +#[cfg(feature = "secp256k1-sys")] +use core::mem; #[cfg(all(feature = "global-context", feature = "std"))] pub use context::global::{self, SECP256K1}; #[cfg(feature = "rand")] pub use rand; +#[cfg(feature = "secp256k1-sys")] pub use secp256k1_sys as ffi; #[cfg(feature = "serde")] pub use serde; +pub use crate::context::{Context, Signing, Verification}; #[cfg(feature = "alloc")] pub use crate::context::{All, SignOnly, VerifyOnly}; +#[cfg(feature = "secp256k1-sys")] pub use crate::context::{ - AllPreallocated, Context, PreallocatedContext, SignOnlyPreallocated, Signing, Verification, - VerifyOnlyPreallocated, + AllPreallocated, PreallocatedContext, SignOnlyPreallocated, VerifyOnlyPreallocated, }; +#[cfg(feature = "secp256k1-sys")] use crate::ffi::types::AlignedType; +#[cfg(feature = "secp256k1-sys")] use crate::ffi::CPtr; pub use crate::key::{InvalidParityValue, Keypair, Parity, PublicKey, SecretKey, XOnlyPublicKey}; pub use crate::scalar::Scalar; @@ -352,21 +362,27 @@ impl std::error::Error for Error { /// The secp256k1 engine, used to execute all signature operations. pub struct Secp256k1 { + #[cfg(feature = "secp256k1-sys")] ctx: NonNull, phantom: PhantomData, } // The underlying secp context does not contain any references to memory it does not own. +#[cfg(feature = "secp256k1-sys")] unsafe impl Send for Secp256k1 {} // The API does not permit any mutation of `Secp256k1` objects except through `&mut` references. +#[cfg(feature = "secp256k1-sys")] unsafe impl Sync for Secp256k1 {} +#[cfg(feature = "secp256k1-sys")] impl PartialEq for Secp256k1 { fn eq(&self, _other: &Secp256k1) -> bool { true } } +#[cfg(feature = "secp256k1-sys")] impl Eq for Secp256k1 {} +#[cfg(feature = "secp256k1-sys")] impl Drop for Secp256k1 { fn drop(&mut self) { unsafe { @@ -379,11 +395,17 @@ impl Drop for Secp256k1 { } impl fmt::Debug for Secp256k1 { + #[cfg(feature = "secp256k1-sys")] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "", self.ctx, C::DESCRIPTION) } + #[cfg(not(feature = "secp256k1-sys"))] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "", C::DESCRIPTION) + } } +#[cfg(feature = "secp256k1-sys")] impl Secp256k1 { /// Getter for the raw pointer to the underlying secp256k1 context. This /// shouldn't be needed with normal usage of the library. It enables @@ -429,6 +451,7 @@ impl Secp256k1 { } } +#[cfg(feature = "secp256k1-sys")] impl Secp256k1 { /// Generates a random keypair. Convenience function for [`SecretKey::new`] and /// [`PublicKey::from_secret_key`]. @@ -500,7 +523,7 @@ fn to_hex<'a>(src: &[u8], target: &'a mut [u8]) -> Result<&'a str, ()> { return unsafe { Ok(str::from_utf8_unchecked(result)) }; } -#[cfg(feature = "rand")] +#[cfg(all(feature = "rand", feature = "secp256k1-sys"))] pub(crate) fn random_32_bytes(rng: &mut R) -> [u8; 32] { let mut ret = [0u8; 32]; rng.fill(&mut ret); @@ -508,6 +531,7 @@ pub(crate) fn random_32_bytes(rng: &mut R) -> [u8; 32] { } #[cfg(test)] +#[cfg(feature = "secp256k1-sys")] mod tests { use std::str::FromStr; diff --git a/src/macros.rs b/src/macros.rs index 4111b3f0c..6c8293104 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,6 +23,7 @@ macro_rules! impl_array_newtype { fn index(&self, index: I) -> &Self::Output { &self.0[index] } } + #[cfg(feature = "secp256k1-sys")] impl $crate::ffi::CPtr for $thing { type Target = $ty; @@ -63,6 +64,7 @@ macro_rules! impl_non_secure_erase { /// is very subtle. For more discussion on this, please see the documentation /// of the [`zeroize`](https://docs.rs/zeroize) crate. #[inline] + #[cfg(feature = "secp256k1-sys")] pub fn non_secure_erase(&mut self) { secp256k1_sys::non_secure_erase_impl(&mut self.$target, $value); } @@ -99,7 +101,14 @@ macro_rules! impl_fast_comparisons { /// 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) + #[cfg(feature = "secp256k1-sys")] + { + self.0.cmp_fast_unstable(&other.0) + } + #[cfg(not(feature = "secp256k1-sys"))] + { + self.0.cmp(&other.0) + } } /// Like `cmp::Eq` but faster and with no guarantees across library versions. @@ -108,7 +117,14 @@ macro_rules! impl_fast_comparisons { /// `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) + #[cfg(feature = "secp256k1-sys")] + { + self.0.eq_fast_unstable(&other.0) + } + #[cfg(not(feature = "secp256k1-sys"))] + { + self.0 == other.0 + } } } }; diff --git a/src/scalar.rs b/src/scalar.rs index 77f3e0cc7..dc638559c 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -99,8 +99,10 @@ impl Scalar { // returns a reference to internal bytes // non-public to not leak the internal representation + #[cfg(feature = "secp256k1-sys")] pub(crate) fn as_be_bytes(&self) -> &[u8; 32] { &self.0 } + #[cfg(feature = "secp256k1-sys")] pub(crate) fn as_c_ptr(&self) -> *const u8 { use secp256k1_sys::CPtr; diff --git a/src/secret.rs b/src/secret.rs index f13d91ae9..6617b40ea 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -6,7 +6,10 @@ use core::fmt; use crate::constants::SECRET_KEY_SIZE; use crate::ecdh::SharedSecret; -use crate::key::{Keypair, SecretKey}; +#[cfg(feature = "secp256k1-sys")] +use crate::key::Keypair; + +use crate::key::SecretKey; use crate::to_hex; macro_rules! impl_display_secret { // Default hasher exists only in standard library and not alloc @@ -127,6 +130,7 @@ impl SecretKey { pub fn display_secret(&self) -> DisplaySecret { DisplaySecret { secret: self.secret_bytes() } } } +#[cfg(feature = "secp256k1-sys")] 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. @@ -171,7 +175,7 @@ impl SharedSecret { /// /// ``` /// # #[cfg(not(secp256k1_fuzz))] - /// # #[cfg(feature = "std")] { + /// # #[cfg(all(feature = "std", feature = "secp256k1-sys"))] { /// # use std::str::FromStr; /// use secp256k1::{SecretKey, PublicKey}; /// use secp256k1::ecdh::SharedSecret;