1#![allow(clippy::cast_possible_truncation)] use super::digest_scalar::digest_scalar;
20use crate::{
21 arithmetic::montgomery::*,
22 cpu, digest,
23 ec::{
24 self,
25 suite_b::{ops::*, private_key},
26 },
27 error,
28 io::der,
29 limb, pkcs8, rand, sealed, signature,
30};
31pub struct EcdsaSigningAlgorithm {
33 curve: &'static ec::Curve,
34 private_scalar_ops: &'static PrivateScalarOps,
35 private_key_ops: &'static PrivateKeyOps,
36 digest_alg: &'static digest::Algorithm,
37 pkcs8_template: &'static pkcs8::Template,
38 format_rs: fn(ops: &'static ScalarOps, r: &Scalar, s: &Scalar, out: &mut [u8]) -> usize,
39 id: AlgorithmID,
40}
41
42#[derive(Debug, Eq, PartialEq)]
43enum AlgorithmID {
44 ECDSA_P256_SHA256_FIXED_SIGNING,
45 ECDSA_P384_SHA384_FIXED_SIGNING,
46 ECDSA_P256_SHA256_ASN1_SIGNING,
47 ECDSA_P384_SHA384_ASN1_SIGNING,
48}
49
50derive_debug_via_id!(EcdsaSigningAlgorithm);
51
52impl PartialEq for EcdsaSigningAlgorithm {
53 fn eq(&self, other: &Self) -> bool {
54 self.id == other.id
55 }
56}
57
58impl Eq for EcdsaSigningAlgorithm {}
59
60impl sealed::Sealed for EcdsaSigningAlgorithm {}
61
62pub struct EcdsaKeyPair {
64 d: Scalar<R>,
65 nonce_key: NonceRandomKey,
66 alg: &'static EcdsaSigningAlgorithm,
67 public_key: PublicKey,
68}
69
70derive_debug_via_field!(EcdsaKeyPair, stringify!(EcdsaKeyPair), public_key);
71
72impl EcdsaKeyPair {
73 pub fn generate_pkcs8(
85 alg: &'static EcdsaSigningAlgorithm,
86 rng: &dyn rand::SecureRandom,
87 ) -> Result<pkcs8::Document, error::Unspecified> {
88 let private_key = ec::Seed::generate(alg.curve, rng, cpu::features())?;
89 let public_key = private_key.compute_public_key()?;
90 Ok(pkcs8::wrap_key(
91 alg.pkcs8_template,
92 private_key.bytes_less_safe(),
93 public_key.as_ref(),
94 ))
95 }
96
97 pub fn from_pkcs8(
108 alg: &'static EcdsaSigningAlgorithm,
109 pkcs8: &[u8],
110 rng: &dyn rand::SecureRandom,
111 ) -> Result<Self, error::KeyRejected> {
112 let key_pair = ec::suite_b::key_pair_from_pkcs8(
113 alg.curve,
114 alg.pkcs8_template,
115 untrusted::Input::from(pkcs8),
116 cpu::features(),
117 )?;
118 Self::new(alg, key_pair, rng)
119 }
120
121 pub fn from_private_key_and_public_key(
138 alg: &'static EcdsaSigningAlgorithm,
139 private_key: &[u8],
140 public_key: &[u8],
141 rng: &dyn rand::SecureRandom,
142 ) -> Result<Self, error::KeyRejected> {
143 let key_pair = ec::suite_b::key_pair_from_bytes(
144 alg.curve,
145 untrusted::Input::from(private_key),
146 untrusted::Input::from(public_key),
147 cpu::features(),
148 )?;
149 Self::new(alg, key_pair, rng)
150 }
151
152 fn new(
153 alg: &'static EcdsaSigningAlgorithm,
154 key_pair: ec::KeyPair,
155 rng: &dyn rand::SecureRandom,
156 ) -> Result<Self, error::KeyRejected> {
157 let (seed, public_key) = key_pair.split();
158 let d = private_key::private_key_as_scalar(alg.private_key_ops, &seed);
159 let d = alg.private_scalar_ops.to_mont(&d);
160
161 let nonce_key = NonceRandomKey::new(alg, &seed, rng)?;
162 Ok(Self {
163 d,
164 nonce_key,
165 alg,
166 public_key: PublicKey(public_key),
167 })
168 }
169
170 pub fn sign(
172 &self,
173 rng: &dyn rand::SecureRandom,
174 message: &[u8],
175 ) -> Result<signature::Signature, error::Unspecified> {
176 let h = digest::digest(self.alg.digest_alg, message);
178
179 let nonce_rng = NonceRandom {
183 key: &self.nonce_key,
184 message_digest: &h,
185 rng,
186 };
187
188 self.sign_digest(h, &nonce_rng)
189 }
190
191 #[cfg(test)]
192 fn sign_with_fixed_nonce_during_test(
193 &self,
194 rng: &dyn rand::SecureRandom,
195 message: &[u8],
196 ) -> Result<signature::Signature, error::Unspecified> {
197 let h = digest::digest(self.alg.digest_alg, message);
199
200 self.sign_digest(h, rng)
201 }
202
203 fn sign_digest(
206 &self,
207 h: digest::Digest,
208 rng: &dyn rand::SecureRandom,
209 ) -> Result<signature::Signature, error::Unspecified> {
210 let ops = self.alg.private_scalar_ops;
235 let scalar_ops = ops.scalar_ops;
236 let cops = scalar_ops.common;
237 let private_key_ops = self.alg.private_key_ops;
238
239 for _ in 0..100 {
240 let k = private_key::random_scalar(self.alg.private_key_ops, rng)?;
243 let k_inv = ops.scalar_inv_to_mont(&k);
244
245 let r = private_key_ops.point_mul_base(&k);
247
248 let r = {
250 let (x, _) = private_key::affine_from_jacobian(private_key_ops, &r)?;
251 let x = cops.elem_unencoded(&x);
252 elem_reduced_to_scalar(cops, &x)
253 };
254 if cops.is_zero(&r) {
255 continue;
256 }
257
258 let e = digest_scalar(scalar_ops, h);
262
263 let s = {
265 let dr = scalar_ops.scalar_product(&self.d, &r);
266 let e_plus_dr = scalar_sum(cops, &e, dr);
267 scalar_ops.scalar_product(&k_inv, &e_plus_dr)
268 };
269 if cops.is_zero(&s) {
270 continue;
271 }
272
273 return Ok(signature::Signature::new(|sig_bytes| {
275 (self.alg.format_rs)(scalar_ops, &r, &s, sig_bytes)
276 }));
277 }
278
279 Err(error::Unspecified)
280 }
281}
282
283struct NonceRandom<'a> {
286 key: &'a NonceRandomKey,
287 message_digest: &'a digest::Digest,
288 rng: &'a dyn rand::SecureRandom,
289}
290
291impl core::fmt::Debug for NonceRandom<'_> {
292 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
293 f.debug_struct("NonceRandom").finish()
294 }
295}
296
297impl rand::sealed::SecureRandom for NonceRandom<'_> {
298 fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
299 let digest_alg = self.key.0.algorithm();
307 let mut ctx = digest::Context::new(digest_alg);
308
309 let key = self.key.0.as_ref();
311 ctx.update(key);
312
313 assert!(key.len() <= digest_alg.block_len() / 2);
317 {
318 let mut rand = [0u8; digest::MAX_BLOCK_LEN];
319 let rand = &mut rand[..digest_alg.block_len() - key.len()];
320 assert!(rand.len() >= dest.len());
321 self.rng.fill(rand)?;
322 ctx.update(rand);
323 }
324
325 ctx.update(self.message_digest.as_ref());
326
327 let nonce = ctx.finish();
328
329 dest.copy_from_slice(nonce.as_ref());
332
333 Ok(())
334 }
335}
336
337impl<'a> sealed::Sealed for NonceRandom<'a> {}
338
339struct NonceRandomKey(digest::Digest);
340
341impl NonceRandomKey {
342 fn new(
343 alg: &EcdsaSigningAlgorithm,
344 seed: &ec::Seed,
345 rng: &dyn rand::SecureRandom,
346 ) -> Result<Self, error::KeyRejected> {
347 let mut rand = [0; digest::MAX_OUTPUT_LEN];
348 let rand = &mut rand[0..alg.curve.elem_scalar_seed_len];
349
350 rng.fill(rand)
354 .map_err(|error::Unspecified| error::KeyRejected::rng_failed())?;
355
356 let mut ctx = digest::Context::new(alg.digest_alg);
357 ctx.update(rand);
358 ctx.update(seed.bytes_less_safe());
359 Ok(Self(ctx.finish()))
360 }
361}
362
363impl signature::KeyPair for EcdsaKeyPair {
364 type PublicKey = PublicKey;
365
366 fn public_key(&self) -> &Self::PublicKey {
367 &self.public_key
368 }
369}
370
371#[derive(Clone, Copy)]
372pub struct PublicKey(ec::PublicKey);
373
374derive_debug_self_as_ref_hex_bytes!(PublicKey);
375
376impl AsRef<[u8]> for PublicKey {
377 fn as_ref(&self) -> &[u8] {
378 self.0.as_ref()
379 }
380}
381
382fn format_rs_fixed(ops: &'static ScalarOps, r: &Scalar, s: &Scalar, out: &mut [u8]) -> usize {
383 let scalar_len = ops.scalar_bytes_len();
384
385 let (r_out, rest) = out.split_at_mut(scalar_len);
386 limb::big_endian_from_limbs(ops.leak_limbs(r), r_out);
387
388 let (s_out, _) = rest.split_at_mut(scalar_len);
389 limb::big_endian_from_limbs(ops.leak_limbs(s), s_out);
390
391 2 * scalar_len
392}
393
394fn format_rs_asn1(ops: &'static ScalarOps, r: &Scalar, s: &Scalar, out: &mut [u8]) -> usize {
395 fn format_integer_tlv(ops: &ScalarOps, a: &Scalar, out: &mut [u8]) -> usize {
398 let mut fixed = [0u8; ec::SCALAR_MAX_BYTES + 1];
399 let fixed = &mut fixed[..(ops.scalar_bytes_len() + 1)];
400 limb::big_endian_from_limbs(ops.leak_limbs(a), &mut fixed[1..]);
401
402 debug_assert_eq!(fixed[0], 0);
405
406 let first_index = fixed.iter().position(|b| *b != 0).unwrap();
408
409 let first_index = if fixed[first_index] & 0x80 != 0 {
411 first_index - 1
412 } else {
413 first_index
414 };
415 let value = &fixed[first_index..];
416
417 out[0] = der::Tag::Integer as u8;
418
419 assert!(value.len() < 128);
421 out[1] = value.len() as u8;
422
423 out[2..][..value.len()].copy_from_slice(value);
424
425 2 + value.len()
426 }
427
428 out[0] = der::Tag::Sequence as u8;
429 let r_tlv_len = format_integer_tlv(ops, r, &mut out[2..]);
430 let s_tlv_len = format_integer_tlv(ops, s, &mut out[2..][r_tlv_len..]);
431
432 let value_len = r_tlv_len + s_tlv_len;
434 assert!(value_len < 128);
435 out[1] = value_len as u8;
436
437 2 + value_len
438}
439
440pub static ECDSA_P256_SHA256_FIXED_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
446 curve: &ec::suite_b::curve::P256,
447 private_scalar_ops: &p256::PRIVATE_SCALAR_OPS,
448 private_key_ops: &p256::PRIVATE_KEY_OPS,
449 digest_alg: &digest::SHA256,
450 pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
451 format_rs: format_rs_fixed,
452 id: AlgorithmID::ECDSA_P256_SHA256_FIXED_SIGNING,
453};
454
455pub static ECDSA_P384_SHA384_FIXED_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
461 curve: &ec::suite_b::curve::P384,
462 private_scalar_ops: &p384::PRIVATE_SCALAR_OPS,
463 private_key_ops: &p384::PRIVATE_KEY_OPS,
464 digest_alg: &digest::SHA384,
465 pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
466 format_rs: format_rs_fixed,
467 id: AlgorithmID::ECDSA_P384_SHA384_FIXED_SIGNING,
468};
469
470pub static ECDSA_P256_SHA256_ASN1_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
476 curve: &ec::suite_b::curve::P256,
477 private_scalar_ops: &p256::PRIVATE_SCALAR_OPS,
478 private_key_ops: &p256::PRIVATE_KEY_OPS,
479 digest_alg: &digest::SHA256,
480 pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
481 format_rs: format_rs_asn1,
482 id: AlgorithmID::ECDSA_P256_SHA256_ASN1_SIGNING,
483};
484
485pub static ECDSA_P384_SHA384_ASN1_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
491 curve: &ec::suite_b::curve::P384,
492 private_scalar_ops: &p384::PRIVATE_SCALAR_OPS,
493 private_key_ops: &p384::PRIVATE_KEY_OPS,
494 digest_alg: &digest::SHA384,
495 pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
496 format_rs: format_rs_asn1,
497 id: AlgorithmID::ECDSA_P384_SHA384_ASN1_SIGNING,
498};
499
500static EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
501 bytes: include_bytes!("ecPublicKey_p256_pkcs8_v1_template.der"),
502 alg_id_range: core::ops::Range { start: 8, end: 27 },
503 curve_id_index: 9,
504 private_key_index: 0x24,
505};
506
507static EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
508 bytes: include_bytes!("ecPublicKey_p384_pkcs8_v1_template.der"),
509 alg_id_range: core::ops::Range { start: 8, end: 24 },
510 curve_id_index: 9,
511 private_key_index: 0x23,
512};
513
514#[cfg(test)]
515mod tests {
516 use crate::{rand, signature, test};
517
518 #[test]
519 fn signature_ecdsa_sign_fixed_test() {
520 let rng = rand::SystemRandom::new();
521
522 test::run(
523 test_file!("ecdsa_sign_fixed_tests.txt"),
524 |section, test_case| {
525 assert_eq!(section, "");
526
527 let curve_name = test_case.consume_string("Curve");
528 let digest_name = test_case.consume_string("Digest");
529 let msg = test_case.consume_bytes("Msg");
530 let d = test_case.consume_bytes("d");
531 let q = test_case.consume_bytes("Q");
532 let k = test_case.consume_bytes("k");
533
534 let expected_result = test_case.consume_bytes("Sig");
535
536 let alg = match (curve_name.as_str(), digest_name.as_str()) {
537 ("P-256", "SHA256") => &signature::ECDSA_P256_SHA256_FIXED_SIGNING,
538 ("P-384", "SHA384") => &signature::ECDSA_P384_SHA384_FIXED_SIGNING,
539 _ => {
540 panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name);
541 }
542 };
543
544 let private_key =
545 signature::EcdsaKeyPair::from_private_key_and_public_key(alg, &d, &q, &rng)
546 .unwrap();
547 let rng = test::rand::FixedSliceRandom { bytes: &k };
548
549 let actual_result = private_key
550 .sign_with_fixed_nonce_during_test(&rng, &msg)
551 .unwrap();
552
553 assert_eq!(actual_result.as_ref(), &expected_result[..]);
554
555 Ok(())
556 },
557 );
558 }
559
560 #[test]
561 fn signature_ecdsa_sign_asn1_test() {
562 let rng = rand::SystemRandom::new();
563
564 test::run(
565 test_file!("ecdsa_sign_asn1_tests.txt"),
566 |section, test_case| {
567 assert_eq!(section, "");
568
569 let curve_name = test_case.consume_string("Curve");
570 let digest_name = test_case.consume_string("Digest");
571 let msg = test_case.consume_bytes("Msg");
572 let d = test_case.consume_bytes("d");
573 let q = test_case.consume_bytes("Q");
574 let k = test_case.consume_bytes("k");
575
576 let expected_result = test_case.consume_bytes("Sig");
577
578 let alg = match (curve_name.as_str(), digest_name.as_str()) {
579 ("P-256", "SHA256") => &signature::ECDSA_P256_SHA256_ASN1_SIGNING,
580 ("P-384", "SHA384") => &signature::ECDSA_P384_SHA384_ASN1_SIGNING,
581 _ => {
582 panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name);
583 }
584 };
585
586 let private_key =
587 signature::EcdsaKeyPair::from_private_key_and_public_key(alg, &d, &q, &rng)
588 .unwrap();
589 let rng = test::rand::FixedSliceRandom { bytes: &k };
590
591 let actual_result = private_key
592 .sign_with_fixed_nonce_during_test(&rng, &msg)
593 .unwrap();
594
595 assert_eq!(actual_result.as_ref(), &expected_result[..]);
596
597 Ok(())
598 },
599 );
600 }
601}