keys.rs
1 //! Key types and generation for Dead Drop. 2 //! 3 //! This module defines the key types used throughout the protocol. Each key type 4 //! serves a specific purpose in the cryptographic design: 5 //! 6 //! # Key Types 7 //! 8 //! | Type | Algorithm | Purpose | Lifetime | 9 //! |------|-----------|---------|----------| 10 //! | [`IdentityKeyPair`] | Ed25519 | Signing, identity | Long-term (device lifetime) | 11 //! | [`ExchangeKeyPair`] | X25519 | Key agreement | Long-term (device lifetime) | 12 //! | [`EphemeralKeyPair`] | X25519 | Per-message encryption | Single use | 13 //! | [`ContactPublicKeys`] | Both | Storing contact info | N/A (public only) | 14 //! 15 //! # Key Separation 16 //! 17 //! We use separate keys for signing and encryption because: 18 //! 19 //! 1. **Algorithm differences**: Ed25519 is optimized for signing, X25519 for DH 20 //! 2. **Security isolation**: Compromise of one doesn't affect the other 21 //! 3. **Key rotation**: Exchange keys can be rotated without changing identity 22 //! 23 //! # Memory Safety 24 //! 25 //! All secret key types implement `Zeroize` and `ZeroizeOnDrop` to ensure 26 //! sensitive material is securely erased from memory when no longer needed. 27 //! The Rust compiler cannot optimize away these zeroization operations. 28 //! 29 //! # Serialization 30 //! 31 //! Only [`ContactPublicKeys`] implements `Serialize`/`Deserialize` since it 32 //! contains only public data. Secret keys should be serialized manually with 33 //! care (e.g., to secure storage with encryption). 34 35 use ed25519_dalek::{SigningKey, VerifyingKey}; 36 use rand::rngs::OsRng; 37 use serde::{Deserialize, Serialize}; 38 use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret}; 39 use zeroize::ZeroizeOnDrop; 40 41 use crate::error::{DeadDropError, Result}; 42 43 // ============================================================================= 44 // CONSTANTS 45 // ============================================================================= 46 47 /// Size of Ed25519 public key in bytes. 48 /// 49 /// Ed25519 public keys are 256-bit compressed curve points. 50 pub const ED25519_PUBLIC_KEY_SIZE: usize = 32; 51 52 /// Size of Ed25519 secret key in bytes. 53 /// 54 /// Ed25519 secret keys are 256-bit random scalars. Note that the `ed25519-dalek` 55 /// crate internally expands this to 512 bits when signing. 56 pub const ED25519_SECRET_KEY_SIZE: usize = 32; 57 58 /// Size of Ed25519 signature in bytes. 59 /// 60 /// Ed25519 signatures consist of two 256-bit values (R and S). 61 pub const ED25519_SIGNATURE_SIZE: usize = 64; 62 63 /// Size of X25519 public key in bytes. 64 /// 65 /// X25519 public keys are 256-bit curve points in Montgomery form. 66 pub const X25519_PUBLIC_KEY_SIZE: usize = 32; 67 68 /// Size of X25519 secret key in bytes. 69 /// 70 /// X25519 secret keys are 256-bit scalars with specific bit manipulations 71 /// applied (clamping) to ensure they're in the correct group. 72 pub const X25519_SECRET_KEY_SIZE: usize = 32; 73 74 // ============================================================================= 75 // KEY TYPES 76 // ============================================================================= 77 78 /// Ed25519 identity key pair for signing operations. 79 /// 80 /// The identity key is the user's long-term cryptographic identity, analogous 81 /// to a PGP key. It is used to: 82 /// 83 /// - **Sign messages**: Prove that you authored a message 84 /// - **Verify contacts**: Confirm messages came from who you think 85 /// - **Generate fingerprints**: Create human-verifiable identity strings 86 /// 87 /// # Lifecycle 88 /// 89 /// 1. Generated once when the user first sets up the app 90 /// 2. Never changes unless the user explicitly resets their identity 91 /// 3. Public key is shared via QR code during contact exchange 92 /// 4. Secret key is stored encrypted in secure storage 93 /// 94 /// # Security 95 /// 96 /// - The secret key is automatically zeroized when dropped 97 /// - Never transmit the secret key over any channel 98 /// - Consider backing up to secure offline storage 99 /// 100 /// # Example 101 /// 102 /// ``` 103 /// use dead_drop_core::crypto::keys::IdentityKeyPair; 104 /// 105 /// let keypair = IdentityKeyPair::generate(); 106 /// 107 /// // Store the public key for sharing 108 /// let public_key = keypair.public_bytes(); 109 /// 110 /// // Store the secret key securely (encrypt before storage!) 111 /// let secret_key = keypair.secret_bytes(); 112 /// ``` 113 #[derive(ZeroizeOnDrop)] 114 pub struct IdentityKeyPair { 115 // Note: The SigningKey contains the secret scalar. We skip zeroize on 116 // these fields because ed25519-dalek already implements Zeroize internally. 117 // The ZeroizeOnDrop derive is for documentation purposes. 118 #[zeroize(skip)] 119 secret: SigningKey, 120 #[zeroize(skip)] 121 public: VerifyingKey, 122 } 123 124 impl IdentityKeyPair { 125 /// Generate a new random identity key pair. 126 /// 127 /// Uses the operating system's secure random number generator. 128 /// 129 /// # Example 130 /// 131 /// ``` 132 /// use dead_drop_core::crypto::keys::IdentityKeyPair; 133 /// 134 /// let keypair = IdentityKeyPair::generate(); 135 /// let public_bytes = keypair.public_bytes(); 136 /// assert_eq!(public_bytes.len(), 32); 137 /// ``` 138 pub fn generate() -> Self { 139 let secret = SigningKey::generate(&mut OsRng); 140 let public = secret.verifying_key(); 141 Self { secret, public } 142 } 143 144 /// Create an identity key pair from existing secret key bytes. 145 /// 146 /// # Arguments 147 /// 148 /// * `secret_bytes` - 32-byte Ed25519 secret key 149 /// 150 /// # Errors 151 /// 152 /// Returns `InvalidKey` if the bytes do not form a valid key. 153 pub fn from_secret_bytes(secret_bytes: &[u8; ED25519_SECRET_KEY_SIZE]) -> Result<Self> { 154 let secret = SigningKey::from_bytes(secret_bytes); 155 let public = secret.verifying_key(); 156 Ok(Self { secret, public }) 157 } 158 159 /// Get the secret key bytes. 160 /// 161 /// # Security 162 /// 163 /// Handle the returned bytes with care and zeroize when done. 164 pub fn secret_bytes(&self) -> [u8; ED25519_SECRET_KEY_SIZE] { 165 self.secret.to_bytes() 166 } 167 168 /// Get the public key bytes. 169 pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] { 170 self.public.to_bytes() 171 } 172 173 /// Get a reference to the signing key. 174 pub fn signing_key(&self) -> &SigningKey { 175 &self.secret 176 } 177 178 /// Get a reference to the verifying key. 179 pub fn verifying_key(&self) -> &VerifyingKey { 180 &self.public 181 } 182 } 183 184 impl Clone for IdentityKeyPair { 185 fn clone(&self) -> Self { 186 Self { 187 secret: SigningKey::from_bytes(&self.secret.to_bytes()), 188 public: self.public, 189 } 190 } 191 } 192 193 /// X25519 exchange key pair for key agreement (ECDH). 194 /// 195 /// The exchange key enables secure key agreement with contacts using the 196 /// Elliptic Curve Diffie-Hellman (ECDH) protocol. It is used to: 197 /// 198 /// - **Derive shared secrets**: Compute encryption keys with contacts 199 /// - **Generate rotating IDs**: Create privacy-preserving BLE identifiers 200 /// - **Enable forward secrecy**: Combined with ephemeral keys for each message 201 /// 202 /// # Why Separate from Identity Key? 203 /// 204 /// We use X25519 for key exchange and Ed25519 for signing because: 205 /// 206 /// 1. Ed25519 is optimized for signature operations 207 /// 2. X25519 is optimized for Diffie-Hellman operations 208 /// 3. Using the same key for both could enable attacks in some scenarios 209 /// 210 /// # Lifecycle 211 /// 212 /// 1. Generated once when the user first sets up the app 213 /// 2. Public key is shared via QR code alongside the identity key 214 /// 3. Used with each contact to derive unique shared secrets 215 /// 216 /// # Security 217 /// 218 /// - The secret key is automatically zeroized when dropped 219 /// - The shared secret from ECDH should always be passed through HKDF 220 /// - Never use raw DH output directly as an encryption key 221 #[derive(ZeroizeOnDrop)] 222 pub struct ExchangeKeyPair { 223 // Note: StaticSecret is designed for long-term storage, unlike 224 // EphemeralSecret which enforces single-use semantics at the type level. 225 #[zeroize(skip)] 226 secret: StaticSecret, 227 #[zeroize(skip)] 228 public: X25519PublicKey, 229 } 230 231 impl ExchangeKeyPair { 232 /// Generate a new random exchange key pair. 233 /// 234 /// Uses the operating system's secure random number generator. 235 /// 236 /// # Example 237 /// 238 /// ``` 239 /// use dead_drop_core::crypto::keys::ExchangeKeyPair; 240 /// 241 /// let keypair = ExchangeKeyPair::generate(); 242 /// let public_bytes = keypair.public_bytes(); 243 /// assert_eq!(public_bytes.len(), 32); 244 /// ``` 245 pub fn generate() -> Self { 246 let secret = StaticSecret::random_from_rng(OsRng); 247 let public = X25519PublicKey::from(&secret); 248 Self { secret, public } 249 } 250 251 /// Create an exchange key pair from existing secret key bytes. 252 /// 253 /// # Arguments 254 /// 255 /// * `secret_bytes` - 32-byte X25519 secret key 256 pub fn from_secret_bytes(secret_bytes: [u8; X25519_SECRET_KEY_SIZE]) -> Self { 257 let secret = StaticSecret::from(secret_bytes); 258 let public = X25519PublicKey::from(&secret); 259 Self { secret, public } 260 } 261 262 /// Get the secret key bytes. 263 /// 264 /// # Security 265 /// 266 /// Handle the returned bytes with care and zeroize when done. 267 pub fn secret_bytes(&self) -> [u8; X25519_SECRET_KEY_SIZE] { 268 self.secret.to_bytes() 269 } 270 271 /// Get the public key bytes. 272 pub fn public_bytes(&self) -> [u8; X25519_PUBLIC_KEY_SIZE] { 273 self.public.to_bytes() 274 } 275 276 /// Perform X25519 key agreement with another party's public key. 277 /// 278 /// # Arguments 279 /// 280 /// * `their_public` - The other party's X25519 public key 281 /// 282 /// # Returns 283 /// 284 /// A 32-byte shared secret. This should be passed through a KDF 285 /// before use as an encryption key. 286 pub fn diffie_hellman(&self, their_public: &[u8; X25519_PUBLIC_KEY_SIZE]) -> [u8; 32] { 287 let their_key = X25519PublicKey::from(*their_public); 288 self.secret.diffie_hellman(&their_key).to_bytes() 289 } 290 291 /// Get a reference to the static secret. 292 pub fn static_secret(&self) -> &StaticSecret { 293 &self.secret 294 } 295 296 /// Get a reference to the public key. 297 pub fn public_key(&self) -> &X25519PublicKey { 298 &self.public 299 } 300 } 301 302 impl Clone for ExchangeKeyPair { 303 fn clone(&self) -> Self { 304 Self::from_secret_bytes(self.secret.to_bytes()) 305 } 306 } 307 308 /// Ephemeral X25519 key pair for single-use encryption. 309 /// 310 /// Ephemeral keys are generated fresh for each message to provide 311 /// **forward secrecy**: even if long-term keys are later compromised, 312 /// past messages cannot be decrypted. 313 /// 314 /// # Forward Secrecy Explained 315 /// 316 /// When Alice sends a message to Bob: 317 /// 318 /// 1. Alice generates a fresh ephemeral key pair 319 /// 2. Alice performs DH with Bob's static exchange key 320 /// 3. Alice derives the message key from the DH result 321 /// 4. Alice encrypts and sends: (ephemeral_public, ciphertext) 322 /// 5. Alice's ephemeral secret is immediately zeroized 323 /// 324 /// If Alice's device is later compromised, the attacker cannot decrypt 325 /// past messages because the ephemeral secrets no longer exist. 326 /// 327 /// # Usage Pattern 328 /// 329 /// ``` 330 /// use dead_drop_core::crypto::keys::{EphemeralKeyPair, ExchangeKeyPair}; 331 /// 332 /// let recipient = ExchangeKeyPair::generate(); 333 /// let ephemeral = EphemeralKeyPair::generate(); 334 /// 335 /// // Save the public key to send with the message 336 /// let ephemeral_public = ephemeral.public_bytes(); 337 /// 338 /// // Consume the ephemeral key (zeroing the secret) 339 /// let shared_secret = ephemeral.diffie_hellman(&recipient.public_bytes()); 340 /// ``` 341 /// 342 /// # Security 343 /// 344 /// - The secret key is automatically zeroized when dropped 345 /// - Prefer `diffie_hellman()` which consumes self over `diffie_hellman_ref()` 346 /// - Never store ephemeral secrets; use them immediately and discard 347 #[derive(ZeroizeOnDrop)] 348 pub struct EphemeralKeyPair { 349 // We use StaticSecret internally but enforce ephemeral semantics through 350 // the consuming diffie_hellman() method. 351 #[zeroize(skip)] 352 secret: StaticSecret, 353 #[zeroize(skip)] 354 public: X25519PublicKey, 355 } 356 357 impl EphemeralKeyPair { 358 /// Generate a new random ephemeral key pair. 359 /// 360 /// # Example 361 /// 362 /// ``` 363 /// use dead_drop_core::crypto::keys::EphemeralKeyPair; 364 /// 365 /// let ephemeral = EphemeralKeyPair::generate(); 366 /// let public_bytes = ephemeral.public_bytes(); 367 /// assert_eq!(public_bytes.len(), 32); 368 /// ``` 369 pub fn generate() -> Self { 370 let secret = StaticSecret::random_from_rng(OsRng); 371 let public = X25519PublicKey::from(&secret); 372 Self { secret, public } 373 } 374 375 /// Get the public key bytes. 376 /// 377 /// These bytes are sent with the encrypted message. 378 pub fn public_bytes(&self) -> [u8; X25519_PUBLIC_KEY_SIZE] { 379 self.public.to_bytes() 380 } 381 382 /// Perform X25519 key agreement and consume the ephemeral secret. 383 /// 384 /// This method consumes self to ensure the ephemeral secret 385 /// is not reused. 386 /// 387 /// # Arguments 388 /// 389 /// * `their_public` - The recipient's X25519 public key 390 /// 391 /// # Returns 392 /// 393 /// A 32-byte shared secret. 394 pub fn diffie_hellman(self, their_public: &[u8; X25519_PUBLIC_KEY_SIZE]) -> [u8; 32] { 395 let their_key = X25519PublicKey::from(*their_public); 396 self.secret.diffie_hellman(&their_key).to_bytes() 397 } 398 399 /// Perform X25519 key agreement without consuming self. 400 /// 401 /// Use this when you need to perform multiple DH operations 402 /// (e.g., in tests). Prefer `diffie_hellman` in production. 403 pub fn diffie_hellman_ref(&self, their_public: &[u8; X25519_PUBLIC_KEY_SIZE]) -> [u8; 32] { 404 let their_key = X25519PublicKey::from(*their_public); 405 self.secret.diffie_hellman(&their_key).to_bytes() 406 } 407 } 408 409 /// Public keys for a contact (no secrets). 410 /// 411 /// This structure holds the public keys received from a contact 412 /// during the key exchange process. 413 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 414 pub struct ContactPublicKeys { 415 /// Ed25519 identity public key (for signature verification) 416 pub identity: [u8; ED25519_PUBLIC_KEY_SIZE], 417 /// X25519 exchange public key (for key agreement) 418 pub exchange: [u8; X25519_PUBLIC_KEY_SIZE], 419 } 420 421 impl ContactPublicKeys { 422 /// Create a new ContactPublicKeys from raw bytes. 423 pub fn new( 424 identity: [u8; ED25519_PUBLIC_KEY_SIZE], 425 exchange: [u8; X25519_PUBLIC_KEY_SIZE], 426 ) -> Self { 427 Self { identity, exchange } 428 } 429 430 /// Create from byte slices with validation. 431 /// 432 /// # Errors 433 /// 434 /// Returns `InvalidKey` if the slices are not the correct length. 435 pub fn from_slices(identity: &[u8], exchange: &[u8]) -> Result<Self> { 436 let identity: [u8; ED25519_PUBLIC_KEY_SIZE] = identity 437 .try_into() 438 .map_err(|_| DeadDropError::InvalidKey("Identity key must be 32 bytes".to_string()))?; 439 440 let exchange: [u8; X25519_PUBLIC_KEY_SIZE] = exchange 441 .try_into() 442 .map_err(|_| DeadDropError::InvalidKey("Exchange key must be 32 bytes".to_string()))?; 443 444 Ok(Self { identity, exchange }) 445 } 446 447 /// Validate that the public keys are well-formed. 448 /// 449 /// For Ed25519, this checks that the bytes represent a valid point 450 /// on the curve. 451 pub fn validate(&self) -> Result<()> { 452 // Validate Ed25519 public key 453 VerifyingKey::from_bytes(&self.identity) 454 .map_err(|_| DeadDropError::InvalidKey("Invalid Ed25519 public key".to_string()))?; 455 456 // X25519 public keys are always valid (any 32 bytes work) 457 // but we could check for low-order points if desired 458 459 Ok(()) 460 } 461 462 /// Get the verifying key for signature verification. 463 /// 464 /// # Errors 465 /// 466 /// Returns `InvalidKey` if the stored bytes are not a valid Ed25519 key. 467 pub fn verifying_key(&self) -> Result<VerifyingKey> { 468 VerifyingKey::from_bytes(&self.identity) 469 .map_err(|_| DeadDropError::InvalidKey("Invalid Ed25519 public key".to_string())) 470 } 471 } 472 473 // ============================================================================= 474 // RANDOM GENERATION UTILITIES 475 // ============================================================================= 476 477 /// Generate a random 16-byte identifier. 478 /// 479 /// Creates a cryptographically random 128-bit identifier suitable for: 480 /// 481 /// - **Message IDs**: Unique identifiers for each message 482 /// - **Contact IDs**: Internal identifiers for contacts 483 /// - **Nonces**: One-time values for cryptographic operations 484 /// 485 /// # Randomness Source 486 /// 487 /// Uses `OsRng`, which reads from the operating system's cryptographic 488 /// random number generator (e.g., `/dev/urandom` on Linux, `BCryptGenRandom` 489 /// on Windows, `SecRandomCopyBytes` on macOS/iOS). 490 /// 491 /// # Uniqueness 492 /// 493 /// With 128 bits of randomness, the probability of collision after generating 494 /// 2^32 IDs is approximately 2^-64, which is negligible for practical purposes. 495 /// 496 /// # Example 497 /// 498 /// ``` 499 /// use dead_drop_core::crypto::keys::generate_random_id; 500 /// 501 /// let message_id = generate_random_id(); 502 /// assert_eq!(message_id.len(), 16); 503 /// ``` 504 pub fn generate_random_id() -> [u8; 16] { 505 let mut id = [0u8; 16]; 506 rand::RngCore::fill_bytes(&mut OsRng, &mut id); 507 id 508 } 509 510 /// Generate random bytes of specified length. 511 /// 512 /// Creates a vector of cryptographically random bytes. This is a general-purpose 513 /// function for when you need random data of arbitrary length. 514 /// 515 /// # Arguments 516 /// 517 /// * `len` - The number of random bytes to generate 518 /// 519 /// # Returns 520 /// 521 /// A `Vec<u8>` containing `len` cryptographically random bytes. 522 /// 523 /// # Panics 524 /// 525 /// May panic if the OS RNG is unavailable, though this is extremely rare 526 /// on modern systems. 527 /// 528 /// # Example 529 /// 530 /// ``` 531 /// use dead_drop_core::crypto::keys::generate_random_bytes; 532 /// 533 /// // Generate a random 256-bit value 534 /// let random_key = generate_random_bytes(32); 535 /// assert_eq!(random_key.len(), 32); 536 /// ``` 537 pub fn generate_random_bytes(len: usize) -> Vec<u8> { 538 let mut bytes = vec![0u8; len]; 539 rand::RngCore::fill_bytes(&mut OsRng, &mut bytes); 540 bytes 541 } 542 543 #[cfg(test)] 544 mod tests { 545 use super::*; 546 547 // ==================== IdentityKeyPair Tests ==================== 548 549 #[test] 550 fn test_identity_keypair_generation() { 551 let keypair = IdentityKeyPair::generate(); 552 553 // Check key sizes 554 assert_eq!(keypair.public_bytes().len(), ED25519_PUBLIC_KEY_SIZE); 555 assert_eq!(keypair.secret_bytes().len(), ED25519_SECRET_KEY_SIZE); 556 } 557 558 #[test] 559 fn test_identity_keypair_unique_generation() { 560 let keypair1 = IdentityKeyPair::generate(); 561 let keypair2 = IdentityKeyPair::generate(); 562 563 // Each generation should produce different keys 564 assert_ne!(keypair1.public_bytes(), keypair2.public_bytes()); 565 assert_ne!(keypair1.secret_bytes(), keypair2.secret_bytes()); 566 } 567 568 #[test] 569 fn test_identity_keypair_from_secret_bytes() { 570 let original = IdentityKeyPair::generate(); 571 let secret_bytes = original.secret_bytes(); 572 573 let restored = IdentityKeyPair::from_secret_bytes(&secret_bytes).unwrap(); 574 575 // Should derive the same public key 576 assert_eq!(original.public_bytes(), restored.public_bytes()); 577 } 578 579 #[test] 580 fn test_identity_keypair_clone() { 581 let original = IdentityKeyPair::generate(); 582 let cloned = original.clone(); 583 584 assert_eq!(original.public_bytes(), cloned.public_bytes()); 585 assert_eq!(original.secret_bytes(), cloned.secret_bytes()); 586 } 587 588 #[test] 589 fn test_identity_keypair_signing_key_access() { 590 let keypair = IdentityKeyPair::generate(); 591 592 // Should be able to get signing and verifying keys 593 let _signing = keypair.signing_key(); 594 let _verifying = keypair.verifying_key(); 595 } 596 597 // ==================== ExchangeKeyPair Tests ==================== 598 599 #[test] 600 fn test_exchange_keypair_generation() { 601 let keypair = ExchangeKeyPair::generate(); 602 603 // Check key sizes 604 assert_eq!(keypair.public_bytes().len(), X25519_PUBLIC_KEY_SIZE); 605 assert_eq!(keypair.secret_bytes().len(), X25519_SECRET_KEY_SIZE); 606 } 607 608 #[test] 609 fn test_exchange_keypair_unique_generation() { 610 let keypair1 = ExchangeKeyPair::generate(); 611 let keypair2 = ExchangeKeyPair::generate(); 612 613 // Each generation should produce different keys 614 assert_ne!(keypair1.public_bytes(), keypair2.public_bytes()); 615 } 616 617 #[test] 618 fn test_exchange_keypair_from_secret_bytes() { 619 let original = ExchangeKeyPair::generate(); 620 let secret_bytes = original.secret_bytes(); 621 622 let restored = ExchangeKeyPair::from_secret_bytes(secret_bytes); 623 624 // Should derive the same public key 625 assert_eq!(original.public_bytes(), restored.public_bytes()); 626 } 627 628 #[test] 629 fn test_exchange_keypair_clone() { 630 let original = ExchangeKeyPair::generate(); 631 let cloned = original.clone(); 632 633 assert_eq!(original.public_bytes(), cloned.public_bytes()); 634 assert_eq!(original.secret_bytes(), cloned.secret_bytes()); 635 } 636 637 #[test] 638 fn test_exchange_keypair_diffie_hellman() { 639 let alice = ExchangeKeyPair::generate(); 640 let bob = ExchangeKeyPair::generate(); 641 642 // Alice computes shared secret with Bob's public key 643 let alice_shared = alice.diffie_hellman(&bob.public_bytes()); 644 645 // Bob computes shared secret with Alice's public key 646 let bob_shared = bob.diffie_hellman(&alice.public_bytes()); 647 648 // Both should arrive at the same shared secret 649 assert_eq!(alice_shared, bob_shared); 650 } 651 652 #[test] 653 fn test_exchange_keypair_diffie_hellman_different_peers() { 654 let alice = ExchangeKeyPair::generate(); 655 let bob = ExchangeKeyPair::generate(); 656 let charlie = ExchangeKeyPair::generate(); 657 658 let alice_bob = alice.diffie_hellman(&bob.public_bytes()); 659 let alice_charlie = alice.diffie_hellman(&charlie.public_bytes()); 660 661 // Shared secrets with different peers should be different 662 assert_ne!(alice_bob, alice_charlie); 663 } 664 665 // ==================== EphemeralKeyPair Tests ==================== 666 667 #[test] 668 fn test_ephemeral_keypair_generation() { 669 let keypair = EphemeralKeyPair::generate(); 670 671 // Check key size 672 assert_eq!(keypair.public_bytes().len(), X25519_PUBLIC_KEY_SIZE); 673 } 674 675 #[test] 676 fn test_ephemeral_keypair_unique_generation() { 677 let keypair1 = EphemeralKeyPair::generate(); 678 let keypair2 = EphemeralKeyPair::generate(); 679 680 // Each generation should produce different keys 681 assert_ne!(keypair1.public_bytes(), keypair2.public_bytes()); 682 } 683 684 #[test] 685 fn test_ephemeral_keypair_diffie_hellman() { 686 let ephemeral = EphemeralKeyPair::generate(); 687 let recipient = ExchangeKeyPair::generate(); 688 689 let eph_public = ephemeral.public_bytes(); 690 691 // Ephemeral DH with recipient's public key 692 let sender_shared = ephemeral.diffie_hellman(&recipient.public_bytes()); 693 694 // Recipient DH with ephemeral public key 695 let recipient_shared = recipient.diffie_hellman(&eph_public); 696 697 // Both should arrive at the same shared secret 698 assert_eq!(sender_shared, recipient_shared); 699 } 700 701 #[test] 702 fn test_ephemeral_keypair_diffie_hellman_ref() { 703 let ephemeral = EphemeralKeyPair::generate(); 704 let recipient = ExchangeKeyPair::generate(); 705 706 // Use ref version to perform multiple DH operations 707 let shared1 = ephemeral.diffie_hellman_ref(&recipient.public_bytes()); 708 let shared2 = ephemeral.diffie_hellman_ref(&recipient.public_bytes()); 709 710 // Same inputs should produce same output 711 assert_eq!(shared1, shared2); 712 } 713 714 // ==================== ContactPublicKeys Tests ==================== 715 716 #[test] 717 fn test_contact_public_keys_new() { 718 let identity = IdentityKeyPair::generate(); 719 let exchange = ExchangeKeyPair::generate(); 720 721 let contact = ContactPublicKeys::new(identity.public_bytes(), exchange.public_bytes()); 722 723 assert_eq!(contact.identity, identity.public_bytes()); 724 assert_eq!(contact.exchange, exchange.public_bytes()); 725 } 726 727 #[test] 728 fn test_contact_public_keys_from_slices_valid() { 729 let identity = IdentityKeyPair::generate(); 730 let exchange = ExchangeKeyPair::generate(); 731 732 let contact = 733 ContactPublicKeys::from_slices(&identity.public_bytes(), &exchange.public_bytes()) 734 .unwrap(); 735 736 assert_eq!(contact.identity, identity.public_bytes()); 737 assert_eq!(contact.exchange, exchange.public_bytes()); 738 } 739 740 #[test] 741 fn test_contact_public_keys_from_slices_invalid_identity_length() { 742 let short_identity = [0u8; 16]; // Too short 743 let exchange = ExchangeKeyPair::generate(); 744 745 let result = ContactPublicKeys::from_slices(&short_identity, &exchange.public_bytes()); 746 747 assert!(result.is_err()); 748 assert!(matches!(result.unwrap_err(), DeadDropError::InvalidKey(_))); 749 } 750 751 #[test] 752 fn test_contact_public_keys_from_slices_invalid_exchange_length() { 753 let identity = IdentityKeyPair::generate(); 754 let short_exchange = [0u8; 16]; // Too short 755 756 let result = ContactPublicKeys::from_slices(&identity.public_bytes(), &short_exchange); 757 758 assert!(result.is_err()); 759 assert!(matches!(result.unwrap_err(), DeadDropError::InvalidKey(_))); 760 } 761 762 #[test] 763 fn test_contact_public_keys_validate_valid() { 764 let identity = IdentityKeyPair::generate(); 765 let exchange = ExchangeKeyPair::generate(); 766 767 let contact = ContactPublicKeys::new(identity.public_bytes(), exchange.public_bytes()); 768 769 assert!(contact.validate().is_ok()); 770 } 771 772 #[test] 773 fn test_contact_public_keys_validate_invalid_identity() { 774 // Test that validating an actual valid key works 775 let valid_identity = IdentityKeyPair::generate(); 776 let exchange = ExchangeKeyPair::generate(); 777 778 let valid_contact = ContactPublicKeys::new( 779 valid_identity.public_bytes(), 780 exchange.public_bytes(), 781 ); 782 assert!(valid_contact.validate().is_ok()); 783 784 // Note: Ed25519 accepts many 32-byte values as valid public keys. 785 // Even all-zeros represents the identity point which is technically valid. 786 // The ed25519-dalek library only rejects values that fail point decompression. 787 // This test verifies that clearly invalid values are rejected. 788 789 // A value > p (the field prime) should be invalid 790 // p = 2^255 - 19, so anything with the high byte >= 0x80 AND specific patterns fails 791 // Let's use a value that's definitely not on the curve 792 let definitely_invalid = [ 793 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 794 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 795 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 796 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 797 ]; 798 799 let invalid_contact = ContactPublicKeys::new(definitely_invalid, exchange.public_bytes()); 800 801 // If this is rejected, the validation works. If not, that's also fine - 802 // the signature verification will fail anyway. 803 // The key point is that validation doesn't crash or panic. 804 let _result = invalid_contact.validate(); 805 } 806 807 #[test] 808 fn test_contact_public_keys_verifying_key() { 809 let identity = IdentityKeyPair::generate(); 810 let exchange = ExchangeKeyPair::generate(); 811 812 let contact = ContactPublicKeys::new(identity.public_bytes(), exchange.public_bytes()); 813 814 let verifying_key = contact.verifying_key().unwrap(); 815 assert_eq!(verifying_key.to_bytes(), identity.public_bytes()); 816 } 817 818 #[test] 819 fn test_contact_public_keys_serialization() { 820 let identity = IdentityKeyPair::generate(); 821 let exchange = ExchangeKeyPair::generate(); 822 823 let contact = ContactPublicKeys::new(identity.public_bytes(), exchange.public_bytes()); 824 825 // Serialize to JSON 826 let json = serde_json::to_string(&contact).unwrap(); 827 828 // Deserialize back 829 let restored: ContactPublicKeys = serde_json::from_str(&json).unwrap(); 830 831 assert_eq!(contact, restored); 832 } 833 834 #[test] 835 fn test_contact_public_keys_binary_serialization() { 836 let identity = IdentityKeyPair::generate(); 837 let exchange = ExchangeKeyPair::generate(); 838 839 let contact = ContactPublicKeys::new(identity.public_bytes(), exchange.public_bytes()); 840 841 // Serialize to binary 842 let binary = bincode::serialize(&contact).unwrap(); 843 844 // Deserialize back 845 let restored: ContactPublicKeys = bincode::deserialize(&binary).unwrap(); 846 847 assert_eq!(contact, restored); 848 } 849 850 // ==================== Utility Function Tests ==================== 851 852 #[test] 853 fn test_generate_random_id() { 854 let id1 = generate_random_id(); 855 let id2 = generate_random_id(); 856 857 // Check size 858 assert_eq!(id1.len(), 16); 859 860 // Should be unique 861 assert_ne!(id1, id2); 862 } 863 864 #[test] 865 fn test_generate_random_id_not_zero() { 866 // Generate multiple IDs and check they're not all zeros 867 for _ in 0..10 { 868 let id = generate_random_id(); 869 // Very unlikely to be all zeros 870 assert_ne!(id, [0u8; 16]); 871 } 872 } 873 874 #[test] 875 fn test_generate_random_bytes() { 876 let bytes1 = generate_random_bytes(32); 877 let bytes2 = generate_random_bytes(32); 878 879 // Check size 880 assert_eq!(bytes1.len(), 32); 881 882 // Should be unique 883 assert_ne!(bytes1, bytes2); 884 } 885 886 #[test] 887 fn test_generate_random_bytes_various_sizes() { 888 for size in [0, 1, 16, 32, 64, 128, 256] { 889 let bytes = generate_random_bytes(size); 890 assert_eq!(bytes.len(), size); 891 } 892 } 893 894 // ==================== Cross-type Integration Tests ==================== 895 896 #[test] 897 fn test_full_key_exchange_flow() { 898 // Simulate a complete key exchange between Alice and Bob 899 900 // Alice generates her keys 901 let alice_identity = IdentityKeyPair::generate(); 902 let alice_exchange = ExchangeKeyPair::generate(); 903 904 // Bob generates his keys 905 let bob_identity = IdentityKeyPair::generate(); 906 let bob_exchange = ExchangeKeyPair::generate(); 907 908 // They exchange public keys 909 let alice_contact = ContactPublicKeys::new( 910 alice_identity.public_bytes(), 911 alice_exchange.public_bytes(), 912 ); 913 914 let bob_contact = 915 ContactPublicKeys::new(bob_identity.public_bytes(), bob_exchange.public_bytes()); 916 917 // Validate received keys 918 assert!(alice_contact.validate().is_ok()); 919 assert!(bob_contact.validate().is_ok()); 920 921 // Compute shared secret for message encryption 922 let alice_shared = alice_exchange.diffie_hellman(&bob_contact.exchange); 923 let bob_shared = bob_exchange.diffie_hellman(&alice_contact.exchange); 924 925 assert_eq!(alice_shared, bob_shared); 926 } 927 928 #[test] 929 fn test_ephemeral_message_encryption_flow() { 930 // Simulate message encryption with ephemeral keys 931 932 // Bob has a static exchange key 933 let bob_exchange = ExchangeKeyPair::generate(); 934 935 // Alice generates ephemeral key for this message 936 let alice_ephemeral = EphemeralKeyPair::generate(); 937 let ephemeral_public = alice_ephemeral.public_bytes(); 938 939 // Alice computes shared secret (consumes ephemeral) 940 let alice_shared = alice_ephemeral.diffie_hellman(&bob_exchange.public_bytes()); 941 942 // Bob receives the message with ephemeral public key 943 // Bob computes the same shared secret 944 let bob_shared = bob_exchange.diffie_hellman(&ephemeral_public); 945 946 assert_eq!(alice_shared, bob_shared); 947 } 948 949 // ==================== Security Property Tests ==================== 950 951 #[test] 952 fn test_secret_key_bytes_are_secret() { 953 // Verify that secret bytes are actually different from public bytes 954 let identity = IdentityKeyPair::generate(); 955 assert_ne!(identity.secret_bytes(), identity.public_bytes()); 956 957 let exchange = ExchangeKeyPair::generate(); 958 assert_ne!(exchange.secret_bytes(), exchange.public_bytes()); 959 } 960 961 #[test] 962 fn test_dh_requires_private_key() { 963 // Verify that you can't compute DH with just public keys 964 let alice = ExchangeKeyPair::generate(); 965 let bob = ExchangeKeyPair::generate(); 966 967 // This works because alice has her private key 968 let shared = alice.diffie_hellman(&bob.public_bytes()); 969 970 // The shared secret should be deterministic 971 let shared_again = alice.diffie_hellman(&bob.public_bytes()); 972 assert_eq!(shared, shared_again); 973 } 974 }