1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7)]
8
9use ed448_goldilocks::{
10 MontgomeryPoint,
11 elliptic_curve::{
12 Generate,
13 array::{Array, typenum::U56},
14 bigint::U448,
15 rand_core::{CryptoRng, TryCryptoRng},
16 scalar::FromUintUnchecked,
17 zeroize::Zeroize,
18 },
19};
20
21type MontgomeryScalar = ed448_goldilocks::Scalar<ed448_goldilocks::Ed448>;
22
23impl From<&EphemeralSecret> for PublicKey {
26 fn from(secret: &EphemeralSecret) -> PublicKey {
27 let secret = secret.as_scalar();
28 let point = &MontgomeryPoint::GENERATOR * &secret;
29 PublicKey(point)
30 }
31}
32
33#[derive(Debug, PartialEq, Eq, Copy, Clone)]
35pub struct PublicKey(MontgomeryPoint);
36
37impl PublicKey {
38 pub fn from_bytes(bytes: &[u8]) -> Option<PublicKey> {
43 let public_key = PublicKey::from_bytes_unchecked(bytes)?;
44 if public_key.0.is_low_order() {
45 return None;
46 }
47 Some(public_key)
48 }
49
50 pub fn from_bytes_unchecked(bytes: &[u8]) -> Option<PublicKey> {
54 if bytes.len() != 56 {
56 return None;
57 }
58
59 let arr = slice_to_array(bytes);
61 let point = MontgomeryPoint(arr);
62
63 Some(PublicKey(point))
64 }
65
66 pub fn as_bytes(&self) -> &[u8; 56] {
68 self.0.as_bytes()
69 }
70}
71
72#[derive(Zeroize)]
75#[zeroize(drop)]
76pub struct SharedSecret(MontgomeryPoint);
77
78impl SharedSecret {
79 pub fn as_bytes(&self) -> &[u8; 56] {
81 self.0.as_bytes()
82 }
83}
84
85#[derive(Clone, Zeroize)]
87#[zeroize(drop)]
88pub struct EphemeralSecret(EphemeralSecretBytes);
89
90type EphemeralSecretBytes = Array<u8, U56>;
91
92impl EphemeralSecret {
93 #[deprecated(since = "0.14.0", note = "use the `Generate` trait instead")]
95 pub fn new<R>(csprng: &mut R) -> Self
96 where
97 R: CryptoRng + ?Sized,
98 {
99 Self::generate_from_rng(csprng)
100 }
101
102 fn as_scalar(&self) -> MontgomeryScalar {
104 let secret = U448::from_le_slice(&self.0);
105 MontgomeryScalar::from_uint_unchecked(secret)
106 }
107
108 pub fn diffie_hellman(&self, public_key: &PublicKey) -> SharedSecret {
110 let shared_key = &public_key.0 * &self.as_scalar();
113 SharedSecret(shared_key)
114 }
115
116 pub fn as_bytes(&self) -> &[u8; 56] {
118 self.0.as_ref()
119 }
120
121 fn clamp(mut bytes: EphemeralSecretBytes) -> Self {
123 bytes[0] &= 252;
124 bytes[55] |= 128;
125 Self(bytes)
126 }
127}
128
129impl Generate for EphemeralSecret {
130 fn try_generate_from_rng<R>(csprng: &mut R) -> Result<Self, R::Error>
131 where
132 R: TryCryptoRng + ?Sized,
133 {
134 let mut bytes = Array::default();
135 csprng.try_fill_bytes(bytes.as_mut_slice())?;
136 Ok(EphemeralSecret::clamp(bytes))
137 }
138}
139
140fn slice_to_array(bytes: &[u8]) -> [u8; 56] {
141 let mut array: [u8; 56] = [0; 56];
142 array.copy_from_slice(bytes);
143 array
144}
145
146pub fn x448(scalar_bytes: [u8; 56], point_bytes: [u8; 56]) -> Option<[u8; 56]> {
153 let point = PublicKey::from_bytes(&point_bytes)?;
154 let scalar = EphemeralSecret::clamp(scalar_bytes.into()).as_scalar();
155 Some((&point.0 * &scalar).0)
156}
157pub fn x448_unchecked(scalar_bytes: [u8; 56], point_bytes: [u8; 56]) -> [u8; 56] {
160 let point = MontgomeryPoint(point_bytes);
161 let scalar = EphemeralSecret::clamp(scalar_bytes.into()).as_scalar();
162 (&point * &scalar).0
163}
164
165pub const X448_BASEPOINT_BYTES: [u8; 56] = MontgomeryPoint::GENERATOR.0;
166
167#[cfg(feature = "static_secrets")]
182#[derive(Clone, Zeroize)]
183#[zeroize(drop)]
184pub struct StaticSecret(Array<u8, U56>);
185
186#[cfg(feature = "static_secrets")]
187impl StaticSecret {
188 fn new(value: Array<u8, U56>) -> Self {
189 let mut out = Self(value);
190 out.clamp();
191 out
192 }
193
194 pub fn random_from_rng<R: CryptoRng + ?Sized>(csprng: &mut R) -> Self {
196 let mut bytes = Array::default();
197 csprng.fill_bytes(bytes.as_mut_slice());
198 Self::new(bytes)
199 }
200
201 fn clamp(&mut self) {
203 self.0[0] &= 252;
204 self.0[55] |= 128;
205 }
206
207 fn as_scalar(&self) -> MontgomeryScalar {
209 let secret = U448::from_le_slice(&self.0);
210 MontgomeryScalar::from_uint_unchecked(secret)
211 }
212
213 pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
216 let shared_key = &their_public.0 * &self.as_scalar();
219 SharedSecret(shared_key)
220 }
221
222 #[inline]
224 pub fn as_bytes(&self) -> &[u8; 56] {
225 self.0.as_ref()
226 }
227}
228
229#[cfg(feature = "static_secrets")]
230impl From<[u8; 56]> for StaticSecret {
231 fn from(bytes: [u8; 56]) -> StaticSecret {
233 StaticSecret::new(bytes.into())
234 }
235}
236
237#[cfg(feature = "static_secrets")]
238impl From<&StaticSecret> for PublicKey {
239 fn from(secret: &StaticSecret) -> PublicKey {
241 let secret = secret.as_scalar();
242 let point = &MontgomeryPoint::GENERATOR * &secret;
243 PublicKey(point)
244 }
245}
246
247#[cfg(feature = "static_secrets")]
248impl AsRef<[u8]> for StaticSecret {
249 #[inline]
251 fn as_ref(&self) -> &[u8] {
252 self.as_bytes()
253 }
254}
255
256#[cfg(all(feature = "static_secrets", feature = "serde"))]
257impl serdect::serde::Serialize for StaticSecret {
258 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
259 where
260 S: serdect::serde::Serializer,
261 {
262 serdect::array::serialize_hex_lower_or_bin(&self.0, s)
263 }
264}
265
266#[cfg(all(feature = "static_secrets", feature = "serde"))]
267impl<'de> serdect::serde::Deserialize<'de> for StaticSecret {
268 fn deserialize<D>(d: D) -> Result<Self, D::Error>
269 where
270 D: serdect::serde::Deserializer<'de>,
271 {
272 let mut bytes = Array::default();
273 serdect::array::deserialize_hex_or_bin(&mut bytes, d)?;
274 Ok(StaticSecret::new(bytes))
275 }
276}
277
278#[cfg(test)]
279mod test {
280 extern crate alloc;
281
282 use super::*;
283 use alloc::vec;
284 use core::array::TryFromSliceError;
285
286 impl From<[u8; 56]> for EphemeralSecret {
289 fn from(arr: [u8; 56]) -> Self {
290 Self::clamp(arr.into())
291 }
292 }
293
294 impl TryFrom<&[u8]> for EphemeralSecret {
295 type Error = TryFromSliceError;
296
297 fn try_from(bytes: &[u8]) -> Result<Self, TryFromSliceError> {
298 Ok(Self::clamp(Array::try_from(bytes)?))
299 }
300 }
301
302 #[test]
303 #[cfg(feature = "getrandom")]
304 fn test_random_dh() {
305 let alice_priv = EphemeralSecret::generate();
306 let alice_pub = PublicKey::from(&alice_priv);
307
308 let bob_priv = EphemeralSecret::generate();
309 let bob_pub = PublicKey::from(&bob_priv);
310
311 let low_order = alice_pub.0.is_low_order() || bob_pub.0.is_low_order();
315 assert!(low_order == false);
316
317 let shared_alice = alice_priv.diffie_hellman(&bob_pub);
320 let shared_bob = bob_priv.diffie_hellman(&alice_pub);
321
322 assert_eq!(shared_alice.as_bytes()[..], shared_bob.as_bytes()[..]);
323 }
324
325 #[test]
326 fn test_rfc_test_vectors_alice_bob() {
327 let alice_priv = EphemeralSecret::from([
328 0x9a, 0x8f, 0x49, 0x25, 0xd1, 0x51, 0x9f, 0x57, 0x75, 0xcf, 0x46, 0xb0, 0x4b, 0x58,
329 0x0, 0xd4, 0xee, 0x9e, 0xe8, 0xba, 0xe8, 0xbc, 0x55, 0x65, 0xd4, 0x98, 0xc2, 0x8d,
330 0xd9, 0xc9, 0xba, 0xf5, 0x74, 0xa9, 0x41, 0x97, 0x44, 0x89, 0x73, 0x91, 0x0, 0x63,
331 0x82, 0xa6, 0xf1, 0x27, 0xab, 0x1d, 0x9a, 0xc2, 0xd8, 0xc0, 0xa5, 0x98, 0x72, 0x6b,
332 ]);
333 let got_alice_pub = PublicKey::from(&alice_priv);
334
335 let expected_alice_pub = [
336 0x9b, 0x8, 0xf7, 0xcc, 0x31, 0xb7, 0xe3, 0xe6, 0x7d, 0x22, 0xd5, 0xae, 0xa1, 0x21, 0x7,
337 0x4a, 0x27, 0x3b, 0xd2, 0xb8, 0x3d, 0xe0, 0x9c, 0x63, 0xfa, 0xa7, 0x3d, 0x2c, 0x22,
338 0xc5, 0xd9, 0xbb, 0xc8, 0x36, 0x64, 0x72, 0x41, 0xd9, 0x53, 0xd4, 0xc, 0x5b, 0x12,
339 0xda, 0x88, 0x12, 0xd, 0x53, 0x17, 0x7f, 0x80, 0xe5, 0x32, 0xc4, 0x1f, 0xa0,
340 ];
341 assert_eq!(got_alice_pub.0.as_bytes()[..], expected_alice_pub[..]);
342
343 let bob_priv = EphemeralSecret::from([
344 0x1c, 0x30, 0x6a, 0x7a, 0xc2, 0xa0, 0xe2, 0xe0, 0x99, 0xb, 0x29, 0x44, 0x70, 0xcb,
345 0xa3, 0x39, 0xe6, 0x45, 0x37, 0x72, 0xb0, 0x75, 0x81, 0x1d, 0x8f, 0xad, 0xd, 0x1d,
346 0x69, 0x27, 0xc1, 0x20, 0xbb, 0x5e, 0xe8, 0x97, 0x2b, 0xd, 0x3e, 0x21, 0x37, 0x4c,
347 0x9c, 0x92, 0x1b, 0x9, 0xd1, 0xb0, 0x36, 0x6f, 0x10, 0xb6, 0x51, 0x73, 0x99, 0x2d,
348 ]);
349 let got_bob_pub = PublicKey::from(&bob_priv);
350
351 let expected_bob_pub = [
352 0x3e, 0xb7, 0xa8, 0x29, 0xb0, 0xcd, 0x20, 0xf5, 0xbc, 0xfc, 0xb, 0x59, 0x9b, 0x6f,
353 0xec, 0xcf, 0x6d, 0xa4, 0x62, 0x71, 0x7, 0xbd, 0xb0, 0xd4, 0xf3, 0x45, 0xb4, 0x30,
354 0x27, 0xd8, 0xb9, 0x72, 0xfc, 0x3e, 0x34, 0xfb, 0x42, 0x32, 0xa1, 0x3c, 0xa7, 0x6,
355 0xdc, 0xb5, 0x7a, 0xec, 0x3d, 0xae, 0x7, 0xbd, 0xc1, 0xc6, 0x7b, 0xf3, 0x36, 0x9,
356 ];
357 assert_eq!(got_bob_pub.0.as_bytes()[..], expected_bob_pub[..]);
358
359 let bob_shared = bob_priv.diffie_hellman(&got_alice_pub);
360 let alice_shared = alice_priv.diffie_hellman(&got_bob_pub);
361 assert_eq!(bob_shared.as_bytes()[..], alice_shared.as_bytes()[..]);
362
363 let expected_shared = [
364 0x7, 0xff, 0xf4, 0x18, 0x1a, 0xc6, 0xcc, 0x95, 0xec, 0x1c, 0x16, 0xa9, 0x4a, 0xf, 0x74,
365 0xd1, 0x2d, 0xa2, 0x32, 0xce, 0x40, 0xa7, 0x75, 0x52, 0x28, 0x1d, 0x28, 0x2b, 0xb6,
366 0xc, 0xb, 0x56, 0xfd, 0x24, 0x64, 0xc3, 0x35, 0x54, 0x39, 0x36, 0x52, 0x1c, 0x24, 0x40,
367 0x30, 0x85, 0xd5, 0x9a, 0x44, 0x9a, 0x50, 0x37, 0x51, 0x4a, 0x87, 0x9d,
368 ];
369
370 assert_eq!(bob_shared.as_bytes()[..], expected_shared[..]);
371 }
372
373 #[test]
374 fn test_rfc_test_vectors_fixed() {
375 struct Test {
376 secret: [u8; 56],
377 point: [u8; 56],
378 expected: [u8; 56],
379 }
380
381 let test_vectors = vec![
382 Test {
383 secret: [
384 0x3d, 0x26, 0x2f, 0xdd, 0xf9, 0xec, 0x8e, 0x88, 0x49, 0x52, 0x66, 0xfe, 0xa1,
385 0x9a, 0x34, 0xd2, 0x88, 0x82, 0xac, 0xef, 0x4, 0x51, 0x4, 0xd0, 0xd1, 0xaa,
386 0xe1, 0x21, 0x70, 0xa, 0x77, 0x9c, 0x98, 0x4c, 0x24, 0xf8, 0xcd, 0xd7, 0x8f,
387 0xbf, 0xf4, 0x49, 0x43, 0xeb, 0xa3, 0x68, 0xf5, 0x4b, 0x29, 0x25, 0x9a, 0x4f,
388 0x1c, 0x60, 0xa, 0xd3,
389 ],
390 point: [
391 0x6, 0xfc, 0xe6, 0x40, 0xfa, 0x34, 0x87, 0xbf, 0xda, 0x5f, 0x6c, 0xf2, 0xd5,
392 0x26, 0x3f, 0x8a, 0xad, 0x88, 0x33, 0x4c, 0xbd, 0x7, 0x43, 0x7f, 0x2, 0xf, 0x8,
393 0xf9, 0x81, 0x4d, 0xc0, 0x31, 0xdd, 0xbd, 0xc3, 0x8c, 0x19, 0xc6, 0xda, 0x25,
394 0x83, 0xfa, 0x54, 0x29, 0xdb, 0x94, 0xad, 0xa1, 0x8a, 0xa7, 0xa7, 0xfb, 0x4e,
395 0xf8, 0xa0, 0x86,
396 ],
397 expected: [
398 0xce, 0x3e, 0x4f, 0xf9, 0x5a, 0x60, 0xdc, 0x66, 0x97, 0xda, 0x1d, 0xb1, 0xd8,
399 0x5e, 0x6a, 0xfb, 0xdf, 0x79, 0xb5, 0xa, 0x24, 0x12, 0xd7, 0x54, 0x6d, 0x5f,
400 0x23, 0x9f, 0xe1, 0x4f, 0xba, 0xad, 0xeb, 0x44, 0x5f, 0xc6, 0x6a, 0x1, 0xb0,
401 0x77, 0x9d, 0x98, 0x22, 0x39, 0x61, 0x11, 0x1e, 0x21, 0x76, 0x62, 0x82, 0xf7,
402 0x3d, 0xd9, 0x6b, 0x6f,
403 ],
404 },
405 Test {
406 secret: [
407 0x20, 0x3d, 0x49, 0x44, 0x28, 0xb8, 0x39, 0x93, 0x52, 0x66, 0x5d, 0xdc, 0xa4,
408 0x2f, 0x9d, 0xe8, 0xfe, 0xf6, 0x0, 0x90, 0x8e, 0xd, 0x46, 0x1c, 0xb0, 0x21,
409 0xf8, 0xc5, 0x38, 0x34, 0x5d, 0xd7, 0x7c, 0x3e, 0x48, 0x6, 0xe2, 0x5f, 0x46,
410 0xd3, 0x31, 0x5c, 0x44, 0xe0, 0xa5, 0xb4, 0x37, 0x12, 0x82, 0xdd, 0x2c, 0x8d,
411 0x5b, 0xe3, 0x9, 0x5f,
412 ],
413 point: [
414 0xf, 0xbc, 0xc2, 0xf9, 0x93, 0xcd, 0x56, 0xd3, 0x30, 0x5b, 0xb, 0x7d, 0x9e,
415 0x55, 0xd4, 0xc1, 0xa8, 0xfb, 0x5d, 0xbb, 0x52, 0xf8, 0xe9, 0xa1, 0xe9, 0xb6,
416 0x20, 0x1b, 0x16, 0x5d, 0x1, 0x58, 0x94, 0xe5, 0x6c, 0x4d, 0x35, 0x70, 0xbe,
417 0xe5, 0x2f, 0xe2, 0x5, 0xe2, 0x8a, 0x78, 0xb9, 0x1c, 0xdf, 0xbd, 0xe7, 0x1c,
418 0xe8, 0xd1, 0x57, 0xdb,
419 ],
420 expected: [
421 0x88, 0x4a, 0x2, 0x57, 0x62, 0x39, 0xff, 0x7a, 0x2f, 0x2f, 0x63, 0xb2, 0xdb,
422 0x6a, 0x9f, 0xf3, 0x70, 0x47, 0xac, 0x13, 0x56, 0x8e, 0x1e, 0x30, 0xfe, 0x63,
423 0xc4, 0xa7, 0xad, 0x1b, 0x3e, 0xe3, 0xa5, 0x70, 0xd, 0xf3, 0x43, 0x21, 0xd6,
424 0x20, 0x77, 0xe6, 0x36, 0x33, 0xc5, 0x75, 0xc1, 0xc9, 0x54, 0x51, 0x4e, 0x99,
425 0xda, 0x7c, 0x17, 0x9d,
426 ],
427 },
428 ];
429
430 for vector in test_vectors {
431 let public_key = PublicKey::from_bytes(&vector.point).unwrap();
432 let secret = EphemeralSecret::try_from(&vector.secret[..]).unwrap();
433
434 let got = secret.diffie_hellman(&public_key);
435
436 assert_eq!(got.0.as_bytes()[..], vector.expected[..])
437 }
438 }
439
440 fn swap(secret: &mut [u8; 56], public_key: &mut [u8; 56], result: &[u8; 56]) {
442 *public_key = *secret;
444 *secret = *result;
446 }
447
448 #[test]
449 #[ignore]
450 fn test_rfc_test_vectors_iteration() {
451 let one_iter = [
452 0x3f, 0x48, 0x2c, 0x8a, 0x9f, 0x19, 0xb0, 0x1e, 0x6c, 0x46, 0xee, 0x97, 0x11, 0xd9,
453 0xdc, 0x14, 0xfd, 0x4b, 0xf6, 0x7a, 0xf3, 0x7, 0x65, 0xc2, 0xae, 0x2b, 0x84, 0x6a,
454 0x4d, 0x23, 0xa8, 0xcd, 0xd, 0xb8, 0x97, 0x8, 0x62, 0x39, 0x49, 0x2c, 0xaf, 0x35, 0xb,
455 0x51, 0xf8, 0x33, 0x86, 0x8b, 0x9b, 0xc2, 0xb3, 0xbc, 0xa9, 0xcf, 0x41, 0x13,
456 ];
457 let one_k_iter = [
458 0xaa, 0x3b, 0x47, 0x49, 0xd5, 0x5b, 0x9d, 0xaf, 0x1e, 0x5b, 0x0, 0x28, 0x88, 0x26,
459 0xc4, 0x67, 0x27, 0x4c, 0xe3, 0xeb, 0xbd, 0xd5, 0xc1, 0x7b, 0x97, 0x5e, 0x9, 0xd4,
460 0xaf, 0x6c, 0x67, 0xcf, 0x10, 0xd0, 0x87, 0x20, 0x2d, 0xb8, 0x82, 0x86, 0xe2, 0xb7,
461 0x9f, 0xce, 0xea, 0x3e, 0xc3, 0x53, 0xef, 0x54, 0xfa, 0xa2, 0x6e, 0x21, 0x9f, 0x38,
462 ];
463 let one_mil_iter = [
464 0x7, 0x7f, 0x45, 0x36, 0x81, 0xca, 0xca, 0x36, 0x93, 0x19, 0x84, 0x20, 0xbb, 0xe5,
465 0x15, 0xca, 0xe0, 0x0, 0x24, 0x72, 0x51, 0x9b, 0x3e, 0x67, 0x66, 0x1a, 0x7e, 0x89,
466 0xca, 0xb9, 0x46, 0x95, 0xc8, 0xf4, 0xbc, 0xd6, 0x6e, 0x61, 0xb9, 0xb9, 0xc9, 0x46,
467 0xda, 0x8d, 0x52, 0x4d, 0xe3, 0xd6, 0x9b, 0xd9, 0xd9, 0xd6, 0x6b, 0x99, 0x7e, 0x37,
468 ];
469
470 let mut point = MontgomeryPoint::GENERATOR.0;
471 let mut scalar = MontgomeryPoint::GENERATOR.0;
472 let mut result = [0u8; 56];
473
474 for _ in 1..=1 {
476 result = x448(scalar, point).unwrap();
477 swap(&mut scalar, &mut point, &result);
478 }
479 assert_eq!(&result[..], &one_iter[..]);
480
481 for _ in 1..=999 {
483 result = x448(scalar, point).unwrap();
484 swap(&mut scalar, &mut point, &result);
485 }
486 assert_eq!(&result[..], &one_k_iter[..]);
487
488 for _ in 1..=999_000 {
490 result = x448(scalar, point).unwrap();
491 swap(&mut scalar, &mut point, &result);
492 }
493 assert_eq!(&result[..], &one_mil_iter[..]);
494 }
495}