Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for parsing ASN.1 ECDSA key pairs without the PKCS#8 metadata. #1456

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/ec/suite_b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,17 @@ pub(crate) fn key_pair_from_pkcs8(
cpu_features: cpu::Features,
) -> Result<ec::KeyPair, error::KeyRejected> {
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<ec::KeyPair, error::KeyRejected> {
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,
Expand Down
24 changes: 24 additions & 0 deletions src/ec/suite_b/ecdsa/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, error::KeyRejected> {
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
Expand Down
13 changes: 13 additions & 0 deletions tests/ecdsa_from_der_tests.txt
Original file line number Diff line number Diff line change
@@ -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
61 changes: 61 additions & 0 deletions tests/ecdsa_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down