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 | |
63 | use crate::{cpu, debug, ec, error, rand}; |
64 | |
65 | pub use crate::ec::{ |
66 | curve25519::x25519::X25519, |
67 | suite_b::ecdh::{ECDH_P256, ECDH_P384}, |
68 | }; |
69 | |
70 | /// A key agreement algorithm. |
71 | pub 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 | |
80 | derive_debug_via_field!(Algorithm, curve); |
81 | |
82 | impl Eq for Algorithm {} |
83 | impl 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. |
92 | pub struct EphemeralPrivateKey { |
93 | private_key: ec::Seed, |
94 | algorithm: &'static Algorithm, |
95 | } |
96 | |
97 | derive_debug_via_field!( |
98 | EphemeralPrivateKey, |
99 | stringify!(EphemeralPrivateKey), |
100 | algorithm |
101 | ); |
102 | |
103 | impl 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)] |
152 | pub struct PublicKey { |
153 | algorithm: &'static Algorithm, |
154 | bytes: ec::PublicKey, |
155 | } |
156 | |
157 | impl AsRef<[u8]> for PublicKey { |
158 | fn as_ref(&self) -> &[u8] { |
159 | self.bytes.as_ref() |
160 | } |
161 | } |
162 | |
163 | impl 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 | |
172 | impl 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)] |
182 | pub struct UnparsedPublicKey<B> { |
183 | algorithm: &'static Algorithm, |
184 | bytes: B, |
185 | } |
186 | |
187 | impl<B> AsRef<[u8]> for UnparsedPublicKey<B> |
188 | where |
189 | B: AsRef<[u8]>, |
190 | { |
191 | fn as_ref(&self) -> &[u8] { |
192 | self.bytes.as_ref() |
193 | } |
194 | } |
195 | |
196 | impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B> |
197 | where |
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 | |
208 | impl<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 ] |
245 | pub 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 | |
257 | fn 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 | |