blvm_consensus/script/
signature.rs1use crate::error::Result;
9use crate::types::Natural;
10
11use super::SigVersion;
12
13#[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
16use secp256k1::Secp256k1;
17
18#[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
23pub(crate) type SecpCtx = secp256k1::Secp256k1<secp256k1::All>;
24
25#[cfg(not(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1"))))]
26#[derive(Default, Clone, Copy)]
27pub(crate) struct SecpCtx;
28
29pub(crate) fn new_secp() -> SecpCtx {
31 #[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
32 {
33 secp256k1::Secp256k1::new()
34 }
35 #[cfg(not(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1"))))]
36 {
37 SecpCtx
38 }
39}
40
41use std::thread_local;
42
43thread_local! {
44 static SECP256K1_CONTEXT: SecpCtx = new_secp();
45}
46
47pub(crate) fn with_secp_context<F, R>(f: F) -> R
49where
50 F: FnOnce(&SecpCtx) -> R,
51{
52 SECP256K1_CONTEXT.with(f)
53}
54
55pub(crate) fn reject_strictenc(flags: u32, sighash_byte: u8, pubkey_bytes: &[u8]) -> Result<()> {
58 if flags & 0x02 == 0 {
59 return Ok(());
60 }
61 let base_sighash = sighash_byte & !0x80;
62 if !(0x01..=0x03).contains(&base_sighash) {
63 return Err(crate::error::ConsensusError::ScriptErrorWithCode {
64 code: crate::error::ScriptErrorCode::SigHashType,
65 message: "undefined sighash type under STRICTENC".into(),
66 });
67 }
68 reject_strictenc_pubkey(flags, pubkey_bytes)?;
69 Ok(())
70}
71
72pub(crate) fn reject_strictenc_pubkey(flags: u32, pubkey_bytes: &[u8]) -> Result<()> {
73 if flags & 0x02 == 0 {
74 return Ok(());
75 }
76 let compressed =
77 pubkey_bytes.len() == 33 && (pubkey_bytes[0] == 0x02 || pubkey_bytes[0] == 0x03);
78 let uncompressed = pubkey_bytes.len() == 65 && pubkey_bytes[0] == 0x04;
79 if !compressed && !uncompressed {
80 return Err(crate::error::ConsensusError::ScriptErrorWithCode {
81 code: crate::error::ScriptErrorCode::PubkeyType,
82 message: "public key is not compressed or uncompressed under STRICTENC".into(),
83 });
84 }
85 Ok(())
86}
87
88pub(crate) fn reject_strictenc_signature(flags: u32, signature_bytes: &[u8]) -> Result<()> {
91 if flags & 0x02 == 0 || signature_bytes.is_empty() {
92 return Ok(());
93 }
94 if !crate::bip_validation::is_strict_der(signature_bytes)? {
95 return Err(crate::error::ConsensusError::ScriptErrorWithCode {
96 code: crate::error::ScriptErrorCode::SigDer,
97 message: "non-DER signature under STRICTENC".into(),
98 });
99 }
100 let sighash_byte = signature_bytes[signature_bytes.len() - 1];
101 let base_sighash = sighash_byte & !0x80;
102 if !(0x01..=0x03).contains(&base_sighash) {
103 return Err(crate::error::ConsensusError::ScriptErrorWithCode {
104 code: crate::error::ScriptErrorCode::SigHashType,
105 message: "undefined sighash type under STRICTENC".into(),
106 });
107 }
108 Ok(())
109}
110
111#[allow(clippy::too_many_arguments)]
116pub(crate) fn verify_signature(
117 _secp: &SecpCtx,
118 pubkey_bytes: &[u8],
119 signature_bytes: &[u8],
120 sighash: &[u8; 32],
121 flags: u32,
122 height: Natural,
123 network: crate::types::Network,
124 sigversion: SigVersion,
125) -> Result<bool> {
126 let _ = (height, network);
127 reject_strictenc_pubkey(flags, pubkey_bytes)?;
128 if signature_bytes.is_empty() {
129 return Ok(false);
130 }
131 reject_strictenc_signature(flags, signature_bytes)?;
132 let sig_len = signature_bytes.len();
133 let sighash_byte = signature_bytes[sig_len - 1];
134 let der_sig = &signature_bytes[..sig_len - 1];
135
136 if flags & 0x04 != 0 && !crate::bip_validation::is_strict_der(signature_bytes)? {
140 return Err(crate::error::ConsensusError::ScriptErrorWithCode {
141 code: crate::error::ScriptErrorCode::SigDer,
142 message: "non-DER signature".into(),
143 });
144 }
145
146 reject_strictenc(flags, sighash_byte, pubkey_bytes)?;
147
148 if (flags & super::flags::SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0
149 && sigversion == SigVersion::WitnessV0
150 && !(pubkey_bytes.len() == 33 && (pubkey_bytes[0] == 0x02 || pubkey_bytes[0] == 0x03))
151 {
152 return Ok(false);
153 }
154
155 let strict_der = flags & 0x04 != 0;
156 let enforce_low_s = flags & 0x08 != 0;
157
158 #[cfg(feature = "blvm-secp256k1")]
159 {
160 return Ok(blvm_secp256k1::ecdsa::verify_ecdsa_direct(
161 der_sig,
162 pubkey_bytes,
163 sighash,
164 strict_der,
165 enforce_low_s,
166 )
167 .unwrap_or(false));
168 }
169
170 #[cfg(all(feature = "secp256k1-fallback", not(feature = "blvm-secp256k1")))]
171 {
172 use secp256k1::{PublicKey, ecdsa::Signature};
173
174 let signature = if strict_der {
175 match Signature::from_der(der_sig) {
176 Ok(sig) => sig,
177 Err(_) => {
178 return Err(crate::error::ConsensusError::ScriptErrorWithCode {
179 code: crate::error::ScriptErrorCode::SigDer,
180 message: "non-DER signature".into(),
181 });
182 }
183 }
184 } else {
185 match Signature::from_der_lax(der_sig) {
186 Ok(sig) => sig,
187 Err(_) => return Ok(false),
188 }
189 };
190
191 if enforce_low_s {
192 let before = signature.serialize_compact();
193 let mut normalized = signature;
194 normalized.normalize_s();
195 if before != normalized.serialize_compact() {
196 return Ok(false);
197 }
198 }
199
200 let pubkey = match PublicKey::from_slice(pubkey_bytes) {
201 Ok(pk) => pk,
202 Err(_) => return Ok(false),
203 };
204
205 let normalized_signature = if enforce_low_s {
206 signature
207 } else {
208 let mut s = signature;
209 s.normalize_s();
210 s
211 };
212
213 let sig_compact = normalized_signature.serialize_compact();
214 let pk_compressed = pubkey.serialize();
215 return crate::secp256k1_backend::verify_ecdsa(sighash, &sig_compact, &pk_compressed);
216 }
217
218 #[allow(unreachable_code)]
219 Ok(false)
220}
221
222#[cfg(feature = "production")]
224pub fn verify_pre_extracted_ecdsa(
225 pubkey_bytes: &[u8],
226 signature_bytes: &[u8],
227 sighash: &[u8; 32],
228 flags: u32,
229 height: Natural,
230 network: crate::types::Network,
231) -> Result<bool> {
232 with_secp_context(|secp| {
233 verify_signature(
234 secp,
235 pubkey_bytes,
236 signature_bytes,
237 sighash,
238 flags,
239 height,
240 network,
241 SigVersion::Base,
242 )
243 })
244}
245
246#[cfg(feature = "production")]
247pub fn batch_verify_signatures(
248 verification_tasks: &[(&[u8], &[u8], [u8; 32])],
249 flags: u32,
250 height: Natural,
251 network: crate::types::Network,
252 sigversion: SigVersion,
253) -> Result<Vec<bool>> {
254 #[cfg(feature = "profile")]
255 let _t0 = std::time::Instant::now();
256
257 if verification_tasks.is_empty() {
258 #[cfg(feature = "profile")]
259 crate::script_profile::add_multisig_ns(_t0.elapsed().as_nanos() as u64);
260 return Ok(Vec::new());
261 }
262
263 let mut results = Vec::with_capacity(verification_tasks.len());
270 with_secp_context(|secp| -> Result<()> {
271 for (pubkey_bytes, signature_bytes, sighash) in verification_tasks {
272 let result = verify_signature(
273 secp,
274 pubkey_bytes,
275 signature_bytes,
276 sighash,
277 flags,
278 height,
279 network,
280 sigversion,
281 )?;
282 results.push(result);
283 }
284 Ok(())
285 })?;
286 #[cfg(feature = "profile")]
287 crate::script_profile::add_multisig_ns(_t0.elapsed().as_nanos() as u64);
288 Ok(results)
289}