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, constant_time, cpu, ec, error, rand};
19 use core::convert::TryInto;
20 use untrusted;
21 
22 static CURVE25519: ec::Curve = ec::Curve {
23     public_key_len: PUBLIC_KEY_LEN,
24     elem_scalar_seed_len: ELEM_AND_SCALAR_LEN,
25     id: ec::CurveID::Curve25519,
26     check_private_key_bytes: x25519_check_private_key_bytes,
27     generate_private_key: x25519_generate_private_key,
28     public_from_private: x25519_public_from_private,
29 };
30 
31 /// X25519 (ECDH using Curve25519) as described in [RFC 7748].
32 ///
33 /// Everything is as described in RFC 7748. Key agreement will fail if the
34 /// result of the X25519 operation is zero; see the notes on the
35 /// "all-zero value" in [RFC 7748 section 6.1].
36 ///
37 /// [RFC 7748]: https://tools.ietf.org/html/rfc7748
38 /// [RFC 7748 section 6.1]: https://tools.ietf.org/html/rfc7748#section-6.1
39 pub static X25519: agreement::Algorithm = agreement::Algorithm {
40     curve: &CURVE25519,
41     ecdh: x25519_ecdh,
42 };
43 
x25519_check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified>44 fn x25519_check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified> {
45     debug_assert_eq!(bytes.len(), PRIVATE_KEY_LEN);
46     Ok(())
47 }
48 
x25519_generate_private_key( rng: &dyn rand::SecureRandom, out: &mut [u8], ) -> Result<(), error::Unspecified>49 fn x25519_generate_private_key(
50     rng: &dyn rand::SecureRandom,
51     out: &mut [u8],
52 ) -> Result<(), error::Unspecified> {
53     rng.fill(out)
54 }
55 
x25519_public_from_private( public_out: &mut [u8], private_key: &ec::Seed, ) -> Result<(), error::Unspecified>56 fn x25519_public_from_private(
57     public_out: &mut [u8],
58     private_key: &ec::Seed,
59 ) -> Result<(), error::Unspecified> {
60     let public_out = public_out.try_into()?;
61 
62     #[cfg(target_arch = "arm")]
63     let cpu_features = private_key.cpu_features;
64 
65     let private_key: &[u8; SCALAR_LEN] = private_key.bytes_less_safe().try_into()?;
66     let private_key = ops::MaskedScalar::from_bytes_masked(*private_key);
67 
68     #[cfg(all(not(target_os = "ios"), target_arch = "arm"))]
69     {
70         if cpu::arm::NEON.available(cpu_features) {
71             static MONTGOMERY_BASE_POINT: [u8; 32] = [
72                 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,
73                 0, 0, 0, 0,
74             ];
75             x25519_neon(public_out, &private_key, &MONTGOMERY_BASE_POINT);
76             return Ok(());
77         }
78     }
79 
80     extern "C" {
81         fn GFp_x25519_public_from_private_generic_masked(
82             public_key_out: &mut PublicKey,
83             private_key: &PrivateKey,
84         );
85     }
86     unsafe {
87         GFp_x25519_public_from_private_generic_masked(public_out, &private_key);
88     }
89 
90     Ok(())
91 }
92 
x25519_ecdh( out: &mut [u8], my_private_key: &ec::Seed, peer_public_key: untrusted::Input, ) -> Result<(), error::Unspecified>93 fn x25519_ecdh(
94     out: &mut [u8],
95     my_private_key: &ec::Seed,
96     peer_public_key: untrusted::Input,
97 ) -> Result<(), error::Unspecified> {
98     let cpu_features = my_private_key.cpu_features;
99     let my_private_key: &[u8; SCALAR_LEN] = my_private_key.bytes_less_safe().try_into()?;
100     let my_private_key = ops::MaskedScalar::from_bytes_masked(*my_private_key);
101     let peer_public_key: &[u8; PUBLIC_KEY_LEN] = peer_public_key.as_slice_less_safe().try_into()?;
102 
103     #[cfg_attr(
104         not(all(not(target_os = "ios"), target_arch = "arm")),
105         allow(unused_variables)
106     )]
107     fn scalar_mult(
108         out: &mut ops::EncodedPoint,
109         scalar: &ops::MaskedScalar,
110         point: &ops::EncodedPoint,
111         cpu_features: cpu::Features,
112     ) {
113         #[cfg(all(not(target_os = "ios"), target_arch = "arm"))]
114         {
115             if cpu::arm::NEON.available(cpu_features) {
116                 return x25519_neon(out, scalar, point);
117             }
118         }
119 
120         extern "C" {
121             fn GFp_x25519_scalar_mult_generic_masked(
122                 out: &mut ops::EncodedPoint,
123                 scalar: &ops::MaskedScalar,
124                 point: &ops::EncodedPoint,
125             );
126         }
127         unsafe {
128             GFp_x25519_scalar_mult_generic_masked(out, scalar, point);
129         }
130     }
131 
132     scalar_mult(
133         out.try_into()?,
134         &my_private_key,
135         peer_public_key,
136         cpu_features,
137     );
138 
139     let zeros: SharedSecret = [0; SHARED_SECRET_LEN];
140     if constant_time::verify_slices_are_equal(out, &zeros).is_ok() {
141         // All-zero output results when the input is a point of small order.
142         return Err(error::Unspecified);
143     }
144 
145     Ok(())
146 }
147 
148 #[cfg(all(not(target_os = "ios"), target_arch = "arm"))]
x25519_neon(out: &mut ops::EncodedPoint, scalar: &ops::MaskedScalar, point: &ops::EncodedPoint)149 fn x25519_neon(out: &mut ops::EncodedPoint, scalar: &ops::MaskedScalar, point: &ops::EncodedPoint) {
150     extern "C" {
151         fn GFp_x25519_NEON(
152             out: &mut ops::EncodedPoint,
153             scalar: &ops::MaskedScalar,
154             point: &ops::EncodedPoint,
155         );
156     }
157     unsafe { GFp_x25519_NEON(out, scalar, point) }
158 }
159 
160 const ELEM_AND_SCALAR_LEN: usize = ops::ELEM_LEN;
161 
162 type PrivateKey = ops::MaskedScalar;
163 const PRIVATE_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
164 
165 // An X25519 public key as an encoded Curve25519 point.
166 type PublicKey = [u8; PUBLIC_KEY_LEN];
167 const PUBLIC_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
168 
169 // An X25519 shared secret as an encoded Curve25519 point.
170 type SharedSecret = [u8; SHARED_SECRET_LEN];
171 const SHARED_SECRET_LEN: usize = ELEM_AND_SCALAR_LEN;
172