dcrypt_sign/ecdsa/p256/
mod.rs1use crate::ecdsa::common::SignatureComponents;
8use dcrypt_algorithms::ec::p256 as ec;
9use dcrypt_algorithms::hash::sha2::Sha256;
10use dcrypt_algorithms::hash::HashFunction;
11use 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;
16
17pub struct EcdsaP256;
21
22#[derive(Clone, Zeroize)]
26pub struct EcdsaP256PublicKey(pub [u8; ec::P256_POINT_UNCOMPRESSED_SIZE]);
27
28#[derive(Clone)]
34pub struct EcdsaP256SecretKey {
35 raw: ec::Scalar,
36 bytes: [u8; ec::P256_SCALAR_SIZE],
37}
38
39impl Zeroize for EcdsaP256SecretKey {
41 fn zeroize(&mut self) {
42 self.bytes.zeroize();
44 }
47}
48
49impl Drop for EcdsaP256SecretKey {
51 fn drop(&mut self) {
52 self.zeroize();
53 }
54}
55
56#[derive(Clone)]
60pub struct EcdsaP256Signature(pub Vec<u8>);
61
62impl AsRef<[u8]> for EcdsaP256PublicKey {
64 fn as_ref(&self) -> &[u8] {
65 &self.0
66 }
67}
68
69impl AsMut<[u8]> for EcdsaP256PublicKey {
70 fn as_mut(&mut self) -> &mut [u8] {
71 &mut self.0
72 }
73}
74
75impl AsRef<[u8]> for EcdsaP256SecretKey {
76 fn as_ref(&self) -> &[u8] {
77 &self.bytes
78 }
79}
80
81impl AsRef<[u8]> for EcdsaP256Signature {
87 fn as_ref(&self) -> &[u8] {
88 &self.0
89 }
90}
91
92impl AsMut<[u8]> for EcdsaP256Signature {
93 fn as_mut(&mut self) -> &mut [u8] {
94 &mut self.0
95 }
96}
97
98impl SignatureTrait for EcdsaP256 {
99 type PublicKey = EcdsaP256PublicKey;
100 type SecretKey = EcdsaP256SecretKey;
101 type SignatureData = EcdsaP256Signature;
102 type KeyPair = (Self::PublicKey, Self::SecretKey);
103
104 fn name() -> &'static str {
105 "ECDSA-P256"
106 }
107
108 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
115 let (sk_scalar, pk_point) = ec::generate_keypair(rng).map_err(ApiError::from)?;
117
118 let sk_bytes: [u8; ec::P256_SCALAR_SIZE] = sk_scalar.serialize();
120
121 if sk_bytes.iter().all(|&b| b == 0) {
123 return Err(ApiError::InvalidParameter {
124 context: "ECDSA-P256 keypair",
125 #[cfg(feature = "std")]
126 message: "Generated secret key is zero (internal error)".to_string(),
127 });
128 }
129
130 let secret_key = EcdsaP256SecretKey {
132 raw: sk_scalar,
133 bytes: sk_bytes,
134 };
135
136 let public_key = EcdsaP256PublicKey(pk_point.serialize_uncompressed());
138
139 Ok((public_key, secret_key))
140 }
141
142 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
143 keypair.0.clone()
144 }
145
146 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
147 keypair.1.clone()
148 }
149
150 fn sign(message: &[u8], secret_key: &Self::SecretKey) -> ApiResult<Self::SignatureData> {
165 let mut hasher = Sha256::new();
167 hasher.update(message).map_err(ApiError::from)?;
168 let hash_output = hasher.finalize().map_err(ApiError::from)?;
169
170 let mut z_bytes = [0u8; ec::P256_SCALAR_SIZE];
173 z_bytes.copy_from_slice(hash_output.as_ref());
174 let z = ec::Scalar::new(z_bytes).map_err(|e| ApiError::InvalidParameter {
175 context: "ECDSA-P256 sign",
176 #[cfg(feature = "std")]
177 message: format!("Hash to scalar conversion failed: {:?}", e),
178 })?;
179
180 let d = secret_key.raw.clone();
182
183 let mut rng = rand::thread_rng();
185
186 loop {
187 let k = deterministic_k_hedged(&d, &z, &mut rng);
189
190 let kg = ec::scalar_mult_base_g(&k).map_err(ApiError::from)?;
192 let r_bytes = kg.x_coordinate_bytes();
193
194 let r = match ec::Scalar::new(r_bytes) {
196 Ok(scalar) => scalar,
197 Err(_) => continue, };
199
200 let k_inv = k.inv_mod_n().map_err(ApiError::from)?;
202
203 let rd = r.mul_mod_n(&d).map_err(ApiError::from)?;
205
206 let z_plus_rd = z.add_mod_n(&rd).map_err(ApiError::from)?;
207
208 let s = k_inv.mul_mod_n(&z_plus_rd).map_err(ApiError::from)?;
209
210 if s.is_zero() {
212 continue;
213 }
214
215 let sig = SignatureComponents {
217 r: r.serialize().to_vec(),
218 s: s.serialize().to_vec(),
219 };
220
221 let der_sig = sig.to_der();
223
224 return Ok(EcdsaP256Signature(der_sig));
225 }
226 }
227
228 fn verify(
244 message: &[u8],
245 signature: &Self::SignatureData,
246 public_key: &Self::PublicKey,
247 ) -> ApiResult<()> {
248 let sig = SignatureComponents::from_der(&signature.0)?;
250
251 if sig.r.len() > ec::P256_SCALAR_SIZE || sig.s.len() > ec::P256_SCALAR_SIZE {
253 return Err(ApiError::InvalidSignature {
254 context: "ECDSA-P256 verify",
255 #[cfg(feature = "std")]
256 message: "Invalid signature component size".to_string(),
257 });
258 }
259
260 let mut r_bytes = [0u8; ec::P256_SCALAR_SIZE];
262 let mut s_bytes = [0u8; ec::P256_SCALAR_SIZE];
263 r_bytes[ec::P256_SCALAR_SIZE - sig.r.len()..].copy_from_slice(&sig.r);
264 s_bytes[ec::P256_SCALAR_SIZE - sig.s.len()..].copy_from_slice(&sig.s);
265
266 let r = ec::Scalar::new(r_bytes).map_err(|_| ApiError::InvalidSignature {
267 context: "ECDSA-P256 verify",
268 #[cfg(feature = "std")]
269 message: "Invalid r component".to_string(),
270 })?;
271
272 let s = ec::Scalar::new(s_bytes).map_err(|_| ApiError::InvalidSignature {
273 context: "ECDSA-P256 verify",
274 #[cfg(feature = "std")]
275 message: "Invalid s component".to_string(),
276 })?;
277
278 let mut hasher = Sha256::new();
280 hasher.update(message).map_err(ApiError::from)?;
281 let hash_output = hasher.finalize().map_err(ApiError::from)?;
282
283 let mut z_bytes = [0u8; ec::P256_SCALAR_SIZE];
285 z_bytes.copy_from_slice(hash_output.as_ref());
286 let z = ec::Scalar::new(z_bytes).map_err(|e| ApiError::InvalidSignature {
287 context: "ECDSA-P256 verify",
288 #[cfg(feature = "std")]
289 message: format!("Hash to scalar conversion failed: {:?}", e),
290 })?;
291
292 let s_inv = s.inv_mod_n().map_err(ApiError::from)?;
294
295 let u1 = z.mul_mod_n(&s_inv).map_err(ApiError::from)?;
297 let u2 = r.mul_mod_n(&s_inv).map_err(ApiError::from)?;
298
299 let q = ec::Point::deserialize_uncompressed(&public_key.0).map_err(ApiError::from)?;
301
302 let u1g = ec::scalar_mult_base_g(&u1).map_err(ApiError::from)?;
304
305 let u2q = ec::scalar_mult(&u2, &q).map_err(ApiError::from)?;
306
307 let point = u1g.add(&u2q);
308
309 if point.is_identity() {
311 return Err(ApiError::InvalidSignature {
312 context: "ECDSA-P256 verify",
313 #[cfg(feature = "std")]
314 message: "Invalid signature: verification point is identity".to_string(),
315 });
316 }
317
318 let x1_bytes = point.x_coordinate_bytes();
320 let x1 = ec::Scalar::new(x1_bytes).map_err(|_| ApiError::InvalidSignature {
321 context: "ECDSA-P256 verify",
322 #[cfg(feature = "std")]
323 message: "Recovered X coordinate out of range".to_string(),
324 })?;
325
326 if !ct_eq(r.serialize(), x1.serialize()) {
328 return Err(ApiError::InvalidSignature {
329 context: "ECDSA-P256 verify",
330 #[cfg(feature = "std")]
331 message: "Signature verification failed".to_string(),
332 });
333 }
334
335 Ok(())
336 }
337}
338
339fn deterministic_k_hedged<R: RngCore + CryptoRng>(
350 d: &ec::Scalar,
351 z: &ec::Scalar,
352 rng: &mut R,
353) -> ec::Scalar {
354 use zeroize::Zeroize;
355
356 let mut rbuf = [0u8; 32];
357 rng.fill_bytes(&mut rbuf); let mut v = [0x01u8; 32];
360 let mut k = [0x00u8; 32];
361
362 {
365 let mut mac = Hmac::<Sha256>::new(&k).unwrap();
366 mac.update(&v).unwrap();
367 mac.update(&[0x00]).unwrap();
368 mac.update(&d.serialize()).unwrap();
369 mac.update(&z.serialize()).unwrap();
370 mac.update(&rbuf).unwrap();
371 k.copy_from_slice(&mac.finalize().unwrap());
372 }
373
374 let v_new = Hmac::<Sha256>::mac(&k, &v).unwrap();
377 v.copy_from_slice(&v_new);
378
379 {
382 let mut mac = Hmac::<Sha256>::new(&k).unwrap();
383 mac.update(&v).unwrap();
384 mac.update(&[0x01]).unwrap();
385 mac.update(&d.serialize()).unwrap();
386 mac.update(&z.serialize()).unwrap();
387 mac.update(&rbuf).unwrap();
388 k.copy_from_slice(&mac.finalize().unwrap());
389 }
390
391 let v_new = Hmac::<Sha256>::mac(&k, &v).unwrap();
394 v.copy_from_slice(&v_new);
395
396 loop {
399 let v_new = Hmac::<Sha256>::mac(&k, &v).unwrap();
400 v.copy_from_slice(&v_new);
401 if let Ok(candidate) = ec::Scalar::new(v) {
402 if !candidate.is_zero() {
403 rbuf.zeroize(); return candidate;
405 }
406 }
407
408 let mut mac = Hmac::<Sha256>::new(&k).unwrap();
410 mac.update(&v).unwrap();
411 mac.update(&[0x00]).unwrap();
412 k.copy_from_slice(&mac.finalize().unwrap());
413 let v_new = Hmac::<Sha256>::mac(&k, &v).unwrap();
414 v.copy_from_slice(&v_new);
415 }
416}
417
418#[cfg(test)]
419mod tests;