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

Introduce EcdsaKeyPair::from_private_key_unchecked. #889

Open
wants to merge 4 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
38 changes: 33 additions & 5 deletions src/ec/suite_b/ecdsa/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,42 @@ impl EcdsaKeyPair {
private_key: &[u8],
public_key: &[u8],
) -> Result<Self, error::KeyRejected> {
let key_pair = ec::suite_b::key_pair_from_bytes(
let pair = Self::from_private_key_unchecked(alg, private_key)?;

if public_key != pair.public_key.as_ref() {
let err = if public_key.len() != pair.public_key.as_ref().len() {
error::KeyRejected::invalid_encoding()
} else {
error::KeyRejected::inconsistent_components()
};
return Err(err);
}
Ok(pair)
}

/// Constructs an ECDSA key pair direclty from the private key bytes.
///
/// It is recommended to use `EcdsaKeyPair::from_pkcs8()` instead. When
/// that is not practical, it is recommended to use
/// `EcdsaKeyPair::from_private_key_and_public_key` instead.
///
/// Since the public key is not given, the public key will be computed from
/// the private key. It is not possible to detect misuse or corruption of
/// the private key since the public key isn't given as input.
pub fn from_private_key_unchecked(
alg: &'static EcdsaSigningAlgorithm,
private_key: &[u8],
) -> Result<Self, error::KeyRejected> {
let rng = rand::SystemRandom::new(); // TODO: make this a parameter.
let private_key = ec::Seed::from_bytes(
alg.curve,
untrusted::Input::from(private_key),
untrusted::Input::from(public_key),
cpu::features(),
)?;
let rng = rand::SystemRandom::new(); // TODO: make this a parameter.
Self::new(alg, key_pair, &rng)
)
.map_err(|_| error::KeyRejected::invalid_encoding())?;
ec::KeyPair::derive(private_key)
.map_err(|_| error::KeyRejected::invalid_encoding())
.and_then(|key_pair| Self::new(alg, key_pair, &rng))
}
partim marked this conversation as resolved.
Show resolved Hide resolved

fn new(
Expand Down
23 changes: 23 additions & 0 deletions tests/ecdsa_from_private_key_and_public_key_test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# P-256: Matching private and public key
Curve = P-256
PrivateKey = 708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f590
PublicKey = 0429578c7ab6ce0d11493c95d5ea05d299d536801ca9cbd50e9924e43b733b83ab08c8049879c6278b2273348474158515accaa38344106ef96803c5a05adc4800

# P-256: Short private key
Curve = P-256
PrivateKey = 708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f5
PublicKey = 0429578c7ab6ce0d11493c95d5ea05d299d536801ca9cbd50e9924e43b733b83ab08c8049879c6278b2273348474158515accaa38344106ef96803c5a05adc4800
Error = InvalidEncoding

# P-256: Short public key
Curve = P-256
PrivateKey = 708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f590
PublicKey = 0429578c7ab6ce0d11493c95d5ea05d299d536801ca9cbd50e9924e43b733b83ab08c8049879c6278b2273348474158515accaa38344106ef96803c5a05adc
Error = InvalidEncoding

# P-256: Wrong public key
Curve = P-256
PrivateKey = 708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f590
PublicKey = 0429578c7ab6ce0d11493c95d500000009d536801ca9cbd50e9924e43b733b83ab08c8049879c6278b2273348474158515accaa38344106ef96803c5a05adc4800
Error = InconsistentComponents

33 changes: 33 additions & 0 deletions tests/ecdsa_from_private_key_unchecked_tests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Correctly encoded P-256 private key
Curve = P-256
PrivateKey = 708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f590
PublicKey = 0429578c7ab6ce0d11493c95d5ea05d299d536801ca9cbd50e9924e43b733b83ab08c8049879c6278b2273348474158515accaa38344106ef96803c5a05adc4800

# Short P-256 private key.
Curve = P-256
PrivateKey = 708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f5
PublicKey = 00
Error = InvalidEncoding

# P-384 private key loaded as P-256 key.
Curve = P-256
PrivateKey = 218ee54a71ef2ccf012aca231fee28a2c665fc395ff5cd20bde9b8df598c282664abf9159c5b3923132983f945056d93
PublicKey = 00
Error = InvalidEncoding

# Correctly encoded P-384 private key
Curve = P-384
PrivateKey = 218ee54a71ef2ccf012aca231fee28a2c665fc395ff5cd20bde9b8df598c282664abf9159c5b3923132983f945056d93
PublicKey = 0401989ff07a7a452d8084937448be946bfedac4049cea34b3db6f7c91d07d69e926cce0af3d6e88855a28120cf3dba8dfeb064e029d7539d4b301aabafe8de8870162deffe6383bc63cc005add6ee1d5ced4a5761219c60cd58ad5b2a7c74aaa9

# Short P-384 key.
Curve = P-384
PrivateKey = 218ee5
PublicKey = 00
Error = InvalidEncoding

# P-256 key loaded as P-384 key
Curve = P-384
PrivateKey = 708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f590
PublicKey = 00
Error = InvalidEncoding
110 changes: 110 additions & 0 deletions tests/ecdsa_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,116 @@ fn ecdsa_generate_pkcs8_test() {
}
}

