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}