dcrypt_sign/ecdsa/p224/
mod.rs1use crate::ecdsa::common::{
8 bits2octets, is_canonical_nonzero_scalar, is_high_s, Rfc6979, SignatureComponents,
9};
10use alloc::vec::Vec;
11use dcrypt_algorithms::ec::p224 as ec;
12use dcrypt_algorithms::hash::sha2::Sha224;
13use dcrypt_algorithms::hash::HashFunction;
14use dcrypt_api::{
15 error::Error as ApiError, Result as ApiResult, Signature as SignatureTrait, ZeroizingBytes,
16};
17use dcrypt_common::SecretBuffer;
18use dcrypt_internal::{
19 constant_time::ct_eq, zeroizing_bytes_from_slice, CryptoRng, RngCore, Zeroize, ZeroizeOnDrop,
20 Zeroizing,
21};
22use dcrypt_params::traditional::ecdsa::NIST_P224;
23
24pub struct EcdsaP224;
26
27#[derive(Clone)]
29pub struct EcdsaP224PublicKey(pub(crate) [u8; ec::P224_POINT_UNCOMPRESSED_SIZE]);
30
31#[derive(Clone)]
33pub struct EcdsaP224SecretKey {
34 raw: ec::Scalar,
35 bytes: SecretBuffer<{ ec::P224_SCALAR_SIZE }>,
36}
37
38impl Zeroize for EcdsaP224SecretKey {
39 fn zeroize(&mut self) {
40 self.raw.zeroize();
41 self.bytes.zeroize();
42 }
43}
44
45impl Drop for EcdsaP224SecretKey {
46 fn drop(&mut self) {
47 self.zeroize();
48 }
49}
50
51impl ZeroizeOnDrop for EcdsaP224SecretKey {}
52
53#[derive(Clone)]
55pub struct EcdsaP224Signature(pub(crate) Vec<u8>);
56
57impl AsRef<[u8]> for EcdsaP224PublicKey {
59 fn as_ref(&self) -> &[u8] {
60 &self.0
61 }
62}
63impl AsMut<[u8]> for EcdsaP224PublicKey {
64 fn as_mut(&mut self) -> &mut [u8] {
65 &mut self.0
66 }
67}
68impl AsRef<[u8]> for EcdsaP224SecretKey {
69 fn as_ref(&self) -> &[u8] {
70 self.bytes.as_ref()
71 }
72}
73impl AsRef<[u8]> for EcdsaP224Signature {
76 fn as_ref(&self) -> &[u8] {
77 &self.0
78 }
79}
80impl AsMut<[u8]> for EcdsaP224Signature {
81 fn as_mut(&mut self) -> &mut [u8] {
82 &mut self.0
83 }
84}
85
86impl EcdsaP224PublicKey {
87 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
89 let point = ec::Point::deserialize_uncompressed(bytes).map_err(ApiError::from)?;
90 if point.is_identity() {
91 return Err(ApiError::InvalidParameter {
92 context: "ECDSA-P224 public key",
93 #[cfg(feature = "std")]
94 message: "Identity is not a valid ECDSA public key".to_string(),
95 });
96 }
97 Ok(Self(point.serialize_uncompressed()))
98 }
99
100 pub fn to_bytes(&self) -> &[u8] {
102 &self.0
103 }
104}
105
106impl EcdsaP224SecretKey {
107 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
109 let raw = ec::Scalar::deserialize(bytes).map_err(ApiError::from)?;
110 let serialized = raw.serialize();
111 Ok(Self {
112 raw,
113 bytes: serialized,
114 })
115 }
116
117 pub fn to_bytes_zeroizing(&self) -> ZeroizingBytes {
119 zeroizing_bytes_from_slice(self.bytes.as_ref())
120 }
121}
122
123impl EcdsaP224Signature {
124 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
126 SignatureComponents::from_der(bytes)?;
127 Ok(Self(bytes.to_vec()))
128 }
129
130 pub fn to_bytes(&self) -> &[u8] {
132 &self.0
133 }
134}
135
136impl SignatureTrait for EcdsaP224 {
137 type PublicKey = EcdsaP224PublicKey;
138 type SecretKey = EcdsaP224SecretKey;
139 type SignatureData = EcdsaP224Signature;
140 type KeyPair = (Self::PublicKey, Self::SecretKey);
141
142 fn name() -> &'static str {
143 "ECDSA-P224"
144 }
145
146 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
147 let (sk_scalar, pk_point) = ec::generate_keypair(rng).map_err(ApiError::from)?;
148
149 let sk_bytes = sk_scalar.serialize();
150
151 if sk_scalar.is_zero() {
152 return Err(ApiError::InvalidParameter {
153 context: "ECDSA-P224 keypair",
154 #[cfg(feature = "std")]
155 message: "Generated secret key is zero".to_string(),
156 });
157 }
158
159 let secret_key = EcdsaP224SecretKey {
160 raw: sk_scalar,
161 bytes: sk_bytes,
162 };
163 let public_key = EcdsaP224PublicKey(pk_point.serialize_uncompressed());
164 Ok((public_key, secret_key))
165 }
166
167 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
168 keypair.0.clone()
169 }
170 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
171 keypair.1.clone()
172 }
173
174 fn sign(message: &[u8], secret_key: &Self::SecretKey) -> ApiResult<Self::SignatureData> {
175 let mut hasher = Sha224::new();
176 hasher.update(message).map_err(ApiError::from)?;
177 let hash_output = hasher.finalize().map_err(ApiError::from)?;
178
179 let z_octets = bits2octets(hash_output.as_ref(), &NIST_P224.n, 224)?;
180 let z_bytes: [u8; ec::P224_SCALAR_SIZE] =
181 (&z_octets[..])
182 .try_into()
183 .map_err(|_| ApiError::InvalidLength {
184 context: "ECDSA-P224 hash conversion",
185 expected: ec::P224_SCALAR_SIZE,
186 actual: z_octets.len(),
187 })?;
188 let z = ec::Scalar::from_bytes_reduced(z_bytes);
189
190 let d = secret_key.raw.clone();
191 let mut d_bytes = d.serialize();
192 let nonces_result =
193 Rfc6979::<Sha224>::new(d_bytes.as_ref(), hash_output.as_ref(), &NIST_P224.n, 224);
194 d_bytes.zeroize();
195 let mut nonces = nonces_result?;
196
197 loop {
198 let mut nonce = nonces.next_nonce()?;
199 let mut nonce_bytes: [u8; ec::P224_SCALAR_SIZE] =
200 (&nonce[..])
201 .try_into()
202 .map_err(|_| ApiError::InvalidLength {
203 context: "ECDSA-P224 nonce",
204 expected: ec::P224_SCALAR_SIZE,
205 actual: nonce.len(),
206 })?;
207 nonce.zeroize();
208 let scalar = ec::Scalar::new(nonce_bytes).map_err(ApiError::from);
209 nonce_bytes.zeroize();
210 let k = scalar?;
211
212 let kg = ec::scalar_mult_base_g(&k).map_err(ApiError::from)?;
213
214 if kg.is_identity() {
215 continue;
216 }
217 let r_bytes = Zeroizing::new(kg.x_coordinate_bytes());
218
219 let r = match reduce_bytes_to_scalar_p224(&r_bytes) {
220 Ok(scalar) if !scalar.is_zero() => scalar,
221 _ => continue,
222 };
223
224 let k_inv = k.inv_mod_n().map_err(ApiError::from)?;
225 let rd = r.mul_mod_n(&d).map_err(ApiError::from)?;
226 let z_plus_rd = z.add_mod_n(&rd).map_err(ApiError::from)?;
227 let mut s = k_inv.mul_mod_n(&z_plus_rd).map_err(ApiError::from)?;
228
229 if s.is_zero() {
230 continue;
231 }
232 if is_high_s(s.serialize().as_ref(), &NIST_P224.n) {
233 s = s.negate();
234 }
235
236 let sig_comps = SignatureComponents {
237 r: r.serialize().to_vec(),
238 s: s.serialize().to_vec(),
239 };
240 return Ok(EcdsaP224Signature(sig_comps.to_der()));
241 }
242 }
243
244 fn verify(
245 message: &[u8],
246 signature: &Self::SignatureData,
247 public_key: &Self::PublicKey,
248 ) -> ApiResult<()> {
249 let sig_comps = SignatureComponents::from_der(&signature.0)?;
250
251 if sig_comps.r.len() > ec::P224_SCALAR_SIZE || sig_comps.s.len() > ec::P224_SCALAR_SIZE {
252 return Err(ApiError::InvalidSignature {
253 context: "ECDSA-P224 verify",
254 #[cfg(feature = "std")]
255 message: "Invalid signature component size".to_string(),
256 });
257 }
258
259 let mut r_bytes = [0u8; ec::P224_SCALAR_SIZE];
260 let mut s_bytes = [0u8; ec::P224_SCALAR_SIZE];
261 let r_offset = ec::P224_SCALAR_SIZE.saturating_sub(sig_comps.r.len());
262 let s_offset = ec::P224_SCALAR_SIZE.saturating_sub(sig_comps.s.len());
263 r_bytes[r_offset..].copy_from_slice(&sig_comps.r);
264 s_bytes[s_offset..].copy_from_slice(&sig_comps.s);
265
266 if !is_canonical_nonzero_scalar(&r_bytes, &NIST_P224.n)
267 || !is_canonical_nonzero_scalar(&s_bytes, &NIST_P224.n)
268 {
269 return Err(ApiError::InvalidSignature {
270 context: "ECDSA-P224 verify",
271 #[cfg(feature = "std")]
272 message: "signature components must be canonical integers in [1, n-1]".to_string(),
273 });
274 }
275
276 let r = ec::Scalar::new(r_bytes).map_err(|_| ApiError::InvalidSignature {
277 context: "ECDSA-P224 verify",
278 #[cfg(feature = "std")]
279 message: "Invalid r component".to_string(),
280 })?;
281 let s = ec::Scalar::new(s_bytes).map_err(|_| ApiError::InvalidSignature {
282 context: "ECDSA-P224 verify",
283 #[cfg(feature = "std")]
284 message: "Invalid s component".to_string(),
285 })?;
286 if is_high_s(s.serialize().as_ref(), &NIST_P224.n) {
287 return Err(ApiError::InvalidSignature {
288 context: "ECDSA-P224 verify",
289 #[cfg(feature = "std")]
290 message: "high-s signatures are non-canonical".to_string(),
291 });
292 }
293
294 let mut hasher = Sha224::new();
295 hasher.update(message).map_err(ApiError::from)?;
296 let hash_output = hasher.finalize().map_err(ApiError::from)?;
297
298 let z_octets = bits2octets(hash_output.as_ref(), &NIST_P224.n, 224)?;
299 let z_bytes: [u8; ec::P224_SCALAR_SIZE] =
300 (&z_octets[..])
301 .try_into()
302 .map_err(|_| ApiError::InvalidLength {
303 context: "ECDSA-P224 hash conversion",
304 expected: ec::P224_SCALAR_SIZE,
305 actual: z_octets.len(),
306 })?;
307 let z = ec::Scalar::from_bytes_reduced(z_bytes);
308
309 let s_inv = s.inv_mod_n().map_err(ApiError::from)?;
310 let u1 = z.mul_mod_n(&s_inv).map_err(ApiError::from)?;
311 let u2 = r.mul_mod_n(&s_inv).map_err(ApiError::from)?;
312
313 let q_point = ec::Point::deserialize_uncompressed(&public_key.0).map_err(ApiError::from)?;
314
315 if q_point.is_identity() {
316 return Err(ApiError::InvalidKey {
317 context: "ECDSA-P224 verify",
318 #[cfg(feature = "std")]
319 message: "Public key is the point at infinity".to_string(),
320 });
321 }
322
323 let u1g = ec::scalar_mult_base_g(&u1).map_err(ApiError::from)?;
324 let u2q = ec::scalar_mult(&u2, &q_point).map_err(ApiError::from)?;
325
326 let point = u1g.add(&u2q);
327
328 if point.is_identity() {
329 return Err(ApiError::InvalidSignature {
330 context: "ECDSA-P224 verify",
331 #[cfg(feature = "std")]
332 message: "Verification point is identity".to_string(),
333 });
334 }
335
336 let x1_bytes = point.x_coordinate_bytes();
337 let v = reduce_bytes_to_scalar_p224(&x1_bytes)?;
338
339 if !ct_eq(r.serialize(), v.serialize()) {
340 return Err(ApiError::InvalidSignature {
341 context: "ECDSA-P224 verify",
342 #[cfg(feature = "std")]
343 message: "Signature verification failed (r != v)".to_string(),
344 });
345 }
346 Ok(())
347 }
348}
349
350fn reduce_bytes_to_scalar_p224(bytes: &[u8; ec::P224_SCALAR_SIZE]) -> ApiResult<ec::Scalar> {
351 Ok(ec::Scalar::from_bytes_reduced(*bytes))
352}
353
354#[cfg(test)]
355mod tests;