Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ee86eaf

Browse files
committedJul 4, 2024·
Make the -sys crate optional
Downstream projects, such as `bitcoin` are using types from this crate and need to compile the C code even if they only use the types for checking validity of the data and don't perform cryptographic operations. Compiling C code is slow and unreliable so there was desire to avoid this. This commit introduces pure Rust implementation of basic non-cryptographic operations (is point on the curve, decompression of point, secret key constant time comparisons) and feature-gates cryptographic operations on `secp256k1-sys` which is now optional. To make use of this, downstream crates will have to deactivate the feature and possibly feature gate themselves. The implementation requires a U256 type which was copied from the `bitcoin` crate. We don't depend on a common crate to avoid needless compilation when the feature is turned on. This is a breaking change because users who did cryptographic operations with `default-features = false` will get compilation errors until they enable the feature.
1 parent 33a1893 commit ee86eaf

File tree

12 files changed

+1409
-375
lines changed

12 files changed

+1409
-375
lines changed
 

‎Cargo.toml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ all-features = true
1818
rustdoc-args = ["--cfg", "docsrs"]
1919

2020
[features]
21-
default = ["std"]
22-
std = ["alloc", "secp256k1-sys/std"]
21+
default = ["sys-std"]
22+
std = ["alloc"]
23+
alloc = []
24+
sys-std = ["std", "sys-alloc", "secp256k1-sys/std"]
2325
# allow use of Secp256k1::new and related API that requires an allocator
24-
alloc = ["secp256k1-sys/alloc"]
26+
sys-alloc = ["secp256k1-sys/alloc"]
2527
hashes-std = ["std", "hashes/std"]
2628
rand-std = ["std", "rand", "rand/std", "rand/std_rng"]
2729
recovery = ["secp256k1-sys/recovery"]
@@ -36,7 +38,7 @@ global-context = ["std"]
3638
global-context-less-secure = ["global-context"]
3739

3840
[dependencies]
39-
secp256k1-sys = { version = "0.10.0", default-features = false, path = "./secp256k1-sys" }
41+
secp256k1-sys = { version = "0.10.0", default-features = false, path = "./secp256k1-sys", optional = true }
4042
serde = { version = "1.0.103", default-features = false, optional = true }
4143

