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
17use super::{ops, scalar::SCALAR_LEN};
18use crate::{agreement, c, constant_time, cpu, ec, error, rand};
19
20static 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
37pub static X25519: agreement::Algorithm = agreement::Algorithm {
38 curve: &CURVE25519,
39 ecdh: x25519_ecdh,
40};
41
42#[allow(clippy::unnecessary_wraps)]
43fn x25519_check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified> {
44 debug_assert_eq!(bytes.len(), PRIVATE_KEY_LEN);
45 Ok(())
46}
47
48fn x25519_generate_private_key(
49 rng: &dyn rand::SecureRandom,
50 out: &mut [u8],
51) -> Result<(), error::Unspecified> {
52 rng.fill(dest:out)
53}
54
55fn 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
96fn 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"))]
162fn 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
173const ELEM_AND_SCALAR_LEN: usize = ops::ELEM_LEN;
174
175type PrivateKey = ops::MaskedScalar;
176const PRIVATE_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
177
178// An X25519 public key as an encoded Curve25519 point.
179type PublicKey = [u8; PUBLIC_KEY_LEN];
180const PUBLIC_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
181
182// An X25519 shared secret as an encoded Curve25519 point.
183type SharedSecret = [u8; SHARED_SECRET_LEN];
184const SHARED_SECRET_LEN: usize = ELEM_AND_SCALAR_LEN;
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use crate::ec;
190 use untrusted::Input;
191
192 #[test]
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