Add ring::signature::ECDSAKeyPair
with PKCS#8 import.
This commit is contained in:
parent
30093dd947
commit
d7ee87e785
@ -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",
|
||||
|
1
src/data/alg-ecPublicKey-P256.der
Normal file
1
src/data/alg-ecPublicKey-P256.der
Normal file
@ -0,0 +1 @@
|
||||
*<2A>ホ=*<2A>ホ=
|
BIN
src/data/alg-ecPublicKey-P384.der
Normal file
BIN
src/data/alg-ecPublicKey-P384.der
Normal file
Binary file not shown.
@ -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],
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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::{
|
||||
|
25
tests/ecdsa_from_pkcs8_tests.txt
Normal file
25
tests/ecdsa_from_pkcs8_tests.txt
Normal 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
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user