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
300 .auth()
301 .signature
302 .as_ref()
303 .ok_or(AuthError::InvalidSignature)?;
304
305 let signature_bytes =
306 Base64::decode_vec(signature_base64).map_err(|_| AuthError::InvalidSignature)?;
307
308 let signing_bytes = entry
309 .signing_bytes()
310 .map_err(|e| AuthError::InvalidAuthConfiguration {
311 reason: format!("Failed to get signing bytes: {e}"),
312 })?;
313
314 public_key.verify(&signing_bytes, &signature_bytes)
315}
316
317pub fn sign_data(data: impl AsRef<[u8]>, signing_key: &PrivateKey) -> String {
319 let sig_bytes = signing_key.sign(data.as_ref());
320 Base64::encode_string(&sig_bytes)
321}
322
323pub fn generate_challenge() -> Vec<u8> {
342 let mut challenge = vec![0u8; CHALLENGE_SIZE];
343 OsRng.fill(&mut challenge[..]);
344 challenge
345}
346
347pub fn create_challenge_response(challenge: impl AsRef<[u8]>, signing_key: &PrivateKey) -> Vec<u8> {
360 signing_key.sign(challenge.as_ref())
361}
362
363pub fn verify_challenge_response(
389 challenge: impl AsRef<[u8]>,
390 response: impl AsRef<[u8]>,
391 public_key: &PublicKey,
392) -> Result<(), AuthError> {
393 public_key.verify(challenge.as_ref(), response.as_ref())
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399 use crate::auth::types::{AuthInfo, SigKey};
400
401 #[test]
402 fn test_keypair_generation() {
403 let (signing_key, verifying_key) = generate_keypair();
404
405 let test_data = b"hello world";
407 let signature = sign_data(test_data, &signing_key);
408 let sig_bytes = Base64::decode_vec(&signature).unwrap();
409
410 verifying_key.verify(test_data, &sig_bytes).unwrap();
411
412 let wrong_data = b"goodbye world";
414 assert!(verifying_key.verify(wrong_data, &sig_bytes).is_err());
415 }
416
417 #[test]
418 fn test_key_formatting() {
419 let (_, verifying_key) = generate_keypair();
420 let formatted = verifying_key.to_string();
421
422 assert!(formatted.starts_with("ed25519:"));
423
424 let parsed = PublicKey::from_prefixed_string(&formatted);
426 assert!(parsed.is_ok());
427 assert_eq!(parsed.unwrap(), verifying_key);
428 }
429
430 #[test]
431 fn test_entry_signing() {
432 let (signing_key, verifying_key) = generate_keypair();
433
434 let entry = Entry::root_builder()
436 .set_auth(
437 AuthInfo::builder()
438 .key(SigKey::from_name("KEY_LAPTOP"))
439 .build(),
440 )
441 .build()
442 .expect("Root entry should build successfully");
443
444 let signature = sign_entry(&entry, &signing_key).unwrap();
446
447 let entry = entry.with_auth(|auth| auth.signature = Some(signature));
449
450 verify_entry_signature(&entry, &verifying_key).unwrap();
452
453 let (_, wrong_key) = generate_keypair();
455 assert!(verify_entry_signature(&entry, &wrong_key).is_err());
456 }
457
458 #[test]
459 fn test_challenge_generation() {
460 let challenge1 = generate_challenge();
461 let challenge2 = generate_challenge();
462
463 assert_eq!(challenge1.len(), CHALLENGE_SIZE);
465 assert_eq!(challenge2.len(), CHALLENGE_SIZE);
466
467 assert_ne!(challenge1, challenge2);
469 }
470
471 #[test]
472 fn test_challenge_response() {
473 let (signing_key, verifying_key) = generate_keypair();
474 let challenge = generate_challenge();
475
476 let response = create_challenge_response(&challenge, &signing_key);
478
479 assert_eq!(response.len(), ED25519_SIGNATURE_SIZE);
481
482 assert!(verify_challenge_response(&challenge, &response, &verifying_key).is_ok());
484
485 let wrong_challenge = generate_challenge();
487 assert!(verify_challenge_response(&wrong_challenge, &response, &verifying_key).is_err());
488
489 let (_, wrong_pubkey) = generate_keypair();
491 assert!(verify_challenge_response(&challenge, &response, &wrong_pubkey).is_err());
492 }
493
494 #[test]
497 fn test_private_key_generate_and_sign() {
498 let key = PrivateKey::generate();
499 let pubkey = key.public_key();
500 let data = b"hello world";
501
502 let signature = key.sign(data);
503 pubkey.verify(data, &signature).unwrap();
504
505 assert!(pubkey.verify(b"wrong data", &signature).is_err());
507 }
508
509 #[test]
510 fn test_public_key_prefixed_string_roundtrip() {
511 let key = PrivateKey::generate();
512 let pubkey = key.public_key();
513
514 let formatted = pubkey.to_prefixed_string();
515 assert!(formatted.starts_with("ed25519:"));
516
517 let parsed = PublicKey::from_prefixed_string(&formatted).unwrap();
518 assert_eq!(parsed, pubkey);
519 }
520
521 #[test]
522 fn test_public_key_from_prefixed_string_invalid() {
523 assert!(PublicKey::from_prefixed_string("rsa:abc").is_err());
525
526 assert!(PublicKey::from_prefixed_string("abc").is_err());
528
529 assert!(PublicKey::from_prefixed_string("ed25519:!!!invalid!!!").is_err());
531
532 assert!(PublicKey::from_prefixed_string("ed25519:AAAA").is_err());
534 }
535
536 #[test]
537 fn test_private_key_algorithm() {
538 let key = PrivateKey::generate();
539 assert_eq!(key.algorithm(), "ed25519");
540 assert_eq!(key.public_key().algorithm(), "ed25519");
541 }
542
543 #[test]
544 fn test_private_key_bytes_roundtrip() {
545 let key = PrivateKey::generate();
546 let bytes = key.to_bytes();
547 let algorithm = key.algorithm();
548
549 let restored = PrivateKey::from_bytes(algorithm, &bytes).unwrap();
550 assert_eq!(
551 key.public_key().to_prefixed_string(),
552 restored.public_key().to_prefixed_string()
553 );
554 }
555
556 #[test]
557 fn test_private_key_from_bytes_invalid() {
558 assert!(PrivateKey::from_bytes("rsa", &[0u8; 32]).is_err());
560
561 assert!(PrivateKey::from_bytes("ed25519", &[0u8; 16]).is_err());
563 }
564
565 #[test]
566 fn test_private_key_prefixed_string_roundtrip() {
567 let key = PrivateKey::generate();
568 let formatted = key.to_prefixed_string();
569 assert!(formatted.starts_with("ed25519:"));
570
571 let restored = PrivateKey::from_prefixed_string(&formatted).unwrap();
572 assert_eq!(
573 key.public_key().to_prefixed_string(),
574 restored.public_key().to_prefixed_string()
575 );
576 }
577
578 #[test]
579 fn test_private_key_from_prefixed_string_invalid() {
580 assert!(PrivateKey::from_prefixed_string("rsa:abc").is_err());
582
583 assert!(PrivateKey::from_prefixed_string("abc").is_err());
585
586 assert!(PrivateKey::from_prefixed_string("ed25519:!!!invalid!!!").is_err());
588
589 assert!(PrivateKey::from_prefixed_string("ed25519:AAAA").is_err());
591 }
592
593 #[test]
594 fn test_private_key_serde_roundtrip() {
595 let key = PrivateKey::generate();
596 let pubkey_str = key.public_key().to_prefixed_string();
597
598 let serialized = serde_json::to_string(&key).unwrap();
599 assert!(serialized.starts_with("\"ed25519:"));
601
602 let deserialized: PrivateKey = serde_json::from_str(&serialized).unwrap();
603 assert_eq!(deserialized.public_key().to_prefixed_string(), pubkey_str);
604 }
605
606 #[test]
607 fn test_private_key_debug_redacted() {
608 let key = PrivateKey::generate();
609 let debug_str = format!("{key:?}");
610 assert_eq!(debug_str, "PrivateKey::Ed25519([REDACTED])");
611 assert!(!debug_str.contains(&format!("{:?}", key.to_bytes())));
612 }
613
614 #[test]
615 fn test_public_key_display() {
616 let key = PrivateKey::generate();
617 let pubkey = key.public_key();
618 assert_eq!(format!("{pubkey}"), pubkey.to_prefixed_string());
619 }
620
621 #[test]
622 fn test_public_key_verify_malformed_signature() {
623 let key = PrivateKey::generate();
624 let pubkey = key.public_key();
625
626 assert!(pubkey.verify(b"data", &[0u8; 10]).is_err());
628
629 assert!(pubkey.verify(b"data", &[0u8; 63]).is_err());
631
632 assert!(pubkey.verify(b"data", &[0u8; 64]).is_err());
634 }
635
636 #[test]
637 fn test_public_key_serde_roundtrip() {
638 let key = PrivateKey::generate();
639 let pubkey = key.public_key();
640
641 let serialized = serde_json::to_string(&pubkey).unwrap();
642 assert_eq!(serialized, format!("\"{}\"", pubkey.to_prefixed_string()));
644
645 let deserialized: PublicKey = serde_json::from_str(&serialized).unwrap();
646 assert_eq!(deserialized, pubkey);
647 }
648
649 #[test]
650 fn test_public_key_hash() {
651 use std::collections::HashSet;
652
653 let key1 = PrivateKey::generate();
654 let key2 = PrivateKey::generate();
655 let pubkey1 = key1.public_key();
656 let pubkey2 = key2.public_key();
657
658 let mut set = HashSet::new();
659 set.insert(pubkey1.clone());
660 set.insert(pubkey2.clone());
661 set.insert(pubkey1.clone()); assert_eq!(set.len(), 2);
664 assert!(set.contains(&pubkey1));
665 assert!(set.contains(&pubkey2));
666 }
667
668 #[test]
669 fn test_public_key_display_matches_prefixed_string() {
670 let (_, verifying_key) = generate_keypair();
672 let formatted = verifying_key.to_string();
673 assert_eq!(verifying_key.to_prefixed_string(), formatted);
674 }
675
676 #[test]
677 fn test_private_key_sign_and_verify_roundtrip() {
678 let (signing_key, verifying_key) = generate_keypair();
679
680 let data = b"test data for signing";
681 let sig_b64 = sign_data(data, &signing_key);
682 let sig_bytes = signing_key.sign(data);
683
684 verifying_key
686 .verify(data, &Base64::decode_vec(&sig_b64).unwrap())
687 .unwrap();
688 verifying_key.verify(data, &sig_bytes).unwrap();
689 }
690}