Remove untrusted from the ring::agreement API.

This commit is contained in:
Brian Smith 2019-04-05 14:39:26 -10:00
parent cfe46261e8
commit 888bdd506f
2 changed files with 79 additions and 46 deletions

View File

@ -23,7 +23,6 @@
//! ```
//! # fn x25519_agreement_example() -> Result<(), ring::error::Unspecified> {
//! use ring::{agreement, rand};
//! use untrusted;
//!
//! let rng = rand::SystemRandom::new();
//!
@ -34,23 +33,21 @@
//! // message.
//! let my_public_key = my_private_key.compute_public_key()?;
//!
//! // In a real application, the peer public key would be parsed out of a
//! // protocol message. Here we just generate one.
//! let peer_public_key = {
//! let peer_private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
//! peer_private_key.compute_public_key()?
//! };
//! let peer_public_key = untrusted::Input::from(peer_public_key.as_ref());
//! // In a real application, the peer public key would be parsed out of a
//! // protocol message. Here we just generate one.
//! let peer_public_key = {
//! let peer_private_key =
//! agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
//! peer_private_key.compute_public_key()?
//! };
//!
//! // In a real application, the protocol specifies how to determine what
//! // algorithm was used to generate the peer's private key. Here, we know it
//! // is X25519 since we just generated it.
//! let peer_public_key_alg = &agreement::X25519;
//! agreement::UnparsedPublicKey::new(&agreement::X25519, peer_public_key)
//! };
//!
//! agreement::agree_ephemeral(
//! my_private_key,
//! peer_public_key_alg,
//! peer_public_key,
//! &peer_public_key,
//! ring::error::Unspecified,
//! |_key_material| {
//! // In a real application, we'd apply a KDF to the key material and the
@ -139,6 +136,31 @@ impl AsRef<[u8]> for PublicKey {
derive_debug_self_as_ref_hex_bytes!(PublicKey);
/// An unparsed (possibly invalid) public key for key agreement.
pub struct UnparsedPublicKey<B: AsRef<[u8]>> {
algorithm: &'static Algorithm,
bytes: B,
}
impl<B: Copy> Copy for UnparsedPublicKey<B> where B: AsRef<[u8]> {}
impl<B: Clone> Clone for UnparsedPublicKey<B>
where
B: AsRef<[u8]>,
{
fn clone(&self) -> Self {
Self {
algorithm: self.algorithm,
bytes: self.bytes.clone(),
}
}
}
impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
/// Constructs a new `UnparsedPublicKey`.
pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self { Self { algorithm, bytes } }
}
/// Performs a key agreement with an ephemeral private key and the given public
/// key.
///
@ -146,14 +168,12 @@ derive_debug_self_as_ref_hex_bytes!(PublicKey);
/// will not be usable after calling `agree_ephemeral`, thus guaranteeing that
/// the key is used for only one key agreement.
///
/// `peer_public_key_alg` is the algorithm/curve for the peer's public key
/// point; `agree_ephemeral` will return `Err(error_value)` if it does not
/// match `my_private_key's` algorithm/curve.
///
/// `peer_public_key` is the peer's public key. `agree_ephemeral` verifies that
/// it is encoded in the standard form for the algorithm and that the key is
/// *valid*; see the algorithm's documentation for details on how keys are to
/// be encoded and what constitutes a valid key for that algorithm.
/// `peer_public_key` is the peer's public key. `agree_ephemeral` will return
/// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve.
/// `agree_ephemeral` verifies that it is encoded in the standard form for the
/// algorithm and that the key is *valid*; see the algorithm's documentation for
/// details on how keys are to be encoded and what constitutes a valid key for
/// that algorithm.
///
/// `error_value` is the value to return if an error occurs before `kdf` is
/// called, e.g. when decoding of the peer's public key fails or when the public
@ -162,9 +182,24 @@ derive_debug_self_as_ref_hex_bytes!(PublicKey);
/// After the key agreement is done, `agree_ephemeral` calls `kdf` with the raw
/// key material from the key agreement operation and then returns what `kdf`
/// returns.
pub fn agree_ephemeral<F, R, E>(
my_private_key: EphemeralPrivateKey, peer_public_key_alg: &Algorithm,
peer_public_key: untrusted::Input, error_value: E, kdf: F,
#[inline]
pub fn agree_ephemeral<B: AsRef<[u8]>, F, R, E>(
my_private_key: EphemeralPrivateKey, peer_public_key: &UnparsedPublicKey<B>, error_value: E,
kdf: F,
) -> Result<R, E>
where
F: FnOnce(&[u8]) -> Result<R, E>,
{
let peer_public_key = UnparsedPublicKey {
algorithm: peer_public_key.algorithm,
bytes: peer_public_key.bytes.as_ref(),
};
agree_ephemeral_(my_private_key, peer_public_key, error_value, kdf)
}
fn agree_ephemeral_<F, R, E>(
my_private_key: EphemeralPrivateKey, peer_public_key: UnparsedPublicKey<&[u8]>, error_value: E,
kdf: F,
) -> Result<R, E>
where
F: FnOnce(&[u8]) -> Result<R, E>,
@ -174,7 +209,7 @@ where
// The domain parameters are hard-coded. This check verifies that the
// peer's public key's domain parameters match the domain parameters of
// this private key.
if peer_public_key_alg != my_private_key.alg {
if peer_public_key.algorithm != my_private_key.alg {
return Err(error_value);
}
@ -197,8 +232,12 @@ where
//
// We have a pretty liberal interpretation of the NIST's spec's "Destroy"
// that doesn't meet the NSA requirement to "zeroize."
(alg.ecdh)(shared_key, &my_private_key.private_key, peer_public_key)
.map_err(|_| error_value)?;
(alg.ecdh)(
shared_key,
&my_private_key.private_key,
untrusted::Input::from(peer_public_key.bytes),
)
.map_err(|_| error_value)?;
// NSA Guide Steps 5 and 6.
//

View File

@ -42,32 +42,28 @@ fn agreement_agree_ephemeral() {
let curve_name = test_case.consume_string("Curve");
let alg = alg_from_curve_name(&curve_name);
let peer_public = test_case.consume_bytes("PeerQ");
let peer_public = untrusted::Input::from(&peer_public);
let peer_public = agreement::UnparsedPublicKey::new(alg, test_case.consume_bytes("PeerQ"));
match test_case.consume_optional_string("Error") {
None => {
let my_private = test_case.consume_bytes("D");
let rng = test::rand::FixedSliceRandom { bytes: &my_private };
let my_private = agreement::EphemeralPrivateKey::generate(alg, &rng)?;
let my_private = {
let rng = test::rand::FixedSliceRandom { bytes: &my_private };
agreement::EphemeralPrivateKey::generate(alg, &rng)?
};
let my_public = test_case.consume_bytes("MyQ");
let output = test_case.consume_bytes("Output");
let computed_public = my_private.compute_public_key().unwrap();
assert_eq!(computed_public.as_ref(), &my_public[..]);
assert!(agreement::agree_ephemeral(
my_private,
alg,
peer_public,
(),
|key_material| {
assert!(
agreement::agree_ephemeral(my_private, &peer_public, (), |key_material| {
assert_eq!(key_material, &output[..]);
Ok(())
}
)
.is_ok());
})
.is_ok()
);
},
Some(_) => {
@ -82,8 +78,7 @@ fn agreement_agree_ephemeral() {
}
assert!(agreement::agree_ephemeral(
dummy_private_key,
alg,
peer_public,
&peer_public,
(),
kdf_not_called
)
@ -151,11 +146,10 @@ fn x25519(private_key: &[u8], public_key: &[u8]) -> Vec<u8> {
fn x25519_(private_key: &[u8], public_key: &[u8]) -> Result<Vec<u8>, error::Unspecified> {
let rng = test::rand::FixedSliceRandom { bytes: private_key };
let private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
let public_key = untrusted::Input::from(public_key);
let public_key = agreement::UnparsedPublicKey::new(&agreement::X25519, public_key);
agreement::agree_ephemeral(
private_key,
&agreement::X25519,
public_key,
&public_key,
error::Unspecified,
|agreed_value| Ok(Vec::from(agreed_value)),
)