4244
# You likely only want to enable these if you explicitly do not want to use "std", otherwise enable
@@ -59,15 +61,15 @@ unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(secp256k1_fu
5961

6062
[[example]]
6163
name = "sign_verify_recovery"
62-
required-features = ["recovery", "hashes-std"]
64+
required-features = ["recovery", "hashes-std", "secp256k1-sys"]
6365

6466
[[example]]
6567
name = "sign_verify"
66-
required-features = ["hashes-std"]
68+
required-features = ["hashes-std", "secp256k1-sys"]
6769

6870
[[example]]
6971
name = "generate_keys"
70-
required-features = ["rand-std"]
72+
required-features = ["rand-std", "secp256k1-sys"]
7173

7274
[workspace]
7375
members = ["secp256k1-sys"]

‎contrib/_test.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set -ex
44

55
REPO_DIR=$(git rev-parse --show-toplevel)
6-
FEATURES="hashes global-context lowmemory rand recovery serde std alloc hashes-std rand-std"
6+
FEATURES="hashes global-context lowmemory rand recovery serde std alloc hashes-std rand-std secp256k1-sys"
77

88
cargo --version
99
rustc --version
@@ -62,17 +62,17 @@ if [ "$DO_FEATURE_MATRIX" = true ]; then
6262
fi
6363

6464
# Examples
65-
cargo run --locked --example sign_verify --features=hashes-std
66-
cargo run --locked --example sign_verify_recovery --features=recovery,hashes-std
67-
cargo run --locked --example generate_keys --features=rand-std
65+
cargo run --locked --example sign_verify --features=hashes-std,secp256k1-sys
66+
cargo run --locked --example sign_verify_recovery --features=recovery,hashes-std,secp256k1-sys
67+
cargo run --locked --example generate_keys --features=rand-std,secp256k1-sys
6868
fi
6969

7070
if [ "$DO_LINT" = true ]
7171
then
7272
cargo clippy --locked --all-features --all-targets -- -D warnings
73-
cargo clippy --locked --example sign_verify --features=hashes-std -- -D warnings
74-
cargo clippy --locked --example sign_verify_recovery --features=recovery,hashes-std -- -D warnings
75-
cargo clippy --locked --example generate_keys --features=rand-std -- -D warnings
73+
cargo clippy --locked --example sign_verify --features=hashes-std,secp256k1-sys -- -D warnings
74+
cargo clippy --locked --example sign_verify_recovery --features=recovery,hashes-std,secp256k1-sys -- -D warnings
75+
cargo clippy --locked --example generate_keys --features=rand-std,secp256k1-sys -- -D warnings
7676
fi
7777

7878
# Build the docs if told to (this only works with the nightly toolchain)

‎src/context.rs

Lines changed: 204 additions & 148 deletions
Large diffs are not rendered by default.

‎src/ecdh.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44
//!
55
66
use core::borrow::Borrow;
7-
use core::{ptr, str};
7+
use core::str;
8+
#[cfg(feature = "secp256k1-sys")]
9+
use core::ptr;
810

11+
#[cfg(feature = "secp256k1-sys")]
912
use secp256k1_sys::types::{c_int, c_uchar, c_void};
1013

14+
#[cfg(feature = "secp256k1-sys")]
1115
use crate::ffi::{self, CPtr};
16+
#[cfg(feature = "secp256k1-sys")]
1217
use crate::key::{PublicKey, SecretKey};
1318
use crate::{constants, Error};
1419

@@ -20,7 +25,7 @@ const SHARED_SECRET_SIZE: usize = constants::SECRET_KEY_SIZE;
2025
/// # Examples
2126
///
2227
/// ```
23-
/// # #[cfg(feature = "rand-std")] {
28+
/// # #[cfg(all(feature = "rand-std", feature = "secp256k1-sys"))] {
2429
/// # use secp256k1::{rand, Secp256k1};
2530
/// # use secp256k1::ecdh::SharedSecret;
2631
/// let s = Secp256k1::new();
@@ -39,6 +44,7 @@ impl_non_secure_erase!(SharedSecret, 0, [0u8; SHARED_SECRET_SIZE]);
3944
impl SharedSecret {
4045
/// Creates a new shared secret from a pubkey and secret key.
4146
#[inline]
47+
#[cfg(feature = "secp256k1-sys")]
4248
pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
4349
let mut buf = [0u8; SHARED_SECRET_SIZE];
4450
let res = unsafe {
@@ -125,6 +131,7 @@ impl AsRef<[u8]> for SharedSecret {
125131
/// assert_eq!(secret1, secret2)
126132
/// # }
127133
/// ```
134+
#[cfg(feature = "secp256k1-sys")]
128135
pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] {
129136
let mut xy = [0u8; 64];
130137

@@ -144,6 +151,7 @@ pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] {
144151
xy
145152
}
146153

154+
#[cfg(feature = "secp256k1-sys")]
147155
unsafe extern "C" fn c_callback(
148156
output: *mut c_uchar,
149157
x: *const c_uchar,
@@ -184,6 +192,7 @@ impl<'de> ::serde::Deserialize<'de> for SharedSecret {
184192
}
185193

186194
#[cfg(test)]
195+
#[cfg(feature = "secp256k1-sys")]
187196
#[allow(unused_imports)]
188197
mod tests {
189198
#[cfg(target_arch = "wasm32")]
@@ -194,6 +203,7 @@ mod tests {
194203

195204
#[test]
196205
#[cfg(feature = "rand-std")]
206+
#[cfg(feature = "secp256k1-sys")]
197207
fn ecdh() {
198208
let s = Secp256k1::signing_only();
199209
let (sk1, pk1) = s.generate_keypair(&mut rand::thread_rng());
@@ -207,6 +217,7 @@ mod tests {
207217
}
208218

209219
#[test]
220+
#[cfg(feature = "secp256k1-sys")]
210221
fn test_c_callback() {
211222
let x = [5u8; 32];
212223
let y = [7u8; 32];
@@ -226,6 +237,7 @@ mod tests {
226237
#[test]
227238
#[cfg(not(secp256k1_fuzz))]
228239
#[cfg(all(feature = "hashes-std", feature = "rand-std"))]
240+
#[cfg(feature = "secp256k1-sys")]
229241
fn hashes_and_sys_generate_same_secret() {
230242
use hashes::{sha256, Hash, HashEngine};
231243

@@ -248,7 +260,10 @@ mod tests {
248260

249261
assert_eq!(secret_bh.as_byte_array(), secret_sys.as_ref());
250262
}
263+
}
251264

265+
#[cfg(test)]
266+
mod non_crypto_tests {
252267
#[test]
253268
#[cfg(all(feature = "serde", feature = "alloc"))]
254269
fn serde() {
@@ -262,7 +277,7 @@ mod tests {
262277
];
263278
static STR: &str = "01010101010101010001020304050607ffff0000ffff00006363636363636363";
264279

265-
let secret = SharedSecret::from_slice(&BYTES).unwrap();
280+
let secret = super::SharedSecret::from_slice(&BYTES).unwrap();
266281

267282
assert_tokens(&secret.compact(), &[Token::BorrowedBytes(&BYTES[..])]);
268283
assert_tokens(&secret.compact(), &[Token::Bytes(&BYTES)]);
@@ -283,6 +298,7 @@ mod benches {
283298
use crate::Secp256k1;
284299

285300
#[bench]
301+
#[cfg(feature = "secp256k1-sys")]
286302
pub fn bench_ecdh(bh: &mut Bencher) {
287303
let s = Secp256k1::signing_only();
288304
let (sk, pk) = s.generate_keypair(&mut rand::thread_rng());

‎src/key.rs

Lines changed: 332 additions & 200 deletions
Large diffs are not rendered by default.

‎src/key/non_c/mod.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//! Pure Rust implementation of basic secp256k1-related checks.
2+
//!
3+
//! It can be useful to have the key types provided by this library without full cryptography and
4+
//! implied compilation/linking of C. However for compatibility, we must check the keys when
5+
//! deserializing. Therefore we need at least some basic secp256k1 math to do that.
6+
//!
7+
//! We explicitly do **not** implement point operations or similarly advanced features.
8+
9+
pub(crate) mod u256;
10+
pub(crate) mod zp;
11+
12+
use zp::Zp;
13+
use crate::Parity;
14+
15+
fn is_point_on_curve(x: Zp, y: Zp) -> bool {
16+
y * y == x * x * x + Zp::wrapping_from(u256::U256::from(7u128))
17+
}
18+
19+
fn compute_y_coord(x: Zp, parity: Parity) -> Option<Zp> {
20+
(x * x * x + Zp::wrapping_from(u256::U256::from(7u128))).sqrt(parity)
21+
}
22+
23+
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
24+
pub(crate) struct PublicKey {
25+
x: Zp,
26+
y: Zp,
27+
}
28+
29+
impl PublicKey {
30+
pub(crate) fn decode(key: &[u8]) -> Option<Self> {
31+
match *key.get(0)? {
32+
0x04 if key.len() == 65 => {
33+
let x = Zp::from_be_bytes(key[1..33].try_into().expect("static len"))?;
34+
let y = Zp::from_be_bytes(key[33..].try_into().expect("static len"))?;
35+
if is_point_on_curve(x, y) {
36+
Some(PublicKey { x, y })
37+
} else {
38+
None
39+
}
40+
},
41+
parity @ (0x02 | 0x03) if key.len() == 33 => {
42+
let x = Zp::from_be_bytes(key[1..33].try_into().expect("static len"))?;
43+
let y = compute_y_coord(x, if parity == 0x02 { Parity::Even } else { Parity::Odd})?;
44+
Some(PublicKey { x, y })
45+
},
46+
_ => None,
47+
}
48+
}
49+
50+
pub(crate) fn serialize_compressed(&self) -> [u8; 33] {
51+
let mut buf = [0; 33];
52+
buf[0] = if self.y.is_even() {
53+
0x02
54+
} else {
55+
0x03
56+
};
57+
buf[1..].copy_from_slice(&self.x.to_be_bytes());
58+
buf
59+
}
60+
61+
pub(crate) fn serialize_uncompressed(&self) -> [u8; 65] {
62+
let mut buf = [0; 65];
63+
buf[0] = 0x04;
64+
buf[1..33].copy_from_slice(&self.x.to_be_bytes());
65+
buf[33..].copy_from_slice(&self.y.to_be_bytes());
66+
buf
67+
}
68+
69+
pub(crate) fn to_xonly(&self) -> (XOnlyPublicKey, Parity) {
70+
let parity = if self.y.is_even() {
71+
Parity::Even
72+
} else {
73+
Parity::Odd
74+
};
75+
(XOnlyPublicKey { x: self.x }, parity)
76+
}
77+
}
78+
79+
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
80+
pub(crate) struct XOnlyPublicKey {
81+
x: Zp,
82+
}
83+
84+
impl XOnlyPublicKey {
85+
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
86+
let x = Zp::from_be_bytes(bytes)?;
87+
compute_y_coord(x, Parity::Even)?;
88+
Some(XOnlyPublicKey { x })
89+
}
90+
91+
pub(crate) fn serialize(&self) -> [u8; 32] {
92+
self.x.to_be_bytes()
93+
}
94+
}
95+
96+
pub(crate) fn is_seckey_valid(scalar: &[u8; 32]) -> bool {
97+
let sum = scalar.iter().copied().fold(0u8, u8::wrapping_add);
98+
if launder_u8(sum) == 0 {
99+
return false;
100+
}
101+
102+
// Translated from the C version
103+
const N: [u32; 8] = [0xD0364141, 0xBFD25E8C, 0xAF48A03B, 0xBAAEDCE6, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF];
104+
let mut buf = [0; 8];
105+
for (chunk, dst) in scalar.chunks_exact(4).rev().zip(&mut buf) {
106+
*dst = u32::from_be_bytes(chunk.try_into().expect("chunk_exact returns 4-byte slices"));
107+
}
108+
let scalar = buf;
109+
let mut yes = 0u8;
110+
let mut no = 0u8;
111+
no |= u8::from(scalar[7] < N[7]); /* No need for a > check. */
112+
no |= u8::from(scalar[6] < N[6]); /* No need for a > check. */
113+
no |= u8::from(scalar[5] < N[5]); /* No need for a > check. */
114+
no |= u8::from(scalar[4] < N[4]);
115+
yes |= u8::from(scalar[4] > N[4]) & !no;
116+
no |= u8::from(scalar[3] < N[3]) & !yes;
117+
yes |= u8::from(scalar[3] > N[3]) & !no;
118+
no |= u8::from(scalar[2] < N[2]) & !yes;
119+
yes |= u8::from(scalar[2] > N[2]) & !no;
120+
no |= u8::from(scalar[1] < N[1]) & !yes;
121+
yes |= u8::from(scalar[1] > N[1]) & !no;
122+
yes |= u8::from(scalar[0] >= N[0]) & !no;
123+
launder_u8(yes) == 0
124+
}
125+
126+
fn launder_u8(val: u8) -> u8 {
127+
unsafe {
128+
core::ptr::read_volatile(&val)
129+
}
130+
}

‎src/key/non_c/u256.rs

Lines changed: 414 additions & 0 deletions
Large diffs are not rendered by default.

‎src/key/non_c/zp.rs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/// Implementation of field of Z<sub>P</sub>, where P is the secp256k1 paramter.
2+
3+
use super::u256::U256;
4+
use core::ops::{Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign, Div, DivAssign};
5+
6+
const P: U256 = U256::from_be_bytes([
7+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
8+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
9+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
10+
0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F,
11+
]);
12+
13+
/// Implementation of `Z_p` cyclic group where `p` is the size of the field used in secp256k1 - se
14+
/// the `P` constant in this library.
15+
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
16+
pub(crate) struct Zp(U256);
17+
18+
impl Zp {
19+
pub(crate) const ZERO: Self = Zp(U256::ZERO);
20+
pub(crate) const ONE: Self = Zp(U256::ONE);
21+
22+
/// Converts the value % P to Self
23+
pub(crate) fn wrapping_from(value: U256) -> Self {
24+
if value >= P {
25+
Zp(value.wrapping_sub(P))
26+
} else {
27+
Zp(value)
28+
}
29+
}
30+
31+
pub(crate) fn checked_from(value: U256) -> Option<Self> {
32+
if value >= P {
33+
None
34+
} else {
35+
Some(Zp(value))
36+
}
37+
}
38+
39+
pub fn to_be_bytes(&self) -> [u8; 32] {
40+
self.0.to_be_bytes()
41+
}
42+
43+
pub fn from_be_bytes(bytes: &[u8; 32]) -> Option<Self> {
44+
Self::checked_from(U256::from_be_bytes(*bytes))
45+
}
46+
47+
pub(crate) fn is_zero(&self) -> bool {
48+
self.0.is_zero()
49+
}
50+
51+
pub(crate) fn is_even(&self) -> bool {
52+
self.0 < P / 2u128.into()
53+
}
54+
55+
pub(crate) fn parity(&self) -> crate::Parity {
56+
if self.is_even() { crate::Parity::Even } else { crate::Parity::Odd }
57+
}
58+
59+
pub(crate) fn pow(mut self, mut exp: U256) -> Self {
60+
let mut res = Zp::ONE;
61+
while exp != U256::ZERO {
62+
if exp & U256::ONE == U256::ONE {
63+
res = res * self;
64+
}
65+
self = self * self;
66+
exp = exp >> 1;
67+
}
68+
res
69+
}
70+
71+
pub fn multiplicative_inverse(self) -> Self {
72+
// refactored from
73+
// https://github.com/paritytech/bigint/blob/master/src/uint.rs
74+
let mut mn = (P, self.0);
75+
let mut xy = (Zp::ZERO, Zp::ONE);
76+
77+
while mn.1 != U256::ZERO {
78+
let sb = xy.1 * (mn.0 / mn.1) ;
79+
xy = (xy.1, xy.0 - sb);
80+
mn = (mn.1, mn.0 % mn.1);
81+
}
82+
83+
xy.0
84+
}
85+
86+
pub(crate) fn sqrt(&self, parity: crate::Parity) -> Option<Self> {
87+
// Copied from https://github.com/KanoczTomas/ecc-generic
88+
// and simplified
89+
if self.is_zero() {
90+
return Some(*self);
91+
}
92+
if !self.is_quadratic_residue() {
93+
return None;
94+
}
95+
let res = self.pow((P + U256::ONE) / 4u128.into());
96+
if parity == res.parity() {
97+
Some(res)
98+
} else {
99+
Some(-res)
100+
}
101+
}
102+
103+
pub fn is_quadratic_residue(self) -> bool {
104+
// Copied from https://github.com/KanoczTomas/ecc-generic
105+
//if self % p == 0
106+
//As Zp is already mod p, we just have to check if it is 0
107+
match self.is_zero() {
108+
true => true,
109+
false => self.pow((P - U256::ONE) / 2u128.into()) == Zp::ONE
110+
}
111+
112+
}
113+
}
114+
115+
// We use simple subtraction instead of modulo as it should be more efficient
116+
impl Add for Zp {
117+
type Output = Self;
118+
119+
fn add(self, rhs: Zp) -> Self::Output {
120+
let (res, overflow) = self.0.overflowing_add(rhs.0);
121+
Zp(if overflow || res >= P {
122+
res.wrapping_sub(P)
123+
} else {
124+
res
125+
})
126+
}
127+
}
128+
129+
impl AddAssign for Zp {
130+
fn add_assign(&mut self, rhs: Self) {
131+
*self = *self + rhs;
132+
}
133+
}
134+
135+
impl Sub for Zp {
136+
type Output = Self;
137+
138+
fn sub(self, rhs: Zp) -> Self::Output {
139+
let (res, overflow) = self.0.overflowing_sub(rhs.0);
140+
Zp(if overflow || res >= P {
141+
res.wrapping_add(P)
142+
} else {
143+
res
144+
})
145+
}
146+
}
147+
148+
impl SubAssign for Zp {
149+
fn sub_assign(&mut self, rhs: Self) {
150+
*self = *self - rhs;
151+
}
152+
}
153+
154+
impl Mul<U256> for Zp {
155+
type Output = Zp;
156+
157+
/// Double-and-add algorithm
158+
fn mul(self, mut rhs: U256) -> Self::Output {
159+
let mut res = Zp::ZERO;
160+
let high = U256::ONE << 255;
161+
162+
for _ in 0..256 {
163+
// Can't use *= 2 - that would cause infinite recursion.
164+
// Don't ask how I know.
165+
res += res;
166+
if rhs & high != U256::ZERO {
167+
res += self;
168+
}
169+
rhs = rhs.wrapping_shl(1);
170+
}
171+
172+
res
173+
}
174+
}
175+
176+
impl Mul<u64> for Zp {
177+
type Output = Zp;
178+
179+
fn mul(self, rhs: u64) -> Self::Output {
180+
self * U256::from(rhs)
181+
}
182+
}
183+
184+
impl MulAssign<u64> for Zp {
185+
fn mul_assign(&mut self, rhs: u64) {
186+
*self = *self * rhs;
187+
}
188+
}
189+
190+
impl Mul for Zp {
191+
type Output = Zp;
192+
193+
fn mul(self, rhs: Zp) -> Self::Output {
194+
self * rhs.0
195+
}
196+
}
197+
198+
impl MulAssign for Zp {
199+
fn mul_assign(&mut self, rhs: Self) {
200+
*self = *self * rhs;
201+
}
202+
}
203+
204+
impl Div for Zp {
205+
type Output = Zp;
206+
207+
fn div(self, rhs: Zp) -> Self::Output {
208+
self * rhs.multiplicative_inverse()
209+
}
210+
}
211+
212+
impl DivAssign for Zp {
213+
fn div_assign(&mut self, rhs: Self) {
214+
*self = *self / rhs;
215+
}
216+
}
217+
218+
impl Neg for Zp {
219+
type Output = Zp;
220+
221+
fn neg(self) -> Self::Output {
222+
if self.is_zero() {
223+
self
224+
} else {
225+
Zp(P - self.0)
226+
}
227+
}
228+
}
229+

‎src/lib.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
//! Alternately, keys and messages can be parsed from slices, like
6565
//!
6666
//! ```rust
67-
//! # #[cfg(feature = "alloc")] {
67+
//! # #[cfg(all(feature = "alloc", feature = "secp256k1-sys"))] {
6868
//! use secp256k1::{Secp256k1, Message, SecretKey, PublicKey};
6969
//!
7070
//! let secp = Secp256k1::new();
@@ -82,7 +82,7 @@
8282
//! Users who only want to verify signatures can use a cheaper context, like so:
8383
//!
8484
//! ```rust
85-
//! # #[cfg(feature = "alloc")] {
85+
//! # #[cfg(all(feature = "alloc", feature = "secp256k1-sys"))] {
8686
//! use secp256k1::{Secp256k1, Message, ecdsa, PublicKey};
8787
//!
8888
//! let secp = Secp256k1::verification_only();
@@ -121,12 +121,21 @@
121121
//! Observe that the same code using, say [`signing_only`](struct.Secp256k1.html#method.signing_only)
122122
//! to generate a context would simply not compile.
123123
//!
124+
//! This library also provides various key types that are sometimes used mainly to verify input and
125+
//! as newtypes. For that the whole C library may be too much so this library also provides a pure
126+
//! Rust implementation of parsing and validity checking (including curve checks). This is used
127+
//! whenever the `secp256k1-sys` feature is disabled. If you're using this library only for parsing
128+
//! you will improve the compile time by disabling the feature. Therefore the set of all possible
129+
//! values the types accept as valid stays the same regardless of the `secp256k1-sys` feature.
130+
//!
124131
//! ## Crate features/optional dependencies
125132
//!
126133
//! This crate provides the following opt-in Cargo features:
127134
//!
128135
//! * `std` - use standard Rust library, enabled by default.
129136
//! * `alloc` - use the `alloc` standard Rust library to provide heap allocations.
137+
//! * `secp256k1-sys` - actually builds/links the C library and enables use of cryptographic
138+
//! algorithms. Enabled by default.
130139
//! * `rand` - use `rand` library to provide random generator (e.g. to generate keys).
131140
//! * `rand-std` - use `rand` library with its `std` feature enabled. (Implies `rand`.)
132141
//! * `hashes` - use the `hashes` library.
@@ -165,32 +174,42 @@ mod key;
165174

166175
pub mod constants;
167176
pub mod ecdh;
177+
#[cfg(feature = "secp256k1-sys")]
168178
pub mod ecdsa;
179+
#[cfg(feature = "secp256k1-sys")]
169180
pub mod ellswift;
170181
pub mod scalar;
182+
#[cfg(feature = "secp256k1-sys")]
171183
pub mod schnorr;
172184
#[cfg(feature = "serde")]
173185
mod serde_util;
174186

175187
use core::marker::PhantomData;
188+
#[cfg(feature = "secp256k1-sys")]
176189
use core::ptr::NonNull;
177-
use core::{fmt, mem, str};
190+
use core::{fmt, str};
191+
#[cfg(feature = "secp256k1-sys")]
192+
use core::mem;
178193

179194
#[cfg(all(feature = "global-context", feature = "std"))]
180195
pub use context::global::{self, SECP256K1};
181196
#[cfg(feature = "rand")]
182197
pub use rand;
198+
#[cfg(feature = "secp256k1-sys")]
183199
pub use secp256k1_sys as ffi;
184200
#[cfg(feature = "serde")]
185201
pub use serde;
186202

203+
pub use crate::context::{Context, Signing, Verification};
187204
#[cfg(feature = "alloc")]
188205
pub use crate::context::{All, SignOnly, VerifyOnly};
206+
#[cfg(feature = "secp256k1-sys")]
189207
pub use crate::context::{
190-
AllPreallocated, Context, PreallocatedContext, SignOnlyPreallocated, Signing, Verification,
191-
VerifyOnlyPreallocated,
208+
AllPreallocated, PreallocatedContext, SignOnlyPreallocated, VerifyOnlyPreallocated,
192209
};
210+
#[cfg(feature = "secp256k1-sys")]
193211
use crate::ffi::types::AlignedType;
212+
#[cfg(feature = "secp256k1-sys")]
194213
use crate::ffi::CPtr;
195214
pub use crate::key::{InvalidParityValue, Keypair, Parity, PublicKey, SecretKey, XOnlyPublicKey};
196215
pub use crate::scalar::Scalar;
@@ -352,21 +371,27 @@ impl std::error::Error for Error {
352371

353372
/// The secp256k1 engine, used to execute all signature operations.
354373
pub struct Secp256k1<C: Context> {
374+
#[cfg(feature = "secp256k1-sys")]
355375
ctx: NonNull<ffi::Context>,
356376
phantom: PhantomData<C>,
357377
}
358378

359379
// The underlying secp context does not contain any references to memory it does not own.
380+
#[cfg(feature = "secp256k1-sys")]
360381
unsafe impl<C: Context> Send for Secp256k1<C> {}
361382
// The API does not permit any mutation of `Secp256k1` objects except through `&mut` references.
383+
#[cfg(feature = "secp256k1-sys")]
362384
unsafe impl<C: Context> Sync for Secp256k1<C> {}
363385

386+
#[cfg(feature = "secp256k1-sys")]
364387
impl<C: Context> PartialEq for Secp256k1<C> {
365388
fn eq(&self, _other: &Secp256k1<C>) -> bool { true }
366389
}
367390

391+
#[cfg(feature = "secp256k1-sys")]
368392
impl<C: Context> Eq for Secp256k1<C> {}
369393

394+
#[cfg(feature = "secp256k1-sys")]
370395
impl<C: Context> Drop for Secp256k1<C> {
371396
fn drop(&mut self) {
372397
unsafe {
@@ -379,11 +404,17 @@ impl<C: Context> Drop for Secp256k1<C> {
379404
}
380405

381406
impl<C: Context> fmt::Debug for Secp256k1<C> {
407+
#[cfg(feature = "secp256k1-sys")]
382408
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
383409
write!(f, "<secp256k1 context {:?}, {}>", self.ctx, C::DESCRIPTION)
384410
}
411+
#[cfg(not(feature = "secp256k1-sys"))]
412+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413+
write!(f, "<secp256k1 context (trivial), {}>", C::DESCRIPTION)
414+
}
385415
}
386416

417+
#[cfg(feature = "secp256k1-sys")]
387418
impl<C: Context> Secp256k1<C> {
388419
/// Getter for the raw pointer to the underlying secp256k1 context. This
389420
/// shouldn't be needed with normal usage of the library. It enables
@@ -429,6 +460,7 @@ impl<C: Context> Secp256k1<C> {
429460
}
430461
}
431462

463+
#[cfg(feature = "secp256k1-sys")]
432464
impl<C: Signing> Secp256k1<C> {
433465
/// Generates a random keypair. Convenience function for [`SecretKey::new`] and
434466
/// [`PublicKey::from_secret_key`].
@@ -500,14 +532,15 @@ fn to_hex<'a>(src: &[u8], target: &'a mut [u8]) -> Result<&'a str, ()> {
500532
return unsafe { Ok(str::from_utf8_unchecked(result)) };
501533
}
502534

503-
#[cfg(feature = "rand")]
535+
#[cfg(all(feature = "rand", feature = "secp256k1-sys"))]
504536
pub(crate) fn random_32_bytes<R: rand::Rng + ?Sized>(rng: &mut R) -> [u8; 32] {
505537
let mut ret = [0u8; 32];
506538
rng.fill(&mut ret);
507539
ret
508540
}
509541

510542
#[cfg(test)]
543+
#[cfg(feature = "secp256k1-sys")]
511544
mod tests {
512545
use std::str::FromStr;
513546

‎src/macros.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ macro_rules! impl_array_newtype {
2323
fn index(&self, index: I) -> &Self::Output { &self.0[index] }
2424
}
2525

26+
#[cfg(feature = "secp256k1-sys")]
2627
impl $crate::ffi::CPtr for $thing {
2728
type Target = $ty;
2829

@@ -63,6 +64,7 @@ macro_rules! impl_non_secure_erase {
6364
/// is very subtle. For more discussion on this, please see the documentation
6465
/// of the [`zeroize`](https://docs.rs/zeroize) crate.
6566
#[inline]
67+
#[cfg(feature = "secp256k1-sys")]
6668
pub fn non_secure_erase(&mut self) {
6769
secp256k1_sys::non_secure_erase_impl(&mut self.$target, $value);
6870
}
@@ -99,7 +101,14 @@ macro_rules! impl_fast_comparisons {
99101
/// serializes `self` and `other` before comparing them. This function provides a faster
100102
/// comparison if you know that your types come from the same library version.
101103
pub fn cmp_fast_unstable(&self, other: &Self) -> core::cmp::Ordering {
102-
self.0.cmp_fast_unstable(&other.0)
104+
#[cfg(feature = "secp256k1-sys")]
105+
{
106+
self.0.cmp_fast_unstable(&other.0)
107+
}
108+
#[cfg(not(feature = "secp256k1-sys"))]
109+
{
110+
self.0.cmp(&other.0)
111+
}
103112
}
104113

105114
/// Like `cmp::Eq` but faster and with no guarantees across library versions.
@@ -108,7 +117,14 @@ macro_rules! impl_fast_comparisons {
108117
/// `self` and `other` before comparing them. This function provides a faster equality
109118
/// check if you know that your types come from the same library version.
110119
pub fn eq_fast_unstable(&self, other: &Self) -> bool {
111-
self.0.eq_fast_unstable(&other.0)
120+
#[cfg(feature = "secp256k1-sys")]
121+
{
122+
self.0.eq_fast_unstable(&other.0)
123+
}
124+
#[cfg(not(feature = "secp256k1-sys"))]
125+
{
126+
self.0 == other.0
127+
}
112128
}
113129
}
114130
};

‎src/scalar.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ impl Scalar {
9999

100100
// returns a reference to internal bytes
101101
// non-public to not leak the internal representation
102+
#[cfg(feature = "secp256k1-sys")]
102103
pub(crate) fn as_be_bytes(&self) -> &[u8; 32] { &self.0 }
103104

105+
#[cfg(feature = "secp256k1-sys")]
104106
pub(crate) fn as_c_ptr(&self) -> *const u8 {
105107
use secp256k1_sys::CPtr;
106108

‎src/secret.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use core::fmt;
66

77
use crate::constants::SECRET_KEY_SIZE;
88
use crate::ecdh::SharedSecret;
9-
use crate::key::{Keypair, SecretKey};
9+
#[cfg(feature = "secp256k1-sys")]
10+
use crate::key::Keypair;
11+
12+
use crate::key::SecretKey;
1013
use crate::to_hex;
1114
macro_rules! impl_display_secret {
1215
// Default hasher exists only in standard library and not alloc
@@ -127,6 +130,7 @@ impl SecretKey {
127130
pub fn display_secret(&self) -> DisplaySecret { DisplaySecret { secret: self.secret_bytes() } }
128131
}
129132

133+
#[cfg(feature = "secp256k1-sys")]
130134
impl Keypair {
131135
/// Formats the explicit byte value of the secret key kept inside the type as a
132136
/// little-endian hexadecimal string using the provided formatter.
@@ -171,7 +175,7 @@ impl SharedSecret {
171175
///
172176
/// ```
173177
/// # #[cfg(not(secp256k1_fuzz))]
174-
/// # #[cfg(feature = "std")] {
178+
/// # #[cfg(all(feature = "std", feature = "secp256k1-sys"))] {
175179
/// # use std::str::FromStr;
176180
/// use secp256k1::{SecretKey, PublicKey};
177181
/// use secp256k1::ecdh::SharedSecret;

0 commit comments

Comments
 (0)
Please sign in to comment.