ring/ec/curve25519/
x25519.rs

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(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}