dcrypt_sign/ecdsa/p384/
mod.rs1use crate::ecdsa::common::{is_canonical_nonzero_scalar, is_high_s, Rfc6979, SignatureComponents};
8use alloc::vec::Vec;
9use dcrypt_algorithms::ec::p384 as ec;
10use dcrypt_algorithms::hash::sha2::Sha384;
11use dcrypt_algorithms::hash::HashFunction;
12use dcrypt_api::{
13 error::Error as ApiError, Result as ApiResult, Signature as SignatureTrait, ZeroizingBytes,
14};
15use dcrypt_common::SecretBuffer;
16use dcrypt_internal::{
17 constant_time::ct_eq, zeroizing_bytes_from_slice, CryptoRng, RngCore, Zeroize, ZeroizeOnDrop,
18 Zeroizing,
19};
20use dcrypt_params::traditional::ecdsa::NIST_P384;
21
22pub struct EcdsaP384;
26
27#[derive(Clone)]
31pub struct EcdsaP384PublicKey(pub(crate) [u8; ec::P384_POINT_UNCOMPRESSED_SIZE]);
32
33#[derive(Clone)]
39pub struct EcdsaP384SecretKey {
40 raw: ec::Scalar,
41 bytes: SecretBuffer<{ ec::P384_SCALAR_SIZE }>,
42}
43
44impl Zeroize for EcdsaP384SecretKey {
46 fn zeroize(&mut self) {
47 self.raw.zeroize();
48 self.bytes.zeroize();
50 }
51}
52
53impl Drop for EcdsaP384SecretKey {
55 fn drop(&mut self) {
56 self.zeroize();
57 }
58}
59
60impl ZeroizeOnDrop for EcdsaP384SecretKey {}
61
62#[derive(Clone)]
66pub struct EcdsaP384Signature(pub(crate) Vec<u8>);
67
68impl AsRef<[u8]> for EcdsaP384PublicKey {
70 fn as_ref(&self) -> &[u8] {
71 &self.0
72 }
73}
74
75impl AsMut<[u8]> for EcdsaP384PublicKey {
76 fn as_mut(&mut self) -> &mut [u8] {
77 &mut self.0
78 }
79}
80
81impl AsRef<[u8]> for EcdsaP384SecretKey {
82 fn as_ref(&self) -> &[u8] {
83 self.bytes.as_ref()
84 }
85}
86
87impl AsRef<[u8]> for EcdsaP384Signature {
93 fn as_ref(&self) -> &[u8] {
94 &self.0
95 }
96}
97
98impl AsMut<[u8]> for EcdsaP384Signature {
99 fn as_mut(&mut self) -> &mut [u8] {
100 &mut self.0
101 }
102}
103
104impl EcdsaP384PublicKey {
105 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
107 let point = ec::Point::deserialize_uncompressed(bytes).map_err(ApiError::from)?;
108 if point.is_identity() {
109 return Err(ApiError::InvalidParameter {
110 context: "ECDSA-P384 public key",
111 #[cfg(feature = "std")]
112 message: "Identity is not a valid ECDSA public key".to_string(),
113 });
114 }
115 Ok(Self(point.serialize_uncompressed()))
116 }
117
118 pub fn to_bytes(&self) -> &[u8] {
120 &self.0
121 }
122}
123
124impl EcdsaP384SecretKey {
125 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
127 let raw = ec::Scalar::deserialize(bytes).map_err(ApiError::from)?;
128 let serialized = raw.serialize();
129 Ok(Self {
130 raw,
131 bytes: serialized,
132 })
133 }
134
135 pub fn to_bytes_zeroizing(&self) -> ZeroizingBytes {
137 zeroizing_bytes_from_slice(self.bytes.as_ref())
138 }
139}
140
141impl EcdsaP384Signature {
142 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
144 SignatureComponents::from_der(bytes)?;
145 Ok(Self(bytes.to_vec()))
146 }
147
148 pub fn to_bytes(&self) -> &[u8] {
150 &self.0
151 }
152}
153
154impl SignatureTrait for EcdsaP384 {
155 type PublicKey = EcdsaP384PublicKey;
156 type SecretKey = EcdsaP384SecretKey;
157 type SignatureData = EcdsaP384Signature;
158 type KeyPair = (Self::PublicKey, Self::SecretKey);
159
160 fn name() -> &'static str {
161 "ECDSA-P384"
162 }
163
164 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
171 let (sk_scalar, pk_point) = ec::generate_keypair(rng).map_err(ApiError::from)?;
173
174 let sk_bytes = sk_scalar.serialize();
176
177 if sk_bytes.iter().all(|&b| b == 0) {
179 return Err(ApiError::InvalidParameter {
180 context: "ECDSA-P384 keypair",
181 #[cfg(feature = "std")]
182 message: "Generated secret key is zero (internal error)".to_string(),
183 });
184 }
185
186 let secret_key = EcdsaP384SecretKey {
188 raw: sk_scalar,
189 bytes: sk_bytes,
190 };
191
192 let public_key = EcdsaP384PublicKey(pk_point.serialize_uncompressed());
194
195 Ok((public_key, secret_key))
196 }
197
198 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
199 keypair.0.clone()
200 }
201
202 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
203 keypair.1.clone()
204 }
205
206 fn sign(message: &[u8], secret_key: &Self::SecretKey) -> ApiResult<Self::SignatureData> {
221 let mut hasher = Sha384::new();
223 hasher.update(message).map_err(ApiError::from)?;
224 let hash_output = hasher.finalize().map_err(ApiError::from)?;
225
226 let mut h_bytes = [0u8; ec::P384_SCALAR_SIZE];
229 h_bytes.copy_from_slice(hash_output.as_ref());
230 let z = reduce_bytes_to_scalar(&h_bytes)?;
231
232 let d = secret_key.raw.clone();
234 let mut d_bytes = d.serialize();
235 let nonces_result =
236 Rfc6979::<Sha384>::new(d_bytes.as_ref(), hash_output.as_ref(), &NIST_P384.n, 384);
237 d_bytes.zeroize();
238 let mut nonces = nonces_result?;
239
240 loop {
241 let mut nonce = nonces.next_nonce()?;
242 let mut nonce_bytes: [u8; ec::P384_SCALAR_SIZE] =
243 (&nonce[..])
244 .try_into()
245 .map_err(|_| ApiError::InvalidLength {
246 context: "ECDSA-P384 nonce",
247 expected: ec::P384_SCALAR_SIZE,
248 actual: nonce.len(),
249 })?;
250 nonce.zeroize();
251 let scalar = ec::Scalar::new(nonce_bytes).map_err(ApiError::from);
252 nonce_bytes.zeroize();
253 let k = scalar?;
254
255 let kg = ec::scalar_mult_base_g(&k).map_err(ApiError::from)?;
257 let r_bytes = Zeroizing::new(kg.x_coordinate_bytes());
258
259 let r = reduce_bytes_to_scalar(&r_bytes)?;
261 if r.is_zero() {
262 continue;
263 }
264
265 let k_inv = k.inv_mod_n().map_err(ApiError::from)?;
267
268 let rd = r.mul_mod_n(&d).map_err(ApiError::from)?;
270
271 let z_plus_rd = z.add_mod_n(&rd).map_err(ApiError::from)?;
272
273 let mut s = k_inv.mul_mod_n(&z_plus_rd).map_err(ApiError::from)?;
274
275 if s.is_zero() {
277 continue;
278 }
279 if is_high_s(s.serialize().as_ref(), &NIST_P384.n) {
280 s = s.negate();
281 }
282
283 let sig = SignatureComponents {
285 r: r.serialize().to_vec(),
286 s: s.serialize().to_vec(),
287 };
288
289 let der_sig = sig.to_der();
291
292 return Ok(EcdsaP384Signature(der_sig));
293 }
294 }
295
296 fn verify(
312 message: &[u8],
313 signature: &Self::SignatureData,
314 public_key: &Self::PublicKey,
315 ) -> ApiResult<()> {
316 let sig = SignatureComponents::from_der(&signature.0)?;
318
319 if sig.r.len() > ec::P384_SCALAR_SIZE || sig.s.len() > ec::P384_SCALAR_SIZE {
321 return Err(ApiError::InvalidSignature {
322 context: "ECDSA-P384 verify",
323 #[cfg(feature = "std")]
324 message: "Invalid signature component size".to_string(),
325 });
326 }
327
328 let mut r_bytes = [0u8; ec::P384_SCALAR_SIZE];
330 let mut s_bytes = [0u8; ec::P384_SCALAR_SIZE];
331 r_bytes[ec::P384_SCALAR_SIZE - sig.r.len()..].copy_from_slice(&sig.r);
332 s_bytes[ec::P384_SCALAR_SIZE - sig.s.len()..].copy_from_slice(&sig.s);
333
334 if !is_canonical_nonzero_scalar(&r_bytes, &NIST_P384.n)
335 || !is_canonical_nonzero_scalar(&s_bytes, &NIST_P384.n)
336 {
337 return Err(ApiError::InvalidSignature {
338 context: "ECDSA-P384 verify",
339 #[cfg(feature = "std")]
340 message: "signature components must be canonical integers in [1, n-1]".to_string(),
341 });
342 }
343
344 let r = ec::Scalar::new(r_bytes).map_err(|_| ApiError::InvalidSignature {
345 context: "ECDSA-P384 verify",
346 #[cfg(feature = "std")]
347 message: "Invalid r component".to_string(),
348 })?;
349
350 let s = ec::Scalar::new(s_bytes).map_err(|_| ApiError::InvalidSignature {
351 context: "ECDSA-P384 verify",
352 #[cfg(feature = "std")]
353 message: "Invalid s component".to_string(),
354 })?;
355 if is_high_s(s.serialize().as_ref(), &NIST_P384.n) {
356 return Err(ApiError::InvalidSignature {
357 context: "ECDSA-P384 verify",
358 #[cfg(feature = "std")]
359 message: "high-s signatures are non-canonical".to_string(),
360 });
361 }
362
363 let mut hasher = Sha384::new();
365 hasher.update(message).map_err(ApiError::from)?;
366 let hash_output = hasher.finalize().map_err(ApiError::from)?;
367
368 let mut h_bytes = [0u8; ec::P384_SCALAR_SIZE];
370 h_bytes.copy_from_slice(hash_output.as_ref());
371 let z = reduce_bytes_to_scalar(&h_bytes)?;
372
373 let s_inv = s.inv_mod_n().map_err(ApiError::from)?;
375
376 let u1 = z.mul_mod_n(&s_inv).map_err(ApiError::from)?;
378 let u2 = r.mul_mod_n(&s_inv).map_err(ApiError::from)?;
379
380 let q = ec::Point::deserialize_uncompressed(&public_key.0).map_err(ApiError::from)?;
382
383 let u1g = ec::scalar_mult_base_g(&u1).map_err(ApiError::from)?;
385
386 let u2q = ec::scalar_mult(&u2, &q).map_err(ApiError::from)?;
387
388 let point = u1g.add(&u2q);
389
390 if point.is_identity() {
392 return Err(ApiError::InvalidSignature {
393 context: "ECDSA-P384 verify",
394 #[cfg(feature = "std")]
395 message: "Invalid signature: verification point is identity".to_string(),
396 });
397 }
398
399 let x1_bytes = point.x_coordinate_bytes();
401 let x1 = reduce_bytes_to_scalar(&x1_bytes)?;
402
403 if !ct_eq(r.serialize(), x1.serialize()) {
405 return Err(ApiError::InvalidSignature {
406 context: "ECDSA-P384 verify",
407 #[cfg(feature = "std")]
408 message: "Signature verification failed".to_string(),
409 });
410 }
411
412 Ok(())
413 }
414}
415
416fn reduce_bytes_to_scalar(bytes: &[u8; 48]) -> ApiResult<ec::Scalar> {
417 Ok(ec::Scalar::from_bytes_reduced(*bytes))
418}
419
420#[cfg(test)]
421mod tests;