Add PKCS#8 generation to ECDSAKeyPair
.
This commit is contained in:
parent
d7ee87e785
commit
eb3843e4dd
@ -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",
|
||||
|
@ -1 +0,0 @@
|
||||
*<2A>ホ=*<2A>ホ=
|
Binary file not shown.
@ -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()`
|
||||
|
11
src/ec/ec.rs
11
src/ec/ec.rs
@ -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;
|
||||
|
||||
|
BIN
src/ec/suite_b/ecPublicKey_p256_pkcs8_v1_template.der
Normal file
BIN
src/ec/suite_b/ecPublicKey_p256_pkcs8_v1_template.der
Normal file
Binary file not shown.
BIN
src/ec/suite_b/ecPublicKey_p384_pkcs8_v1_template.der
Normal file
BIN
src/ec/suite_b/ecPublicKey_p384_pkcs8_v1_template.der
Normal file
Binary file not shown.
@ -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 {
|
||||
|
@ -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
|
||||
|
77
src/pkcs8.rs
77
src/pkcs8.rs
@ -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())]
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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| {
|
||||
|
Loading…
x
Reference in New Issue
Block a user