#[test]
fn ecdsa_from_private_key_and_public_key_test() {
test::run(
test_file!("ecdsa_from_private_key_and_public_key_test.txt"),
|section, test_case| {
assert_eq!(section, "");

let curve_name = test_case.consume_string("Curve");
let (this_fixed, this_asn1) = match curve_name.as_str() {
"P-256" => (
&signature::ECDSA_P256_SHA256_FIXED_SIGNING,
&signature::ECDSA_P256_SHA256_ASN1_SIGNING,
),
"P-384" => (
&signature::ECDSA_P384_SHA384_FIXED_SIGNING,
&signature::ECDSA_P384_SHA384_ASN1_SIGNING,
),
_ => unreachable!(),
};

let private_key = test_case.consume_bytes("PrivateKey");
let public_key = test_case.consume_bytes("PublicKey");
let error = test_case.consume_optional_string("Error");

match (
signature::EcdsaKeyPair::from_private_key_and_public_key(
this_fixed,
&private_key,
&public_key,
),
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_private_key_and_public_key(
this_asn1,
&private_key,
&public_key,
),
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),
};

Ok(())
},
);
}

#[test]
fn ecdsa_from_private_key_unchecked_test() {
test::run(
test_file!("ecdsa_from_private_key_unchecked_tests.txt"),
|section, test_case| {
assert_eq!(section, "");

let curve_name = test_case.consume_string("Curve");
let (this_fixed, this_asn1) = match curve_name.as_str() {
"P-256" => (
&signature::ECDSA_P256_SHA256_FIXED_SIGNING,
&signature::ECDSA_P256_SHA256_ASN1_SIGNING,
),
"P-384" => (
&signature::ECDSA_P384_SHA384_FIXED_SIGNING,
&signature::ECDSA_P384_SHA384_ASN1_SIGNING,
),
_ => unreachable!(),
};

let private_key = test_case.consume_bytes("PrivateKey");
let public_key = test_case.consume_bytes("PublicKey");
let error = test_case.consume_optional_string("Error");

match (
signature::EcdsaKeyPair::from_private_key_unchecked(this_fixed, &private_key),
error.clone(),
) {
(Ok(key_pair), None) => {
assert_eq!(key_pair.public_key().as_ref(), public_key.as_slice());
}
(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_private_key_unchecked(this_asn1, &private_key),
error.clone(),
) {
(Ok(key_pair), None) => {
assert_eq!(key_pair.public_key().as_ref(), public_key.as_slice());
}
(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),
};

Ok(())
},
);
}

#[test]
fn signature_ecdsa_verify_asn1_test() {
test::run(
Expand Down