dcrypt_sign/ecdsa/p521/
mod.rs1use crate::ecdsa::common::{is_canonical_nonzero_scalar, is_high_s, Rfc6979, SignatureComponents};
8use alloc::vec::Vec;
9use dcrypt_algorithms::ec::p521 as ec;
10use dcrypt_algorithms::hash::sha2::Sha512;
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_P521;
21
22pub struct EcdsaP521;
27
28#[derive(Clone)]
32pub struct EcdsaP521PublicKey(pub(crate) [u8; ec::P521_POINT_UNCOMPRESSED_SIZE]);
33
34#[derive(Clone)]
40pub struct EcdsaP521SecretKey {
41 raw: ec::Scalar,
42 bytes: SecretBuffer<{ ec::P521_SCALAR_SIZE }>,
43}
44
45impl Zeroize for EcdsaP521SecretKey {
47 fn zeroize(&mut self) {
48 self.raw.zeroize();
49 self.bytes.zeroize();
51 }
52}
53
54impl Drop for EcdsaP521SecretKey {
56 fn drop(&mut self) {
57 self.zeroize();
58 }
59}
60
61impl ZeroizeOnDrop for EcdsaP521SecretKey {}
62
63#[derive(Clone)]
67pub struct EcdsaP521Signature(pub(crate) Vec<u8>);
68
69impl AsRef<[u8]> for EcdsaP521PublicKey {
71 fn as_ref(&self) -> &[u8] {
72 &self.0
73 }
74}
75
76impl AsMut<[u8]> for EcdsaP521PublicKey {
77 fn as_mut(&mut self) -> &mut [u8] {
78 &mut self.0
79 }
80}
81
82impl AsRef<[u8]> for EcdsaP521SecretKey {
83 fn as_ref(&self) -> &[u8] {
84 self.bytes.as_ref()
85 }
86}
87
88impl AsRef<[u8]> for EcdsaP521Signature {
94 fn as_ref(&self) -> &[u8] {
95 &self.0
96 }
97}
98
99impl AsMut<[u8]> for EcdsaP521Signature {
100 fn as_mut(&mut self) -> &mut [u8] {
101 &mut self.0
102 }
103}
104
105impl EcdsaP521PublicKey {
106 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
108 let point = ec::Point::deserialize_uncompressed(bytes).map_err(ApiError::from)?;
109 if point.is_identity() {
110 return Err(ApiError::InvalidParameter {
111 context: "ECDSA-P521 public key",
112 #[cfg(feature = "std")]
113 message: "Identity is not a valid ECDSA public key".to_string(),
114 });
115 }
116 Ok(Self(point.serialize_uncompressed()))
117 }
118
119 pub fn to_bytes(&self) -> &[u8] {
121 &self.0
122 }
123}
124
125impl EcdsaP521SecretKey {
126 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
128 let raw = ec::Scalar::deserialize(bytes).map_err(ApiError::from)?;
129 let serialized = raw.serialize();
130 Ok(Self {
131 raw,
132 bytes: serialized,
133 })
134 }
135
136 pub fn to_bytes_zeroizing(&self) -> ZeroizingBytes {
138 zeroizing_bytes_from_slice(self.bytes.as_ref())
139 }
140}
141
142impl EcdsaP521Signature {
143 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
145 SignatureComponents::from_der(bytes)?;
146 Ok(Self(bytes.to_vec()))
147 }
148
149 pub fn to_bytes(&self) -> &[u8] {
151 &self.0
152 }
153}
154
155impl SignatureTrait for EcdsaP521 {
156 type PublicKey = EcdsaP521PublicKey;
157 type SecretKey = EcdsaP521SecretKey;
158 type SignatureData = EcdsaP521Signature;
159 type KeyPair = (Self::PublicKey, Self::SecretKey);
160
161 fn name() -> &'static str {
162 "ECDSA-P521"
163 }
164
165 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
172 let (sk_scalar, pk_point) = ec::generate_keypair(rng).map_err(ApiError::from)?;
174
175 let sk_bytes = sk_scalar.serialize();
177
178 if sk_bytes.iter().all(|&b| b == 0) {
180 return Err(ApiError::InvalidParameter {
181 context: "ECDSA-P521 keypair",
182 #[cfg(feature = "std")]
183 message: "Generated secret key is zero (internal error)".to_string(),
184 });
185 }
186
187 let secret_key = EcdsaP521SecretKey {
189 raw: sk_scalar,
190 bytes: sk_bytes,
191 };
192
193 let public_key = EcdsaP521PublicKey(pk_point.serialize_uncompressed());
195
196 Ok((public_key, secret_key))
197 }
198
199 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
200 keypair.0.clone()
201 }
202
203 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
204 keypair.1.clone()
205 }
206
207 fn sign(message: &[u8], secret_key: &Self::SecretKey) -> ApiResult<Self::SignatureData> {
222 let mut hasher = Sha512::new();
224 hasher.update(message).map_err(ApiError::from)?;
225 let hash_output = hasher.finalize().map_err(ApiError::from)?;
226
227 let mut z_bytes = [0u8; ec::P521_SCALAR_SIZE];
231 z_bytes[ec::P521_SCALAR_SIZE - 64..].copy_from_slice(hash_output.as_ref());
233 let z = reduce_bytes_to_scalar(&z_bytes)?;
234
235 let d = secret_key.raw.clone();
237 let mut d_bytes = d.serialize();
238 let nonces_result =
239 Rfc6979::<Sha512>::new(d_bytes.as_ref(), hash_output.as_ref(), &NIST_P521.n, 521);
240 d_bytes.zeroize();
241 let mut nonces = nonces_result?;
242
243 loop {
244 let mut nonce = nonces.next_nonce()?;
245 let mut nonce_bytes: [u8; ec::P521_SCALAR_SIZE] =
246 (&nonce[..])
247 .try_into()
248 .map_err(|_| ApiError::InvalidLength {
249 context: "ECDSA-P521 nonce",
250 expected: ec::P521_SCALAR_SIZE,
251 actual: nonce.len(),
252 })?;
253 nonce.zeroize();
254 let scalar = ec::Scalar::new(nonce_bytes).map_err(ApiError::from);
255 nonce_bytes.zeroize();
256 let k = scalar?;
257
258 let kg = ec::scalar_mult_base_g(&k).map_err(ApiError::from)?;
260 let r_bytes = Zeroizing::new(kg.x_coordinate_bytes());
261
262 let r = reduce_bytes_to_scalar(&r_bytes)?;
264 if r.is_zero() {
265 continue;
266 }
267
268 let k_inv = k.inv_mod_n().map_err(ApiError::from)?;
270
271 let rd = r.mul_mod_n(&d).map_err(ApiError::from)?;
273
274 let z_plus_rd = z.add_mod_n(&rd).map_err(ApiError::from)?;
275
276 let mut s = k_inv.mul_mod_n(&z_plus_rd).map_err(ApiError::from)?;
277
278 if s.is_zero() {
280 continue;
281 }
282 if is_high_s(s.serialize().as_ref(), &NIST_P521.n) {
283 s = s.negate();
284 }
285
286 let sig = SignatureComponents {
288 r: r.serialize().to_vec(),
289 s: s.serialize().to_vec(),
290 };
291
292 let der_sig = sig.to_der();
294
295 return Ok(EcdsaP521Signature(der_sig));
296 }
297 }
298
299 fn verify(
315 message: &[u8],
316 signature: &Self::SignatureData,
317 public_key: &Self::PublicKey,
318 ) -> ApiResult<()> {
319 let sig = SignatureComponents::from_der(&signature.0)?;
321
322 if sig.r.len() > ec::P521_SCALAR_SIZE || sig.s.len() > ec::P521_SCALAR_SIZE {
324 return Err(ApiError::InvalidSignature {
325 context: "ECDSA-P521 verify",
326 #[cfg(feature = "std")]
327 message: "Invalid signature component size".to_string(),
328 });
329 }
330
331 let mut r_bytes = [0u8; ec::P521_SCALAR_SIZE];
333 let mut s_bytes = [0u8; ec::P521_SCALAR_SIZE];
334 r_bytes[ec::P521_SCALAR_SIZE - sig.r.len()..].copy_from_slice(&sig.r);
335 s_bytes[ec::P521_SCALAR_SIZE - sig.s.len()..].copy_from_slice(&sig.s);
336
337 if !is_canonical_nonzero_scalar(&r_bytes, &NIST_P521.n)
338 || !is_canonical_nonzero_scalar(&s_bytes, &NIST_P521.n)
339 {
340 return Err(ApiError::InvalidSignature {
341 context: "ECDSA-P521 verify",
342 #[cfg(feature = "std")]
343 message: "signature components must be canonical integers in [1, n-1]".to_string(),
344 });
345 }
346
347 let r = ec::Scalar::new(r_bytes).map_err(|_| ApiError::InvalidSignature {
348 context: "ECDSA-P521 verify",
349 #[cfg(feature = "std")]
350 message: "Invalid r component".to_string(),
351 })?;
352
353 let s = ec::Scalar::new(s_bytes).map_err(|_| ApiError::InvalidSignature {
354 context: "ECDSA-P521 verify",
355 #[cfg(feature = "std")]
356 message: "Invalid s component".to_string(),
357 })?;
358 if is_high_s(s.serialize().as_ref(), &NIST_P521.n) {
359 return Err(ApiError::InvalidSignature {
360 context: "ECDSA-P521 verify",
361 #[cfg(feature = "std")]
362 message: "high-s signatures are non-canonical".to_string(),
363 });
364 }
365
366 let mut hasher = Sha512::new();
368 hasher.update(message).map_err(ApiError::from)?;
369 let hash_output = hasher.finalize().map_err(ApiError::from)?;
370
371 let mut z_bytes = [0u8; ec::P521_SCALAR_SIZE];
373 z_bytes[ec::P521_SCALAR_SIZE - 64..].copy_from_slice(hash_output.as_ref());
374 let z = reduce_bytes_to_scalar(&z_bytes)?;
375
376 let s_inv = s.inv_mod_n().map_err(ApiError::from)?;
378
379 let u1 = z.mul_mod_n(&s_inv).map_err(ApiError::from)?;
381 let u2 = r.mul_mod_n(&s_inv).map_err(ApiError::from)?;
382
383 let q = ec::Point::deserialize_uncompressed(&public_key.0).map_err(ApiError::from)?;
385
386 let u1g = ec::scalar_mult_base_g(&u1).map_err(ApiError::from)?;
388
389 let u2q = ec::scalar_mult(&u2, &q).map_err(ApiError::from)?;
390
391 let point = u1g.add(&u2q);
392
393 if point.is_identity() {
395 return Err(ApiError::InvalidSignature {
396 context: "ECDSA-P521 verify",
397 #[cfg(feature = "std")]
398 message: "Invalid signature: verification point is identity".to_string(),
399 });
400 }
401
402 let x1_bytes = point.x_coordinate_bytes();
404 let x1 = reduce_bytes_to_scalar(&x1_bytes)?;
405
406 if !ct_eq(r.serialize(), x1.serialize()) {
408 return Err(ApiError::InvalidSignature {
409 context: "ECDSA-P521 verify",
410 #[cfg(feature = "std")]
411 message: "Signature verification failed".to_string(),
412 });
413 }
414
415 Ok(())
416 }
417}
418
419fn reduce_bytes_to_scalar(bytes: &[u8; 66]) -> ApiResult<ec::Scalar> {
420 Ok(ec::Scalar::from_bytes_reduced(*bytes))
421}
422
423#[cfg(test)]
424mod tests;