diff --git a/src/ec/suite_b/ecdsa/signing.rs b/src/ec/suite_b/ecdsa/signing.rs index 5c2547aaf5..dc72879712 100644 --- a/src/ec/suite_b/ecdsa/signing.rs +++ b/src/ec/suite_b/ecdsa/signing.rs @@ -153,6 +153,8 @@ impl EcdsaKeyPair { /// Returns the signature of the `message` using a random nonce /// generated by `rng`. + /// + /// The `message` is digested with the appropriate algorithm. pub fn sign( &self, rng: &dyn rand::SecureRandom, @@ -163,6 +165,23 @@ impl EcdsaKeyPair { self.sign_(rng, h) } + /// Returns the signature of the `digest` using a random nonce + /// generated by `rng`. + /// + /// The `digest` algorithm must match that of the signing algorithm. + pub fn sign_digest( + &self, + rng: &dyn rand::SecureRandom, + digest: digest::Digest, + ) -> Result { + // Step 4 (out of order, already performed by caller). + if digest.algorithm() == self.alg.digest_alg { + self.sign_(rng, digest) + } else { + Err(error::Unspecified) + } + } + /// Returns the signature of message digest `h` using a "random" nonce /// generated by `rng`. fn sign_( @@ -435,6 +454,44 @@ mod tests { ); } + #[test] + fn signature_ecdsa_sign_digest_fixed_test() { + test::run( + test_file!("ecdsa_sign_fixed_tests.txt"), + |section, test_case| { + assert_eq!(section, ""); + + let curve_name = test_case.consume_string("Curve"); + let digest_name = test_case.consume_string("Digest"); + let msg = test_case.consume_bytes("Msg"); + let d = test_case.consume_bytes("d"); + let q = test_case.consume_bytes("Q"); + let k = test_case.consume_bytes("k"); + + let expected_result = test_case.consume_bytes("Sig"); + + let alg = match (curve_name.as_str(), digest_name.as_str()) { + ("P-256", "SHA256") => &signature::ECDSA_P256_SHA256_FIXED_SIGNING, + ("P-384", "SHA384") => &signature::ECDSA_P384_SHA384_FIXED_SIGNING, + _ => { + panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name); + } + }; + + let private_key = + signature::EcdsaKeyPair::from_private_key_and_public_key(alg, &d, &q).unwrap(); + let rng = test::rand::FixedSliceRandom { bytes: &k }; + + let digest = crate::digest::digest(alg.digest_alg, &msg); + let actual_result = private_key.sign_digest(&rng, digest).unwrap(); + + assert_eq!(actual_result.as_ref(), &expected_result[..]); + + Ok(()) + }, + ); + } + #[test] fn signature_ecdsa_sign_asn1_test() { test::run( @@ -471,4 +528,42 @@ mod tests { }, ); } + + #[test] + fn signature_ecdsa_sign_digest_asn1_test() { + test::run( + test_file!("ecdsa_sign_asn1_tests.txt"), + |section, test_case| { + assert_eq!(section, ""); + + let curve_name = test_case.consume_string("Curve"); + let digest_name = test_case.consume_string("Digest"); + let msg = test_case.consume_bytes("Msg"); + let d = test_case.consume_bytes("d"); + let q = test_case.consume_bytes("Q"); + let k = test_case.consume_bytes("k"); + + let expected_result = test_case.consume_bytes("Sig"); + + let alg = match (curve_name.as_str(), digest_name.as_str()) { + ("P-256", "SHA256") => &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + ("P-384", "SHA384") => &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + _ => { + panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name); + } + }; + + let private_key = + signature::EcdsaKeyPair::from_private_key_and_public_key(alg, &d, &q).unwrap(); + let rng = test::rand::FixedSliceRandom { bytes: &k }; + + let digest = crate::digest::digest(alg.digest_alg, &msg); + let actual_result = private_key.sign_digest(&rng, digest).unwrap(); + + assert_eq!(actual_result.as_ref(), &expected_result[..]); + + Ok(()) + }, + ); + } }