| // Copyright 2015-2017 Brian Smith. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| //! Key Agreement: ECDH, including X25519. |
| //! |
| //! # Example |
| //! |
| //! Note that this example uses X25519, but ECDH using NIST P-256/P-384 is done |
| //! exactly the same way, just substituting |
| //! `agreement::ECDH_P256`/`agreement::ECDH_P384` for `agreement::X25519`. |
| //! |
| //! ``` |
| //! use ring::{agreement, rand}; |
| //! |
| //! let rng = rand::SystemRandom::new(); |
| //! |
| //! let my_private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?; |
| //! |
| //! // Make `my_public_key` a byte slice containing my public key. In a real |
| //! // application, this would be sent to the peer in an encoded protocol |
| //! // message. |
| //! let my_public_key = my_private_key.compute_public_key()?; |
| //! |
| //! let peer_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()? |
| //! }; |
| //! |
| //! agreement::UnparsedPublicKey::new(&agreement::X25519, peer_public_key) |
| //! }; |
| //! |
| //! agreement::agree_ephemeral( |
| //! my_private_key, |
| //! &peer_public_key, |
| //! ring::error::Unspecified, |
| //! |_key_material| { |
| //! // In a real application, we'd apply a KDF to the key material and the |
| //! // public keys (as recommended in RFC 7748) and then derive session |
| //! // keys from the result. We omit all that here. |
| //! Ok(()) |
| //! }, |
| //! )?; |
| //! |
| //! # Ok::<(), ring::error::Unspecified>(()) |
| //! ``` |
| |
| // The "NSA Guide" steps here are from from section 3.1, "Ephemeral Unified |
| // Model." |
| |
| use crate::{cpu, debug, ec, error, rand}; |
| |
| pub use crate::ec::{ |
| curve25519::x25519::X25519, |
| suite_b::ecdh::{ECDH_P256, ECDH_P384}, |
| }; |
| |
| /// A key agreement algorithm. |
| pub struct Algorithm { |
| pub(crate) curve: &'static ec::Curve, |
| pub(crate) ecdh: fn( |
| out: &mut [u8], |
| private_key: &ec::Seed, |
| peer_public_key: untrusted::Input, |
| ) -> Result<(), error::Unspecified>, |
| } |
| |
| derive_debug_via_field!(Algorithm, curve); |
| |
| impl Eq for Algorithm {} |
| impl PartialEq for Algorithm { |
| fn eq(&self, other: &Algorithm) -> bool { |
| self.curve.id == other.curve.id |
| } |
| } |
| |
| /// An ephemeral private key for use (only) with `agree_ephemeral`. The |
| /// signature of `agree_ephemeral` ensures that an `EphemeralPrivateKey` can be |
| /// used for at most one key agreement. |
| pub struct EphemeralPrivateKey { |
| private_key: ec::Seed, |
| algorithm: &'static Algorithm, |
| } |
| |
| derive_debug_via_field!( |
| EphemeralPrivateKey, |
| stringify!(EphemeralPrivateKey), |
| algorithm |
| ); |
| |
| impl EphemeralPrivateKey { |
| /// Generate a new ephemeral private key for the given algorithm. |
| pub fn generate( |
| alg: &'static Algorithm, |
| rng: &dyn rand::SecureRandom, |
| ) -> Result<Self, error::Unspecified> { |
| let cpu_features = cpu::features(); |
| |
| // NSA Guide Step 1. |
| // |
| // This only handles the key generation part of step 1. The rest of |
| // step one is done by `compute_public_key()`. |
| let private_key = ec::Seed::generate(&alg.curve, rng, cpu_features)?; |
| Ok(Self { |
| private_key, |
| algorithm: alg, |
| }) |
| } |
| |
| /// Computes the public key from the private key. |
| #[inline(always)] |
| pub fn compute_public_key(&self) -> Result<PublicKey, error::Unspecified> { |
| // NSA Guide Step 1. |
| // |
| // Obviously, this only handles the part of Step 1 between the private |
| // key generation and the sending of the public key to the peer. `out` |
| // is what should be sent to the peer. |
| self.private_key |
| .compute_public_key() |
| .map(|public_key| PublicKey { |
| algorithm: self.algorithm, |
| bytes: public_key, |
| }) |
| } |
| |
| /// The algorithm for the private key. |
| #[inline] |
| pub fn algorithm(&self) -> &'static Algorithm { |
| self.algorithm |
| } |
| |
| #[cfg(test)] |
| pub fn bytes(&self) -> &[u8] { |
| self.private_key.bytes_less_safe() |
| } |
| } |
| |
| /// A public key for key agreement. |
| #[derive(Clone)] |
| pub struct PublicKey { |
| algorithm: &'static Algorithm, |
| bytes: ec::PublicKey, |
| } |
| |
| impl AsRef<[u8]> for PublicKey { |
| fn as_ref(&self) -> &[u8] { |
| self.bytes.as_ref() |
| } |
| } |
| |
| impl core::fmt::Debug for PublicKey { |
| fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
| f.debug_struct("PublicKey") |
| .field("algorithm", &self.algorithm) |
| .field("bytes", &debug::HexStr(self.as_ref())) |
| .finish() |
| } |
| } |
| |
| impl PublicKey { |
| /// The algorithm for the public key. |
| #[inline] |
| pub fn algorithm(&self) -> &'static Algorithm { |
| self.algorithm |
| } |
| } |
| |
| /// An unparsed, possibly malformed, 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: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B> |
| where |
| B: AsRef<[u8]>, |
| { |
| fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
| f.debug_struct("UnparsedPublicKey") |
| .field("algorithm", &self.algorithm) |
| .field("bytes", &debug::HexStr(self.bytes.as_ref())) |
| .finish() |
| } |
| } |
| |
| impl<B: AsRef<[u8]>> UnparsedPublicKey<B> { |
| /// Constructs a new `UnparsedPublicKey`. |
| pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self { |
| Self { algorithm, bytes } |
| } |
| |
| /// TODO: doc |
| #[inline] |
| pub fn algorithm(&self) -> &'static Algorithm { |
| self.algorithm |
| } |
| |
| /// TODO: doc |
| #[inline] |
| pub fn bytes(&self) -> &B { |
| &self.bytes |
| } |
| } |
| |
| /// Performs a key agreement with an ephemeral private key and the given public |
| /// key. |
| /// |
| /// `my_private_key` is the ephemeral private key to use. Since it is moved, it |
| /// will not be usable after calling `agree_ephemeral`, thus guaranteeing that |
| /// the key is used for only one key agreement. |
| /// |
| /// `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 |
| /// key is otherwise invalid. |
| /// |
| /// 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. |
| #[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>, |
| { |
| // NSA Guide Prerequisite 1. |
| // |
| // 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.algorithm != my_private_key.algorithm { |
| return Err(error_value); |
| } |
| |
| let alg = &my_private_key.algorithm; |
| |
| // NSA Guide Prerequisite 2, regarding which KDFs are allowed, is delegated |
| // to the caller. |
| |
| // NSA Guide Prerequisite 3, "Prior to or during the key-agreement process, |
| // each party shall obtain the identifier associated with the other party |
| // during the key-agreement scheme," is delegated to the caller. |
| |
| // NSA Guide Step 1 is handled by `EphemeralPrivateKey::generate()` and |
| // `EphemeralPrivateKey::compute_public_key()`. |
| |
| let mut shared_key = [0u8; ec::ELEM_MAX_BYTES]; |
| let shared_key = &mut shared_key[..alg.curve.elem_scalar_seed_len]; |
| |
| // NSA Guide Steps 2, 3, and 4. |
| // |
| // 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, |
| untrusted::Input::from(peer_public_key.bytes), |
| ) |
| .map_err(|_| error_value)?; |
| |
| // NSA Guide Steps 5 and 6. |
| // |
| // Again, we have a pretty liberal interpretation of the NIST's spec's |
| // "Destroy" that doesn't meet the NSA requirement to "zeroize." |
| kdf(shared_key) |
| } |