1use std::fmt;
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12use subtle::ConstantTimeEq;
13use zeroize::{Zeroize, ZeroizeOnDrop};
14
15use crate::classic::crypto_box::crypto_box_seed_keypair_inplace;
16use crate::constants::{
17 CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES,
18 CRYPTO_BOX_SEEDBYTES, CRYPTO_KX_SESSIONKEYBYTES,
19};
20use crate::error::Error;
21use crate::kx;
22use crate::precalc::PrecalcSecretKey;
23use crate::types::*;
24
25pub type PublicKey = StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
27pub type SecretKey = StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>;
29pub type StackKeyPair = KeyPair<PublicKey, SecretKey>;
31
32#[cfg_attr(
33 feature = "serde",
34 derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize, Clone)
35)]
36#[cfg_attr(not(feature = "serde"), derive(Zeroize, ZeroizeOnDrop, Clone))]
37pub struct KeyPair<
40 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
41 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
42> {
43 pub public_key: PublicKey,
45 pub secret_key: SecretKey,
47}
48
49impl<
50 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
51 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
52> fmt::Debug for KeyPair<PublicKey, SecretKey>
53{
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 f.debug_struct("KeyPair")
56 .field("public_key", &"[REDACTED]")
57 .field("secret_key", &"[REDACTED]")
58 .finish()
59 }
60}
61
62impl<
63 PublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
64 SecretKey: NewByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
65> KeyPair<PublicKey, SecretKey>
66{
67 pub fn new() -> Self {
69 Self {
70 public_key: PublicKey::new_byte_array(),
71 secret_key: SecretKey::new_byte_array(),
72 }
73 }
74
75 pub fn generate() -> Self {
77 use crate::classic::crypto_box::crypto_box_keypair_inplace;
78
79 let mut public_key = PublicKey::new_byte_array();
80 let mut secret_key = SecretKey::new_byte_array();
81 crypto_box_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
82
83 Self {
84 public_key,
85 secret_key,
86 }
87 }
88
89 #[deprecated(note = "use generate() instead")]
94 pub fn r#gen() -> Self {
95 Self::generate()
96 }
97
98 pub fn from_secret_key(secret_key: SecretKey) -> Self {
101 use crate::classic::crypto_core::crypto_scalarmult_base;
102
103 let mut public_key = PublicKey::new_byte_array();
104 crypto_scalarmult_base(public_key.as_mut_array(), secret_key.as_array());
105
106 Self {
107 public_key,
108 secret_key,
109 }
110 }
111
112 pub fn from_seed<Seed: ByteArray<CRYPTO_BOX_SEEDBYTES>>(seed: &Seed) -> Self {
114 let mut public_key = PublicKey::new_byte_array();
115 let mut secret_key = SecretKey::new_byte_array();
116
117 crypto_box_seed_keypair_inplace(
118 public_key.as_mut_array(),
119 secret_key.as_mut_array(),
120 seed.as_array(),
121 );
122
123 Self {
124 public_key,
125 secret_key,
126 }
127 }
128}
129
130impl KeyPair<StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>, StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>> {
131 pub fn generate_with_defaults() -> Self {
134 Self::generate()
135 }
136
137 #[deprecated(note = "use generate_with_defaults() instead")]
143 pub fn gen_with_defaults() -> Self {
144 Self::generate_with_defaults()
145 }
146}
147
148impl<
149 'a,
150 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
151 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
152> KeyPair<PublicKey, SecretKey>
153{
154 pub fn from_slices(public_key: &'a [u8], secret_key: &'a [u8]) -> Result<Self, Error> {
162 validate_length!(
163 exact CRYPTO_BOX_PUBLICKEYBYTES,
164 public_key.len(),
165 crate::ErrorContext::PublicKey
166 );
167 validate_length!(
168 exact CRYPTO_BOX_SECRETKEYBYTES,
169 secret_key.len(),
170 crate::ErrorContext::SecretKey
171 );
172
173 Ok(Self {
174 public_key: PublicKey::try_from(public_key)
175 .map_err(|_| Error::invalid_key(crate::ErrorContext::PublicKey))?,
176 secret_key: SecretKey::try_from(secret_key)
177 .map_err(|_| Error::invalid_key(crate::ErrorContext::SecretKey))?,
178 })
179 }
180}
181
182impl<
183 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
184 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
185> KeyPair<PublicKey, SecretKey>
186{
187 pub fn is_valid_public_key<PK: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(key: &PK) -> bool {
224 let scalar = [0u8; CRYPTO_BOX_SECRETKEYBYTES];
225 let mut shared_secret = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
226
227 crate::classic::crypto_core::crypto_scalarmult(&mut shared_secret, &scalar, key.as_array())
228 .is_ok()
229 }
230
231 pub fn is_valid_ed25519_key<PK: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(key: &PK) -> bool {
245 crate::classic::crypto_core::crypto_core_ed25519_is_valid_point(key.as_array())
246 }
247
248 pub fn kx_new_client_session<
256 SessionKey: NewByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize + ZeroizeOnDrop,
257 >(
258 &self,
259 server_public_key: &PublicKey,
260 ) -> Result<kx::Session<SessionKey>, Error> {
261 kx::Session::new_client(self, server_public_key)
262 }
263
264 pub fn kx_new_server_session<
272 SessionKey: NewByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize + ZeroizeOnDrop,
273 >(
274 &self,
275 client_public_key: &PublicKey,
276 ) -> Result<kx::Session<SessionKey>, Error> {
277 kx::Session::new_server(self, client_public_key)
278 }
279
280 #[inline]
290 pub fn precalculate(
291 &self,
292 third_party_public_key: &PublicKey,
293 ) -> Result<PrecalcSecretKey<StackByteArray<CRYPTO_BOX_BEFORENMBYTES>>, Error> {
294 PrecalcSecretKey::precalculate(third_party_public_key, &self.secret_key)
295 }
296}
297
298impl<
299 PublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
300 SecretKey: NewByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
301> Default for KeyPair<PublicKey, SecretKey>
302{
303 fn default() -> Self {
304 Self::new()
305 }
306}
307
308#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
309#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
310pub mod protected {
311 use super::*;
313 use crate::classic::crypto_box::crypto_box_keypair_inplace;
314 pub use crate::protected::*;
315
316 impl
317 KeyPair<
318 Locked<HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>,
319 Locked<HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>>,
320 >
321 {
322 pub fn new_locked_keypair() -> Result<Self, Error> {
334 Ok(Self {
335 public_key: HeapByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::new_locked()?,
336 secret_key: HeapByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::new_locked()?,
337 })
338 }
339
340 pub fn generate_locked_keypair() -> Result<Self, Error> {
352 let mut res = Self::new_locked_keypair()?;
353
354 crypto_box_keypair_inplace(
355 res.public_key.as_mut_array(),
356 res.secret_key.as_mut_array(),
357 );
358
359 Ok(res)
360 }
361
362 #[deprecated(note = "use generate_locked_keypair() instead")]
377 pub fn gen_locked_keypair() -> Result<Self, Error> {
378 Self::generate_locked_keypair()
379 }
380
381 #[inline]
397 pub fn precalculate_locked<OtherPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(
398 &self,
399 third_party_public_key: &OtherPublicKey,
400 ) -> Result<PrecalcSecretKey<Locked<HeapByteArray<CRYPTO_BOX_BEFORENMBYTES>>>, Error>
401 {
402 PrecalcSecretKey::precalculate_locked(third_party_public_key, &self.secret_key)
403 }
404 }
405
406 impl
407 KeyPair<
408 LockedRO<HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>,
409 LockedRO<HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>>,
410 >
411 {
412 pub fn generate_readonly_locked_keypair() -> Result<Self, Error> {
425 let mut public_key = HeapByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::new_locked()?;
426 let mut secret_key = HeapByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::new_locked()?;
427
428 crypto_box_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
429
430 let public_key = public_key.mprotect_readonly()?;
431 let secret_key = secret_key.mprotect_readonly()?;
432
433 Ok(Self {
434 public_key,
435 secret_key,
436 })
437 }
438
439 #[deprecated(note = "use generate_readonly_locked_keypair() instead")]
455 pub fn gen_readonly_locked_keypair() -> Result<Self, Error> {
456 Self::generate_readonly_locked_keypair()
457 }
458
459 #[inline]
476 pub fn precalculate_readonly_locked<
477 OtherPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
478 >(
479 &self,
480 third_party_public_key: &OtherPublicKey,
481 ) -> Result<PrecalcSecretKey<LockedRO<HeapByteArray<CRYPTO_BOX_BEFORENMBYTES>>>, Error>
482 {
483 PrecalcSecretKey::precalculate_readonly_locked(third_party_public_key, &self.secret_key)
484 }
485 }
486}
487
488impl<
489 PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
490 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
491> PartialEq<KeyPair<PublicKey, SecretKey>> for KeyPair<PublicKey, SecretKey>
492{
493 fn eq(&self, other: &Self) -> bool {
494 self.public_key
495 .as_slice()
496 .ct_eq(other.public_key.as_slice())
497 .unwrap_u8()
498 == 1
499 && self
500 .secret_key
501 .as_slice()
502 .ct_eq(other.secret_key.as_slice())
503 .unwrap_u8()
504 == 1
505 }
506}
507
508#[cfg(test)]
509mod tests {
510
511 use super::*;
512 use crate::kx::Session;
513
514 #[test]
515 fn keypair_debug_redacts_keys() {
516 let keypair = StackKeyPair::generate();
517 let debug = format!("{keypair:?}");
518
519 assert_eq!(
520 debug,
521 "KeyPair { public_key: \"[REDACTED]\", secret_key: \"[REDACTED]\" }"
522 );
523 }
524
525 fn all_eq<T>(t: &[T], v: T) -> bool
526 where
527 T: PartialEq,
528 {
529 t.iter().all(|x| *x == v)
530 }
531
532 #[test]
533 fn test_new() {
534 let keypair = KeyPair::<
535 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
536 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
537 >::new();
538
539 assert!(all_eq(&keypair.public_key, 0));
540 assert!(all_eq(&keypair.secret_key, 0));
541 }
542
543 #[test]
544 fn test_default() {
545 let keypair = KeyPair::<
546 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
547 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
548 >::default();
549
550 assert!(all_eq(&keypair.public_key, 0));
551 assert!(all_eq(&keypair.secret_key, 0));
552 }
553
554 #[test]
555 fn test_from_secret_key() {
556 let keypair_1 = KeyPair::<
557 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
558 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
559 >::generate();
560 let keypair_2 = KeyPair::from_secret_key(keypair_1.secret_key.clone());
561
562 assert_eq!(keypair_1.public_key, keypair_2.public_key);
563 }
564
565 #[test]
566 fn test_keypair_precalculate() {
567 let kp1 = KeyPair::generate_with_defaults();
568 let kp2 = KeyPair::generate_with_defaults();
569 let precalc = kp1.precalculate(&kp2.public_key).unwrap();
570 assert_eq!(precalc.len(), crate::constants::CRYPTO_BOX_BEFORENMBYTES);
571 }
572
573 #[cfg(all(feature = "protected", any(unix, windows)))]
574 #[test]
575 fn test_keypair_precalculate_locked() {
576 use crate::keypair::protected::*;
577 let kp1 = KeyPair::generate_locked_keypair().unwrap();
578 let kp2 = KeyPair::generate_locked_keypair().unwrap();
579 let precalc = kp1.precalculate_locked(&kp2.public_key).unwrap();
580 assert_eq!(precalc.len(), crate::constants::CRYPTO_BOX_BEFORENMBYTES);
581 }
582
583 #[test]
584 fn test_keypair_kx_new_client_session() {
585 let server_kp = KeyPair::generate_with_defaults();
586 let client_kp = KeyPair::generate_with_defaults();
587 let session: Session<StackByteArray<CRYPTO_KX_SESSIONKEYBYTES>> = client_kp
588 .kx_new_client_session(&server_kp.public_key)
589 .unwrap();
590 assert_eq!(
591 session.rx_as_slice().len(),
592 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
593 );
594 assert_eq!(
595 session.tx_as_slice().len(),
596 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
597 );
598 }
599
600 #[test]
601 fn test_keypair_kx_new_server_session() {
602 let client_kp = KeyPair::generate_with_defaults();
603 let server_kp = KeyPair::generate_with_defaults();
604 let session: Session<StackByteArray<CRYPTO_KX_SESSIONKEYBYTES>> = server_kp
605 .kx_new_server_session(&client_kp.public_key)
606 .unwrap();
607 assert_eq!(
608 session.rx_as_slice().len(),
609 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
610 );
611 assert_eq!(
612 session.tx_as_slice().len(),
613 crate::constants::CRYPTO_KX_SESSIONKEYBYTES
614 );
615 }
616
617 #[test]
618 fn test_keypair_from_seed() {
619 let seed = [42u8; 32];
620 let kp: StackKeyPair = KeyPair::from_seed(&seed);
621 assert!(!kp.public_key.iter().all(|x| *x == 0));
622 }
623
624 #[test]
625 fn test_keypair_generate_with_defaults() {
626 let kp = KeyPair::generate_with_defaults();
627 assert!(!kp.public_key.iter().all(|x| *x == 0));
628 }
629
630 #[test]
631 fn test_is_valid_public_key() {
632 let valid_pk_bytes = [
635 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114,
636 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
637 ];
638 let valid_pk = PublicKey::from(valid_pk_bytes);
639 assert!(
640 KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&valid_pk),
641 "Known valid key failed validation"
642 );
643
644 let mut high_bit_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
647 high_bit_bytes[0] = 9;
648 high_bit_bytes[31] = 0x80;
649 let high_bit = PublicKey::from(high_bit_bytes);
650 assert!(
651 KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&high_bit),
652 "RFC 7748 high-bit encoding should be accepted"
653 );
654
655 let zero_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
657 let zero_pk = PublicKey::from(zero_bytes);
658 assert!(
659 !KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&zero_pk),
660 "Zero key should be invalid"
661 );
662
663 let mut identity_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
664 identity_bytes[0] = 1;
665 let identity = PublicKey::from(identity_bytes);
666 assert!(
667 !KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&identity),
668 "Low-order key should be invalid"
669 );
670
671 let kp = KeyPair::generate_with_defaults();
673 assert!(
674 KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&kp.public_key),
675 "Generated key failed validation"
676 );
677 }
678
679 #[test]
680 fn test_is_valid_ed25519_key() {
681 let (valid_pk, _) = crate::classic::crypto_sign::crypto_sign_keypair();
682 assert!(
683 KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&valid_pk),
684 "Ed25519 key from crypto_sign_keypair should pass validation"
685 );
686
687 let mut negative_basepoint =
688 curve25519_dalek::constants::ED25519_BASEPOINT_COMPRESSED.to_bytes();
689 negative_basepoint[31] |= 0x80;
690 assert!(
691 KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&negative_basepoint),
692 "the Ed25519 x-coordinate sign bit should be accepted"
693 );
694
695 let zero_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
696 let zero_pk = PublicKey::from(zero_bytes);
697 assert!(
698 !KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&zero_pk),
699 "zero key should be invalid"
700 );
701
702 let identity_bytes = [
703 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,
704 0, 0, 0,
705 ];
706 let identity_pk = PublicKey::from(identity_bytes);
707 assert!(
708 !KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&identity_pk),
709 "identity element should be invalid"
710 );
711
712 let mut noncanonical_identity = [0xff; CRYPTO_BOX_PUBLICKEYBYTES];
713 noncanonical_identity[0] = 0xee;
714 noncanonical_identity[31] = 0x7f;
715 assert!(
716 !KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&noncanonical_identity),
717 "noncanonical identity encoding should be invalid"
718 );
719
720 let mut mixed_order = [0x99; CRYPTO_BOX_PUBLICKEYBYTES];
721 mixed_order[0] = 0x95;
722 assert!(
723 !KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&mixed_order),
724 "mixed-order Ed25519 key should fail the prime-subgroup policy"
725 );
726 }
727
728 #[cfg(dryoc_native_tests)]
729 mod native_tests {
730 use super::*;
731
732 #[test]
733 fn test_gen_keypair() {
734 use sodiumoxide::crypto::scalarmult::curve25519::{Scalar, scalarmult_base};
735
736 use crate::classic::crypto_core::crypto_scalarmult_base;
737
738 let keypair = KeyPair::<
739 StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
740 StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
741 >::generate();
742
743 let mut public_key = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
744 crypto_scalarmult_base(&mut public_key, keypair.secret_key.as_array());
745
746 assert_eq!(keypair.public_key.as_array(), &public_key);
747
748 let ge = scalarmult_base(&Scalar::from_slice(&keypair.secret_key).unwrap());
749
750 assert_eq!(ge.as_ref(), public_key);
751 }
752 }
753}