1#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10use subtle::ConstantTimeEq;
11use zeroize::{Zeroize, ZeroizeOnDrop};
12
13use crate::classic::crypto_box::crypto_box_seed_keypair_inplace;
14use crate::constants::{
15 CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES,
16 CRYPTO_KX_SESSIONKEYBYTES,
17};
18use crate::error::Error;
19use crate::kx;
20use crate::precalc::PrecalcSecretKey;
21use crate::types::*;
22
23pub type PublicKey = StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
25pub type SecretKey = StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>;
27pub type StackKeyPair = KeyPair<PublicKey, SecretKey>;
29
30#[cfg_attr(
31 feature = "serde",
32 derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize, Debug, Clone)
33)]
34#[cfg_attr(not(feature = "serde"), derive(Zeroize, ZeroizeOnDrop, Debug, Clone))]
35pub struct KeyPair<
38 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
39 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
40> {
41 pub public_key: PublicKey,
43 pub secret_key: SecretKey,
45}
46
47impl<
48 PublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
49 SecretKey: NewByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
50> KeyPair<PublicKey, SecretKey>
51{
52 pub fn new() -> Self {
54 Self {
55 public_key: PublicKey::new_byte_array(),
56 secret_key: SecretKey::new_byte_array(),
57 }
58 }
59
60 pub fn gen() -> Self {
62 use crate::classic::crypto_box::crypto_box_keypair_inplace;
63
64 let mut public_key = PublicKey::new_byte_array();
65 let mut secret_key = SecretKey::new_byte_array();
66 crypto_box_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
67
68 Self {
69 public_key,
70 secret_key,
71 }
72 }
73
74 pub fn from_secret_key(secret_key: SecretKey) -> Self {
77 use crate::classic::crypto_core::crypto_scalarmult_base;
78
79 let mut public_key = PublicKey::new_byte_array();
80 crypto_scalarmult_base(public_key.as_mut_array(), secret_key.as_array());
81
82 Self {
83 public_key,
84 secret_key,
85 }
86 }
87
88 pub fn from_seed<Seed: Bytes>(seed: &Seed) -> Self {
91 let mut public_key = PublicKey::new_byte_array();
92 let mut secret_key = SecretKey::new_byte_array();
93
94 crypto_box_seed_keypair_inplace(
95 public_key.as_mut_array(),
96 secret_key.as_mut_array(),
97 seed.as_slice(),
98 );
99
100 Self {
101 public_key,
102 secret_key,
103 }
104 }
105}
106
107impl KeyPair<StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>, StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>> {
108 pub fn gen_with_defaults() -> Self {
111 Self::gen()
112 }
113}
114
115impl<
116 'a,
117 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
118 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
119> KeyPair<PublicKey, SecretKey>
120{
121 pub fn from_slices(public_key: &'a [u8], secret_key: &'a [u8]) -> Result<Self, Error> {
124 Ok(Self {
125 public_key: PublicKey::try_from(public_key)
126 .map_err(|_e| dryoc_error!("invalid public key"))?,
127 secret_key: SecretKey::try_from(secret_key)
128 .map_err(|_e| dryoc_error!("invalid secret key"))?,
129 })
130 }
131}
132
133impl<
134 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
135 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
136> KeyPair<PublicKey, SecretKey>
137{
138 pub fn is_valid_public_key<PK: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(key: &PK) -> bool {
200 const ZERO_POINT: [u8; CRYPTO_BOX_PUBLICKEYBYTES] = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
201 let key_array = key.as_array();
202
203 if key_array == &ZERO_POINT {
205 return false;
206 }
207
208 if key_array[CRYPTO_BOX_PUBLICKEYBYTES - 1] & 0x80 != 0 {
211 return false;
212 }
213
214 true
217 }
218
219 pub fn is_valid_ed25519_key<PK: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(key: &PK) -> bool {
230 crate::classic::crypto_core::crypto_core_ed25519_is_valid_point_relaxed(key.as_array())
231 }
232
233 pub fn kx_new_client_session<SessionKey: NewByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize>(
236 &self,
237 server_public_key: &PublicKey,
238 ) -> Result<kx::Session<SessionKey>, Error> {
239 kx::Session::new_client(self, server_public_key)
240 }
241
242 pub fn kx_new_server_session<SessionKey: NewByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize>(
245 &self,
246 client_public_key: &PublicKey,
247 ) -> Result<kx::Session<SessionKey>, Error> {
248 kx::Session::new_server(self, client_public_key)
249 }
250
251 #[inline]
256 pub fn precalculate(
257 &self,
258 third_party_public_key: &PublicKey,
259 ) -> PrecalcSecretKey<StackByteArray<CRYPTO_BOX_BEFORENMBYTES>> {
260 PrecalcSecretKey::precalculate(third_party_public_key, &self.secret_key)
261 }
262}
263
264impl<
265 PublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
266 SecretKey: NewByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
267> Default for KeyPair<PublicKey, SecretKey>
268{
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
275#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
276pub mod protected {
277 use super::*;
279 use crate::classic::crypto_box::crypto_box_keypair_inplace;
280 pub use crate::protected::*;
281
282 impl
283 KeyPair<
284 Locked<HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>,
285 Locked<HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>>,
286 >
287 {
288 pub fn new_locked_keypair() -> Result<Self, std::io::Error> {
290 Ok(Self {
291 public_key: HeapByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::new_locked()?,
292 secret_key: HeapByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::new_locked()?,
293 })
294 }
295
296 pub fn gen_locked_keypair() -> Result<Self, std::io::Error> {
298 let mut res = Self::new_locked_keypair()?;
299
300 crypto_box_keypair_inplace(
301 res.public_key.as_mut_array(),
302 res.secret_key.as_mut_array(),
303 );
304
305 Ok(res)
306 }
307
308 #[inline]
314 pub fn precalculate_locked<OtherPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(
315 &self,
316 third_party_public_key: &OtherPublicKey,
317 ) -> Result<PrecalcSecretKey<Locked<HeapByteArray<CRYPTO_BOX_BEFORENMBYTES>>>, std::io::Error>
318 {
319 PrecalcSecretKey::precalculate_locked(third_party_public_key, &self.secret_key)
320 }
321 }
322
323 impl
324 KeyPair<
325 LockedRO<HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>,
326 LockedRO<HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>>,
327 >
328 {
329 pub fn gen_readonly_locked_keypair() -> Result<Self, std::io::Error> {
331 let mut public_key = HeapByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::new_locked()?;
332 let mut secret_key = HeapByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::new_locked()?;
333
334 crypto_box_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
335
336 let public_key = public_key.mprotect_readonly()?;
337 let secret_key = secret_key.mprotect_readonly()?;
338
339 Ok(Self {
340 public_key,
341 secret_key,
342 })
343 }
344
345 #[inline]
351 pub fn precalculate_readonly_locked<
352 OtherPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
353 >(
354 &self,
355 third_party_public_key: &OtherPublicKey,
356 ) -> Result<
357 PrecalcSecretKey<LockedRO<HeapByteArray<CRYPTO_BOX_BEFORENMBYTES>>>,
358 std::io::Error,
359 > {
360 PrecalcSecretKey::precalculate_readonly_locked(third_party_public_key, &self.secret_key)
361 }
362 }
363}
364
365impl<
366 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
367 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
368> PartialEq<KeyPair<PublicKey, SecretKey>> for KeyPair<PublicKey, SecretKey>
369{
370 fn eq(&self, other: &Self) -> bool {
371 self.public_key
372 .as_slice()
373 .ct_eq(other.public_key.as_slice())
374 .unwrap_u8()
375 == 1
376 && self
377 .secret_key
378 .as_slice()
379 .ct_eq(other.secret_key.as_slice())
380 .unwrap_u8()
381 == 1
382 }
383}
384
385#[cfg(test)]
386mod tests {
387
388 use super::*;
389 use crate::kx::Session;
390
391 fn all_eq<T>(t: &[T], v: T) -> bool
392 where
393 T: PartialEq,
394 {
395 t.iter().all(|x| *x == v)
396 }
397
398 #[test]
399 fn test_new() {
400 let keypair = KeyPair::<
401 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
402 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
403 >::new();
404
405 assert!(all_eq(&keypair.public_key, 0));
406 assert!(all_eq(&keypair.secret_key, 0));
407 }
408
409 #[test]
410 fn test_default() {
411 let keypair = KeyPair::<
412 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
413 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
414 >::default();
415
416 assert!(all_eq(&keypair.public_key, 0));
417 assert!(all_eq(&keypair.secret_key, 0));
418 }
419
420 #[test]
421 fn test_gen_keypair() {
422 use sodiumoxide::crypto::scalarmult::curve25519::{Scalar, scalarmult_base};
423
424 use crate::classic::crypto_core::crypto_scalarmult_base;
425
426 let keypair = KeyPair::<
427 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
428 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
429 >::gen();
430
431 let mut public_key = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
432 crypto_scalarmult_base(&mut public_key, keypair.secret_key.as_array());
433
434 assert_eq!(keypair.public_key.as_array(), &public_key);
435
436 let ge = scalarmult_base(&Scalar::from_slice(&keypair.secret_key).unwrap());
437
438 assert_eq!(ge.as_ref(), public_key);
439 }
440
441 #[test]
442 fn test_from_secret_key() {
443 let keypair_1 = KeyPair::<
444 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
445 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
446 >::gen();
447 let keypair_2 = KeyPair::from_secret_key(keypair_1.secret_key.clone());
448
449 assert_eq!(keypair_1.public_key, keypair_2.public_key);
450 }
451
452 #[test]
453 fn test_keypair_precalculate() {
454 let kp1 = KeyPair::gen_with_defaults();
455 let kp2 = KeyPair::gen_with_defaults();
456 let precalc = kp1.precalculate(&kp2.public_key);
457 assert_eq!(precalc.len(), crate::constants::CRYPTO_BOX_BEFORENMBYTES);
458 }
459
460 #[cfg(feature = "nightly")]
461 #[test]
462 fn test_keypair_precalculate_locked() {
463 use crate::keypair::protected::*;
464 let kp1 = KeyPair::gen_locked_keypair().unwrap();
465 let kp2 = KeyPair::gen_locked_keypair().unwrap();
466 let precalc = kp1.precalculate_locked(&kp2.public_key).unwrap();
467 assert_eq!(precalc.len(), crate::constants::CRYPTO_BOX_BEFORENMBYTES);
468 }
469
470 #[test]
471 fn test_keypair_kx_new_client_session() {
472 let server_kp = KeyPair::gen_with_defaults();
473 let client_kp = KeyPair::gen_with_defaults();
474 let session: Session<StackByteArray<CRYPTO_KX_SESSIONKEYBYTES>> = client_kp
475 .kx_new_client_session(&server_kp.public_key)
476 .unwrap();
477 assert_eq!(
478 session.rx_as_slice().len(),
479 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
480 );
481 assert_eq!(
482 session.tx_as_slice().len(),
483 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
484 );
485 }
486
487 #[test]
488 fn test_keypair_kx_new_server_session() {
489 let client_kp = KeyPair::gen_with_defaults();
490 let server_kp = KeyPair::gen_with_defaults();
491 let session: Session<StackByteArray<CRYPTO_KX_SESSIONKEYBYTES>> = server_kp
492 .kx_new_server_session(&client_kp.public_key)
493 .unwrap();
494 assert_eq!(
495 session.rx_as_slice().len(),
496 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
497 );
498 assert_eq!(
499 session.tx_as_slice().len(),
500 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
501 );
502 }
503
504 #[test]
505 fn test_keypair_from_seed() {
506 let seed = [42u8; 32];
507 let kp: StackKeyPair = KeyPair::from_seed(&seed);
508 assert!(!kp.public_key.iter().all(|x| *x == 0));
509 }
510
511 #[test]
512 fn test_keypair_gen_with_defaults() {
513 let kp = KeyPair::gen_with_defaults();
514 assert!(!kp.public_key.iter().all(|x| *x == 0));
515 }
516
517 #[test]
518 fn test_is_valid_public_key() {
519 let valid_pk_bytes = [
522 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114,
523 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
524 ];
525 let valid_pk = PublicKey::from(valid_pk_bytes);
526 assert!(
527 KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&valid_pk),
528 "Known valid key failed validation"
529 );
530
531 let mut invalid_high_bit_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
533 invalid_high_bit_bytes[31] = 0x80;
534 let invalid_high_bit = PublicKey::from(invalid_high_bit_bytes);
535 assert!(
536 !KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&invalid_high_bit),
537 "Key with high bit set should be invalid"
538 );
539
540 let zero_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
542 let zero_pk = PublicKey::from(zero_bytes);
543 assert!(
544 !KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&zero_pk),
545 "Zero key should be invalid"
546 );
547
548 let kp = KeyPair::gen_with_defaults();
553 assert!(
554 KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&kp.public_key),
555 "Generated key failed validation"
556 );
557 }
558
559 #[test]
560 fn test_is_valid_ed25519_key() {
561 let (valid_pk, _) = crate::classic::crypto_sign::crypto_sign_keypair();
563
564 assert!(
566 KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&valid_pk),
567 "Ed25519 key from crypto_sign_keypair should pass relaxed validation"
568 );
569
570 let zero_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
574 let zero_pk = PublicKey::from(zero_bytes);
575 assert!(
576 !KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&zero_pk),
577 "Zero key should be invalid even with relaxed validation"
578 );
579
580 let identity_bytes = [
582 1, 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,
583 0, 0, 0,
584 ];
585 let identity_pk = PublicKey::from(identity_bytes);
586 assert!(
587 !KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&identity_pk),
588 "Identity element (small order point) should be invalid even with relaxed validation"
589 );
590 }
591}