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 | cpu: cpu::Features, |
78 | ) -> Result<(), error::Unspecified>, |
79 | } |
80 | |
81 | derive_debug_via_field!(Algorithm, curve); |
82 | |
83 | impl Eq for Algorithm {} |
84 | impl 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. |
93 | pub struct EphemeralPrivateKey { |
94 | private_key: ec::Seed, |
95 | algorithm: &'static Algorithm, |
96 | } |
97 | |
98 | derive_debug_via_field!( |
99 | EphemeralPrivateKey, |
100 | stringify!(EphemeralPrivateKey), |
101 | algorithm |
102 | ); |
103 | |
104 | impl 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)] |
160 | pub struct PublicKey { |
161 | algorithm: &'static Algorithm, |
162 | bytes: ec::PublicKey, |
163 | } |
164 | |
165 | impl AsRef<[u8]> for PublicKey { |
166 | fn as_ref(&self) -> &[u8] { |
167 | self.bytes.as_ref() |
168 | } |
169 | } |
170 | |
171 | impl core::fmt::Debug for PublicKey { |
172 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
173 | f&mut DebugStruct<'_, '_>.debug_struct("PublicKey" ) |
174 | .field("algorithm" , &self.algorithm) |
175 | .field(name:"bytes" , &debug::HexStr(self.as_ref())) |
176 | .finish() |
177 | } |
178 | } |
179 | |
180 | impl 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)] |
190 | pub struct UnparsedPublicKey<B> { |
191 | algorithm: &'static Algorithm, |
192 | bytes: B, |
193 | } |
194 | |
195 | impl<B> AsRef<[u8]> for UnparsedPublicKey<B> |
196 | where |
197 | B: AsRef<[u8]>, |
198 | { |
199 | fn as_ref(&self) -> &[u8] { |
200 | self.bytes.as_ref() |
201 | } |
202 | } |
203 | |
204 | impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B> |
205 | where |
206 | B: AsRef<[u8]>, |
207 | { |
208 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
209 | f&mut DebugStruct<'_, '_>.debug_struct("UnparsedPublicKey" ) |
210 | .field("algorithm" , &self.algorithm) |
211 | .field(name:"bytes" , &debug::HexStr(self.bytes.as_ref())) |
212 | .finish() |
213 | } |
214 | } |
215 | |
216 | impl<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 ] |
253 | pub 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<&[u8]> = 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:cpu::features()) |
263 | } |
264 | |
265 | fn 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 | } |
312 | |