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 AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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_bytes = {
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_private_key =
39//!         agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
40//!     peer_private_key.compute_public_key()?
41//! };
42//!
43//! let peer_public_key = agreement::UnparsedPublicKey::new(
44//!     &agreement::X25519,
45//!     peer_public_key_bytes);
46//!
47//! agreement::agree_ephemeral(
48//!     my_private_key,
49//!     &peer_public_key,
50//!     |_key_material| {
51//!         // In a real application, we'd apply a KDF to the key material and the
52//!         // public keys (as recommended in RFC 7748) and then derive session
53//!         // keys from the result. We omit all that here.
54//!     },
55//! )?;
56//!
57//! # Ok::<(), ring::error::Unspecified>(())
58//! ```
59
60// The "NSA Guide" steps here are from from section 3.1, "Ephemeral Unified
61// Model."
62
63use crate::{cpu, debug, ec, error, rand};
64
65pub use crate::ec::{
66    curve25519::x25519::X25519,
67    suite_b::ecdh::{ECDH_P256, ECDH_P384},
68};
69
70/// A key agreement algorithm.
71pub struct Algorithm {
72    pub(crate) curve: &'static ec::Curve,
73    pub(crate) ecdh: fn(
74        out: &mut [u8],
75        private_key: &ec::Seed,
76        peer_public_key: untrusted::Input,
77        cpu: cpu::Features,
78    ) -> Result<(), error::Unspecified>,
79}
80
81derive_debug_via_field!(Algorithm, curve);
82
83impl Eq for Algorithm {}
84impl PartialEq for Algorithm {
85    fn eq(&self, other: &Self) -> bool {
86        self.curve.id == other.curve.id
87    }
88}
89
90/// An ephemeral private key for use (only) with `agree_ephemeral`. The
91/// signature of `agree_ephemeral` ensures that an `EphemeralPrivateKey` can be
92/// used for at most one key agreement.
93pub struct EphemeralPrivateKey {
94    private_key: ec::Seed,
95    algorithm: &'static Algorithm,
96}
97
98derive_debug_via_field!(
99    EphemeralPrivateKey,
100    stringify!(EphemeralPrivateKey),
101    algorithm
102);
103
104impl EphemeralPrivateKey {
105    /// Generate a new ephemeral private key for the given algorithm.
106    pub fn generate(
107        alg: &'static Algorithm,
108        rng: &dyn rand::SecureRandom,
109    ) -> Result<Self, error::Unspecified> {
110        let cpu_features = cpu::features();
111
112        // NSA Guide Step 1.
113        //
114        // This only handles the key generation part of step 1. The rest of
115        // step one is done by `compute_public_key()`.
116        let private_key = ec::Seed::generate(alg.curve, rng, cpu_features)?;
117        Ok(Self {
118            private_key,
119            algorithm: alg,
120        })
121    }
122
123    /// Computes the public key from the private key.
124    #[inline(always)]
125    pub fn compute_public_key(&self) -> Result<PublicKey, error::Unspecified> {
126        // NSA Guide Step 1.
127        //
128        // Obviously, this only handles the part of Step 1 between the private
129        // key generation and the sending of the public key to the peer. `out`
130        // is what should be sent to the peer.
131        self.private_key
132            .compute_public_key(cpu::features())
133            .map(|public_key| PublicKey {
134                algorithm: self.algorithm,
135                bytes: public_key,
136            })
137    }
138
139    /// The algorithm for the private key.
140    #[inline]
141    pub fn algorithm(&self) -> &'static Algorithm {
142        self.algorithm
143    }
144
145    /// Do not use.
146    #[deprecated]
147    #[cfg(test)]
148    pub fn bytes(&self) -> &[u8] {
149        self.bytes_for_test()
150    }
151
152    #[cfg(test)]
153    pub(super) fn bytes_for_test(&self) -> &[u8] {
154        self.private_key.bytes_less_safe()
155    }
156}
157
158/// A public key for key agreement.
159#[derive(Clone)]
160pub struct PublicKey {
161    algorithm: &'static Algorithm,
162    bytes: ec::PublicKey,
163}
164
165impl AsRef<[u8]> for PublicKey {
166    fn as_ref(&self) -> &[u8] {
167        self.bytes.as_ref()
168    }
169}
170
171impl core::fmt::Debug for PublicKey {
172    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
173        f.debug_struct("PublicKey")
174            .field("algorithm", &self.algorithm)
175            .field("bytes", &debug::HexStr(self.as_ref()))
176            .finish()
177    }
178}
179
180impl PublicKey {
181    /// The algorithm for the public key.
182    #[inline]
183    pub fn algorithm(&self) -> &'static Algorithm {
184        self.algorithm
185    }
186}
187
188/// An unparsed, possibly malformed, public key for key agreement.
189#[derive(Clone, Copy)]
190pub struct UnparsedPublicKey<B> {
191    algorithm: &'static Algorithm,
192    bytes: B,
193}
194
195impl<B> AsRef<[u8]> for UnparsedPublicKey<B>
196where
197    B: AsRef<[u8]>,
198{
199    fn as_ref(&self) -> &[u8] {
200        self.bytes.as_ref()
201    }
202}
203
204impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B>
205where
206    B: AsRef<[u8]>,
207{
208    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
209        f.debug_struct("UnparsedPublicKey")
210            .field("algorithm", &self.algorithm)
211            .field("bytes", &debug::HexStr(self.bytes.as_ref()))
212            .finish()
213    }
214}
215
216impl<B> UnparsedPublicKey<B> {
217    /// Constructs a new `UnparsedPublicKey`.
218    pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
219        Self { algorithm, bytes }
220    }
221
222    /// The algorithm for the public key.
223    #[inline]
224    pub fn algorithm(&self) -> &'static Algorithm {
225        self.algorithm
226    }
227
228    /// TODO: doc
229    #[inline]
230    pub fn bytes(&self) -> &B {
231        &self.bytes
232    }
233}
234
235/// Performs a key agreement with an ephemeral private key and the given public
236/// key.
237///
238/// `my_private_key` is the ephemeral private key to use. Since it is moved, it
239/// will not be usable after calling `agree_ephemeral`, thus guaranteeing that
240/// the key is used for only one key agreement.
241///
242/// `peer_public_key` is the peer's public key. `agree_ephemeral` will return
243/// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve.
244/// `agree_ephemeral` verifies that it is encoded in the standard form for the
245/// algorithm and that the key is *valid*; see the algorithm's documentation for
246/// details on how keys are to be encoded and what constitutes a valid key for
247/// that algorithm.
248///
249/// After the key agreement is done, `agree_ephemeral` calls `kdf` with the raw
250/// key material from the key agreement operation and then returns what `kdf`
251/// returns.
252#[inline]
253pub fn agree_ephemeral<B: AsRef<[u8]>, R>(
254    my_private_key: EphemeralPrivateKey,
255    peer_public_key: &UnparsedPublicKey<B>,
256    kdf: impl FnOnce(&[u8]) -> R,
257) -> Result<R, error::Unspecified> {
258    let peer_public_key = UnparsedPublicKey {
259        algorithm: peer_public_key.algorithm,
260        bytes: peer_public_key.bytes.as_ref(),
261    };
262    agree_ephemeral_(my_private_key, peer_public_key, kdf, cpu::features())
263}
264
265fn agree_ephemeral_<R>(
266    my_private_key: EphemeralPrivateKey,
267    peer_public_key: UnparsedPublicKey<&[u8]>,
268    kdf: impl FnOnce(&[u8]) -> R,
269    cpu: cpu::Features,
270) -> Result<R, error::Unspecified> {
271    // NSA Guide Prerequisite 1.
272    //
273    // The domain parameters are hard-coded. This check verifies that the
274    // peer's public key's domain parameters match the domain parameters of
275    // this private key.
276    if peer_public_key.algorithm != my_private_key.algorithm {
277        return Err(error::Unspecified);
278    }
279
280    let alg = &my_private_key.algorithm;
281
282    // NSA Guide Prerequisite 2, regarding which KDFs are allowed, is delegated
283    // to the caller.
284
285    // NSA Guide Prerequisite 3, "Prior to or during the key-agreement process,
286    // each party shall obtain the identifier associated with the other party
287    // during the key-agreement scheme," is delegated to the caller.
288
289    // NSA Guide Step 1 is handled by `EphemeralPrivateKey::generate()` and
290    // `EphemeralPrivateKey::compute_public_key()`.
291
292    let mut shared_key = [0u8; ec::ELEM_MAX_BYTES];
293    let shared_key = &mut shared_key[..alg.curve.elem_scalar_seed_len];
294
295    // NSA Guide Steps 2, 3, and 4.
296    //
297    // We have a pretty liberal interpretation of the NIST's spec's "Destroy"
298    // that doesn't meet the NSA requirement to "zeroize."
299    (alg.ecdh)(
300        shared_key,
301        &my_private_key.private_key,
302        untrusted::Input::from(peer_public_key.bytes),
303        cpu,
304    )?;
305
306    // NSA Guide Steps 5 and 6.
307    //
308    // Again, we have a pretty liberal interpretation of the NIST's spec's
309    // "Destroy" that doesn't meet the NSA requirement to "zeroize."
310    Ok(kdf(shared_key))
311}