1 // Copyright 2015-2016 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 //! X25519 Key agreement.
16 
17 use super::{ops, scalar::SCALAR_LEN};
18 use crate::{agreement, c, constant_time, cpu, ec, error, rand};
19 
20 static CURVE25519: ec::Curve = ec::Curve {
21     public_key_len: PUBLIC_KEY_LEN,
22     elem_scalar_seed_len: ELEM_AND_SCALAR_LEN,
23     id: ec::CurveID::Curve25519,
24     check_private_key_bytes: x25519_check_private_key_bytes,
25     generate_private_key: x25519_generate_private_key,
26     public_from_private: x25519_public_from_private,
27 };
28 
29 /// X25519 (ECDH using Curve25519) as described in [RFC 7748].
30 ///
31 /// Everything is as described in RFC 7748. Key agreement will fail if the
32 /// result of the X25519 operation is zero; see the notes on the
33 /// "all-zero value" in [RFC 7748 section 6.1].
34 ///
35 /// [RFC 7748]: https://tools.ietf.org/html/rfc7748
36 /// [RFC 7748 section 6.1]: https://tools.ietf.org/html/rfc7748#section-6.1
37 pub static X25519: agreement::Algorithm = agreement::Algorithm {
38     curve: &CURVE25519,
39     ecdh: x25519_ecdh,
40 };
41 
42 #[allow(clippy::unnecessary_wraps)]
x25519_check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified>43 fn x25519_check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified> {
44     debug_assert_eq!(bytes.len(), PRIVATE_KEY_LEN);
45     Ok(())
46 }
47 
x25519_generate_private_key( rng: &dyn rand::SecureRandom, out: &mut [u8], ) -> Result<(), error::Unspecified>48 fn x25519_generate_private_key(
49     rng: &dyn rand::SecureRandom,
50     out: &mut [u8],
51 ) -> Result<(), error::Unspecified> {
52     rng.fill(out)
53 }
54 
x25519_public_from_private( public_out: &mut [u8], private_key: &ec::Seed, ) -> Result<(), error::Unspecified>55 fn x25519_public_from_private(
56     public_out: &mut [u8],
57     private_key: &ec::Seed,
58 ) -> Result<(), error::Unspecified> {
59     let public_out = public_out.try_into()?;
60 
61     let cpu_features = private_key.cpu_features;
62 
63     let private_key: &[u8; SCALAR_LEN] = private_key.bytes_less_safe().try_into()?;
64     let private_key = ops::MaskedScalar::from_bytes_masked(*private_key);
65 
66     #[cfg(all(not(target_os = "ios"), target_arch = "arm"))]
67     {
68         if cpu::arm::NEON.available(cpu_features) {
69             static MONTGOMERY_BASE_POINT: [u8; 32] = [
70                 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71                 0, 0, 0, 0,
72             ];
73             x25519_neon(public_out, &private_key, &MONTGOMERY_BASE_POINT);
74             return Ok(());
75         }
76     }
77 
78     prefixed_extern! {
79         fn x25519_public_from_private_generic_masked(
80             public_key_out: &mut PublicKey,
81             private_key: &PrivateKey,
82             use_adx: c::int,
83         );
84     }
85     unsafe {
86         x25519_public_from_private_generic_masked(
87             public_out,
88             &private_key,
89             ops::has_fe25519_adx(cpu_features).into(),
90         );
91     }
92 
93     Ok(())
94 }
95 
x25519_ecdh( out: &mut [u8], my_private_key: &ec::Seed, peer_public_key: untrusted::Input, ) -> Result<(), error::Unspecified>96 fn x25519_ecdh(
97     out: &mut [u8],
98     my_private_key: &ec::Seed,
99     peer_public_key: untrusted::Input,
100 ) -> Result<(), error::Unspecified> {
101     let cpu_features = my_private_key.cpu_features;
102     let my_private_key: &[u8; SCALAR_LEN] = my_private_key.bytes_less_safe().try_into()?;
103     let my_private_key = ops::MaskedScalar::from_bytes_masked(*my_private_key);
104     let peer_public_key: &[u8; PUBLIC_KEY_LEN] = peer_public_key.as_slice_less_safe().try_into()?;
105 
106     fn scalar_mult(
107         out: &mut ops::EncodedPoint,
108         scalar: &ops::MaskedScalar,
109         point: &ops::EncodedPoint,
110         #[allow(unused_variables)] cpu_features: cpu::Features,
111     ) {
112         #[cfg(all(not(target_os = "ios"), target_arch = "arm"))]
113         {
114             if cpu::arm::NEON.available(cpu_features) {
115                 return x25519_neon(out, scalar, point);
116             }
117         }
118 
119         #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
120         {
121             if ops::has_fe25519_adx(cpu_features) {
122                 prefixed_extern! {
123                     fn x25519_scalar_mult_adx(
124                         out: &mut ops::EncodedPoint,
125                         scalar: &ops::MaskedScalar,
126                         point: &ops::EncodedPoint,
127                     );
128                 }
129                 return unsafe { x25519_scalar_mult_adx(out, scalar, point) };
130             }
131         }
132 
133         prefixed_extern! {
134             fn x25519_scalar_mult_generic_masked(
135                 out: &mut ops::EncodedPoint,
136                 scalar: &ops::MaskedScalar,
137                 point: &ops::EncodedPoint,
138             );
139         }
140         unsafe {
141             x25519_scalar_mult_generic_masked(out, scalar, point);
142         }
143     }
144 
145     scalar_mult(
146         out.try_into()?,
147         &my_private_key,
148         peer_public_key,
149         cpu_features,
150     );
151 
152     let zeros: SharedSecret = [0; SHARED_SECRET_LEN];
153     if constant_time::verify_slices_are_equal(out, &zeros).is_ok() {
154         // All-zero output results when the input is a point of small order.
155         return Err(error::Unspecified);
156     }
157 
158     Ok(())
159 }
160 
161 #[cfg(all(not(target_os = "ios"), target_arch = "arm"))]
x25519_neon(out: &mut ops::EncodedPoint, scalar: &ops::MaskedScalar, point: &ops::EncodedPoint)162 fn x25519_neon(out: &mut ops::EncodedPoint, scalar: &ops::MaskedScalar, point: &ops::EncodedPoint) {
163     prefixed_extern! {
164         fn x25519_NEON(
165             out: &mut ops::EncodedPoint,
166             scalar: &ops::MaskedScalar,
167             point: &ops::EncodedPoint,
168         );
169     }
170     unsafe { x25519_NEON(out, scalar, point) }
171 }
172 
173 const ELEM_AND_SCALAR_LEN: usize = ops::ELEM_LEN;
174 
175 type PrivateKey = ops::MaskedScalar;
176 const PRIVATE_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
177 
178 // An X25519 public key as an encoded Curve25519 point.
179 type PublicKey = [u8; PUBLIC_KEY_LEN];
180 const PUBLIC_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
181 
182 // An X25519 shared secret as an encoded Curve25519 point.
183 type SharedSecret = [u8; SHARED_SECRET_LEN];
184 const SHARED_SECRET_LEN: usize = ELEM_AND_SCALAR_LEN;
185 
186 #[cfg(test)]
187 mod tests {
188     use super::*;
189     use crate::ec;
190     use untrusted::Input;
191 
192     #[test]
test_x25519_public_from_private()193     fn test_x25519_public_from_private() {
194         struct TestVector {
195             private: [u8; 32],
196             public: [u8; 32],
197         }
198         static TEST_CASES: &[TestVector] = &[
199             TestVector {
200                 private: [
201                     0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51,
202                     0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77,
203                     0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a,
204                 ],
205                 public: [
206                     0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4,
207                     0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4,
208                     0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a,
209                 ],
210             },
211             TestVector {
212                 private: [
213                     0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83,
214                     0x80, 0x0e, 0xe6, 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f,
215                     0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb,
216                 ],
217                 public: [
218                     0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec,
219                     0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc,
220                     0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f,
221                 ],
222             },
223         ];
224         let cpu_features = cpu::features();
225         for test_case in TEST_CASES {
226             let seed =
227                 ec::Seed::from_bytes(&CURVE25519, Input::from(&test_case.private), cpu_features)
228                     .unwrap();
229             let mut output = [0u8; 32];
230             x25519_public_from_private(&mut output, &seed).unwrap();
231             assert_eq!(output, test_case.public);
232         }
233     }
234 }
235