1pub use digest::dev::blobby;
7
8use crate::EcdsaCurve;
9use elliptic_curve::dev::mock_curve::MockCurve;
10
11#[macro_export]
13macro_rules! bench_ecdsa {
14 ($name:ident, $desc:expr, $signing_key:expr, $signature:ty) => {
15 fn bench_sign<M: ::criterion::measurement::Measurement>(
16 group: &mut ::criterion::BenchmarkGroup<'_, M>,
17 ) {
18 use $crate::signature::Signer as _;
19 let sk = core::hint::black_box($signing_key);
20 let msg = core::hint::black_box(b"example message");
21 group.bench_function("sign", |b| {
22 b.iter(|| {
23 let sig: Signature = sk.sign(msg);
24 core::hint::black_box(sig)
25 })
26 });
27 }
28
29 fn bench_verify<M: ::criterion::measurement::Measurement>(
30 group: &mut ::criterion::BenchmarkGroup<'_, M>,
31 ) {
32 use $crate::signature::{Signer as _, Verifier as _};
33 let sk = $signing_key;
34 let vk = sk.verifying_key();
35 let msg = core::hint::black_box(b"example message");
36 let sig: Signature = core::hint::black_box(sk.sign(msg));
37 group.bench_function("verify", |b| b.iter(|| vk.verify(msg, &sig)));
38 }
39
40 fn $name(c: &mut ::criterion::Criterion) {
41 let mut group = c.benchmark_group($desc);
42 bench_sign(&mut group);
43 bench_verify(&mut group);
44 group.finish();
45 }
46 };
47}
48
49#[macro_export]
51macro_rules! new_signing_test {
52 ($curve:path, $vectors:expr) => {
53 use $crate::{
54 elliptic_curve::{
55 Curve, CurveArithmetic, FieldBytes, NonZeroScalar, Scalar,
56 array::{Array, typenum::Unsigned},
57 bigint::Encoding,
58 group::ff::PrimeField,
59 },
60 hazmat::sign_prehashed,
61 };
62
63 fn decode_scalar(bytes: &[u8]) -> Option<NonZeroScalar<$curve>> {
64 if bytes.len() == <$curve as Curve>::FieldBytesSize::USIZE {
65 NonZeroScalar::<$curve>::from_repr(bytes.try_into().unwrap()).into()
66 } else {
67 None
68 }
69 }
70
71 #[test]
72 fn ecdsa_signing() {
73 for vector in $vectors {
74 let d = decode_scalar(vector.d).expect("invalid vector.d");
75 let k = decode_scalar(vector.k).expect("invalid vector.k");
76
77 assert_eq!(
78 <$curve as Curve>::FieldBytesSize::USIZE,
79 vector.m.len(),
80 "invalid vector.m (must be field-sized digest)"
81 );
82 let z = FieldBytes::<$curve>::try_from(vector.m).unwrap();
83 let sig = sign_prehashed::<$curve>(&d, &k, &z)
84 .expect("ECDSA sign failed")
85 .0;
86
87 assert_eq!(vector.r, sig.r().to_bytes().as_slice());
88 assert_eq!(vector.s, sig.s().to_bytes().as_slice());
89 }
90 }
91 };
92}
93
94#[macro_export]
96macro_rules! new_verification_test {
97 ($curve:path, $vectors:expr) => {
98 use $crate::{
99 Signature, VerifyingKey,
100 elliptic_curve::{
101 AffinePoint, CurveArithmetic, Scalar,
102 array::Array,
103 group::ff::PrimeField,
104 sec1::{FromSec1Point, Sec1Point},
105 },
106 signature::hazmat::PrehashVerifier,
107 };
108
109 #[test]
110 fn ecdsa_verify_success() {
111 for vector in $vectors {
112 let q_encoded = Sec1Point::<$curve>::from_affine_coordinates(
113 &Array::try_from(vector.q_x).unwrap(),
114 &Array::try_from(vector.q_y).unwrap(),
115 false,
116 );
117
118 let q = VerifyingKey::<$curve>::from_sec1_point(&q_encoded).unwrap();
119
120 let sig = Signature::from_scalars(
121 Array::try_from(vector.r).unwrap(),
122 Array::try_from(vector.s).unwrap(),
123 )
124 .unwrap();
125
126 let result = q.verify_prehash(vector.m, &sig);
127 assert!(result.is_ok());
128 }
129 }
130
131 #[test]
132 fn ecdsa_verify_invalid_s() {
133 for vector in $vectors {
134 let q_encoded = Sec1Point::<$curve>::from_affine_coordinates(
135 &Array::try_from(vector.q_x).unwrap(),
136 &Array::try_from(vector.q_y).unwrap(),
137 false,
138 );
139
140 let q = VerifyingKey::<$curve>::from_sec1_point(&q_encoded).unwrap();
141 let r = Array::try_from(vector.r).unwrap();
142
143 let mut s_tweaked = Array::try_from(vector.s).unwrap();
145 s_tweaked[0] ^= 1;
146
147 let sig = Signature::from_scalars(r, s_tweaked).unwrap();
148 let result = q.verify_prehash(vector.m, &sig);
149 assert!(result.is_err());
150 }
151 }
152
153 };
155}
156
157#[macro_export]
159macro_rules! new_wycheproof_test {
160 ($name:ident, $test_name: expr, $curve:path) => {
161 use $crate::{
162 Signature,
163 elliptic_curve::sec1::Sec1Point,
164 signature::Verifier,
165 };
166
167 #[test]
168 fn $name() {
169 use $crate::elliptic_curve::{self, array::typenum::Unsigned};
170
171 fn element_from_padded_slice<C: elliptic_curve::Curve>(
174 data: &[u8],
175 ) -> elliptic_curve::FieldBytes<C> {
176 let point_len = C::FieldBytesSize::USIZE;
177 if data.len() >= point_len {
178 let offset = data.len() - point_len;
179 for v in data.iter().take(offset) {
180 assert_eq!(*v, 0, "EcdsaVerifier: point too large");
181 }
182 elliptic_curve::FieldBytes::<C>::try_from(&data[offset..]).unwrap()
183 } else {
184 let iter = core::iter::repeat(0)
187 .take(point_len - data.len())
188 .chain(data.iter().cloned());
189 elliptic_curve::FieldBytes::<C>::from_iter(iter)
190 }
191 }
192
193 fn run_test(
194 wx: &[u8],
195 wy: &[u8],
196 msg: &[u8],
197 sig: &[u8],
198 pass: bool,
199 ) -> Option<&'static str> {
200 let x = element_from_padded_slice::<$curve>(wx);
201 let y = element_from_padded_slice::<$curve>(wy);
202 let q_encoded = Sec1Point::<$curve>::from_affine_coordinates(
203 &x, &y, false,
204 );
205 let verifying_key =
206 $crate::VerifyingKey::<$curve>::from_sec1_point(&q_encoded).unwrap();
207
208 let sig = match Signature::from_der(sig) {
209 Ok(s) => s,
210 Err(_) if !pass => return None,
211 Err(_) => return Some("failed to parse signature ASN.1"),
212 };
213
214 match verifying_key.verify(msg, &sig) {
215 Ok(_) if pass => None,
216 Ok(_) => Some("signature verify unexpectedly succeeded"),
217 Err(_) if !pass => None,
218 Err(_) => Some("signature verify failed"),
219 }
220 }
221
222 #[derive(Debug,Clone,Copy)]
223 struct TestVector {
224 pub wx: &'static [u8],
226 pub wy: &'static [u8],
228 pub msg: &'static [u8],
230 pub sig: &'static [u8],
232 pub pass_: &'static [u8],
234 }
235
236 impl TestVector {
237 pub(crate) fn pass(&self) -> bool {
238 match self.pass_ {
239 &[0] => false,
240 &[1] => true,
241 other => panic!(
242 concat!(
243 "Unsupported value for pass in `",
244 $test_name,
245 "`.\n",
246 "found=`{other:?}`,\n",
247 "expected=[0] or [1]"
248 ),
249 other=other
250 ),
251 }
252 }
253 }
254
255 $crate::dev::blobby::parse_into_structs!(
256 include_bytes!(concat!("test_vectors/data/", $test_name, ".blb"));
257 static TEST_VECTORS: &[
258 TestVector { wx, wy, msg, sig, pass_ }
259 ];
260 );
261
262 for (i, tv) in TEST_VECTORS.iter().enumerate() {
263 if let Some(desc) = run_test(tv.wx, tv.wy, tv.msg, tv.sig, tv.pass()) {
264 panic!(
265 "\n\
266 Failed test №{}: {}\n\
267 wx:\t{:?}\n\
268 wy:\t{:?}\n\
269 msg:\t{:?}\n\
270 sig:\t{:?}\n\
271 pass:\t{}\n",
272 i, desc, tv.wx, tv.wy, tv.msg, tv.sig, tv.pass(),
273 );
274 }
275 }
276 }
277 };
278}
279
280impl EcdsaCurve for MockCurve {
281 const NORMALIZE_S: bool = false;
282}
283
284#[derive(Clone, Copy, Debug)]
286pub struct TestVector {
287 pub d: &'static [u8],
289
290 pub q_x: &'static [u8],
292
293 pub q_y: &'static [u8],
295
296 pub k: &'static [u8],
298
299 pub m: &'static [u8],
301
302 pub r: &'static [u8],
304
305 pub s: &'static [u8],
307}
308
309#[cfg(test)]
310#[allow(clippy::integer_division_remainder_used, reason = "tests")]
311mod tests {
312 use super::*;
313
314 impl crate::DigestAlgorithm for MockCurve {
315 type Digest = sha2::Sha256;
316 }
317
318 new_wycheproof_test!(wycheproof_mock, "wycheproof-mock", MockCurve);
319}