1use base64ct::{Base64, Encoding};
8use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
9use rand::Rng;
10use rand::rngs::OsRng;
11use serde::{Deserialize, Serialize};
12use zeroize::{ZeroizeOnDrop, Zeroizing};
13
14use super::errors::AuthError;
15use crate::{Entry, Error};
16
17pub const ED25519_PUBLIC_KEY_SIZE: usize = 32;
19
20pub const ED25519_PRIVATE_KEY_SIZE: usize = 32;
22
23pub const ED25519_SIGNATURE_SIZE: usize = 64;
25
26pub const CHALLENGE_SIZE: usize = 32;
28
29#[non_exhaustive]
37#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38pub enum PublicKey {
39 Ed25519(VerifyingKey),
41}
42
43impl PublicKey {
44 pub fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), AuthError> {
49 match self {
50 PublicKey::Ed25519(key) => {
51 let sig_array: [u8; ED25519_SIGNATURE_SIZE] = signature
52 .try_into()
53 .map_err(|_| AuthError::InvalidSignature)?;
54 let sig = Signature::from_bytes(&sig_array);
55 key.verify(data, &sig)
56 .map_err(|_| AuthError::InvalidSignature)
57 }
58 }
59 }
60
61 pub fn to_prefixed_string(&self) -> String {
63 match self {
64 PublicKey::Ed25519(key) => {
65 let encoded = Base64::encode_string(&key.to_bytes());
66 format!("ed25519:{encoded}")
67 }
68 }
69 }
70
71 pub fn from_prefixed_string(s: &str) -> Result<Self, AuthError> {
73 let (prefix, key_data) = s
74 .split_once(':')
75 .ok_or_else(|| AuthError::InvalidKeyFormat {
76 reason: "Expected 'algorithm:key' format".to_string(),
77 })?;
78 match prefix {
79 "ed25519" => {
80 let key_bytes =
81 Base64::decode_vec(key_data).map_err(|e| AuthError::InvalidKeyFormat {
82 reason: format!("Invalid base64 for key: {e}"),
83 })?;
84 let key_array: [u8; ED25519_PUBLIC_KEY_SIZE] = key_bytes.try_into().map_err(
85 |v: Vec<u8>| AuthError::InvalidKeyFormat {
86 reason: format!(
87 "Ed25519 public key must be {ED25519_PUBLIC_KEY_SIZE} bytes, got {}",
88 v.len()
89 ),
90 },
91 )?;
92 let verifying_key = VerifyingKey::from_bytes(&key_array).map_err(|e| {
93 AuthError::KeyParsingFailed {
94 reason: e.to_string(),
95 }
96 })?;
97 Ok(PublicKey::Ed25519(verifying_key))
98 }
99 _ => Err(AuthError::InvalidKeyFormat {
100 reason: format!("Unknown key algorithm prefix: '{prefix}'"),
101 }),
102 }
103 }
104
105 pub fn random() -> Self {
110 PrivateKey::generate().public_key()
111 }
112
113 pub fn algorithm(&self) -> &'static str {
115 match self {
116 PublicKey::Ed25519(_) => "ed25519",
117 }
118 }
119}
120
121impl std::fmt::Display for PublicKey {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 write!(f, "{}", self.to_prefixed_string())
124 }
125}
126
127impl Serialize for PublicKey {
129 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
130 serializer.serialize_str(&self.to_prefixed_string())
131 }
132}
133
134impl<'de> Deserialize<'de> for PublicKey {
136 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
137 let s = String::deserialize(deserializer)?;
138 PublicKey::from_prefixed_string(&s).map_err(serde::de::Error::custom)
139 }
140}
141
142#[non_exhaustive]
148#[derive(Clone)]
149pub enum PrivateKey {
150 Ed25519(SigningKey),
152}
153
154impl std::fmt::Debug for PrivateKey {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 PrivateKey::Ed25519(_) => f.write_str("PrivateKey::Ed25519([REDACTED])"),
158 }
159 }
160}
161
162impl PrivateKey {
163 pub fn sign(&self, data: &[u8]) -> Vec<u8> {
165 match self {
166 PrivateKey::Ed25519(key) => {
167 let signature: Signature = key.sign(data);
168 signature.to_bytes().to_vec()
169 }
170 }
171 }
172
173 pub fn public_key(&self) -> PublicKey {
175 match self {
176 PrivateKey::Ed25519(key) => PublicKey::Ed25519(key.verifying_key()),
177 }
178 }
179
180 pub fn generate() -> Self {
182 PrivateKey::Ed25519(SigningKey::generate(&mut OsRng))
183 }
184
185 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
190 match self {
191 PrivateKey::Ed25519(key) => Zeroizing::new(key.to_bytes().to_vec()),
192 }
193 }
194
195 pub fn from_bytes(algorithm: &str, bytes: &[u8]) -> Result<Self, AuthError> {
197 match algorithm {
198 "ed25519" => {
199 let key_array: [u8; ED25519_PRIVATE_KEY_SIZE] =
200 bytes.try_into().map_err(|_| AuthError::InvalidKeyFormat {
201 reason: format!(
202 "Ed25519 private key must be {ED25519_PRIVATE_KEY_SIZE} bytes, got {}",
203 bytes.len()
204 ),
205 })?;
206 Ok(PrivateKey::Ed25519(SigningKey::from_bytes(&key_array)))
207 }
208 _ => Err(AuthError::InvalidKeyFormat {
209 reason: format!("Unknown key algorithm: {algorithm}"),
210 }),
211 }
212 }
213
214 pub fn to_prefixed_string(&self) -> Zeroizing<String> {
219 let bytes = self.to_bytes();
220 let encoded = Base64::encode_string(&bytes);
221 Zeroizing::new(format!("{}:{encoded}", self.algorithm()))
222 }
223
224 pub fn from_prefixed_string(s: &str) -> Result<Self, AuthError> {
226 let (prefix, key_data) = s
227 .split_once(':')
228 .ok_or_else(|| AuthError::InvalidKeyFormat {
229 reason: "Expected 'algorithm:key' format".to_string(),
230 })?;
231 match prefix {
232 "ed25519" => {
233 let key_bytes =
234 Base64::decode_vec(key_data).map_err(|e| AuthError::InvalidKeyFormat {
235 reason: format!("Invalid base64 for key: {e}"),
236 })?;
237 Self::from_bytes("ed25519", &key_bytes)
238 }
239 _ => Err(AuthError::InvalidKeyFormat {
240 reason: format!("Unknown key algorithm prefix: '{prefix}'"),
241 }),
242 }
243 }
244
245 pub fn algorithm(&self) -> &'static str {
247 match self {
248 PrivateKey::Ed25519(_) => "ed25519",
249 }
250 }
251}
252
253impl ZeroizeOnDrop for PrivateKey {}
259
260impl Serialize for PrivateKey {
262 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
263 serializer.serialize_str(&self.to_prefixed_string())
264 }
265}
266
267impl<'de> Deserialize<'de> for PrivateKey {
269 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
270 let s = String::deserialize(deserializer)?;
271 PrivateKey::from_prefixed_string(&s).map_err(serde::de::Error::custom)
272 }
273}
274
275pub fn generate_keypair() -> (PrivateKey, PublicKey) {
282 let key = PrivateKey::generate();
283 let pubkey = key.public_key();
284 (key, pubkey)
285}
286
287pub fn sign_entry(entry: &Entry, signing_key: &PrivateKey) -> Result<String, Error> {
291 Ok(sign_data(entry.signing_bytes()?, signing_key))
292}
293
294pub fn verify_entry_signature(entry: &Entry, public_key: &PublicKey) -> Result<(), AuthError> {
299 let signature_base64 = entry.sig.sig.as_ref().ok_or(AuthError::InvalidSignature)?;
300
301 let signature_bytes =
302 Base64::decode_vec(signature_base64).map_err(|_| AuthError::InvalidSignature)?;
303
304 let signing_bytes = entry
305 .signing_bytes()
306 .map_err(|e| AuthError::InvalidAuthConfiguration {
307 reason: format!("Failed to get signing bytes: {e}"),
308 })?;
309
310 public_key.verify(&signing_bytes, &signature_bytes)
311}
312
313pub fn sign_data(data: impl AsRef<[u8]>, signing_key: &PrivateKey) -> String {
315 let sig_bytes = signing_key.sign(data.as_ref());
316 Base64::encode_string(&sig_bytes)
317}
318
319pub fn generate_challenge() -> Vec<u8> {
338 let mut challenge = vec![0u8; CHALLENGE_SIZE];
339 OsRng.fill(&mut challenge[..]);
340 challenge
341}
342
343pub fn create_challenge_response(challenge: impl AsRef<[u8]>, signing_key: &PrivateKey) -> Vec<u8> {
356 signing_key.sign(challenge.as_ref())
357}
358
359pub fn verify_challenge_response(
385 challenge: impl AsRef<[u8]>,
386 response: impl AsRef<[u8]>,
387 public_key: &PublicKey,
388) -> Result<(), AuthError> {
389 public_key.verify(challenge.as_ref(), response.as_ref())
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use crate::auth::types::{SigInfo, SigKey};
396
397 #[test]
398 fn test_keypair_generation() {
399 let (signing_key, verifying_key) = generate_keypair();
400
401 let test_data = b"hello world";
403 let signature = sign_data(test_data, &signing_key);
404 let sig_bytes = Base64::decode_vec(&signature).unwrap();
405
406 verifying_key.verify(test_data, &sig_bytes).unwrap();
407
408 let wrong_data = b"goodbye world";
410 assert!(verifying_key.verify(wrong_data, &sig_bytes).is_err());
411 }
412
413 #[test]
414 fn test_key_formatting() {
415 let (_, verifying_key) = generate_keypair();
416 let formatted = verifying_key.to_string();
417
418 assert!(formatted.starts_with("ed25519:"));
419
420 let parsed = PublicKey::from_prefixed_string(&formatted);
422 assert!(parsed.is_ok());
423 assert_eq!(parsed.unwrap(), verifying_key);
424 }
425
426 #[test]
427 fn test_entry_signing() {
428 let (signing_key, verifying_key) = generate_keypair();
429
430 let mut entry = Entry::root_builder()
432 .build()
433 .expect("Root entry should build successfully");
434
435 entry.sig = SigInfo::builder()
437 .key(SigKey::from_name("KEY_LAPTOP"))
438 .build();
439
440 let signature = sign_entry(&entry, &signing_key).unwrap();
442
443 entry.sig.sig = Some(signature);
445
446 verify_entry_signature(&entry, &verifying_key).unwrap();
448
449 let (_, wrong_key) = generate_keypair();
451 assert!(verify_entry_signature(&entry, &wrong_key).is_err());
452 }
453
454 #[test]
455 fn test_challenge_generation() {
456 let challenge1 = generate_challenge();
457 let challenge2 = generate_challenge();
458
459 assert_eq!(challenge1.len(), CHALLENGE_SIZE);
461 assert_eq!(challenge2.len(), CHALLENGE_SIZE);
462
463 assert_ne!(challenge1, challenge2);
465 }
466
467 #[test]
468 fn test_challenge_response() {
469 let (signing_key, verifying_key) = generate_keypair();
470 let challenge = generate_challenge();
471
472 let response = create_challenge_response(&challenge, &signing_key);
474
475 assert_eq!(response.len(), ED25519_SIGNATURE_SIZE);
477
478 assert!(verify_challenge_response(&challenge, &response, &verifying_key).is_ok());
480
481 let wrong_challenge = generate_challenge();
483 assert!(verify_challenge_response(&wrong_challenge, &response, &verifying_key).is_err());
484
485 let (_, wrong_pubkey) = generate_keypair();
487 assert!(verify_challenge_response(&challenge, &response, &wrong_pubkey).is_err());
488 }
489
490 #[test]
493 fn test_private_key_generate_and_sign() {
494 let key = PrivateKey::generate();
495 let pubkey = key.public_key();
496 let data = b"hello world";
497
498 let signature = key.sign(data);
499 pubkey.verify(data, &signature).unwrap();
500
501 assert!(pubkey.verify(b"wrong data", &signature).is_err());
503 }
504
505 #[test]
506 fn test_public_key_prefixed_string_roundtrip() {
507 let key = PrivateKey::generate();
508 let pubkey = key.public_key();
509
510 let formatted = pubkey.to_prefixed_string();
511 assert!(formatted.starts_with("ed25519:"));
512
513 let parsed = PublicKey::from_prefixed_string(&formatted).unwrap();
514 assert_eq!(parsed, pubkey);
515 }
516
517 #[test]
518 fn test_public_key_from_prefixed_string_invalid() {
519 assert!(PublicKey::from_prefixed_string("rsa:abc").is_err());
521
522 assert!(PublicKey::from_prefixed_string("abc").is_err());
524
525 assert!(PublicKey::from_prefixed_string("ed25519:!!!invalid!!!").is_err());
527
528 assert!(PublicKey::from_prefixed_string("ed25519:AAAA").is_err());
530 }
531
532 #[test]
533 fn test_private_key_algorithm() {
534 let key = PrivateKey::generate();
535 assert_eq!(key.algorithm(), "ed25519");
536 assert_eq!(key.public_key().algorithm(), "ed25519");
537 }
538
539 #[test]
540 fn test_private_key_bytes_roundtrip() {
541 let key = PrivateKey::generate();
542 let bytes = key.to_bytes();
543 let algorithm = key.algorithm();
544
545 let restored = PrivateKey::from_bytes(algorithm, &bytes).unwrap();
546 assert_eq!(
547 key.public_key().to_prefixed_string(),
548 restored.public_key().to_prefixed_string()
549 );
550 }
551
552 #[test]
553 fn test_private_key_from_bytes_invalid() {
554 assert!(PrivateKey::from_bytes("rsa", &[0u8; 32]).is_err());
556
557 assert!(PrivateKey::from_bytes("ed25519", &[0u8; 16]).is_err());
559 }
560
561 #[test]
562 fn test_private_key_prefixed_string_roundtrip() {
563 let key = PrivateKey::generate();
564 let formatted = key.to_prefixed_string();
565 assert!(formatted.starts_with("ed25519:"));
566
567 let restored = PrivateKey::from_prefixed_string(&formatted).unwrap();
568 assert_eq!(
569 key.public_key().to_prefixed_string(),
570 restored.public_key().to_prefixed_string()
571 );
572 }
573
574 #[test]
575 fn test_private_key_from_prefixed_string_invalid() {
576 assert!(PrivateKey::from_prefixed_string("rsa:abc").is_err());
578
579 assert!(PrivateKey::from_prefixed_string("abc").is_err());
581
582 assert!(PrivateKey::from_prefixed_string("ed25519:!!!invalid!!!").is_err());
584
585 assert!(PrivateKey::from_prefixed_string("ed25519:AAAA").is_err());
587 }
588
589 #[test]
590 fn test_private_key_serde_roundtrip() {
591 let key = PrivateKey::generate();
592 let pubkey_str = key.public_key().to_prefixed_string();
593
594 let serialized = serde_json::to_string(&key).unwrap();
595 assert!(serialized.starts_with("\"ed25519:"));
597
598 let deserialized: PrivateKey = serde_json::from_str(&serialized).unwrap();
599 assert_eq!(deserialized.public_key().to_prefixed_string(), pubkey_str);
600 }
601
602 #[test]
603 fn test_private_key_debug_redacted() {
604 let key = PrivateKey::generate();
605 let debug_str = format!("{key:?}");
606 assert_eq!(debug_str, "PrivateKey::Ed25519([REDACTED])");
607 assert!(!debug_str.contains(&format!("{:?}", key.to_bytes())));
608 }
609
610 #[test]
611 fn test_public_key_display() {
612 let key = PrivateKey::generate();
613 let pubkey = key.public_key();
614 assert_eq!(format!("{pubkey}"), pubkey.to_prefixed_string());
615 }
616
617 #[test]
618 fn test_public_key_verify_malformed_signature() {
619 let key = PrivateKey::generate();
620 let pubkey = key.public_key();
621
622 assert!(pubkey.verify(b"data", &[0u8; 10]).is_err());
624
625 assert!(pubkey.verify(b"data", &[0u8; 63]).is_err());
627
628 assert!(pubkey.verify(b"data", &[0u8; 64]).is_err());
630 }
631
632 #[test]
633 fn test_public_key_serde_roundtrip() {
634 let key = PrivateKey::generate();
635 let pubkey = key.public_key();
636
637 let serialized = serde_json::to_string(&pubkey).unwrap();
638 assert_eq!(serialized, format!("\"{}\"", pubkey.to_prefixed_string()));
640
641 let deserialized: PublicKey = serde_json::from_str(&serialized).unwrap();
642 assert_eq!(deserialized, pubkey);
643 }
644
645 #[test]
646 fn test_public_key_hash() {
647 use std::collections::HashSet;
648
649 let key1 = PrivateKey::generate();
650 let key2 = PrivateKey::generate();
651 let pubkey1 = key1.public_key();
652 let pubkey2 = key2.public_key();
653
654 let mut set = HashSet::new();
655 set.insert(pubkey1.clone());
656 set.insert(pubkey2.clone());
657 set.insert(pubkey1.clone()); assert_eq!(set.len(), 2);
660 assert!(set.contains(&pubkey1));
661 assert!(set.contains(&pubkey2));
662 }
663
664 #[test]
665 fn test_public_key_display_matches_prefixed_string() {
666 let (_, verifying_key) = generate_keypair();
668 let formatted = verifying_key.to_string();
669 assert_eq!(verifying_key.to_prefixed_string(), formatted);
670 }
671
672 #[test]
673 fn test_private_key_sign_and_verify_roundtrip() {
674 let (signing_key, verifying_key) = generate_keypair();
675
676 let data = b"test data for signing";
677 let sig_b64 = sign_data(data, &signing_key);
678 let sig_bytes = signing_key.sign(data);
679
680 verifying_key
682 .verify(data, &Base64::decode_vec(&sig_b64).unwrap())
683 .unwrap();
684 verifying_key.verify(data, &sig_bytes).unwrap();
685 }
686}