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 //! ECDH key agreement using the P-256 and P-384 curves.
16 
17 use super::{ops::*, private_key::*, public_key::*};
18 use crate::{agreement, ec, error};
19 
20 /// A key agreement algorithm.
21 macro_rules! ecdh {
22     ( $NAME:ident, $curve:expr, $name_str:expr, $private_key_ops:expr,
23       $public_key_ops:expr, $ecdh:ident ) => {
24         #[doc = "ECDH using the NSA Suite B"]
25         #[doc=$name_str]
26         #[doc = "curve."]
27         ///
28         /// Public keys are encoding in uncompressed form using the
29         /// Octet-String-to-Elliptic-Curve-Point algorithm in
30         /// [SEC 1: Elliptic Curve Cryptography, Version 2.0]. Public keys are
31         /// validated during key agreement according to
32         /// [NIST Special Publication 800-56A, revision 2] and Appendix B.3 of
33         /// the NSA's [Suite B Implementer's Guide to NIST SP 800-56A].
34         ///
35         /// [SEC 1: Elliptic Curve Cryptography, Version 2.0]:
36         ///     http://www.secg.org/sec1-v2.pdf
37         /// [NIST Special Publication 800-56A, revision 2]:
38         ///     http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf
39         /// [Suite B Implementer's Guide to NIST SP 800-56A]:
40         ///     https://github.com/briansmith/ring/blob/main/doc/ecdh.pdf
41         pub static $NAME: agreement::Algorithm = agreement::Algorithm {
42             curve: $curve,
43             ecdh: $ecdh,
44         };
45 
46         fn $ecdh(
47             out: &mut [u8],
48             my_private_key: &ec::Seed,
49             peer_public_key: untrusted::Input,
50         ) -> Result<(), error::Unspecified> {
51             ecdh(
52                 $private_key_ops,
53                 $public_key_ops,
54                 out,
55                 my_private_key,
56                 peer_public_key,
57             )
58         }
59     };
60 }
61 
62 ecdh!(
63     ECDH_P256,
64     &ec::suite_b::curve::P256,
65     "P-256 (secp256r1)",
66     &p256::PRIVATE_KEY_OPS,
67     &p256::PUBLIC_KEY_OPS,
68     p256_ecdh
69 );
70 
71 ecdh!(
72     ECDH_P384,
73     &ec::suite_b::curve::P384,
74     "P-384 (secp384r1)",
75     &p384::PRIVATE_KEY_OPS,
76     &p384::PUBLIC_KEY_OPS,
77     p384_ecdh
78 );
79 
ecdh( private_key_ops: &PrivateKeyOps, public_key_ops: &PublicKeyOps, out: &mut [u8], my_private_key: &ec::Seed, peer_public_key: untrusted::Input, ) -> Result<(), error::Unspecified>80 fn ecdh(
81     private_key_ops: &PrivateKeyOps,
82     public_key_ops: &PublicKeyOps,
83     out: &mut [u8],
84     my_private_key: &ec::Seed,
85     peer_public_key: untrusted::Input,
86 ) -> Result<(), error::Unspecified> {
87     // The NIST SP 800-56Ar2 steps are from section 5.7.1.2 Elliptic Curve
88     // Cryptography Cofactor Diffie-Hellman (ECC CDH) Primitive.
89     //
90     // The "NSA Guide" steps are from section 3.1 of the NSA guide, "Ephemeral
91     // Unified Model."
92 
93     // NSA Guide Step 1 is handled separately.
94 
95     // NIST SP 800-56Ar2 5.6.2.2.2.
96     // NSA Guide Step 2.
97     //
98     // `parse_uncompressed_point` verifies that the point is not at infinity
99     // and that it is on the curve, using the Partial Public-Key Validation
100     // Routine.
101     let peer_public_key = parse_uncompressed_point(public_key_ops, peer_public_key)?;
102 
103     // NIST SP 800-56Ar2 Step 1.
104     // NSA Guide Step 3 (except point at infinity check).
105     //
106     // Note that the cofactor (h) is one since we only support prime-order
107     // curves, so we can safely ignore the cofactor.
108     //
109     // It is impossible for the result to be the point at infinity because our
110     // private key is in the range [1, n) and the curve has prime order and
111     // `parse_uncompressed_point` verified that the peer public key is on the
112     // curve and not at infinity. However, since the standards require the
113     // check, we do it using `assert!`.
114     //
115     // NIST SP 800-56Ar2 defines "Destroy" thusly: "In this Recommendation, to
116     // destroy is an action applied to a key or a piece of secret data. After
117     // a key or a piece of secret data is destroyed, no information about its
118     // value can be recovered." We interpret "destroy" somewhat liberally: we
119     // assume that since we throw away the values to be destroyed, no
120     // information about their values can be recovered. This doesn't meet the
121     // NSA guide's explicit requirement to "zeroize" them though.
122     // TODO: this only needs common scalar ops
123     let my_private_key = private_key_as_scalar(private_key_ops, my_private_key);
124     let product = private_key_ops.point_mul(&my_private_key, &peer_public_key);
125 
126     // NIST SP 800-56Ar2 Steps 2, 3, 4, and 5.
127     // NSA Guide Steps 3 (point at infinity check) and 4.
128     //
129     // Again, we have a pretty liberal interpretation of the NIST's spec's
130     // "Destroy" that doesn't meet the NSA requirement to "zeroize."
131     // `big_endian_affine_from_jacobian` verifies that the result is not at
132     // infinity and also does an extra check to verify that the point is on
133     // the curve.
134     big_endian_affine_from_jacobian(private_key_ops, Some(out), None, &product)
135 
136     // NSA Guide Step 5 & 6 are deferred to the caller. Again, we have a
137     // pretty liberal interpretation of the NIST's spec's "Destroy" that
138     // doesn't meet the NSA requirement to "zeroize."
139 }
140 
141 #[cfg(test)]
142 mod tests {
143     use super::super::ops;
144     use crate::{agreement, ec, limb, test};
145 
146     static SUPPORTED_SUITE_B_ALGS: [(&str, &agreement::Algorithm, &ec::Curve, &ops::CommonOps); 2] = [
147         (
148             "P-256",
149             &agreement::ECDH_P256,
150             &super::super::curve::P256,
151             &super::super::ops::p256::COMMON_OPS,
152         ),
153         (
154             "P-384",
155             &agreement::ECDH_P384,
156             &super::super::curve::P384,
157             &super::super::ops::p384::COMMON_OPS,
158         ),
159     ];
160 
161     #[test]
test_agreement_suite_b_ecdh_generate()162     fn test_agreement_suite_b_ecdh_generate() {
163         // Generates a string of bytes 0x00...00, which will always result in
164         // a scalar value of zero.
165         let random_00 = test::rand::FixedByteRandom { byte: 0x00 };
166 
167         // Generates a string of bytes 0xFF...FF, which will be larger than the
168         // group order of any curve that is supported.
169         let random_ff = test::rand::FixedByteRandom { byte: 0xff };
170 
171         for &(_, alg, curve, ops) in SUPPORTED_SUITE_B_ALGS.iter() {
172             // Test that the private key value zero is rejected and that
173             // `generate` gives up after a while of only getting zeros.
174             assert!(agreement::EphemeralPrivateKey::generate(alg, &random_00).is_err());
175 
176             // Test that the private key value larger than the group order is
177             // rejected and that `generate` gives up after a while of only
178             // getting values larger than the group order.
179             assert!(agreement::EphemeralPrivateKey::generate(alg, &random_ff).is_err());
180 
181             // Test that a private key value exactly equal to the group order
182             // is rejected and that `generate` gives up after a while of only
183             // getting that value from the PRNG.
184             let mut n_bytes = [0u8; ec::SCALAR_MAX_BYTES];
185             let num_bytes = curve.elem_scalar_seed_len;
186             limb::big_endian_from_limbs(&ops.n.limbs[..ops.num_limbs], &mut n_bytes[..num_bytes]);
187             {
188                 let n_bytes = &mut n_bytes[..num_bytes];
189                 let rng = test::rand::FixedSliceRandom { bytes: n_bytes };
190                 assert!(agreement::EphemeralPrivateKey::generate(alg, &rng).is_err());
191             }
192 
193             // Test that a private key value exactly equal to the group order
194             // minus 1 is accepted.
195             let mut n_minus_1_bytes = n_bytes;
196             {
197                 let n_minus_1_bytes = &mut n_minus_1_bytes[..num_bytes];
198                 n_minus_1_bytes[num_bytes - 1] -= 1;
199                 let rng = test::rand::FixedSliceRandom {
200                     bytes: n_minus_1_bytes,
201                 };
202                 let key = agreement::EphemeralPrivateKey::generate(alg, &rng).unwrap();
203                 assert_eq!(&n_minus_1_bytes[..], key.bytes());
204             }
205 
206             // Test that n + 1 also fails.
207             let mut n_plus_1_bytes = n_bytes;
208             {
209                 let n_plus_1_bytes = &mut n_plus_1_bytes[..num_bytes];
210                 n_plus_1_bytes[num_bytes - 1] += 1;
211                 let rng = test::rand::FixedSliceRandom {
212                     bytes: n_plus_1_bytes,
213                 };
214                 assert!(agreement::EphemeralPrivateKey::generate(alg, &rng).is_err());
215             }
216 
217             // Test recovery from initial RNG failure. The first value will be
218             // n, then n + 1, then zero, the next value will be n - 1, which
219             // will be accepted.
220             {
221                 let bytes = [
222                     &n_bytes[..num_bytes],
223                     &n_plus_1_bytes[..num_bytes],
224                     &[0u8; ec::SCALAR_MAX_BYTES][..num_bytes],
225                     &n_minus_1_bytes[..num_bytes],
226                 ];
227                 let rng = test::rand::FixedSliceSequenceRandom {
228                     bytes: &bytes,
229                     current: core::cell::UnsafeCell::new(0),
230                 };
231                 let key = agreement::EphemeralPrivateKey::generate(alg, &rng).unwrap();
232                 assert_eq!(&n_minus_1_bytes[..num_bytes], key.bytes());
233             }
234         }
235     }
236 }
237