Add PKCS#8 generation to ECDSAKeyPair.

This commit is contained in:
Brian Smith 2017-05-12 14:12:53 -10:00
parent d7ee87e785
commit eb3843e4dd
13 changed files with 160 additions and 35 deletions

View File

@ -66,6 +66,8 @@ include = [
"src/ec/suite_b/ecdh.rs",
"src/ec/suite_b/ecdsa.rs",
"src/ec/suite_b/ecdsa_digest_scalar_tests.txt",
"src/ec/suite_b/ecPublicKey_p256_pkcs8_v1_template.der",
"src/ec/suite_b/ecPublicKey_p384_pkcs8_v1_template.der",
"src/ec/suite_b/ops/elem.rs",
"src/ec/suite_b/ops/ops.rs",
"src/ec/suite_b/ops/p256.rs",

View File

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

Binary file not shown.

View File

@ -14,6 +14,7 @@
//! EdDSA Signatures.
use core;
use {der, digest, error, pkcs8, private, rand, signature, signature_impl};
use super::ops::*;
use untrusted;
@ -57,9 +58,10 @@ impl<'a> Ed25519KeyPair {
let mut seed = [0u8; SEED_LEN];
try!(rng.fill(&mut seed));
let key_pair = Ed25519KeyPair::from_seed_(&seed);
// TODO: Replace this with `wrap_key()` and return a `PKCS8Document`.
let mut bytes = [0; ED25519_PKCS8_V2_LEN];
pkcs8::wrap_key(PKCS8_TEMPLATE, PKCS8_SEED_INDEX, &seed[..],
key_pair.public_key_bytes(), &mut bytes[..]);
pkcs8::wrap_key_(&PKCS8_TEMPLATE, &seed[..], key_pair.public_key_bytes(),
&mut bytes[..]);
Ok(bytes)
}
@ -211,7 +213,7 @@ fn unwrap_pkcs8(version: pkcs8::Version, input: untrusted::Input)
-> Result<(untrusted::Input, Option<untrusted::Input>),
error::Unspecified> {
let (private_key, public_key) =
try!(pkcs8::unwrap_key(version, input, ed25519_alg_id()));
try!(pkcs8::unwrap_key(&PKCS8_TEMPLATE, version, input));
let private_key = try!(private_key.read_all(error::Unspecified, |input| {
der::expect_tag_and_get_value(input, der::Tag::OctetString)
}));
@ -308,15 +310,15 @@ const SIGNATURE_LEN: usize = ELEM_LEN + SCALAR_LEN;
type Seed = [u8; SEED_LEN];
const SEED_LEN: usize = 32;
const PKCS8_TEMPLATE: &[u8] = include_bytes!("ed25519_pkcs8_v2_template.der");
const PKCS8_SEED_INDEX: usize = 0x10;
static PKCS8_TEMPLATE: pkcs8::Template = pkcs8::Template {
bytes: include_bytes!("ed25519_pkcs8_v2_template.der"),
alg_id_range: core::ops::Range { start: 7, end: 12 },
private_key_index: 0x10,
};
/// The length of an Ed25519 public key.
pub const ED25519_PUBLIC_KEY_LEN: usize = PUBLIC_KEY_LEN;
#[inline]
fn ed25519_alg_id() -> &'static [u8] { &PKCS8_TEMPLATE[7..12] }
/// The length of a Ed25519 PKCS#8 (v2) private key generated by
/// `Ed25519KeyPair::generate_pkcs8()`. Ed25519 PKCS#8 files generated by other
/// software may have different lengths, and `Ed25519KeyPair::generate_pkcs8()`

View File

@ -101,6 +101,17 @@ pub const SCALAR_MAX_BYTES: usize = ELEM_MAX_BYTES;
/// The maximum length, in bytes, of an encoded public key.
pub const PUBLIC_KEY_MAX_LEN: usize = 1 + (2 * ELEM_MAX_BYTES);
/// The maximum length of a PKCS#8 documents generated by *ring* for ECC keys.
///
/// This is NOT the maximum length of a PKCS#8 document that can be consumed by
/// `pkcs8::unwrap_key()`.
///
/// `40` is the length of the P-384 template. It is actually one byte shorter
/// than the P-256 template, but the private key and the public key are much
/// longer.
pub const PKCS8_DOCUMENT_MAX_LEN: usize =
40 + SCALAR_MAX_BYTES + PUBLIC_KEY_MAX_LEN;
#[path = "curve25519/curve25519.rs"]
pub mod curve25519;

Binary file not shown.

Binary file not shown.

View File

@ -15,7 +15,8 @@
//! ECDSA Signatures using the P-256 and P-384 curves.
use arithmetic::montgomery::*;
use {der, digest, ec, error, private, signature};
use core;
use {der, digest, ec, error, pkcs8, private, rand, signature};
use super::verify_jacobian_point_is_on_the_curve;
use super::ops::*;
use super::public_key::*;
@ -24,7 +25,7 @@ use untrusted;
/// An ECDSA signing algorithm.
pub struct ECDSASigningAlgorithm {
curve: &'static ec::Curve,
key_alg_id: &'static [u8],
pkcs8_template: &'static pkcs8::Template,
}
@ -149,6 +150,28 @@ pub struct ECDSAKeyPair {
}
impl<'a> ECDSAKeyPair {
/// Generates a new key pair and returns the key pair serialized as a
/// PKCS#8 document.
///
/// The PKCS#8 document will be a v1 `OneAsymmetricKey` with the public key
/// included in the `ECPrivateKey` structure, as described in
/// [RFC 5958 Section 2] and [RFC 5915]. The `ECPrivateKey` structure will
/// not have a `parameters` field so the generated key is compatible with
/// PKCS#11.
///
/// [RFC 5915]: https://tools.ietf.org/html/rfc5915
/// [RFC 5958 Section 2]: https://tools.ietf.org/html/rfc5958#section-2
pub fn generate_pkcs8(alg: &'static ECDSASigningAlgorithm,
rng: &rand::SecureRandom)
-> Result<pkcs8::PKCS8Document, error::Unspecified> {
let private_key = try!(ec::PrivateKey::generate(alg.curve, rng));
let mut public_key_bytes = [0; ec::PUBLIC_KEY_MAX_LEN];
let public_key_bytes = &mut public_key_bytes[..alg.curve.public_key_len];
try!((alg.curve.public_from_private)(public_key_bytes, &private_key));
Ok(pkcs8::wrap_key(&alg.pkcs8_template, private_key.bytes(alg.curve),
public_key_bytes))
}
/// Constructs an ECDSA key pair by parsing an unencrypted PKCS#8 v1
/// id-ecPublicKey `ECPrivateKey` key.
///
@ -160,8 +183,8 @@ impl<'a> ECDSAKeyPair {
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));
let key_pair = try!(ec::suite_b::key_pair_from_pkcs8(alg.curve,
alg.pkcs8_template, input));
Ok(ECDSAKeyPair { key_pair, alg })
}
@ -266,7 +289,7 @@ fn twin_mul(ops: &PrivateKeyOps, g_scalar: &Scalar, p_scalar: &Scalar,
pub static ECDSA_P256_SHA256_FIXED_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P256,
key_alg_id: EC_PUBLIC_KEY_P256,
pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
};
/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
@ -289,7 +312,7 @@ pub static ECDSA_P256_SHA256_FIXED: ECDSAVerificationAlgorithm =
pub static ECDSA_P384_SHA384_FIXED_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P384,
key_alg_id: EC_PUBLIC_KEY_P384,
pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
};
/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
@ -312,7 +335,7 @@ pub static ECDSA_P384_SHA384_FIXED: ECDSAVerificationAlgorithm =
pub static ECDSA_P256_SHA256_ASN1_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P256,
key_alg_id: EC_PUBLIC_KEY_P256,
pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
};
/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-256 curve
@ -369,7 +392,7 @@ pub static ECDSA_P384_SHA256_ASN1: ECDSAVerificationAlgorithm =
pub static ECDSA_P384_SHA384_ASN1_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P384,
key_alg_id: EC_PUBLIC_KEY_P384,
pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
};
/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-384 curve
@ -384,11 +407,17 @@ 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");
static EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
bytes: include_bytes ! ("ecPublicKey_p256_pkcs8_v1_template.der"),
alg_id_range: core::ops::Range { start: 8, end: 27 },
private_key_index: 0x24,
};
static EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
bytes: include_bytes!("ecPublicKey_p384_pkcs8_v1_template.der"),
alg_id_range: core::ops::Range { start: 8, end: 24 },
private_key_index: 0x23,
};
#[cfg(test)]
mod tests {

View File

@ -153,11 +153,11 @@ fn verify_affine_point_is_on_the_curve_scaled(
Ok(())
}
pub fn key_pair_from_pkcs8(curve: &ec::Curve, key_alg_id: &[u8],
pub fn key_pair_from_pkcs8(curve: &ec::Curve, template: &pkcs8::Template,
input: untrusted::Input)
-> Result<ec::KeyPair, error::Unspecified> {
let (ec_private_key, _) = try!(pkcs8::unwrap_key(
pkcs8::Version::V1Only, input, key_alg_id));
template, pkcs8::Version::V1Only, input));
let (private_key, public_key) = try!(ec_private_key.read_all(
error::Unspecified, |input| {
// https://tools.ietf.org/html/rfc5915#section-3

View File

@ -16,7 +16,8 @@
//!
//! [RFC 5958]: https://tools.ietf.org/html/rfc5958.
use {der, error};
use core;
use {der, ec, error};
use untrusted;
pub enum Version {
@ -25,6 +26,43 @@ pub enum Version {
V2Only,
}
/// A template for constructing PKCS#8 documents.
///
/// Note that this only works for ECC.
pub struct Template {
pub bytes: &'static [u8],
// The range within `bytes` that holds the value (not including the tag and
// length) for use in the PKCS#8 document's privateKeyAlgorithm field.
pub alg_id_range: core::ops::Range<usize>,
// `bytes` will be split into two parts at `private_key_index`, where the
// first part is written before the private key and the second part is
// written after the private key. The public key is written after the second
// part.
pub private_key_index: usize,
}
impl Template {
#[inline]
fn alg_id_value(&self) -> &[u8] {
&self.bytes[self.alg_id_range.start..self.alg_id_range.end]
}
}
/// Parses an unencrypted PKCS#8 private key, verifies that it is the right type
/// of key, and returns the key value.
///
/// PKCS#8 is specified in [RFC 5958].
///
/// [RFC 5958]: https://tools.ietf.org/html/rfc5958.
pub fn unwrap_key<'a>(template: &Template, version: Version,
input: untrusted::Input<'a>)
-> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>),
error::Unspecified> {
unwrap_key_(template.alg_id_value(), version, input)
}
/// Parses an unencrypted PKCS#8 private key, verifies that it is the right type
/// of key, and returns the key value.
///
@ -35,8 +73,8 @@ pub enum Version {
/// PKCS#8 is specified in [RFC 5958].
///
/// [RFC 5958]: https://tools.ietf.org/html/rfc5958.
pub fn unwrap_key<'a>(version: Version, input: untrusted::Input<'a>,
alg_id: &[u8])
pub fn unwrap_key_<'a>(alg_id: &[u8], version: Version,
input: untrusted::Input<'a>)
-> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>),
error::Unspecified> {
input.read_all(error::Unspecified, |input| {
@ -80,15 +118,36 @@ pub fn unwrap_key<'a>(version: Version, input: untrusted::Input<'a>,
})
}
/// A generated PKCS#8 document.
pub struct PKCS8Document {
bytes: [u8; ec::PKCS8_DOCUMENT_MAX_LEN],
len: usize,
}
impl AsRef<[u8]> for PKCS8Document {
#[inline]
fn as_ref(&self) -> &[u8] { &self.bytes[..self.len] }
}
pub fn wrap_key(template: &Template, private_key: &[u8], public_key: &[u8])
-> PKCS8Document {
let mut result = PKCS8Document {
bytes: [0; ec::PKCS8_DOCUMENT_MAX_LEN],
len: template.bytes.len() + private_key.len() + public_key.len(),
};
wrap_key_(template, private_key, public_key, &mut result.bytes[..result.len]);
result
}
/// Formats a private key "prefix||private_key||middle||public_key" where
/// `template` is "prefix||middle" split at position `private_key_index`.
pub fn wrap_key(template: &[u8], private_key_index: usize, private_key: &[u8],
public_key: &[u8], bytes: &mut [u8]) {
pub fn wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8],
bytes: &mut [u8]) {
let (before_private_key, after_private_key) =
template.split_at(private_key_index);
let private_key_end_index = private_key_index + private_key.len();
bytes[..private_key_index].copy_from_slice(before_private_key);
bytes[private_key_index..private_key_end_index]
template.bytes.split_at(template.private_key_index);
let private_key_end_index = template.private_key_index + private_key.len();
bytes[..template.private_key_index].copy_from_slice(before_private_key);
bytes[template.private_key_index..private_key_end_index]
.copy_from_slice(&private_key);
bytes[private_key_end_index..
(private_key_end_index + after_private_key.len())]

View File

@ -142,8 +142,8 @@ impl RSAKeyPair {
-> Result<RSAKeyPair, error::Unspecified> {
const RSA_ENCRYPTION: &'static [u8] =
include_bytes!("../data/alg-rsa-encryption.der");
let (der, _) = try!(pkcs8::unwrap_key(pkcs8::Version::V1Only, input,
&RSA_ENCRYPTION));
let (der, _) = try!(pkcs8::unwrap_key_(&RSA_ENCRYPTION,
pkcs8::Version::V1Only, input));
Self::from_der(der)
}

View File

@ -280,6 +280,8 @@ pub use ec::curve25519::ed25519::{
ED25519_PUBLIC_KEY_LEN,
};
pub use pkcs8::PKCS8Document;
#[cfg(all(feature = "rsa_signing", feature = "use_heap"))]
pub use rsa::signing::{RSAKeyPair, RSASigningState};

View File

@ -15,7 +15,7 @@
extern crate ring;
extern crate untrusted;
use ring::{signature, test};
use ring::{rand, signature, test};
#[test]
fn ecdsa_from_pkcs8_test() {
@ -56,6 +56,27 @@ fn ecdsa_from_pkcs8_test() {
});
}
// Verify that, at least, we generate PKCS#8 documents that we can read.
#[test]
fn ecdsa_generate_pkcs8_test() {
let rng = rand::SystemRandom::new();
for alg in &[&signature::ECDSA_P256_SHA256_ASN1_SIGNING,
&signature::ECDSA_P256_SHA256_FIXED_SIGNING,
&signature::ECDSA_P384_SHA384_ASN1_SIGNING,
&signature::ECDSA_P384_SHA384_FIXED_SIGNING] {
let pkcs8 = signature::ECDSAKeyPair::generate_pkcs8(alg, &rng).unwrap();
println!();
for b in pkcs8.as_ref() {
print!("{:02x}", *b);
}
println!();
println!();
let _ = signature::ECDSAKeyPair::from_pkcs8(
alg, untrusted::Input::from(pkcs8.as_ref())).unwrap();
}
}
#[test]
fn signature_ecdsa_verify_asn1_test() {
test::from_file("tests/ecdsa_verify_asn1_tests.txt", |section, test_case| {