1use crate::hazmat::FromDigest;
4use elliptic_curve_flow::{
5 bigint::{ArrayEncoding, Encoding},
6 consts::U32,
7 dev::{MockCurve, Scalar},
8 group::ff::PrimeField,
9 subtle::{ConditionallySelectable, ConstantTimeLess},
10 Curve,
11};
12use signature_flow::digest::Digest;
13
14type UInt = <MockCurve as Curve>::UInt;
15
16impl FromDigest<MockCurve> for Scalar {
17 fn from_digest<D>(digest: D) -> Self
18 where
19 D: Digest<OutputSize = U32>,
20 {
21 let uint = UInt::from_be_bytes(digest.finalize().into());
22 let overflow = !uint.ct_lt(&MockCurve::ORDER);
23 let scalar = uint.wrapping_add(&UInt::conditional_select(
24 &UInt::ZERO,
25 &MockCurve::ORDER,
26 overflow,
27 ));
28
29 Self::from_repr(scalar.to_be_byte_array()).unwrap()
30 }
31}
32
33pub struct TestVector {
38 pub d: &'static [u8],
40
41 pub q_x: &'static [u8],
43
44 pub q_y: &'static [u8],
46
47 pub k: &'static [u8],
49
50 pub m: &'static [u8],
52
53 pub r: &'static [u8],
55
56 pub s: &'static [u8],
58}
59
60#[macro_export]
62#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
63macro_rules! new_signing_test {
64 ($curve:path, $vectors:expr) => {
65 use core::convert::TryInto;
66 use $crate::{
67 elliptic_curve_flow::{
68 bigint::Encoding, generic_array::GenericArray, group::ff::PrimeField, Curve,
69 ProjectiveArithmetic, Scalar,
70 },
71 hazmat::SignPrimitive,
72 };
73
74 fn decode_scalar(bytes: &[u8]) -> Option<Scalar<$curve>> {
75 if bytes.len() == <$curve as Curve>::UInt::BYTE_SIZE {
76 Scalar::<$curve>::from_repr(GenericArray::clone_from_slice(bytes)).into()
77 } else {
78 None
79 }
80 }
81
82 #[test]
83 fn ecdsa_signing() {
84 for vector in $vectors {
85 let d = decode_scalar(vector.d).expect("invalid vector.d");
86 let k = decode_scalar(vector.k).expect("invalid vector.m");
87 let z = decode_scalar(vector.m).expect("invalid vector.z");
88 let sig = d.try_sign_prehashed(&k, &z).expect("ECDSA sign failed");
89
90 assert_eq!(vector.r, sig.r().to_bytes().as_slice());
91 assert_eq!(vector.s, sig.s().to_bytes().as_slice());
92 }
93 }
94 };
95}
96
97#[macro_export]
99#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
100macro_rules! new_verification_test {
101 ($curve:path, $vectors:expr) => {
102 use core::convert::TryInto;
103 use $crate::{
104 elliptic_curve_flow::{
105 generic_array::GenericArray, group::ff::PrimeField, sec1::EncodedPoint,
106 AffinePoint, ProjectiveArithmetic, Scalar,
107 },
108 hazmat::VerifyPrimitive,
109 Signature,
110 };
111
112 #[test]
113 fn ecdsa_verify_success() {
114 for vector in $vectors {
115 let q_encoded = EncodedPoint::from_affine_coordinates(
116 GenericArray::from_slice(vector.q_x),
117 GenericArray::from_slice(vector.q_y),
118 false,
119 );
120
121 let q: AffinePoint<$curve> = q_encoded.decode().unwrap();
122
123 let maybe_z = Scalar::<$curve>::from_repr(GenericArray::clone_from_slice(vector.m));
124 assert!(bool::from(maybe_z.is_some()), "invalid vector.m");
125 let z = maybe_z.unwrap();
126
127 let sig = Signature::from_scalars(
128 GenericArray::clone_from_slice(vector.r),
129 GenericArray::clone_from_slice(vector.s),
130 )
131 .unwrap();
132
133 let result = q.verify_prehashed(&z, &sig);
134 assert!(result.is_ok());
135 }
136 }
137
138 #[test]
139 fn ecdsa_verify_invalid_s() {
140 for vector in $vectors {
141 let q_encoded = EncodedPoint::from_affine_coordinates(
142 GenericArray::from_slice(vector.q_x),
143 GenericArray::from_slice(vector.q_y),
144 false,
145 );
146
147 let q: AffinePoint<$curve> = q_encoded.decode().unwrap();
148
149 let maybe_z = Scalar::<$curve>::from_repr(GenericArray::clone_from_slice(vector.m));
150 assert!(bool::from(maybe_z.is_some()), "invalid vector.m");
151 let z = maybe_z.unwrap();
152
153 let mut s_tweaked = GenericArray::clone_from_slice(vector.s);
155 s_tweaked[0] ^= 1;
156
157 let sig =
158 Signature::from_scalars(GenericArray::clone_from_slice(vector.r), s_tweaked)
159 .unwrap();
160
161 let result = q.verify_prehashed(&z, &sig);
162 assert!(result.is_err());
163 }
164 }
165
166 };
168}
169
170#[macro_export]
172#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
173macro_rules! new_wycheproof_test {
174 ($name:ident, $test_name: expr, $curve:path) => {
175 use $crate::{elliptic_curve_flow::sec1::EncodedPoint, signature_flow::Verifier, Signature};
176
177 #[test]
178 fn $name() {
179 use blobby::Blob5Iterator;
180 use elliptic_curve_flow::{bigint::Encoding as _, generic_array::typenum::Unsigned};
181
182 fn element_from_padded_slice<C: elliptic_curve_flow::Curve>(
185 data: &[u8],
186 ) -> elliptic_curve_flow::FieldBytes<C> {
187 let point_len = C::UInt::BYTE_SIZE;
188 if data.len() >= point_len {
189 let offset = data.len() - point_len;
190 for v in data.iter().take(offset) {
191 assert_eq!(*v, 0, "EcdsaVerifier: point too large");
192 }
193 elliptic_curve_flow::FieldBytes::<C>::clone_from_slice(&data[offset..])
194 } else {
195 let iter = core::iter::repeat(0)
198 .take(point_len - data.len())
199 .chain(data.iter().cloned());
200 elliptic_curve_flow::FieldBytes::<C>::from_exact_iter(iter).unwrap()
201 }
202 }
203
204 fn run_test(
205 wx: &[u8],
206 wy: &[u8],
207 msg: &[u8],
208 sig: &[u8],
209 pass: bool,
210 ) -> Option<&'static str> {
211 let x = element_from_padded_slice::<$curve>(wx);
212 let y = element_from_padded_slice::<$curve>(wy);
213 let q_encoded: EncodedPoint<$curve> =
214 EncodedPoint::from_affine_coordinates(&x, &y, false);
215 let verifying_key = $crate::VerifyingKey::from_encoded_point(&q_encoded).unwrap();
216
217 let sig = match Signature::from_der(sig) {
218 Ok(s) => s,
219 Err(_) if !pass => return None,
220 Err(_) => return Some("failed to parse signature ASN.1"),
221 };
222
223 match verifying_key.verify(msg, &sig) {
224 Ok(_) if pass => None,
225 Ok(_) => Some("signature verify unexpectedly succeeded"),
226 Err(_) if !pass => None,
227 Err(_) => Some("signature verify failed"),
228 }
229 }
230
231 let data = include_bytes!(concat!("test_vectors/data/", $test_name, ".blb"));
232
233 for (i, row) in Blob5Iterator::new(data).unwrap().enumerate() {
234 let [wx, wy, msg, sig, status] = row.unwrap();
235 let pass = match status[0] {
236 0 => false,
237 1 => true,
238 _ => panic!("invalid value for pass flag"),
239 };
240 if let Some(desc) = run_test(wx, wy, msg, sig, pass) {
241 panic!(
242 "\n\
243 Failed test №{}: {}\n\
244 wx:\t{:?}\n\
245 wy:\t{:?}\n\
246 msg:\t{:?}\n\
247 sig:\t{:?}\n\
248 pass:\t{}\n",
249 i, desc, wx, wy, msg, sig, pass,
250 );
251 }
252 }
253 }
254 };
255}