/rust/registry/src/index.crates.io-6f17d22bba15001f/aws-lc-rs-1.13.0/src/agreement.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2015-2017 Brian Smith. |
2 | | // SPDX-License-Identifier: ISC |
3 | | // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
4 | | // SPDX-License-Identifier: Apache-2.0 OR ISC |
5 | | |
6 | | //! Key Agreement: ECDH, including X25519. |
7 | | //! |
8 | | //! # Example |
9 | | //! |
10 | | //! Note that this example uses X25519, but ECDH using NIST P-256/P-384 is done |
11 | | //! exactly the same way, just substituting |
12 | | //! `agreement::ECDH_P256`/`agreement::ECDH_P384` for `agreement::X25519`. |
13 | | //! |
14 | | //! ``` |
15 | | //! use aws_lc_rs::{agreement, rand}; |
16 | | //! |
17 | | //! let rng = rand::SystemRandom::new(); |
18 | | //! |
19 | | //! let my_private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?; |
20 | | //! |
21 | | //! // Make `my_public_key` a byte slice containing my public key. In a real |
22 | | //! // application, this would be sent to the peer in an encoded protocol |
23 | | //! // message. |
24 | | //! let my_public_key = my_private_key.compute_public_key()?; |
25 | | //! |
26 | | //! let peer_public_key = { |
27 | | //! // In a real application, the peer public key would be parsed out of a |
28 | | //! // protocol message. Here we just generate one. |
29 | | //! let peer_public_key = { |
30 | | //! let peer_private_key = |
31 | | //! agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?; |
32 | | //! peer_private_key.compute_public_key()? |
33 | | //! }; |
34 | | //! |
35 | | //! agreement::UnparsedPublicKey::new(&agreement::X25519, peer_public_key) |
36 | | //! }; |
37 | | //! |
38 | | //! agreement::agree_ephemeral( |
39 | | //! my_private_key, |
40 | | //! &peer_public_key, |
41 | | //! aws_lc_rs::error::Unspecified, |
42 | | //! |_key_material| { |
43 | | //! // In a real application, we'd apply a KDF to the key material and the |
44 | | //! // public keys (as recommended in RFC 7748) and then derive session |
45 | | //! // keys from the result. We omit all that here. |
46 | | //! Ok(()) |
47 | | //! }, |
48 | | //! )?; |
49 | | //! |
50 | | //! # Ok::<(), aws_lc_rs::error::Unspecified>(()) |
51 | | //! ``` |
52 | | mod ephemeral; |
53 | | |
54 | | use crate::ec::encoding::sec1::{ |
55 | | marshal_sec1_private_key, marshal_sec1_public_point, marshal_sec1_public_point_into_buffer, |
56 | | parse_sec1_private_bn, |
57 | | }; |
58 | | #[cfg(feature = "fips")] |
59 | | use crate::ec::validate_ec_evp_key; |
60 | | #[cfg(not(feature = "fips"))] |
61 | | use crate::ec::verify_evp_key_nid; |
62 | | use crate::ec::{encoding, evp_key_generate}; |
63 | | use crate::error::{KeyRejected, Unspecified}; |
64 | | use crate::hex; |
65 | | use crate::ptr::ConstPointer; |
66 | | pub use ephemeral::{agree_ephemeral, EphemeralPrivateKey}; |
67 | | |
68 | | use crate::aws_lc::{ |
69 | | EVP_PKEY_derive, EVP_PKEY_derive_init, EVP_PKEY_derive_set_peer, EVP_PKEY_get0_EC_KEY, |
70 | | NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, EVP_PKEY, EVP_PKEY_EC, EVP_PKEY_X25519, |
71 | | NID_X25519, |
72 | | }; |
73 | | |
74 | | use crate::buffer::Buffer; |
75 | | use crate::ec; |
76 | | use crate::ec::encoding::rfc5915::parse_rfc5915_private_key; |
77 | | use crate::encoding::{ |
78 | | AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der, |
79 | | EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, Pkcs8V1Der, PublicKeyX509Der, |
80 | | }; |
81 | | use crate::evp_pkey::No_EVP_PKEY_CTX_consumer; |
82 | | use crate::fips::indicator_check; |
83 | | use crate::pkcs8::Version; |
84 | | use crate::ptr::LcPtr; |
85 | | use core::fmt; |
86 | | use core::fmt::{Debug, Formatter}; |
87 | | use core::ptr::null_mut; |
88 | | |
89 | | #[allow(non_camel_case_types)] |
90 | | #[derive(PartialEq, Eq)] |
91 | | enum AlgorithmID { |
92 | | ECDH_P256, |
93 | | ECDH_P384, |
94 | | ECDH_P521, |
95 | | X25519, |
96 | | } |
97 | | |
98 | | impl AlgorithmID { |
99 | | #[inline] |
100 | 0 | const fn nid(&self) -> i32 { |
101 | 0 | match self { |
102 | 0 | AlgorithmID::ECDH_P256 => NID_X9_62_prime256v1, |
103 | 0 | AlgorithmID::ECDH_P384 => NID_secp384r1, |
104 | 0 | AlgorithmID::ECDH_P521 => NID_secp521r1, |
105 | 0 | AlgorithmID::X25519 => NID_X25519, |
106 | | } |
107 | 0 | } |
108 | | |
109 | | // Uncompressed public key length in bytes |
110 | | #[inline] |
111 | 0 | const fn pub_key_len(&self) -> usize { |
112 | 0 | match self { |
113 | 0 | AlgorithmID::ECDH_P256 => ec::uncompressed_public_key_size_bytes(256), |
114 | 0 | AlgorithmID::ECDH_P384 => ec::uncompressed_public_key_size_bytes(384), |
115 | 0 | AlgorithmID::ECDH_P521 => ec::uncompressed_public_key_size_bytes(521), |
116 | 0 | AlgorithmID::X25519 => 32, |
117 | | } |
118 | 0 | } |
119 | | |
120 | | #[inline] |
121 | 0 | const fn private_key_len(&self) -> usize { |
122 | 0 | match self { |
123 | 0 | AlgorithmID::ECDH_P256 | AlgorithmID::X25519 => 32, |
124 | 0 | AlgorithmID::ECDH_P384 => 48, |
125 | 0 | AlgorithmID::ECDH_P521 => 66, |
126 | | } |
127 | 0 | } |
128 | | } |
129 | | |
130 | | impl Debug for AlgorithmID { |
131 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { |
132 | 0 | let output = match self { |
133 | 0 | AlgorithmID::ECDH_P256 => "curve: P256", |
134 | 0 | AlgorithmID::ECDH_P384 => "curve: P384", |
135 | 0 | AlgorithmID::ECDH_P521 => "curve: P521", |
136 | 0 | AlgorithmID::X25519 => "curve: Curve25519", |
137 | | }; |
138 | 0 | f.write_str(output) |
139 | 0 | } |
140 | | } |
141 | | |
142 | | /// A key agreement algorithm. |
143 | | #[derive(PartialEq, Eq)] |
144 | | pub struct Algorithm { |
145 | | id: AlgorithmID, |
146 | | } |
147 | | |
148 | | impl Debug for Algorithm { |
149 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { |
150 | 0 | f.write_str(&format!("Algorithm {{ {:?} }}", self.id)) |
151 | 0 | } |
152 | | } |
153 | | |
154 | | /// ECDH using the NSA Suite B P-256 (secp256r1) curve. |
155 | | pub const ECDH_P256: Algorithm = Algorithm { |
156 | | id: AlgorithmID::ECDH_P256, |
157 | | }; |
158 | | |
159 | | /// ECDH using the NSA Suite B P-384 (secp384r1) curve. |
160 | | pub const ECDH_P384: Algorithm = Algorithm { |
161 | | id: AlgorithmID::ECDH_P384, |
162 | | }; |
163 | | |
164 | | /// ECDH using the NSA Suite B P-521 (secp521r1) curve. |
165 | | pub const ECDH_P521: Algorithm = Algorithm { |
166 | | id: AlgorithmID::ECDH_P521, |
167 | | }; |
168 | | |
169 | | /// X25519 (ECDH using Curve25519) as described in [RFC 7748]. |
170 | | /// |
171 | | /// Everything is as described in RFC 7748. Key agreement will fail if the |
172 | | /// result of the X25519 operation is zero; see the notes on the |
173 | | /// "all-zero value" in [RFC 7748 section 6.1]. |
174 | | /// |
175 | | /// [RFC 7748]: https://tools.ietf.org/html/rfc7748 |
176 | | /// [RFC 7748 section 6.1]: https://tools.ietf.org/html/rfc7748#section-6.1 |
177 | | pub const X25519: Algorithm = Algorithm { |
178 | | id: AlgorithmID::X25519, |
179 | | }; |
180 | | |
181 | | #[allow(non_camel_case_types)] |
182 | | enum KeyInner { |
183 | | ECDH_P256(LcPtr<EVP_PKEY>), |
184 | | ECDH_P384(LcPtr<EVP_PKEY>), |
185 | | ECDH_P521(LcPtr<EVP_PKEY>), |
186 | | X25519(LcPtr<EVP_PKEY>), |
187 | | } |
188 | | |
189 | | impl Clone for KeyInner { |
190 | 0 | fn clone(&self) -> KeyInner { |
191 | 0 | match self { |
192 | 0 | KeyInner::ECDH_P256(evp_pkey) => KeyInner::ECDH_P256(evp_pkey.clone()), |
193 | 0 | KeyInner::ECDH_P384(evp_pkey) => KeyInner::ECDH_P384(evp_pkey.clone()), |
194 | 0 | KeyInner::ECDH_P521(evp_pkey) => KeyInner::ECDH_P521(evp_pkey.clone()), |
195 | 0 | KeyInner::X25519(evp_pkey) => KeyInner::X25519(evp_pkey.clone()), |
196 | | } |
197 | 0 | } |
198 | | } |
199 | | |
200 | | /// A private key for use (only) with `agree`. The |
201 | | /// signature of `agree` allows `PrivateKey` to be |
202 | | /// used for more than one key agreement. |
203 | | pub struct PrivateKey { |
204 | | inner_key: KeyInner, |
205 | | } |
206 | | |
207 | | impl KeyInner { |
208 | | #[inline] |
209 | 0 | fn algorithm(&self) -> &'static Algorithm { |
210 | 0 | match self { |
211 | 0 | KeyInner::ECDH_P256(..) => &ECDH_P256, |
212 | 0 | KeyInner::ECDH_P384(..) => &ECDH_P384, |
213 | 0 | KeyInner::ECDH_P521(..) => &ECDH_P521, |
214 | 0 | KeyInner::X25519(..) => &X25519, |
215 | | } |
216 | 0 | } |
217 | | |
218 | 0 | fn get_evp_pkey(&self) -> &LcPtr<EVP_PKEY> { |
219 | 0 | match self { |
220 | 0 | KeyInner::ECDH_P256(evp_pkey) |
221 | 0 | | KeyInner::ECDH_P384(evp_pkey) |
222 | 0 | | KeyInner::ECDH_P521(evp_pkey) |
223 | 0 | | KeyInner::X25519(evp_pkey) => evp_pkey, |
224 | 0 | } |
225 | 0 | } |
226 | | } |
227 | | |
228 | | unsafe impl Send for PrivateKey {} |
229 | | |
230 | | // https://github.com/awslabs/aws-lc/blob/main/include/openssl/ec_key.h#L88 |
231 | | // An |EC_KEY| object represents a public or private EC key. A given object may |
232 | | // be used concurrently on multiple threads by non-mutating functions, provided |
233 | | // no other thread is concurrently calling a mutating function. Unless otherwise |
234 | | // documented, functions which take a |const| pointer are non-mutating and |
235 | | // functions which take a non-|const| pointer are mutating. |
236 | | unsafe impl Sync for PrivateKey {} |
237 | | |
238 | | impl Debug for PrivateKey { |
239 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { |
240 | 0 | f.write_str(&format!( |
241 | 0 | "PrivateKey {{ algorithm: {:?} }}", |
242 | 0 | self.inner_key.algorithm() |
243 | 0 | )) |
244 | 0 | } |
245 | | } |
246 | | |
247 | | impl PrivateKey { |
248 | 0 | fn new(alg: &'static Algorithm, evp_pkey: LcPtr<EVP_PKEY>) -> Self { |
249 | 0 | match alg.id { |
250 | 0 | AlgorithmID::X25519 => Self { |
251 | 0 | inner_key: KeyInner::X25519(evp_pkey), |
252 | 0 | }, |
253 | 0 | AlgorithmID::ECDH_P256 => Self { |
254 | 0 | inner_key: KeyInner::ECDH_P256(evp_pkey), |
255 | 0 | }, |
256 | 0 | AlgorithmID::ECDH_P384 => Self { |
257 | 0 | inner_key: KeyInner::ECDH_P384(evp_pkey), |
258 | 0 | }, |
259 | 0 | AlgorithmID::ECDH_P521 => Self { |
260 | 0 | inner_key: KeyInner::ECDH_P521(evp_pkey), |
261 | 0 | }, |
262 | | } |
263 | 0 | } |
264 | | |
265 | | #[inline] |
266 | | /// Generate a new private key for the given algorithm. |
267 | | // # FIPS |
268 | | // Use this function with one of the following algorithms: |
269 | | // * `ECDH_P256` |
270 | | // * `ECDH_P384` |
271 | | // * `ECDH_P521` |
272 | | // |
273 | | /// # Errors |
274 | | /// `error::Unspecified` when operation fails due to internal error. |
275 | 0 | pub fn generate(alg: &'static Algorithm) -> Result<Self, Unspecified> { |
276 | 0 | let evp_pkey = match alg.id { |
277 | 0 | AlgorithmID::X25519 => generate_x25519()?, |
278 | 0 | _ => evp_key_generate(alg.id.nid())?, |
279 | | }; |
280 | 0 | Ok(Self::new(alg, evp_pkey)) |
281 | 0 | } |
282 | | |
283 | | /// Deserializes a DER-encoded private key structure to produce a `agreement::PrivateKey`. |
284 | | /// |
285 | | /// This function is typically used to deserialize RFC 5915 encoded private keys, but it will |
286 | | /// attempt to automatically detect other key formats. This function supports unencrypted |
287 | | /// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats. |
288 | | /// |
289 | | /// X25519 keys are not supported. See `PrivateKey::as_der`. |
290 | | /// |
291 | | /// # Errors |
292 | | /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. |
293 | | /// |
294 | | /// # Panics |
295 | 0 | pub fn from_private_key_der( |
296 | 0 | alg: &'static Algorithm, |
297 | 0 | key_bytes: &[u8], |
298 | 0 | ) -> Result<Self, KeyRejected> { |
299 | 0 | if AlgorithmID::X25519 == alg.id { |
300 | 0 | return Err(KeyRejected::invalid_encoding()); |
301 | 0 | } |
302 | 0 | let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(key_bytes, EVP_PKEY_EC) |
303 | 0 | .or(parse_rfc5915_private_key(key_bytes, alg.id.nid()))?; |
304 | | #[cfg(not(feature = "fips"))] |
305 | 0 | verify_evp_key_nid(&evp_pkey.as_const(), alg.id.nid())?; |
306 | | #[cfg(feature = "fips")] |
307 | | validate_ec_evp_key(&evp_pkey.as_const(), alg.id.nid())?; |
308 | | |
309 | 0 | Ok(Self::new(alg, evp_pkey)) |
310 | 0 | } |
311 | | |
312 | | /// Constructs an ECDH key from private key bytes |
313 | | /// |
314 | | /// The private key must encoded as a big-endian fixed-length integer. For |
315 | | /// example, a P-256 private key must be 32 bytes prefixed with leading |
316 | | /// zeros as needed. |
317 | | /// |
318 | | /// # Errors |
319 | | /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. |
320 | 0 | pub fn from_private_key( |
321 | 0 | alg: &'static Algorithm, |
322 | 0 | key_bytes: &[u8], |
323 | 0 | ) -> Result<Self, KeyRejected> { |
324 | 0 | if key_bytes.len() != alg.id.private_key_len() { |
325 | 0 | return Err(KeyRejected::wrong_algorithm()); |
326 | 0 | } |
327 | 0 | let evp_pkey = if AlgorithmID::X25519 == alg.id { |
328 | 0 | LcPtr::<EVP_PKEY>::parse_raw_private_key(key_bytes, EVP_PKEY_X25519)? |
329 | | } else { |
330 | 0 | parse_sec1_private_bn(key_bytes, alg.id.nid())? |
331 | | }; |
332 | 0 | Ok(Self::new(alg, evp_pkey)) |
333 | 0 | } |
334 | | |
335 | | #[cfg(test)] |
336 | | #[allow(missing_docs, clippy::missing_errors_doc)] |
337 | | pub fn generate_for_test( |
338 | | alg: &'static Algorithm, |
339 | | rng: &dyn crate::rand::SecureRandom, |
340 | | ) -> Result<Self, Unspecified> { |
341 | | match alg.id { |
342 | | AlgorithmID::X25519 => { |
343 | | let mut priv_key = [0u8; AlgorithmID::X25519.private_key_len()]; |
344 | | rng.fill(&mut priv_key)?; |
345 | | Self::from_x25519_private_key(&priv_key) |
346 | | } |
347 | | AlgorithmID::ECDH_P256 => { |
348 | | let mut priv_key = [0u8; AlgorithmID::ECDH_P256.private_key_len()]; |
349 | | rng.fill(&mut priv_key)?; |
350 | | Self::from_p256_private_key(&priv_key) |
351 | | } |
352 | | AlgorithmID::ECDH_P384 => { |
353 | | let mut priv_key = [0u8; AlgorithmID::ECDH_P384.private_key_len()]; |
354 | | rng.fill(&mut priv_key)?; |
355 | | Self::from_p384_private_key(&priv_key) |
356 | | } |
357 | | AlgorithmID::ECDH_P521 => { |
358 | | let mut priv_key = [0u8; AlgorithmID::ECDH_P521.private_key_len()]; |
359 | | rng.fill(&mut priv_key)?; |
360 | | Self::from_p521_private_key(&priv_key) |
361 | | } |
362 | | } |
363 | | } |
364 | | |
365 | | #[cfg(test)] |
366 | | fn from_x25519_private_key( |
367 | | priv_key: &[u8; AlgorithmID::X25519.private_key_len()], |
368 | | ) -> Result<Self, Unspecified> { |
369 | | let pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(priv_key, EVP_PKEY_X25519)?; |
370 | | |
371 | | Ok(PrivateKey { |
372 | | inner_key: KeyInner::X25519(pkey), |
373 | | }) |
374 | | } |
375 | | |
376 | | #[cfg(test)] |
377 | | fn from_p256_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> { |
378 | | let pkey = parse_sec1_private_bn(priv_key, ECDH_P256.id.nid())?; |
379 | | Ok(PrivateKey { |
380 | | inner_key: KeyInner::ECDH_P256(pkey), |
381 | | }) |
382 | | } |
383 | | |
384 | | #[cfg(test)] |
385 | | fn from_p384_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> { |
386 | | let pkey = parse_sec1_private_bn(priv_key, ECDH_P384.id.nid())?; |
387 | | Ok(PrivateKey { |
388 | | inner_key: KeyInner::ECDH_P384(pkey), |
389 | | }) |
390 | | } |
391 | | |
392 | | #[cfg(test)] |
393 | | fn from_p521_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> { |
394 | | let pkey = parse_sec1_private_bn(priv_key, ECDH_P521.id.nid())?; |
395 | | Ok(PrivateKey { |
396 | | inner_key: KeyInner::ECDH_P521(pkey), |
397 | | }) |
398 | | } |
399 | | |
400 | | /// Computes the public key from the private key. |
401 | | /// |
402 | | /// # Errors |
403 | | /// `error::Unspecified` when operation fails due to internal error. |
404 | 0 | pub fn compute_public_key(&self) -> Result<PublicKey, Unspecified> { |
405 | 0 | match &self.inner_key { |
406 | 0 | KeyInner::ECDH_P256(evp_pkey) |
407 | 0 | | KeyInner::ECDH_P384(evp_pkey) |
408 | 0 | | KeyInner::ECDH_P521(evp_pkey) => { |
409 | 0 | let mut public_key = [0u8; MAX_PUBLIC_KEY_LEN]; |
410 | 0 | let len = marshal_sec1_public_point_into_buffer(&mut public_key, evp_pkey, false)?; |
411 | 0 | Ok(PublicKey { |
412 | 0 | inner_key: self.inner_key.clone(), |
413 | 0 | key_bytes: public_key, |
414 | 0 | len, |
415 | 0 | }) |
416 | | } |
417 | 0 | KeyInner::X25519(priv_key) => { |
418 | 0 | let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN]; |
419 | 0 | let out_len = priv_key.marshal_raw_public_to_buffer(&mut buffer)?; |
420 | 0 | Ok(PublicKey { |
421 | 0 | inner_key: self.inner_key.clone(), |
422 | 0 | key_bytes: buffer, |
423 | 0 | len: out_len, |
424 | 0 | }) |
425 | | } |
426 | | } |
427 | 0 | } |
428 | | |
429 | | /// The algorithm for the private key. |
430 | | #[inline] |
431 | | #[must_use] |
432 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
433 | 0 | self.inner_key.algorithm() |
434 | 0 | } |
435 | | } |
436 | | |
437 | | impl AsDer<EcPrivateKeyRfc5915Der<'static>> for PrivateKey { |
438 | | /// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. |
439 | | /// |
440 | | /// X25519 is not supported. |
441 | | /// |
442 | | /// # Errors |
443 | | /// `error::Unspecified` if serialization failed. |
444 | 0 | fn as_der(&self) -> Result<EcPrivateKeyRfc5915Der<'static>, Unspecified> { |
445 | 0 | if AlgorithmID::X25519 == self.inner_key.algorithm().id { |
446 | 0 | return Err(Unspecified); |
447 | 0 | } |
448 | 0 |
|
449 | 0 | let mut outp = null_mut::<u8>(); |
450 | 0 | let ec_key = { |
451 | 0 | ConstPointer::new(unsafe { |
452 | 0 | EVP_PKEY_get0_EC_KEY(*self.inner_key.get_evp_pkey().as_const()) |
453 | 0 | })? |
454 | | }; |
455 | 0 | let length = usize::try_from(unsafe { aws_lc::i2d_ECPrivateKey(*ec_key, &mut outp) }) |
456 | 0 | .map_err(|_| Unspecified)?; |
457 | 0 | let mut outp = LcPtr::new(outp)?; |
458 | 0 | Ok(EcPrivateKeyRfc5915Der::take_from_slice(unsafe { |
459 | 0 | core::slice::from_raw_parts_mut(*outp.as_mut(), length) |
460 | 0 | })) |
461 | 0 | } |
462 | | } |
463 | | |
464 | | impl AsDer<Pkcs8V1Der<'static>> for PrivateKey { |
465 | | /// Serializes the key as a PKCS #8 private key structure. |
466 | | /// |
467 | | /// X25519 is not supported. |
468 | | /// |
469 | | /// # Errors |
470 | | /// `error::Unspecified` if serialization failed. |
471 | 0 | fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> { |
472 | 0 | if AlgorithmID::X25519 == self.inner_key.algorithm().id { |
473 | 0 | return Err(Unspecified); |
474 | 0 | } |
475 | 0 |
|
476 | 0 | Ok(Pkcs8V1Der::new( |
477 | 0 | self.inner_key |
478 | 0 | .get_evp_pkey() |
479 | 0 | .marshal_rfc5208_private_key(Version::V1)?, |
480 | | )) |
481 | 0 | } |
482 | | } |
483 | | |
484 | | impl AsBigEndian<EcPrivateKeyBin<'static>> for PrivateKey { |
485 | | /// Exposes the private key encoded as a big-endian fixed-length integer. |
486 | | /// |
487 | | /// X25519 is not supported. |
488 | | /// |
489 | | /// # Errors |
490 | | /// `error::Unspecified` if serialization failed. |
491 | 0 | fn as_be_bytes(&self) -> Result<EcPrivateKeyBin<'static>, Unspecified> { |
492 | 0 | if AlgorithmID::X25519 == self.inner_key.algorithm().id { |
493 | 0 | return Err(Unspecified); |
494 | 0 | } |
495 | 0 | let buffer = marshal_sec1_private_key(self.inner_key.get_evp_pkey())?; |
496 | 0 | Ok(EcPrivateKeyBin::new(buffer)) |
497 | 0 | } |
498 | | } |
499 | | |
500 | | impl AsBigEndian<Curve25519SeedBin<'static>> for PrivateKey { |
501 | | /// Exposes the seed encoded as a big-endian fixed-length integer. |
502 | | /// |
503 | | /// Only X25519 is supported. |
504 | | /// |
505 | | /// # Errors |
506 | | /// `error::Unspecified` if serialization failed. |
507 | 0 | fn as_be_bytes(&self) -> Result<Curve25519SeedBin<'static>, Unspecified> { |
508 | 0 | if AlgorithmID::X25519 != self.inner_key.algorithm().id { |
509 | 0 | return Err(Unspecified); |
510 | 0 | } |
511 | 0 | let evp_pkey = self.inner_key.get_evp_pkey(); |
512 | 0 | Ok(Curve25519SeedBin::new(evp_pkey.marshal_raw_private_key()?)) |
513 | 0 | } |
514 | | } |
515 | | |
516 | 0 | pub(crate) fn generate_x25519() -> Result<LcPtr<EVP_PKEY>, Unspecified> { |
517 | 0 | LcPtr::<EVP_PKEY>::generate(EVP_PKEY_X25519, No_EVP_PKEY_CTX_consumer) |
518 | 0 | } |
519 | | |
520 | | const MAX_PUBLIC_KEY_LEN: usize = ec::PUBLIC_KEY_MAX_LEN; |
521 | | |
522 | | /// A public key for key agreement. |
523 | | pub struct PublicKey { |
524 | | inner_key: KeyInner, |
525 | | key_bytes: [u8; MAX_PUBLIC_KEY_LEN], |
526 | | len: usize, |
527 | | } |
528 | | |
529 | | impl PublicKey { |
530 | | /// The algorithm for the public key. |
531 | | #[must_use] |
532 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
533 | 0 | self.inner_key.algorithm() |
534 | 0 | } |
535 | | } |
536 | | |
537 | | unsafe impl Send for PublicKey {} |
538 | | unsafe impl Sync for PublicKey {} |
539 | | |
540 | | impl Debug for PublicKey { |
541 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
542 | 0 | f.write_str(&format!( |
543 | 0 | "PublicKey {{ algorithm: {:?}, bytes: \"{}\" }}", |
544 | 0 | self.inner_key.algorithm(), |
545 | 0 | hex::encode(&self.key_bytes[0..self.len]) |
546 | 0 | )) |
547 | 0 | } |
548 | | } |
549 | | |
550 | | impl AsRef<[u8]> for PublicKey { |
551 | | /// Serializes the public key in an uncompressed form (X9.62) using the |
552 | | /// Octet-String-to-Elliptic-Curve-Point algorithm in |
553 | | /// [SEC 1: Elliptic Curve Cryptography, Version 2.0]. |
554 | 0 | fn as_ref(&self) -> &[u8] { |
555 | 0 | &self.key_bytes[0..self.len] |
556 | 0 | } |
557 | | } |
558 | | |
559 | | impl Clone for PublicKey { |
560 | 0 | fn clone(&self) -> Self { |
561 | 0 | PublicKey { |
562 | 0 | inner_key: self.inner_key.clone(), |
563 | 0 | key_bytes: self.key_bytes, |
564 | 0 | len: self.len, |
565 | 0 | } |
566 | 0 | } |
567 | | } |
568 | | |
569 | | impl AsDer<PublicKeyX509Der<'static>> for PublicKey { |
570 | | /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure. |
571 | | /// # Errors |
572 | | /// Returns an error if the public key fails to marshal to X.509. |
573 | 0 | fn as_der(&self) -> Result<PublicKeyX509Der<'static>, crate::error::Unspecified> { |
574 | 0 | match &self.inner_key { |
575 | 0 | KeyInner::ECDH_P256(evp_pkey) |
576 | 0 | | KeyInner::ECDH_P384(evp_pkey) |
577 | 0 | | KeyInner::ECDH_P521(evp_pkey) |
578 | 0 | | KeyInner::X25519(evp_pkey) => { |
579 | 0 | let der = evp_pkey.marshal_rfc5280_public_key()?; |
580 | 0 | Ok(PublicKeyX509Der::from(Buffer::new(der))) |
581 | | } |
582 | | } |
583 | 0 | } |
584 | | } |
585 | | |
586 | | impl AsBigEndian<EcPublicKeyCompressedBin<'static>> for PublicKey { |
587 | | /// Provides the public key elliptic curve point to a compressed point format. |
588 | | /// # Errors |
589 | | /// Returns an error if the underlying implementation is unable to marshal the public key to this format. |
590 | 0 | fn as_be_bytes(&self) -> Result<EcPublicKeyCompressedBin<'static>, crate::error::Unspecified> { |
591 | 0 | let evp_pkey = match &self.inner_key { |
592 | 0 | KeyInner::ECDH_P256(evp_pkey) |
593 | 0 | | KeyInner::ECDH_P384(evp_pkey) |
594 | 0 | | KeyInner::ECDH_P521(evp_pkey) => evp_pkey, |
595 | 0 | KeyInner::X25519(_) => return Err(Unspecified), |
596 | | }; |
597 | 0 | let pub_point = marshal_sec1_public_point(evp_pkey, true)?; |
598 | 0 | Ok(EcPublicKeyCompressedBin::new(pub_point)) |
599 | 0 | } |
600 | | } |
601 | | |
602 | | impl AsBigEndian<EcPublicKeyUncompressedBin<'static>> for PublicKey { |
603 | | /// Provides the public key elliptic curve point to a compressed point format. |
604 | | /// |
605 | | /// Equivalent to [`PublicKey::as_ref`] for ECDH key types, except that it provides you a copy instead of a reference. |
606 | | /// |
607 | | /// # Errors |
608 | | /// Returns an error if the underlying implementation is unable to marshal the public key to this format. |
609 | 0 | fn as_be_bytes( |
610 | 0 | &self, |
611 | 0 | ) -> Result<EcPublicKeyUncompressedBin<'static>, crate::error::Unspecified> { |
612 | 0 | if self.algorithm().id == AlgorithmID::X25519 { |
613 | 0 | return Err(Unspecified); |
614 | 0 | } |
615 | 0 |
|
616 | 0 | let mut buffer = vec![0u8; self.len]; |
617 | 0 | buffer.copy_from_slice(&self.key_bytes[0..self.len]); |
618 | 0 |
|
619 | 0 | Ok(EcPublicKeyUncompressedBin::new(buffer)) |
620 | 0 | } |
621 | | } |
622 | | |
623 | | /// An unparsed, possibly malformed, public key for key agreement. |
624 | | #[derive(Clone)] |
625 | | pub struct UnparsedPublicKey<B: AsRef<[u8]>> { |
626 | | alg: &'static Algorithm, |
627 | | bytes: B, |
628 | | } |
629 | | |
630 | | impl<B: Copy + AsRef<[u8]>> Copy for UnparsedPublicKey<B> {} |
631 | | |
632 | | impl<B: Debug + AsRef<[u8]>> Debug for UnparsedPublicKey<B> { |
633 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { |
634 | 0 | f.write_str(&format!( |
635 | 0 | "UnparsedPublicKey {{ algorithm: {:?}, bytes: {:?} }}", |
636 | 0 | self.alg, |
637 | 0 | hex::encode(self.bytes.as_ref()) |
638 | 0 | )) |
639 | 0 | } |
640 | | } |
641 | | |
642 | | impl<B: AsRef<[u8]>> UnparsedPublicKey<B> { |
643 | | /// Constructs a new `UnparsedPublicKey`. |
644 | 0 | pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self { |
645 | 0 | UnparsedPublicKey { |
646 | 0 | alg: algorithm, |
647 | 0 | bytes, |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | /// The agreement algorithm associated with this public key |
652 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
653 | 0 | self.alg |
654 | 0 | } |
655 | | |
656 | | /// The bytes provided for this public key |
657 | 0 | pub fn bytes(&self) -> &B { |
658 | 0 | &self.bytes |
659 | 0 | } |
660 | | } |
661 | | |
662 | | /// Performs a key agreement with a private key and the given public key. |
663 | | /// |
664 | | /// `my_private_key` is the private key to use. Only a reference to the key |
665 | | /// is required, allowing the key to continue to be used. |
666 | | /// |
667 | | /// `peer_public_key` is the peer's public key. `agree` will return |
668 | | /// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve. |
669 | | /// `agree` verifies that it is encoded in the standard form for the |
670 | | /// algorithm and that the key is *valid*; see the algorithm's documentation for |
671 | | /// details on how keys are to be encoded and what constitutes a valid key for |
672 | | /// that algorithm. |
673 | | /// |
674 | | /// `error_value` is the value to return if an error occurs before `kdf` is |
675 | | /// called, e.g. when decoding of the peer's public key fails or when the public |
676 | | /// key is otherwise invalid. |
677 | | /// |
678 | | /// After the key agreement is done, `agree` calls `kdf` with the raw |
679 | | /// key material from the key agreement operation and then returns what `kdf` |
680 | | /// returns. |
681 | | // # FIPS |
682 | | // Use this function with one of the following key algorithms: |
683 | | // * `ECDH_P256` |
684 | | // * `ECDH_P384` |
685 | | // * `ECDH_P521` |
686 | | // |
687 | | /// # Errors |
688 | | /// `error_value` on internal failure. |
689 | | #[inline] |
690 | | #[allow(clippy::missing_panics_doc)] |
691 | 0 | pub fn agree<B: AsRef<[u8]>, F, R, E>( |
692 | 0 | my_private_key: &PrivateKey, |
693 | 0 | peer_public_key: &UnparsedPublicKey<B>, |
694 | 0 | error_value: E, |
695 | 0 | kdf: F, |
696 | 0 | ) -> Result<R, E> |
697 | 0 | where |
698 | 0 | F: FnOnce(&[u8]) -> Result<R, E>, |
699 | 0 | { |
700 | 0 | let expected_alg = my_private_key.algorithm(); |
701 | 0 | let expected_nid = expected_alg.id.nid(); |
702 | 0 |
|
703 | 0 | if peer_public_key.alg != expected_alg { |
704 | 0 | return Err(error_value); |
705 | 0 | } |
706 | 0 |
|
707 | 0 | let peer_pub_bytes = peer_public_key.bytes.as_ref(); |
708 | 0 |
|
709 | 0 | let mut buffer = [0u8; MAX_AGREEMENT_SECRET_LEN]; |
710 | | |
711 | 0 | let secret: &[u8] = match &my_private_key.inner_key { |
712 | 0 | KeyInner::X25519(priv_key) => { |
713 | 0 | x25519_diffie_hellman(&mut buffer, priv_key, peer_pub_bytes).or(Err(error_value))? |
714 | | } |
715 | 0 | KeyInner::ECDH_P256(priv_key) |
716 | 0 | | KeyInner::ECDH_P384(priv_key) |
717 | 0 | | KeyInner::ECDH_P521(priv_key) => { |
718 | 0 | ec_key_ecdh(&mut buffer, priv_key, peer_pub_bytes, expected_nid).or(Err(error_value))? |
719 | | } |
720 | | }; |
721 | 0 | kdf(secret) |
722 | 0 | } |
723 | | |
724 | | // Current max secret length is P-521's. |
725 | | const MAX_AGREEMENT_SECRET_LEN: usize = AlgorithmID::ECDH_P521.private_key_len(); |
726 | | |
727 | | #[inline] |
728 | | #[allow(clippy::needless_pass_by_value)] |
729 | 0 | fn ec_key_ecdh<'a>( |
730 | 0 | buffer: &'a mut [u8; MAX_AGREEMENT_SECRET_LEN], |
731 | 0 | priv_key: &LcPtr<EVP_PKEY>, |
732 | 0 | peer_pub_key_bytes: &[u8], |
733 | 0 | nid: i32, |
734 | 0 | ) -> Result<&'a [u8], Unspecified> { |
735 | 0 | let mut pub_key = encoding::parse_ec_public_key(peer_pub_key_bytes, nid)?; |
736 | | |
737 | 0 | let mut pkey_ctx = priv_key.create_EVP_PKEY_CTX()?; |
738 | | |
739 | 0 | if 1 != unsafe { EVP_PKEY_derive_init(*pkey_ctx.as_mut()) } { |
740 | 0 | return Err(Unspecified); |
741 | 0 | } |
742 | 0 |
|
743 | 0 | if 1 != unsafe { EVP_PKEY_derive_set_peer(*pkey_ctx.as_mut(), *pub_key.as_mut()) } { |
744 | 0 | return Err(Unspecified); |
745 | 0 | } |
746 | 0 |
|
747 | 0 | let mut out_key_len = buffer.len(); |
748 | 0 |
|
749 | 0 | if 1 != indicator_check!(unsafe { |
750 | 0 | EVP_PKEY_derive(*pkey_ctx.as_mut(), buffer.as_mut_ptr(), &mut out_key_len) |
751 | 0 | }) { |
752 | 0 | return Err(Unspecified); |
753 | 0 | } |
754 | 0 |
|
755 | 0 | if 0 == out_key_len { |
756 | 0 | return Err(Unspecified); |
757 | 0 | } |
758 | 0 |
|
759 | 0 | Ok(&buffer[0..out_key_len]) |
760 | 0 | } |
761 | | |
762 | | #[inline] |
763 | 0 | fn x25519_diffie_hellman<'a>( |
764 | 0 | buffer: &'a mut [u8; MAX_AGREEMENT_SECRET_LEN], |
765 | 0 | priv_key: &LcPtr<EVP_PKEY>, |
766 | 0 | peer_pub_key: &[u8], |
767 | 0 | ) -> Result<&'a [u8], ()> { |
768 | 0 | let mut pkey_ctx = priv_key.create_EVP_PKEY_CTX()?; |
769 | | |
770 | 0 | if 1 != unsafe { EVP_PKEY_derive_init(*pkey_ctx.as_mut()) } { |
771 | 0 | return Err(()); |
772 | 0 | } |
773 | | |
774 | 0 | let mut pub_key = try_parse_x25519_public_key_bytes(peer_pub_key)?; |
775 | | |
776 | 0 | if 1 != unsafe { EVP_PKEY_derive_set_peer(*pkey_ctx.as_mut(), *pub_key.as_mut()) } { |
777 | 0 | return Err(()); |
778 | 0 | } |
779 | 0 |
|
780 | 0 | let mut out_key_len = buffer.len(); |
781 | 0 |
|
782 | 0 | if 1 != indicator_check!(unsafe { |
783 | 0 | EVP_PKEY_derive(*pkey_ctx.as_mut(), buffer.as_mut_ptr(), &mut out_key_len) |
784 | 0 | }) { |
785 | 0 | return Err(()); |
786 | 0 | } |
787 | 0 |
|
788 | 0 | debug_assert!(out_key_len == AlgorithmID::X25519.pub_key_len()); |
789 | | |
790 | 0 | Ok(&buffer[0..AlgorithmID::X25519.pub_key_len()]) |
791 | 0 | } |
792 | | |
793 | 0 | pub(crate) fn try_parse_x25519_public_key_bytes( |
794 | 0 | key_bytes: &[u8], |
795 | 0 | ) -> Result<LcPtr<EVP_PKEY>, Unspecified> { |
796 | 0 | LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(key_bytes, EVP_PKEY_X25519) |
797 | 0 | .or(try_parse_x25519_public_key_raw_bytes(key_bytes)) |
798 | 0 | } |
799 | | |
800 | 0 | fn try_parse_x25519_public_key_raw_bytes(key_bytes: &[u8]) -> Result<LcPtr<EVP_PKEY>, Unspecified> { |
801 | 0 | let expected_pub_key_len = X25519.id.pub_key_len(); |
802 | 0 | if key_bytes.len() != expected_pub_key_len { |
803 | 0 | return Err(Unspecified); |
804 | 0 | } |
805 | 0 |
|
806 | 0 | Ok(LcPtr::<EVP_PKEY>::parse_raw_public_key( |
807 | 0 | key_bytes, |
808 | 0 | EVP_PKEY_X25519, |
809 | 0 | )?) |
810 | 0 | } |
811 | | |
812 | | #[cfg(test)] |
813 | | mod tests { |
814 | | use crate::agreement::{ |
815 | | agree, Algorithm, PrivateKey, PublicKey, UnparsedPublicKey, ECDH_P256, ECDH_P384, |
816 | | ECDH_P521, X25519, |
817 | | }; |
818 | | use crate::encoding::{ |
819 | | AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der, |
820 | | EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, Pkcs8V1Der, PublicKeyX509Der, |
821 | | }; |
822 | | use crate::{rand, test}; |
823 | | |
824 | | #[test] |
825 | | fn test_agreement_x25519() { |
826 | | let alg = &X25519; |
827 | | let peer_public = UnparsedPublicKey::new( |
828 | | alg, |
829 | | test::from_dirty_hex( |
830 | | "e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c", |
831 | | ), |
832 | | ); |
833 | | |
834 | | let my_private = test::from_dirty_hex( |
835 | | "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4", |
836 | | ); |
837 | | |
838 | | let my_private = { |
839 | | let rng = test::rand::FixedSliceRandom { bytes: &my_private }; |
840 | | PrivateKey::generate_for_test(alg, &rng).unwrap() |
841 | | }; |
842 | | |
843 | | let my_public = test::from_dirty_hex( |
844 | | "1c9fd88f45606d932a80c71824ae151d15d73e77de38e8e000852e614fae7019", |
845 | | ); |
846 | | let output = test::from_dirty_hex( |
847 | | "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552", |
848 | | ); |
849 | | |
850 | | assert_eq!(my_private.algorithm(), alg); |
851 | | |
852 | | let be_private_key_buffer: Curve25519SeedBin = my_private.as_be_bytes().unwrap(); |
853 | | let be_private_key = |
854 | | PrivateKey::from_private_key(&X25519, be_private_key_buffer.as_ref()).unwrap(); |
855 | | { |
856 | | let result = agree(&be_private_key, &peer_public, (), |key_material| { |
857 | | assert_eq!(key_material, &output[..]); |
858 | | Ok(()) |
859 | | }); |
860 | | assert_eq!(result, Ok(())); |
861 | | } |
862 | | |
863 | | let computed_public = my_private.compute_public_key().unwrap(); |
864 | | assert_eq!(computed_public.as_ref(), &my_public[..]); |
865 | | |
866 | | assert_eq!(computed_public.algorithm(), alg); |
867 | | { |
868 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
869 | | assert_eq!(key_material, &output[..]); |
870 | | Ok(()) |
871 | | }); |
872 | | assert_eq!(result, Ok(())); |
873 | | } |
874 | | { |
875 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
876 | | assert_eq!(key_material, &output[..]); |
877 | | Ok(()) |
878 | | }); |
879 | | assert_eq!(result, Ok(())); |
880 | | } |
881 | | } |
882 | | |
883 | | #[test] |
884 | | fn test_agreement_invalid_keys() { |
885 | | fn test_with_key(alg: &'static Algorithm, my_private_key: &PrivateKey, test_key: &[u8]) { |
886 | | assert!(PrivateKey::from_private_key(alg, test_key).is_err()); |
887 | | assert!(PrivateKey::from_private_key_der(alg, test_key).is_err()); |
888 | | assert!(agree( |
889 | | my_private_key, |
890 | | &UnparsedPublicKey::new(alg, test_key), |
891 | | (), |
892 | | |_| Ok(()) |
893 | | ) |
894 | | .is_err()); |
895 | | } |
896 | | |
897 | | let alg_variants: [&'static Algorithm; 4] = [&X25519, &ECDH_P256, &ECDH_P384, &ECDH_P521]; |
898 | | |
899 | | for alg in alg_variants { |
900 | | let my_private_key = PrivateKey::generate(alg).unwrap(); |
901 | | |
902 | | let empty_key = []; |
903 | | test_with_key(alg, &my_private_key, &empty_key); |
904 | | |
905 | | let wrong_size_key: [u8; 31] = [ |
906 | | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
907 | | 23, 24, 25, 26, 27, 28, 29, 30, |
908 | | ]; |
909 | | test_with_key(alg, &my_private_key, &wrong_size_key); |
910 | | } |
911 | | } |
912 | | |
913 | | #[test] |
914 | | fn test_agreement_ecdh_p256() { |
915 | | let alg = &ECDH_P256; |
916 | | let peer_public = UnparsedPublicKey::new( |
917 | | alg, |
918 | | test::from_dirty_hex( |
919 | | "04D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF6356FBF3CA366CC23E8157854C13C58D6AAC23F046ADA30F8353E74F33039872AB", |
920 | | ), |
921 | | ); |
922 | | assert_eq!(peer_public.algorithm(), alg); |
923 | | assert_eq!(peer_public.bytes(), &peer_public.bytes); |
924 | | |
925 | | let my_private = test::from_dirty_hex( |
926 | | "C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433", |
927 | | ); |
928 | | |
929 | | let my_private = { |
930 | | let rng = test::rand::FixedSliceRandom { bytes: &my_private }; |
931 | | PrivateKey::generate_for_test(alg, &rng).unwrap() |
932 | | }; |
933 | | |
934 | | let my_public = test::from_dirty_hex( |
935 | | "04DAD0B65394221CF9B051E1FECA5787D098DFE637FC90B9EF945D0C37725811805271A0461CDB8252D61F1C456FA3E59AB1F45B33ACCF5F58389E0577B8990BB3", |
936 | | ); |
937 | | let output = test::from_dirty_hex( |
938 | | "D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE", |
939 | | ); |
940 | | |
941 | | assert_eq!(my_private.algorithm(), alg); |
942 | | |
943 | | let be_private_key_buffer: EcPrivateKeyBin = my_private.as_be_bytes().unwrap(); |
944 | | let be_private_key = |
945 | | PrivateKey::from_private_key(&ECDH_P256, be_private_key_buffer.as_ref()).unwrap(); |
946 | | { |
947 | | let result = agree(&be_private_key, &peer_public, (), |key_material| { |
948 | | assert_eq!(key_material, &output[..]); |
949 | | Ok(()) |
950 | | }); |
951 | | assert_eq!(result, Ok(())); |
952 | | } |
953 | | |
954 | | let der_private_key_buffer: EcPrivateKeyRfc5915Der = my_private.as_der().unwrap(); |
955 | | let der_private_key = |
956 | | PrivateKey::from_private_key_der(&ECDH_P256, der_private_key_buffer.as_ref()).unwrap(); |
957 | | { |
958 | | let result = agree(&der_private_key, &peer_public, (), |key_material| { |
959 | | assert_eq!(key_material, &output[..]); |
960 | | Ok(()) |
961 | | }); |
962 | | assert_eq!(result, Ok(())); |
963 | | } |
964 | | |
965 | | let pkcs8_private_key_buffer: Pkcs8V1Der = my_private.as_der().unwrap(); |
966 | | let pkcs8_private_key = |
967 | | PrivateKey::from_private_key_der(&ECDH_P256, pkcs8_private_key_buffer.as_ref()) |
968 | | .unwrap(); |
969 | | { |
970 | | let result = agree(&pkcs8_private_key, &peer_public, (), |key_material| { |
971 | | assert_eq!(key_material, &output[..]); |
972 | | Ok(()) |
973 | | }); |
974 | | assert_eq!(result, Ok(())); |
975 | | } |
976 | | |
977 | | let computed_public = my_private.compute_public_key().unwrap(); |
978 | | assert_eq!(computed_public.as_ref(), &my_public[..]); |
979 | | |
980 | | assert_eq!(computed_public.algorithm(), alg); |
981 | | |
982 | | { |
983 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
984 | | assert_eq!(key_material, &output[..]); |
985 | | Ok(()) |
986 | | }); |
987 | | assert_eq!(result, Ok(())); |
988 | | } |
989 | | |
990 | | { |
991 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
992 | | assert_eq!(key_material, &output[..]); |
993 | | Ok(()) |
994 | | }); |
995 | | assert_eq!(result, Ok(())); |
996 | | } |
997 | | } |
998 | | |
999 | | #[test] |
1000 | | fn test_agreement_ecdh_p384() { |
1001 | | let alg = &ECDH_P384; |
1002 | | let peer_public = UnparsedPublicKey::new( |
1003 | | alg, |
1004 | | test::from_dirty_hex( |
1005 | | "04E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571DCFBEC7AACF3196472169E838430367F66EEBE3C6E70C416DD5F0C68759DD1FFF83FA40142209DFF5EAAD96DB9E6386C", |
1006 | | ), |
1007 | | ); |
1008 | | |
1009 | | let my_private = test::from_dirty_hex( |
1010 | | "099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194", |
1011 | | ); |
1012 | | |
1013 | | let my_private = { |
1014 | | let rng = test::rand::FixedSliceRandom { bytes: &my_private }; |
1015 | | PrivateKey::generate_for_test(alg, &rng).unwrap() |
1016 | | }; |
1017 | | |
1018 | | let my_public = test::from_dirty_hex( |
1019 | | "04667842D7D180AC2CDE6F74F37551F55755C7645C20EF73E31634FE72B4C55EE6DE3AC808ACB4BDB4C88732AEE95F41AA9482ED1FC0EEB9CAFC4984625CCFC23F65032149E0E144ADA024181535A0F38EEB9FCFF3C2C947DAE69B4C634573A81C", |
1020 | | ); |
1021 | | let output = test::from_dirty_hex( |
1022 | | "11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746", |
1023 | | ); |
1024 | | |
1025 | | assert_eq!(my_private.algorithm(), alg); |
1026 | | |
1027 | | let be_private_key_buffer: EcPrivateKeyBin = my_private.as_be_bytes().unwrap(); |
1028 | | let be_private_key = |
1029 | | PrivateKey::from_private_key(&ECDH_P384, be_private_key_buffer.as_ref()).unwrap(); |
1030 | | { |
1031 | | let result = agree(&be_private_key, &peer_public, (), |key_material| { |
1032 | | assert_eq!(key_material, &output[..]); |
1033 | | Ok(()) |
1034 | | }); |
1035 | | assert_eq!(result, Ok(())); |
1036 | | } |
1037 | | |
1038 | | let der_private_key_buffer: EcPrivateKeyRfc5915Der = my_private.as_der().unwrap(); |
1039 | | let der_private_key = |
1040 | | PrivateKey::from_private_key_der(&ECDH_P384, der_private_key_buffer.as_ref()).unwrap(); |
1041 | | { |
1042 | | let result = agree(&der_private_key, &peer_public, (), |key_material| { |
1043 | | assert_eq!(key_material, &output[..]); |
1044 | | Ok(()) |
1045 | | }); |
1046 | | assert_eq!(result, Ok(())); |
1047 | | } |
1048 | | |
1049 | | let computed_public = my_private.compute_public_key().unwrap(); |
1050 | | assert_eq!(computed_public.as_ref(), &my_public[..]); |
1051 | | |
1052 | | assert_eq!(computed_public.algorithm(), alg); |
1053 | | |
1054 | | { |
1055 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
1056 | | assert_eq!(key_material, &output[..]); |
1057 | | Ok(()) |
1058 | | }); |
1059 | | assert_eq!(result, Ok(())); |
1060 | | } |
1061 | | } |
1062 | | |
1063 | | #[test] |
1064 | | fn test_agreement_ecdh_p521() { |
1065 | | let alg = &ECDH_P521; |
1066 | | let peer_public = UnparsedPublicKey::new( |
1067 | | alg, |
1068 | | test::from_dirty_hex( |
1069 | | "0401a32099b02c0bd85371f60b0dd20890e6c7af048c8179890fda308b359dbbc2b7a832bb8c6526c4af99a7ea3f0b3cb96ae1eb7684132795c478ad6f962e4a6f446d017627357b39e9d7632a1370b3e93c1afb5c851b910eb4ead0c9d387df67cde85003e0e427552f1cd09059aad0262e235cce5fba8cedc4fdc1463da76dcd4b6d1a46", |
1070 | | ), |
1071 | | ); |
1072 | | |
1073 | | let my_private = test::from_dirty_hex( |
1074 | | "00df14b1f1432a7b0fb053965fd8643afee26b2451ecb6a8a53a655d5fbe16e4c64ce8647225eb11e7fdcb23627471dffc5c2523bd2ae89957cba3a57a23933e5a78", |
1075 | | ); |
1076 | | |
1077 | | let my_private = { |
1078 | | let rng = test::rand::FixedSliceRandom { bytes: &my_private }; |
1079 | | PrivateKey::generate_for_test(alg, &rng).unwrap() |
1080 | | }; |
1081 | | |
1082 | | let my_public = test::from_dirty_hex( |
1083 | | "04004e8583bbbb2ecd93f0714c332dff5ab3bc6396e62f3c560229664329baa5138c3bb1c36428abd4e23d17fcb7a2cfcc224b2e734c8941f6f121722d7b6b9415457601cf0874f204b0363f020864672fadbf87c8811eb147758b254b74b14fae742159f0f671a018212bbf25b8519e126d4cad778cfff50d288fd39ceb0cac635b175ec0", |
1084 | | ); |
1085 | | let output = test::from_dirty_hex( |
1086 | | "01aaf24e5d47e4080c18c55ea35581cd8da30f1a079565045d2008d51b12d0abb4411cda7a0785b15d149ed301a3697062f42da237aa7f07e0af3fd00eb1800d9c41", |
1087 | | ); |
1088 | | |
1089 | | assert_eq!(my_private.algorithm(), alg); |
1090 | | |
1091 | | let be_private_key_buffer: EcPrivateKeyBin = my_private.as_be_bytes().unwrap(); |
1092 | | let be_private_key = |
1093 | | PrivateKey::from_private_key(&ECDH_P521, be_private_key_buffer.as_ref()).unwrap(); |
1094 | | { |
1095 | | let result = agree(&be_private_key, &peer_public, (), |key_material| { |
1096 | | assert_eq!(key_material, &output[..]); |
1097 | | Ok(()) |
1098 | | }); |
1099 | | assert_eq!(result, Ok(())); |
1100 | | } |
1101 | | |
1102 | | let der_private_key_buffer: EcPrivateKeyRfc5915Der = my_private.as_der().unwrap(); |
1103 | | let der_private_key = |
1104 | | PrivateKey::from_private_key_der(&ECDH_P521, der_private_key_buffer.as_ref()).unwrap(); |
1105 | | { |
1106 | | let result = agree(&der_private_key, &peer_public, (), |key_material| { |
1107 | | assert_eq!(key_material, &output[..]); |
1108 | | Ok(()) |
1109 | | }); |
1110 | | assert_eq!(result, Ok(())); |
1111 | | } |
1112 | | |
1113 | | let computed_public = my_private.compute_public_key().unwrap(); |
1114 | | assert_eq!(computed_public.as_ref(), &my_public[..]); |
1115 | | |
1116 | | assert_eq!(computed_public.algorithm(), alg); |
1117 | | { |
1118 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
1119 | | assert_eq!(key_material, &output[..]); |
1120 | | Ok(()) |
1121 | | }); |
1122 | | assert_eq!(result, Ok(())); |
1123 | | } |
1124 | | { |
1125 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
1126 | | assert_eq!(key_material, &output[..]); |
1127 | | Ok(()) |
1128 | | }); |
1129 | | assert_eq!(result, Ok(())); |
1130 | | } |
1131 | | } |
1132 | | |
1133 | | #[test] |
1134 | | fn agreement_traits() { |
1135 | | use crate::test; |
1136 | | use regex::{self, Regex}; |
1137 | | |
1138 | | let rng = rand::SystemRandom::new(); |
1139 | | let private_key = PrivateKey::generate_for_test(&ECDH_P256, &rng).unwrap(); |
1140 | | |
1141 | | test::compile_time_assert_send::<PrivateKey>(); |
1142 | | test::compile_time_assert_sync::<PrivateKey>(); |
1143 | | |
1144 | | assert_eq!( |
1145 | | format!("{:?}", &private_key), |
1146 | | "PrivateKey { algorithm: Algorithm { curve: P256 } }" |
1147 | | ); |
1148 | | |
1149 | | let ephemeral_private_key = PrivateKey::generate_for_test(&ECDH_P256, &rng).unwrap(); |
1150 | | |
1151 | | test::compile_time_assert_send::<PrivateKey>(); |
1152 | | test::compile_time_assert_sync::<PrivateKey>(); |
1153 | | |
1154 | | assert_eq!( |
1155 | | format!("{:?}", &ephemeral_private_key), |
1156 | | "PrivateKey { algorithm: Algorithm { curve: P256 } }" |
1157 | | ); |
1158 | | |
1159 | | let public_key = private_key.compute_public_key().unwrap(); |
1160 | | let pubkey_re = Regex::new( |
1161 | | "PublicKey \\{ algorithm: Algorithm \\{ curve: P256 \\}, bytes: \"[0-9a-f]+\" \\}", |
1162 | | ) |
1163 | | .unwrap(); |
1164 | | let pubkey_debug = format!("{:?}", &public_key); |
1165 | | |
1166 | | assert!( |
1167 | | pubkey_re.is_match(&pubkey_debug), |
1168 | | "pubkey_debug: {pubkey_debug}" |
1169 | | ); |
1170 | | |
1171 | | #[allow(clippy::redundant_clone)] |
1172 | | let pubkey_clone = public_key.clone(); |
1173 | | assert_eq!(public_key.as_ref(), pubkey_clone.as_ref()); |
1174 | | assert_eq!(pubkey_debug, format!("{:?}", &pubkey_clone)); |
1175 | | |
1176 | | test::compile_time_assert_clone::<PublicKey>(); |
1177 | | test::compile_time_assert_send::<PublicKey>(); |
1178 | | test::compile_time_assert_sync::<PublicKey>(); |
1179 | | |
1180 | | // Verify `PublicKey` implements `Debug`. |
1181 | | // |
1182 | | // TODO: Test the actual output. |
1183 | | let _: &dyn core::fmt::Debug = &public_key; |
1184 | | |
1185 | | test::compile_time_assert_clone::<UnparsedPublicKey<&[u8]>>(); |
1186 | | test::compile_time_assert_copy::<UnparsedPublicKey<&[u8]>>(); |
1187 | | test::compile_time_assert_sync::<UnparsedPublicKey<&[u8]>>(); |
1188 | | |
1189 | | test::compile_time_assert_clone::<UnparsedPublicKey<Vec<u8>>>(); |
1190 | | test::compile_time_assert_sync::<UnparsedPublicKey<Vec<u8>>>(); |
1191 | | |
1192 | | let bytes = [0x01, 0x02, 0x03]; |
1193 | | |
1194 | | let unparsed_public_key = UnparsedPublicKey::new(&X25519, &bytes); |
1195 | | let unparsed_pubkey_clone = unparsed_public_key; |
1196 | | assert_eq!( |
1197 | | format!("{unparsed_public_key:?}"), |
1198 | | r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"# |
1199 | | ); |
1200 | | assert_eq!( |
1201 | | format!("{unparsed_pubkey_clone:?}"), |
1202 | | r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"# |
1203 | | ); |
1204 | | |
1205 | | let unparsed_public_key = UnparsedPublicKey::new(&X25519, Vec::from(bytes)); |
1206 | | #[allow(clippy::redundant_clone)] |
1207 | | let unparsed_pubkey_clone = unparsed_public_key.clone(); |
1208 | | assert_eq!( |
1209 | | format!("{unparsed_public_key:?}"), |
1210 | | r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"# |
1211 | | ); |
1212 | | assert_eq!( |
1213 | | format!("{unparsed_pubkey_clone:?}"), |
1214 | | r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"# |
1215 | | ); |
1216 | | } |
1217 | | |
1218 | | #[test] |
1219 | | fn test_agreement_random() { |
1220 | | let test_algorithms = [&ECDH_P256, &ECDH_P384, &ECDH_P521, &X25519]; |
1221 | | |
1222 | | for alg in test_algorithms { |
1223 | | test_agreement_random_helper(alg); |
1224 | | } |
1225 | | } |
1226 | | |
1227 | | fn test_agreement_random_helper(alg: &'static Algorithm) { |
1228 | | let peer_private = PrivateKey::generate(alg).unwrap(); |
1229 | | let my_private = PrivateKey::generate(alg).unwrap(); |
1230 | | |
1231 | | let peer_public_keys = |
1232 | | public_key_formats_helper(&peer_private.compute_public_key().unwrap()); |
1233 | | |
1234 | | let my_public_keys = public_key_formats_helper(&my_private.compute_public_key().unwrap()); |
1235 | | |
1236 | | let mut results: Vec<Vec<u8>> = Vec::new(); |
1237 | | |
1238 | | for peer_public in peer_public_keys { |
1239 | | let peer_public = UnparsedPublicKey::new(alg, peer_public); |
1240 | | let result = agree(&my_private, &peer_public, (), |key_material| { |
1241 | | results.push(key_material.to_vec()); |
1242 | | Ok(()) |
1243 | | }); |
1244 | | assert_eq!(result, Ok(())); |
1245 | | } |
1246 | | |
1247 | | for my_public in my_public_keys { |
1248 | | let my_public = UnparsedPublicKey::new(alg, my_public); |
1249 | | let result = agree(&peer_private, &my_public, (), |key_material| { |
1250 | | results.push(key_material.to_vec()); |
1251 | | Ok(()) |
1252 | | }); |
1253 | | assert_eq!(result, Ok(())); |
1254 | | } |
1255 | | |
1256 | | let key_types_tested = match alg.id { |
1257 | | crate::agreement::AlgorithmID::ECDH_P256 |
1258 | | | crate::agreement::AlgorithmID::ECDH_P384 |
1259 | | | crate::agreement::AlgorithmID::ECDH_P521 => 4, |
1260 | | crate::agreement::AlgorithmID::X25519 => 2, |
1261 | | }; |
1262 | | |
1263 | | assert_eq!(results.len(), key_types_tested * 2); // Multiplied by two because we tested the other direction |
1264 | | |
1265 | | assert_eq!(results[0..key_types_tested], results[key_types_tested..]); |
1266 | | } |
1267 | | |
1268 | | fn public_key_formats_helper(public_key: &PublicKey) -> Vec<Vec<u8>> { |
1269 | | let verify_ec_raw_traits = matches!( |
1270 | | public_key.algorithm().id, |
1271 | | crate::agreement::AlgorithmID::ECDH_P256 |
1272 | | | crate::agreement::AlgorithmID::ECDH_P384 |
1273 | | | crate::agreement::AlgorithmID::ECDH_P521 |
1274 | | ); |
1275 | | |
1276 | | let mut public_keys = Vec::<Vec<u8>>::new(); |
1277 | | public_keys.push(public_key.as_ref().into()); |
1278 | | |
1279 | | if verify_ec_raw_traits { |
1280 | | let raw = AsBigEndian::<EcPublicKeyCompressedBin>::as_be_bytes(public_key).unwrap(); |
1281 | | public_keys.push(raw.as_ref().into()); |
1282 | | let raw = AsBigEndian::<EcPublicKeyUncompressedBin>::as_be_bytes(public_key).unwrap(); |
1283 | | public_keys.push(raw.as_ref().into()); |
1284 | | } |
1285 | | |
1286 | | let peer_x509 = AsDer::<PublicKeyX509Der>::as_der(public_key).unwrap(); |
1287 | | public_keys.push(peer_x509.as_ref().into()); |
1288 | | |
1289 | | public_keys |
1290 | | } |
1291 | | |
1292 | | #[test] |
1293 | | fn private_key_drop() { |
1294 | | let private_key = PrivateKey::generate(&ECDH_P256).unwrap(); |
1295 | | let public_key = private_key.compute_public_key().unwrap(); |
1296 | | // PublicKey maintains a reference counted pointer to private keys EVP_PKEY so we test that with drop |
1297 | | drop(private_key); |
1298 | | let _ = AsBigEndian::<EcPublicKeyCompressedBin>::as_be_bytes(&public_key).unwrap(); |
1299 | | let _ = AsBigEndian::<EcPublicKeyUncompressedBin>::as_be_bytes(&public_key).unwrap(); |
1300 | | let _ = AsDer::<PublicKeyX509Der>::as_der(&public_key).unwrap(); |
1301 | | } |
1302 | | } |