From 207dd6810c56e5332c54347c15317f1b4305c18d Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Fri, 4 Feb 2022 14:19:38 +0000 Subject: [PATCH 1/2] Add Sec1 EC key support Signed-off-by: Vladimir Pouzanov --- src/ec/suite_b.rs | 11 ++++++++++- src/ec/suite_b/ecdsa/signing.rs | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ec/suite_b.rs b/src/ec/suite_b.rs index b861075019..9d31a1ce3c 100644 --- a/src/ec/suite_b.rs +++ b/src/ec/suite_b.rs @@ -160,8 +160,17 @@ pub(crate) fn key_pair_from_pkcs8( cpu_features: cpu::Features, ) -> Result { let (ec_private_key, _) = pkcs8::unwrap_key(template, pkcs8::Version::V1Only, input)?; + key_pair_from_der(curve, template, ec_private_key, cpu_features) +} + +pub(crate) fn key_pair_from_der( + curve: &'static ec::Curve, + template: &pkcs8::Template, + input: untrusted::Input, + cpu_features: cpu::Features, +) -> Result { let (private_key, public_key) = - ec_private_key.read_all(error::KeyRejected::invalid_encoding(), |input| { + input.read_all(error::KeyRejected::invalid_encoding(), |input| { // https://tools.ietf.org/html/rfc5915#section-3 der::nested( input, diff --git a/src/ec/suite_b/ecdsa/signing.rs b/src/ec/suite_b/ecdsa/signing.rs index f8e8adce58..577ea465d3 100644 --- a/src/ec/suite_b/ecdsa/signing.rs +++ b/src/ec/suite_b/ecdsa/signing.rs @@ -116,6 +116,30 @@ impl EcdsaKeyPair { Self::new(alg, key_pair, rng) } + /// Constructs an ECDSA key pair by parsing an unencrypted Sec1 v1 + /// id-ecPublicKey `ECPrivateKey` key. + /// + /// The input must be in Sec1 v1 format. It must contain the public key in + /// the `ECPrivateKey` structure; `from_der()` will verify that the public + /// key and the private key are consistent with each other. The algorithm + /// identifier must identify the curve by name; it must not use an + /// "explicit" encoding of the curve. The `parameters` field of the + /// `ECPrivateKey`, if present, must be the same named curve that is in the + /// `alg`. + pub fn from_der( + alg: &'static EcdsaSigningAlgorithm, + der: &[u8], + ) -> Result { + let key_pair = ec::suite_b::key_pair_from_der( + alg.curve, + alg.pkcs8_template, + untrusted::Input::from(der), + cpu::features(), + )?; + let rng = rand::SystemRandom::new(); // TODO: make this a parameter. + Self::new(alg, key_pair, &rng) + } + /// Constructs an ECDSA key pair from the private key and public key bytes /// /// The private key must encoded as a big-endian fixed-length integer. For From 2ee234402c9606a21e3c52387980bb941fb6c9c9 Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Sat, 5 Feb 2022 17:37:41 +0000 Subject: [PATCH 2/2] Add a basic unit test that covers the valid use cases. Invalid cases are already covered by the pkcs#8 test. Signed-off-by: Vladimir Pouzanov --- tests/ecdsa_from_der_tests.txt | 13 ++++++++ tests/ecdsa_tests.rs | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/ecdsa_from_der_tests.txt diff --git a/tests/ecdsa_from_der_tests.txt b/tests/ecdsa_from_der_tests.txt new file mode 100644 index 0000000000..10bf62d067 --- /dev/null +++ b/tests/ecdsa_from_der_tests.txt @@ -0,0 +1,13 @@ +Curve = P-256 +Input = 30770201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a00a06082a8648ce3d030107a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 + +Curve = P-384 +Input = 3081a40201010430fc0603810412769beeabbf97ce9764e104bca45b3b7428006fb42d1fa69a344bf475ce17bf06daf553c4eccffcfecc26a00706052b81040022a1640362000417e425506a81d85e607a3caeaccbe6cc7ef58b559115b9867175ef9911f66ea77eb5b7f43e42f3129a1fe2841f6717ed4fc02bf8cfe2d10cac06a150dcba7ae9f035ec9b6b034a4ddc554da7c2da4719a1d990097fbb451a3ea1e664fc444cfa + +# A P-256 key where the ECPrivateKey contains a parameters field that matches the PKCS#8 algorithm identifier. +Curve = P-256 +Input = 30770201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a00a06082a8648ce3d030107a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724 + +# A P-384 key where the ECPrivateKey contains a parameters field identifying P-384. +Curve = P-384 +Input = 3081a40201010430fc0603810412769beeabbf97ce9764e104bca45b3b7428006fb42d1fa69a344bf475ce17bf06daf553c4eccffcfecc26a00706052b81040022a1640362000417e425506a81d85e607a3caeaccbe6cc7ef58b559115b9867175ef9911f66ea77eb5b7f43e42f3129a1fe2841f6717ed4fc02bf8cfe2d10cac06a150dcba7ae9f035ec9b6b034a4ddc554da7c2da4719a1d990097fbb451a3ea1e664fc444cfa diff --git a/tests/ecdsa_tests.rs b/tests/ecdsa_tests.rs index 455940e3f8..c13c896fea 100644 --- a/tests/ecdsa_tests.rs +++ b/tests/ecdsa_tests.rs @@ -86,6 +86,67 @@ fn ecdsa_from_pkcs8_test() { ); } +#[test] +fn ecdsa_from_der_test() { + test::run( + test_file!("ecdsa_from_der_tests.txt"), + |section, test_case| { + assert_eq!(section, ""); + + let curve_name = test_case.consume_string("Curve"); + let ((this_fixed, this_asn1), (other_fixed, other_asn1)) = match curve_name.as_str() { + "P-256" => ( + ( + &signature::ECDSA_P256_SHA256_FIXED_SIGNING, + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + ), + ( + &signature::ECDSA_P384_SHA384_FIXED_SIGNING, + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + ), + ), + "P-384" => ( + ( + &signature::ECDSA_P384_SHA384_FIXED_SIGNING, + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + ), + ( + &signature::ECDSA_P256_SHA256_FIXED_SIGNING, + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + ), + ), + _ => unreachable!(), + }; + + let input = test_case.consume_bytes("Input"); + + let error = test_case.consume_optional_string("Error"); + + match ( + signature::EcdsaKeyPair::from_der(this_fixed, &input), + error.clone(), + ) { + (Ok(_), None) => (), + (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e), + (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e), + (Err(actual), Some(expected)) => assert_eq!(format!("{}", actual), expected), + }; + + match (signature::EcdsaKeyPair::from_der(this_asn1, &input), error) { + (Ok(_), None) => (), + (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e), + (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e), + (Err(actual), Some(expected)) => assert_eq!(format!("{}", actual), expected), + }; + + assert!(signature::EcdsaKeyPair::from_der(other_fixed, &input).is_err()); + assert!(signature::EcdsaKeyPair::from_der(other_asn1, &input).is_err()); + + Ok(()) + }, + ); +} + // Verify that, at least, we generate PKCS#8 documents that we can read. #[test] fn ecdsa_generate_pkcs8_test() {