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