ring/
agreement.rs

1// Copyright 2015-2017 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//! Key Agreement: ECDH, including X25519.
16//!
17//! # Example
18//!
19//! Note that this example uses X25519, but ECDH using NIST P-256/P-384 is done
20//! exactly the same way, just substituting
21//! `agreement::ECDH_P256`/`agreement::ECDH_P384` for `agreement::X25519`.
22//!
23//! ```
24//! use ring::{agreement, rand};
25//!
26//! let rng = rand::SystemRandom::new();
27//!
28//! let my_private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
29//!
30//! // Make `my_public_key` a byte slice containing my public key. In a real
31//! // application, this would be sent to the peer in an encoded protocol
32//! // message.
33//! let my_public_key = my_private_key.compute_public_key()?;
34//!
35//! let peer_public_key = {
36//!     // In a real application, the peer public key would be parsed out of a
37//!     // protocol message. Here we just generate one.
38//!     let peer_public_key = {
39//!         let peer_private_key =
40//!             agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
41//!         peer_private_key.compute_public_key()?
42//!     };
43//!
44//!     agreement::UnparsedPublicKey::new(&agreement::X25519, peer_public_key)
45//! };
46//!
47//! agreement::agree_ephemeral(
48//!     my_private_key,
49//!     &peer_public_key,
50//!     ring::error::Unspecified,
51//!     |_key_material| {
52//!         // In a real application, we'd apply a KDF to the key material and the
53//!         // public keys (as recommended in RFC 7748) and then derive session
54//!         // keys from the result. We omit all that here.
55//!         Ok(())
56//!     },
57//! )?;
58//!
59//! # Ok::<(), ring::error::Unspecified>(())
60//! ```
61
62// The "NSA Guide" steps here are from from section 3.1, "Ephemeral Unified
63// Model."
64
65use crate::{cpu, debug, ec, error, rand};
66
67pub use crate::ec::{
68    curve25519::x25519::X25519,
69    suite_b::ecdh::{ECDH_P256, ECDH_P384},
70};
71
72/// A key agreement algorithm.
73pub struct Algorithm {
74    pub(crate) curve: &'static ec::Curve,
75    pub(crate) ecdh: fn(
76        out: &mut [u8],
77        private_key: &ec::Seed,
78        peer_public_key: untrusted::Input,
79    ) -> Result<(), error::Unspecified>,
80}
81
82derive_debug_via_field!(Algorithm, curve);
83
84impl Eq for Algorithm {}
85impl PartialEq for Algorithm {
86    fn eq(&self, other: &Algorithm) -> bool {
87        self.curve.id == other.curve.id
88    }
89}
90
91/// An ephemeral private key for use (only) with `agree_ephemeral`. The
92/// signature of `agree_ephemeral` ensures that an `EphemeralPrivateKey` can be
93/// used for at most one key agreement.
94pub struct EphemeralPrivateKey {
95    private_key: ec::Seed,
96    algorithm: &'static Algorithm,
97}
98
99derive_debug_via_field!(
100    EphemeralPrivateKey,
101    stringify!(EphemeralPrivateKey),
102    algorithm
103);
104
105impl EphemeralPrivateKey {
106    /// Generate a new ephemeral private key for the given algorithm.
107    pub fn generate(
108        alg: &'static Algorithm,
109        rng: &dyn rand::SecureRandom,
110    ) -> Result<Self, error::Unspecified> {
111        let cpu_features = cpu::features();
112
113        // NSA Guide Step 1.
114        //
115        // This only handles the key generation part of step 1. The rest of
116        // step one is done by `compute_public_key()`.
117        let private_key = ec::Seed::generate(&alg.curve, rng, cpu_features)?;
118        Ok(Self {
119            private_key,
120            algorithm: alg,
121        })
122    }
123
124    /// Computes the public key from the private key.
125    #[inline(always)]
126    pub fn compute_public_key(&self) -> Result<PublicKey, error::Unspecified> {
127        // NSA Guide Step 1.
128        //
129        // Obviously, this only handles the part of Step 1 between the private
130        // key generation and the sending of the public key to the peer. `out`
131        // is what should be sent to the peer.
132        self.private_key
133            .compute_public_key()
134            .map(|public_key| PublicKey {
135                algorithm: self.algorithm,
136                bytes: public_key,
137            })
138    }
139
140    /// The algorithm for the private key.
141    #[inline]
142    pub fn algorithm(&self) -> &'static Algorithm {
143        self.algorithm
144    }
145
146    #[cfg(test)]
147    pub fn bytes(&self) -> &[u8] {
148        self.private_key.bytes_less_safe()
149    }
150}
151
152/// A public key for key agreement.
153#[derive(Clone)]
154pub struct PublicKey {
155    algorithm: &'static Algorithm,
156    bytes: ec::PublicKey,
157}
158
159impl AsRef<[u8]> for PublicKey {
160    fn as_ref(&self) -> &[u8] {
161        self.bytes.as_ref()
162    }
163}
164
165impl core::fmt::Debug for PublicKey {
166    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
167        f.debug_struct("PublicKey")
168            .field("algorithm", &self.algorithm)
169            .field("bytes", &debug::HexStr(self.as_ref()))
170            .finish()
171    }
172}
173
174impl PublicKey {
175    /// The algorithm for the public key.
176    #[inline]
177    pub fn algorithm(&self) -> &'static Algorithm {
178        self.algorithm
179    }
180}
181
182/// An unparsed, possibly malformed, public key for key agreement.
183pub struct UnparsedPublicKey<B: AsRef<[u8]>> {
184    algorithm: &'static Algorithm,
185    bytes: B,
186}
187
188impl<B: Copy> Copy for UnparsedPublicKey<B> where B: AsRef<[u8]> {}
189
190impl<B: Clone> Clone for UnparsedPublicKey<B>
191where
192    B: AsRef<[u8]>,
193{
194    fn clone(&self) -> Self {
195        Self {
196            algorithm: self.algorithm,
197            bytes: self.bytes.clone(),
198        }
199    }
200}
201
202impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B>
203where
204    B: AsRef<[u8]>,
205{
206    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
207        f.debug_struct("UnparsedPublicKey")
208            .field("algorithm", &self.algorithm)
209            .field("bytes", &debug::HexStr(self.bytes.as_ref()))
210            .finish()
211    }
212}
213
214impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
215    /// Constructs a new `UnparsedPublicKey`.
216    pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
217        Self { algorithm, bytes }
218    }
219
220    /// TODO: doc
221    #[inline]
222    pub fn algorithm(&self) -> &'static Algorithm {
223        self.algorithm
224    }
225
226    /// TODO: doc
227    #[inline]
228    pub fn bytes(&self) -> &B {
229        &self.bytes
230    }
231}
232
233/// Performs a key agreement with an ephemeral private key and the given public
234/// key.
235///
236/// `my_private_key` is the ephemeral private key to use. Since it is moved, it
237/// will not be usable after calling `agree_ephemeral`, thus guaranteeing that
238/// the key is used for only one key agreement.
239///
240/// `peer_public_key` is the peer's public key. `agree_ephemeral` will return
241/// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve.
242/// `agree_ephemeral` verifies that it is encoded in the standard form for the
243/// algorithm and that the key is *valid*; see the algorithm's documentation for
244/// details on how keys are to be encoded and what constitutes a valid key for
245/// that algorithm.
246///
247/// `error_value` is the value to return if an error occurs before `kdf` is
248/// called, e.g. when decoding of the peer's public key fails or when the public
249/// key is otherwise invalid.
250///
251/// After the key agreement is done, `agree_ephemeral` calls `kdf` with the raw
252/// key material from the key agreement operation and then returns what `kdf`
253/// returns.
254#[inline]
255pub fn agree_ephemeral<B: AsRef<[u8]>, F, R, E>(
256    my_private_key: EphemeralPrivateKey,
257    peer_public_key: &UnparsedPublicKey<B>,
258    error_value: E,
259    kdf: F,
260) -> Result<R, E>
261where
262    F: FnOnce(&[u8]) -> Result<R, E>,
263{
264    let peer_public_key = UnparsedPublicKey {
265        algorithm: peer_public_key.algorithm,
266        bytes: peer_public_key.bytes.as_ref(),
267    };
268    agree_ephemeral_(my_private_key, peer_public_key, error_value, kdf)
269}
270
271fn agree_ephemeral_<F, R, E>(
272    my_private_key: EphemeralPrivateKey,
273    peer_public_key: UnparsedPublicKey<&[u8]>,
274    error_value: E,
275    kdf: F,
276) -> Result<R, E>
277where
278    F: FnOnce(&[u8]) -> Result<R, E>,
279{
280    // NSA Guide Prerequisite 1.
281    //
282    // The domain parameters are hard-coded. This check verifies that the
283    // peer's public key's domain parameters match the domain parameters of
284    // this private key.
285    if peer_public_key.algorithm != my_private_key.algorithm {
286        return Err(error_value);
287    }
288
289    let alg = &my_private_key.algorithm;
290
291    // NSA Guide Prerequisite 2, regarding which KDFs are allowed, is delegated
292    // to the caller.
293
294    // NSA Guide Prerequisite 3, "Prior to or during the key-agreement process,
295    // each party shall obtain the identifier associated with the other party
296    // during the key-agreement scheme," is delegated to the caller.
297
298    // NSA Guide Step 1 is handled by `EphemeralPrivateKey::generate()` and
299    // `EphemeralPrivateKey::compute_public_key()`.
300
301    let mut shared_key = [0u8; ec::ELEM_MAX_BYTES];
302    let shared_key = &mut shared_key[..alg.curve.elem_scalar_seed_len];
303
304    // NSA Guide Steps 2, 3, and 4.
305    //
306    // We have a pretty liberal interpretation of the NIST's spec's "Destroy"
307    // that doesn't meet the NSA requirement to "zeroize."
308    (alg.ecdh)(
309        shared_key,
310        &my_private_key.private_key,
311        untrusted::Input::from(peer_public_key.bytes),
312    )
313    .map_err(|_| error_value)?;
314
315    // NSA Guide Steps 5 and 6.
316    //
317    // Again, we have a pretty liberal interpretation of the NIST's spec's
318    // "Destroy" that doesn't meet the NSA requirement to "zeroize."
319    kdf(shared_key)
320}