Add ring::signature::ECDSAKeyPair with PKCS#8 import.

This commit is contained in:
Brian Smith 2017-05-14 22:35:40 -10:00
parent 30093dd947
commit d7ee87e785
10 changed files with 233 additions and 6 deletions

View File

@ -229,6 +229,7 @@ include = [
"include/GFp/mem.h",
"include/GFp/type_check.h",
"examples/checkdigest.rs",
"tests/ecdsa_from_pkcs8_tests.txt",
"tests/ecdsa_tests.rs",
"tests/ecdsa_verify_asn1_tests.txt",
"tests/ecdsa_verify_fixed_tests.txt",

View File

@ -0,0 +1 @@
*<2A>ホ=*<2A>ホ=

Binary file not shown.

View File

@ -47,6 +47,11 @@ pub enum CurveID {
P384,
}
pub struct KeyPair {
pub private_key: PrivateKey,
pub public_key: [u8; PUBLIC_KEY_MAX_LEN],
}
pub struct PrivateKey {
bytes: [u8; SCALAR_MAX_BYTES],
}

View File

@ -15,13 +15,20 @@
//! ECDSA Signatures using the P-256 and P-384 curves.
use arithmetic::montgomery::*;
use {der, digest, error, private, signature};
use {der, digest, ec, error, private, signature};
use super::verify_jacobian_point_is_on_the_curve;
use super::ops::*;
use super::public_key::*;
use untrusted;
/// An ECDSA signature verification algorithm.
/// An ECDSA signing algorithm.
pub struct ECDSASigningAlgorithm {
curve: &'static ec::Curve,
key_alg_id: &'static [u8],
}
/// An ECDSA verification algorithm.
pub struct ECDSAVerificationAlgorithm {
ops: &'static PublicScalarOps,
digest_alg: &'static digest::Algorithm,
@ -132,6 +139,48 @@ impl signature::VerificationAlgorithm for ECDSAVerificationAlgorithm {
impl private::Private for ECDSAVerificationAlgorithm {}
/// An ECDSA key pair, used for signing.
pub struct ECDSAKeyPair {
#[allow(dead_code)] // XXX: Temporary, since signing isn't implemented yet.
key_pair: ec::KeyPair,
#[allow(dead_code)] // XXX: Temporary, since signing isn't implemented yet.
alg: &'static ECDSASigningAlgorithm,
}
impl<'a> ECDSAKeyPair {
/// Constructs an ECDSA key pair by parsing an unencrypted PKCS#8 v1
/// id-ecPublicKey `ECPrivateKey` key.
///
/// The input must be in PKCS#8 v1 format. It must contain the public key in
/// the `ECPrivateKey` structure; `from_pkcs8()` 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.
pub fn from_pkcs8(alg: &'static ECDSASigningAlgorithm,
input: untrusted::Input)
-> Result<ECDSAKeyPair, error::Unspecified> {
let key_pair = try!(ec::suite_b::key_pair_from_pkcs8(
alg.curve, alg.key_alg_id, input));
Ok(ECDSAKeyPair { key_pair, alg })
}
/// Constructs an ECDSA key pair directly from the big-endian-encoded
/// private key and public key bytes.
///
/// This is intended for use by code that deserializes key pairs. It is
/// recommended to use `ECDSAKeyPair::from_pkcs8()` (with a PKCS#8-encoded
/// key) instead.
pub fn from_private_key_and_public_key(alg: &'static ECDSASigningAlgorithm,
private_key: untrusted::Input,
public_key: untrusted::Input)
-> Result<ECDSAKeyPair, error::Unspecified> {
let key_pair = try!(ec::suite_b::key_pair_from_bytes(
alg.curve, private_key, public_key));
Ok(ECDSAKeyPair { key_pair, alg })
}
}
fn split_rs_fixed<'a>(
ops: &'static ScalarOps, input: &mut untrusted::Reader<'a>)
-> Result<(untrusted::Input<'a>, untrusted::Input<'a>),
@ -209,6 +258,17 @@ fn twin_mul(ops: &PrivateKeyOps, g_scalar: &Scalar, p_scalar: &Scalar,
}
/// Signing of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-256 curve and SHA-256.
///
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P256_SHA256_FIXED_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P256,
key_alg_id: EC_PUBLIC_KEY_P256,
};
/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-256 curve and SHA-256.
///
@ -221,6 +281,17 @@ pub static ECDSA_P256_SHA256_FIXED: ECDSAVerificationAlgorithm =
split_rs: split_rs_fixed,
};
/// Signing of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-256 curve and SHA-256.
///
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P384_SHA384_FIXED_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P384,
key_alg_id: EC_PUBLIC_KEY_P384,
};
/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-384 curve and SHA-384.
///
@ -233,6 +304,17 @@ pub static ECDSA_P384_SHA384_FIXED: ECDSAVerificationAlgorithm =
split_rs: split_rs_fixed,
};
/// Signing of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-256 curve and SHA-256.
///
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P256_SHA256_ASN1_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P256,
key_alg_id: EC_PUBLIC_KEY_P256,
};
/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-256 curve
/// and SHA-256.
///
@ -279,6 +361,17 @@ pub static ECDSA_P384_SHA256_ASN1: ECDSAVerificationAlgorithm =
split_rs: split_rs_asn1,
};
/// Signing of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-384 curve and SHA-384.
///
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P384_SHA384_ASN1_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P384,
key_alg_id: EC_PUBLIC_KEY_P384,
};
/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-384 curve
/// and SHA-384.
///
@ -291,6 +384,11 @@ pub static ECDSA_P384_SHA384_ASN1: ECDSAVerificationAlgorithm =
split_rs: split_rs_asn1,
};
const EC_PUBLIC_KEY_P256: &'static [u8] =
include_bytes!("../../data/alg-ecPublicKey-P256.der");
const EC_PUBLIC_KEY_P384: &'static [u8] =
include_bytes!("../../data/alg-ecPublicKey-P384.der");
#[cfg(test)]
mod tests {

View File

@ -15,8 +15,9 @@
//! Elliptic curve operations on P-256 & P-384.
use arithmetic::montgomery::*;
use error;
use {der, ec, error, pkcs8};
use self::ops::*;
use untrusted;
// NIST SP 800-56A Step 3: "If q is an odd prime p, verify that
@ -152,6 +153,59 @@ fn verify_affine_point_is_on_the_curve_scaled(
Ok(())
}
pub fn key_pair_from_pkcs8(curve: &ec::Curve, key_alg_id: &[u8],
input: untrusted::Input)
-> Result<ec::KeyPair, error::Unspecified> {
let (ec_private_key, _) = try!(pkcs8::unwrap_key(
pkcs8::Version::V1Only, input, key_alg_id));
let (private_key, public_key) = try!(ec_private_key.read_all(
error::Unspecified, |input| {
// https://tools.ietf.org/html/rfc5915#section-3
der::nested(input, der::Tag::Sequence, error::Unspecified, |input| {
let version = try!(der::small_nonnegative_integer(input));
if version != 1 {
return Err(error::Unspecified);
}
let private_key = try!(der::expect_tag_and_get_value(
input, der::Tag::OctetString));
// [0] parameters. TODO: support this.
// [1] publicKey. The RFC says it is optional, but we require it
// to be present.
let public_key = try!(der::nested(
input, der::Tag::ContextSpecificConstructed1,
error::Unspecified, |input| {
der::bit_string_with_no_unused_bits(input)
}));
Ok((private_key, public_key))
})
}));
key_pair_from_bytes(curve, private_key, public_key)
}
pub fn key_pair_from_bytes(curve: &ec::Curve,
private_key_bytes: untrusted::Input,
public_key_bytes: untrusted::Input)
-> Result<ec::KeyPair, error::Unspecified> {
let private_key = try!(ec::PrivateKey::from_bytes(curve, private_key_bytes));
let mut public_key_check = [0; ec::PUBLIC_KEY_MAX_LEN];
{ // Borrow `public_key_check`.
let public_key_check = &mut public_key_check[..curve.public_key_len];
try!((curve.public_from_private)(public_key_check, &private_key));
if public_key_bytes != &*public_key_check {
return Err(error::Unspecified);
}
}
Ok(ec::KeyPair {
private_key: private_key,
public_key: public_key_check,
})
}
pub mod curve;
pub mod ecdsa;

View File

@ -20,7 +20,7 @@ use {der, error};
use untrusted;
pub enum Version {
#[cfg(feature = "rsa_signing")] V1Only,
V1Only,
V1OrV2,
V2Only,
}
@ -45,7 +45,7 @@ pub fn unwrap_key<'a>(version: Version, input: untrusted::Input<'a>,
// in v1 form, so reject v2 and any later form.
let require_public_key =
match (try!(der::small_nonnegative_integer(input)), version) {
#[cfg(feature = "rsa_signing")] (0, Version::V1Only) => false,
(0, Version::V1Only) => false,
(0, Version::V1OrV2) => false,
(1, Version::V1OrV2) |
(1, Version::V2Only) => true,

View File

@ -258,13 +258,16 @@ use {error, init, private};
use untrusted;
pub use ec::suite_b::ecdsa::{
ECDSAKeyPair,
ECDSAVerificationAlgorithm,
ECDSA_P256_SHA256_ASN1, ECDSA_P256_SHA256_FIXED,
ECDSA_P256_SHA256_ASN1_SIGNING, ECDSA_P256_SHA256_FIXED_SIGNING,
ECDSA_P256_SHA384_ASN1,
ECDSA_P384_SHA256_ASN1,
ECDSA_P384_SHA384_ASN1, ECDSA_P384_SHA384_FIXED,
ECDSA_P384_SHA384_ASN1_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
};
pub use ec::curve25519::ed25519::{

View File

@ -0,0 +1,25 @@
Curve = P-256
Input = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724
Curve = P-384
Input = 3081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430fc0603810412769beeabbf97ce9764e104bca45b3b7428006fb42d1fa69a344bf475ce17bf06daf553c4eccffcfecc26a1640362000417e425506a81d85e607a3caeaccbe6cc7ef58b559115b9867175ef9911f66ea77eb5b7f43e42f3129a1fe2841f6717ed4fc02bf8cfe2d10cac06a150dcba7ae9f035ec9b6b034a4ddc554da7c2da4719a1d990097fbb451a3ea1e664fc444cfa
# The curve P-256 is encoded explicitly, instead of as a reference to a named curve.
Curve = P-256
Input = 308201220201010420c6c1aada15b07661f8142c6caf0fdb241aff2efe46c0938b74f2bcc53052b077a081fa3081f7020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff305b0420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b031500c49d360886e704936a6678e1139d26b7819f7e900441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020101
Error = DECODE_ERROR
# A valid PKCS#8 P-256 private key, but without the public key in the ECPrivateKey.
Curve = P-256
Input = 308141020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07
Error = DECODE_ERROR
# The AlgorithmIdentifier is ecPublicKey w/ P-256, but it's an RSAPrivateKey.
Curve = P-256
Input = 308181020100300d06092a864886f70d0101010500046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724
Error = DECODE_ERROR
# A P-256 ECPrivateKey, but the AlgorithmIdentifier is The AlgorithmIdentifier is rsaEncryption.
Curve = P-256
Input = 308181020100300d06092a864886f70d0101010500046d306b0201010420090460075f15d2a256248000fb02d83ad77593dde4ae59fc5e96142dffb2bd07a14403420004cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724
Error = DECODE_ERROR

View File

@ -17,6 +17,45 @@ extern crate untrusted;
use ring::{signature, test};
#[test]
fn ecdsa_from_pkcs8_test() {
test::from_file("tests/ecdsa_from_pkcs8_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 input = untrusted::Input::from(&input);
let error = test_case.consume_optional_string("Error");
assert_eq!(
signature::ECDSAKeyPair::from_pkcs8(this_fixed, input).is_ok(),
error.is_none());
assert_eq!(
signature::ECDSAKeyPair::from_pkcs8(this_asn1, input).is_ok(),
error.is_none());
assert!(
signature::ECDSAKeyPair::from_pkcs8(other_fixed, input).is_err());
assert!(
signature::ECDSAKeyPair::from_pkcs8(other_asn1, input).is_err());
Ok(())
});
}
#[test]
fn signature_ecdsa_verify_asn1_test() {
test::from_file("tests/ecdsa_verify_asn1_tests.txt", |section, test_case| {
@ -77,7 +116,8 @@ fn signature_ecdsa_verify_fixed_test() {
("P-256", "SHA256") => &signature::ECDSA_P256_SHA256_FIXED,
("P-384", "SHA384") => &signature::ECDSA_P384_SHA384_FIXED,
_ => {
panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name);
panic!("Unsupported curve+digest: {}+{}", curve_name,
digest_name);
}
};