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