/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/src/agreement.rs
Line | Count | Source (jump to first uncovered line) |
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 | | |
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 | 0 | fn eq(&self, other: &Self) -> bool { |
86 | 0 | self.curve.id == other.curve.id |
87 | 0 | } |
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 | 0 | pub fn generate( |
107 | 0 | alg: &'static Algorithm, |
108 | 0 | rng: &dyn rand::SecureRandom, |
109 | 0 | ) -> Result<Self, error::Unspecified> { |
110 | 0 | 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 | 0 | let private_key = ec::Seed::generate(alg.curve, rng, cpu_features)?; |
117 | 0 | Ok(Self { |
118 | 0 | private_key, |
119 | 0 | algorithm: alg, |
120 | 0 | }) |
121 | 0 | } |
122 | | |
123 | | /// Computes the public key from the private key. |
124 | | #[inline(always)] |
125 | 0 | pub fn compute_public_key(&self) -> Result<PublicKey, error::Unspecified> { |
126 | 0 | // NSA Guide Step 1. |
127 | 0 | // |
128 | 0 | // Obviously, this only handles the part of Step 1 between the private |
129 | 0 | // key generation and the sending of the public key to the peer. `out` |
130 | 0 | // is what should be sent to the peer. |
131 | 0 | self.private_key |
132 | 0 | .compute_public_key(cpu::features()) |
133 | 0 | .map(|public_key| PublicKey { |
134 | 0 | algorithm: self.algorithm, |
135 | 0 | bytes: public_key, |
136 | 0 | }) |
137 | 0 | } |
138 | | |
139 | | /// The algorithm for the private key. |
140 | | #[inline] |
141 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
142 | 0 | self.algorithm |
143 | 0 | } |
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 | 0 | fn as_ref(&self) -> &[u8] { |
167 | 0 | self.bytes.as_ref() |
168 | 0 | } |
169 | | } |
170 | | |
171 | | impl core::fmt::Debug for PublicKey { |
172 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
173 | 0 | f.debug_struct("PublicKey") |
174 | 0 | .field("algorithm", &self.algorithm) |
175 | 0 | .field("bytes", &debug::HexStr(self.as_ref())) |
176 | 0 | .finish() |
177 | 0 | } |
178 | | } |
179 | | |
180 | | impl PublicKey { |
181 | | /// The algorithm for the public key. |
182 | | #[inline] |
183 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
184 | 0 | self.algorithm |
185 | 0 | } |
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 | 0 | fn as_ref(&self) -> &[u8] { |
200 | 0 | self.bytes.as_ref() |
201 | 0 | } |
202 | | } |
203 | | |
204 | | impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B> |
205 | | where |
206 | | B: AsRef<[u8]>, |
207 | | { |
208 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
209 | 0 | f.debug_struct("UnparsedPublicKey") |
210 | 0 | .field("algorithm", &self.algorithm) |
211 | 0 | .field("bytes", &debug::HexStr(self.bytes.as_ref())) |
212 | 0 | .finish() |
213 | 0 | } |
214 | | } |
215 | | |
216 | | impl<B> UnparsedPublicKey<B> { |
217 | | /// Constructs a new `UnparsedPublicKey`. |
218 | 0 | pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self { |
219 | 0 | Self { algorithm, bytes } |
220 | 0 | } |
221 | | |
222 | | /// The algorithm for the public key. |
223 | | #[inline] |
224 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
225 | 0 | self.algorithm |
226 | 0 | } |
227 | | |
228 | | /// TODO: doc |
229 | | #[inline] |
230 | 0 | pub fn bytes(&self) -> &B { |
231 | 0 | &self.bytes |
232 | 0 | } |
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 | 0 | pub fn agree_ephemeral<B: AsRef<[u8]>, R>( |
254 | 0 | my_private_key: EphemeralPrivateKey, |
255 | 0 | peer_public_key: &UnparsedPublicKey<B>, |
256 | 0 | kdf: impl FnOnce(&[u8]) -> R, |
257 | 0 | ) -> Result<R, error::Unspecified> { |
258 | 0 | let peer_public_key = UnparsedPublicKey { |
259 | 0 | algorithm: peer_public_key.algorithm, |
260 | 0 | bytes: peer_public_key.bytes.as_ref(), |
261 | 0 | }; |
262 | 0 | agree_ephemeral_(my_private_key, peer_public_key, kdf, cpu::features()) |
263 | 0 | } |
264 | | |
265 | 0 | fn agree_ephemeral_<R>( |
266 | 0 | my_private_key: EphemeralPrivateKey, |
267 | 0 | peer_public_key: UnparsedPublicKey<&[u8]>, |
268 | 0 | kdf: impl FnOnce(&[u8]) -> R, |
269 | 0 | cpu: cpu::Features, |
270 | 0 | ) -> Result<R, error::Unspecified> { |
271 | 0 | // NSA Guide Prerequisite 1. |
272 | 0 | // |
273 | 0 | // The domain parameters are hard-coded. This check verifies that the |
274 | 0 | // peer's public key's domain parameters match the domain parameters of |
275 | 0 | // this private key. |
276 | 0 | if peer_public_key.algorithm != my_private_key.algorithm { |
277 | 0 | return Err(error::Unspecified); |
278 | 0 | } |
279 | 0 |
|
280 | 0 | let alg = &my_private_key.algorithm; |
281 | 0 |
|
282 | 0 | // NSA Guide Prerequisite 2, regarding which KDFs are allowed, is delegated |
283 | 0 | // to the caller. |
284 | 0 |
|
285 | 0 | // NSA Guide Prerequisite 3, "Prior to or during the key-agreement process, |
286 | 0 | // each party shall obtain the identifier associated with the other party |
287 | 0 | // during the key-agreement scheme," is delegated to the caller. |
288 | 0 |
|
289 | 0 | // NSA Guide Step 1 is handled by `EphemeralPrivateKey::generate()` and |
290 | 0 | // `EphemeralPrivateKey::compute_public_key()`. |
291 | 0 |
|
292 | 0 | let mut shared_key = [0u8; ec::ELEM_MAX_BYTES]; |
293 | 0 | let shared_key = &mut shared_key[..alg.curve.elem_scalar_seed_len]; |
294 | 0 |
|
295 | 0 | // NSA Guide Steps 2, 3, and 4. |
296 | 0 | // |
297 | 0 | // We have a pretty liberal interpretation of the NIST's spec's "Destroy" |
298 | 0 | // that doesn't meet the NSA requirement to "zeroize." |
299 | 0 | (alg.ecdh)( |
300 | 0 | shared_key, |
301 | 0 | &my_private_key.private_key, |
302 | 0 | untrusted::Input::from(peer_public_key.bytes), |
303 | 0 | cpu, |
304 | 0 | )?; |
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 | 0 | Ok(kdf(shared_key)) |
311 | 0 | } |