dcrypt_sign/ecdsa/p224/
mod.rs1use crate::ecdsa::common::SignatureComponents;
8use dcrypt_algorithms::ec::p224 as ec;
9use dcrypt_algorithms::hash::sha2::{Sha224, Sha224Algorithm}; use dcrypt_algorithms::hash::{HashAlgorithm, HashFunction}; use dcrypt_algorithms::mac::hmac::Hmac;
12use dcrypt_api::{error::Error as ApiError, Result as ApiResult, Signature as SignatureTrait};
13use dcrypt_internal::constant_time::ct_eq;
14use rand::{CryptoRng, RngCore};
15use zeroize::Zeroize; pub struct EcdsaP224;
19
20#[derive(Clone, Zeroize)]
22pub struct EcdsaP224PublicKey(pub [u8; ec::P224_POINT_UNCOMPRESSED_SIZE]);
23
24#[derive(Clone)]
26pub struct EcdsaP224SecretKey {
27 raw: ec::Scalar,
28 bytes: [u8; ec::P224_SCALAR_SIZE],
29}
30
31impl Zeroize for EcdsaP224SecretKey {
32 fn zeroize(&mut self) {
33 self.bytes.zeroize();
34 }
36}
37
38impl Drop for EcdsaP224SecretKey {
39 fn drop(&mut self) {
40 self.zeroize();
41 }
42}
43
44#[derive(Clone)]
46pub struct EcdsaP224Signature(pub Vec<u8>);
47
48impl AsRef<[u8]> for EcdsaP224PublicKey {
50 fn as_ref(&self) -> &[u8] {
51 &self.0
52 }
53}
54impl AsMut<[u8]> for EcdsaP224PublicKey {
55 fn as_mut(&mut self) -> &mut [u8] {
56 &mut self.0
57 }
58}
59impl AsRef<[u8]> for EcdsaP224SecretKey {
60 fn as_ref(&self) -> &[u8] {
61 &self.bytes
62 }
63}
64impl AsRef<[u8]> for EcdsaP224Signature {
67 fn as_ref(&self) -> &[u8] {
68 &self.0
69 }
70}
71impl AsMut<[u8]> for EcdsaP224Signature {
72 fn as_mut(&mut self) -> &mut [u8] {
73 &mut self.0
74 }
75}
76
77impl SignatureTrait for EcdsaP224 {
78 type PublicKey = EcdsaP224PublicKey;
79 type SecretKey = EcdsaP224SecretKey;
80 type SignatureData = EcdsaP224Signature;
81 type KeyPair = (Self::PublicKey, Self::SecretKey);
82
83 fn name() -> &'static str {
84 "ECDSA-P224"
85 }
86
87 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
88 let (sk_scalar, pk_point) = ec::generate_keypair(rng).map_err(ApiError::from)?;
89
90 let sk_bytes: [u8; ec::P224_SCALAR_SIZE] = sk_scalar.serialize();
91
92 if sk_scalar.is_zero() {
93 return Err(ApiError::InvalidParameter {
94 context: "ECDSA-P224 keypair",
95 #[cfg(feature = "std")]
96 message: "Generated secret key is zero".to_string(),
97 });
98 }
99
100 let secret_key = EcdsaP224SecretKey {
101 raw: sk_scalar,
102 bytes: sk_bytes,
103 };
104 let public_key = EcdsaP224PublicKey(pk_point.serialize_uncompressed());
105 Ok((public_key, secret_key))
106 }
107
108 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
109 keypair.0.clone()
110 }
111 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
112 keypair.1.clone()
113 }
114
115 fn sign(message: &[u8], secret_key: &Self::SecretKey) -> ApiResult<Self::SignatureData> {
116 let mut hasher = Sha224::new();
117 hasher.update(message).map_err(ApiError::from)?;
118 let hash_output = hasher.finalize().map_err(ApiError::from)?;
119
120 let mut z_bytes_fixed_size = [0u8; ec::P224_SCALAR_SIZE];
121 z_bytes_fixed_size.copy_from_slice(hash_output.as_ref());
122 let z = reduce_bytes_to_scalar_p224(&z_bytes_fixed_size)?;
123
124 let d = secret_key.raw.clone();
125 let mut rng = rand::thread_rng();
126
127 loop {
128 let k = deterministic_k_hedged_p224(&d, &z, &mut rng);
129
130 let kg = ec::scalar_mult_base_g(&k).map_err(ApiError::from)?;
131
132 if kg.is_identity() {
133 continue;
134 }
135 let r_bytes = kg.x_coordinate_bytes();
136
137 let r = match reduce_bytes_to_scalar_p224(&r_bytes) {
138 Ok(scalar) if !scalar.is_zero() => scalar,
139 _ => continue,
140 };
141
142 let k_inv = k.inv_mod_n().map_err(ApiError::from)?;
143 let rd = r.mul_mod_n(&d).map_err(ApiError::from)?;
144 let z_plus_rd = z.add_mod_n(&rd).map_err(ApiError::from)?;
145 let s = k_inv.mul_mod_n(&z_plus_rd).map_err(ApiError::from)?;
146
147 if s.is_zero() {
148 continue;
149 }
150
151 let sig_comps = SignatureComponents {
152 r: r.serialize().to_vec(),
153 s: s.serialize().to_vec(),
154 };
155 return Ok(EcdsaP224Signature(sig_comps.to_der()));
156 }
157 }
158
159 fn verify(
160 message: &[u8],
161 signature: &Self::SignatureData,
162 public_key: &Self::PublicKey,
163 ) -> ApiResult<()> {
164 let sig_comps = SignatureComponents::from_der(&signature.0)?;
165
166 if sig_comps.r.len() > ec::P224_SCALAR_SIZE || sig_comps.s.len() > ec::P224_SCALAR_SIZE {
167 return Err(ApiError::InvalidSignature {
168 context: "ECDSA-P224 verify",
169 #[cfg(feature = "std")]
170 message: "Invalid signature component size".to_string(),
171 });
172 }
173
174 let mut r_bytes = [0u8; ec::P224_SCALAR_SIZE];
175 let mut s_bytes = [0u8; ec::P224_SCALAR_SIZE];
176 let r_offset = ec::P224_SCALAR_SIZE.saturating_sub(sig_comps.r.len());
177 let s_offset = ec::P224_SCALAR_SIZE.saturating_sub(sig_comps.s.len());
178 r_bytes[r_offset..].copy_from_slice(&sig_comps.r);
179 s_bytes[s_offset..].copy_from_slice(&sig_comps.s);
180
181 let r = ec::Scalar::new(r_bytes).map_err(|_| ApiError::InvalidSignature {
182 context: "ECDSA-P224 verify",
183 #[cfg(feature = "std")]
184 message: "Invalid r component".to_string(),
185 })?;
186 let s = ec::Scalar::new(s_bytes).map_err(|_| ApiError::InvalidSignature {
187 context: "ECDSA-P224 verify",
188 #[cfg(feature = "std")]
189 message: "Invalid s component".to_string(),
190 })?;
191
192 let mut hasher = Sha224::new();
193 hasher.update(message).map_err(ApiError::from)?;
194 let hash_output = hasher.finalize().map_err(ApiError::from)?;
195
196 let mut z_bytes_fixed_size = [0u8; ec::P224_SCALAR_SIZE];
197 z_bytes_fixed_size.copy_from_slice(hash_output.as_ref());
198 let z = reduce_bytes_to_scalar_p224(&z_bytes_fixed_size)?;
199
200 let s_inv = s.inv_mod_n().map_err(ApiError::from)?;
201 let u1 = z.mul_mod_n(&s_inv).map_err(ApiError::from)?;
202 let u2 = r.mul_mod_n(&s_inv).map_err(ApiError::from)?;
203
204 let q_point = ec::Point::deserialize_uncompressed(&public_key.0).map_err(ApiError::from)?;
205
206 if q_point.is_identity() {
207 return Err(ApiError::InvalidKey {
208 context: "ECDSA-P224 verify",
209 #[cfg(feature = "std")]
210 message: "Public key is the point at infinity".to_string(),
211 });
212 }
213
214 let u1g = ec::scalar_mult_base_g(&u1).map_err(ApiError::from)?;
215 let u2q = ec::scalar_mult(&u2, &q_point).map_err(ApiError::from)?;
216
217 let point = u1g.add(&u2q);
218
219 if point.is_identity() {
220 return Err(ApiError::InvalidSignature {
221 context: "ECDSA-P224 verify",
222 #[cfg(feature = "std")]
223 message: "Verification point is identity".to_string(),
224 });
225 }
226
227 let x1_bytes = point.x_coordinate_bytes();
228 let v = reduce_bytes_to_scalar_p224(&x1_bytes)?;
229
230 if !ct_eq(r.serialize(), v.serialize()) {
231 return Err(ApiError::InvalidSignature {
232 context: "ECDSA-P224 verify",
233 #[cfg(feature = "std")]
234 message: "Signature verification failed (r != v)".to_string(),
235 });
236 }
237 Ok(())
238 }
239}
240
241fn deterministic_k_hedged_p224<R: RngCore + CryptoRng>(
242 d: &ec::Scalar,
243 z: &ec::Scalar,
244 rng: &mut R,
245) -> ec::Scalar {
246 use zeroize::Zeroize;
247 let hash_len = Sha224Algorithm::OUTPUT_SIZE; let mut rbuf = [0u8; ec::P224_SCALAR_SIZE];
250 rng.fill_bytes(&mut rbuf[..hash_len]);
251
252 let mut v_hmac_block = [0x01u8; Sha224Algorithm::BLOCK_SIZE];
253 let mut k_hmac_block = [0x00u8; Sha224Algorithm::BLOCK_SIZE];
254
255 {
257 let mut mac = Hmac::<Sha224>::new(&k_hmac_block).unwrap();
258 mac.update(&v_hmac_block).unwrap();
259 mac.update(&[0x00]).unwrap();
260 mac.update(&d.serialize()).unwrap();
261 mac.update(&z.serialize()).unwrap();
262 mac.update(&rbuf[..hash_len]).unwrap();
263 let mac_res = mac.finalize().unwrap();
264 k_hmac_block[..hash_len].copy_from_slice(&mac_res);
265 k_hmac_block[hash_len..].fill(0);
266 }
267 let v_new_res = Hmac::<Sha224>::mac(&k_hmac_block, &v_hmac_block).unwrap();
269 v_hmac_block[..hash_len].copy_from_slice(&v_new_res);
270 v_hmac_block[hash_len..].fill(0);
271 {
273 let mut mac = Hmac::<Sha224>::new(&k_hmac_block).unwrap();
274 mac.update(&v_hmac_block).unwrap();
275 mac.update(&[0x01]).unwrap();
276 mac.update(&d.serialize()).unwrap();
277 mac.update(&z.serialize()).unwrap();
278 mac.update(&rbuf[..hash_len]).unwrap();
279 let mac_res = mac.finalize().unwrap();
280 k_hmac_block[..hash_len].copy_from_slice(&mac_res);
281 k_hmac_block[hash_len..].fill(0);
282 }
283 let v_new_res = Hmac::<Sha224>::mac(&k_hmac_block, &v_hmac_block).unwrap();
285 v_hmac_block[..hash_len].copy_from_slice(&v_new_res);
286 v_hmac_block[hash_len..].fill(0);
287
288 loop {
290 let v_new_res = Hmac::<Sha224>::mac(&k_hmac_block, &v_hmac_block).unwrap();
291 v_hmac_block[..hash_len].copy_from_slice(&v_new_res);
292 v_hmac_block[hash_len..].fill(0);
293
294 let mut candidate_scalar_bytes = [0u8; ec::P224_SCALAR_SIZE];
295 candidate_scalar_bytes.copy_from_slice(&v_hmac_block[..ec::P224_SCALAR_SIZE]);
296
297 if let Ok(candidate) = ec::Scalar::new(candidate_scalar_bytes) {
299 rbuf.zeroize();
301 return candidate;
302 }
303
304 let mut mac = Hmac::<Sha224>::new(&k_hmac_block).unwrap();
306 mac.update(&v_hmac_block).unwrap();
307 mac.update(&[0x00]).unwrap();
308 let mac_res = mac.finalize().unwrap();
309 k_hmac_block[..hash_len].copy_from_slice(&mac_res);
310 k_hmac_block[hash_len..].fill(0);
311
312 let v_new_res = Hmac::<Sha224>::mac(&k_hmac_block, &v_hmac_block).unwrap();
313 v_hmac_block[..hash_len].copy_from_slice(&v_new_res);
314 v_hmac_block[hash_len..].fill(0);
315 }
316}
317
318fn reduce_bytes_to_scalar_p224(bytes: &[u8; ec::P224_SCALAR_SIZE]) -> ApiResult<ec::Scalar> {
319 ec::Scalar::new(*bytes).map_err(|algo_err| {
320 match algo_err {
321 dcrypt_algorithms::error::Error::Parameter {
322 ref name,
323 ref reason,
324 } if name.as_ref() == "P-224 Scalar"
325 && reason.as_ref().contains("Scalar cannot be zero") =>
326 {
327 ApiError::InvalidSignature {
328 context: "ECDSA-P224 scalar reduction",
329 #[cfg(feature = "std")]
330 message: "Computed scalar component is zero or invalid".to_string(),
331 }
332 }
333 _ => ApiError::from(algo_err),
335 }
336 })
337}
338
339#[cfg(test)]
340mod tests;