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