1use crate::signing::{KeyPair, PublicKey, SecretKey, SigningError};
9use thiserror::Error;
10
11#[derive(Debug, Error)]
13pub enum KeySerdeError {
14 #[error("Invalid PEM format")]
15 InvalidPemFormat,
16
17 #[error("Invalid base64 encoding: {0}")]
18 InvalidBase64(String),
19
20 #[error("Invalid hex encoding: {0}")]
21 InvalidHex(String),
22
23 #[error("Invalid key length: expected {expected}, got {actual}")]
24 InvalidKeyLength { expected: usize, actual: usize },
25
26 #[error("Signing error: {0}")]
27 SigningError(#[from] SigningError),
28
29 #[error("Unknown key type: {0}")]
30 UnknownKeyType(String),
31}
32
33const ED25519_PRIVATE_KEY_HEADER: &str = "-----BEGIN ED25519 PRIVATE KEY-----";
35const ED25519_PRIVATE_KEY_FOOTER: &str = "-----END ED25519 PRIVATE KEY-----";
36
37const ED25519_PUBLIC_KEY_HEADER: &str = "-----BEGIN ED25519 PUBLIC KEY-----";
39const ED25519_PUBLIC_KEY_FOOTER: &str = "-----END ED25519 PUBLIC KEY-----";
40
41pub struct KeySerializer;
43
44impl KeySerializer {
45 pub fn secret_key_to_hex(key: &SecretKey) -> String {
51 hex::encode(key)
52 }
53
54 pub fn secret_key_from_hex(hex_str: &str) -> Result<SecretKey, KeySerdeError> {
56 let bytes = hex::decode(hex_str).map_err(|e| KeySerdeError::InvalidHex(e.to_string()))?;
57
58 if bytes.len() != 32 {
59 return Err(KeySerdeError::InvalidKeyLength {
60 expected: 32,
61 actual: bytes.len(),
62 });
63 }
64
65 let mut key = [0u8; 32];
66 key.copy_from_slice(&bytes);
67 Ok(key)
68 }
69
70 pub fn public_key_to_hex(key: &PublicKey) -> String {
72 hex::encode(key)
73 }
74
75 pub fn public_key_from_hex(hex_str: &str) -> Result<PublicKey, KeySerdeError> {
77 let bytes = hex::decode(hex_str).map_err(|e| KeySerdeError::InvalidHex(e.to_string()))?;
78
79 if bytes.len() != 32 {
80 return Err(KeySerdeError::InvalidKeyLength {
81 expected: 32,
82 actual: bytes.len(),
83 });
84 }
85
86 let mut key = [0u8; 32];
87 key.copy_from_slice(&bytes);
88 Ok(key)
89 }
90
91 pub fn secret_key_to_base64(key: &SecretKey) -> String {
97 base64_encode(key)
98 }
99
100 pub fn secret_key_from_base64(b64_str: &str) -> Result<SecretKey, KeySerdeError> {
102 let bytes = base64_decode(b64_str)?;
103
104 if bytes.len() != 32 {
105 return Err(KeySerdeError::InvalidKeyLength {
106 expected: 32,
107 actual: bytes.len(),
108 });
109 }
110
111 let mut key = [0u8; 32];
112 key.copy_from_slice(&bytes);
113 Ok(key)
114 }
115
116 pub fn public_key_to_base64(key: &PublicKey) -> String {
118 base64_encode(key)
119 }
120
121 pub fn public_key_from_base64(b64_str: &str) -> Result<PublicKey, KeySerdeError> {
123 let bytes = base64_decode(b64_str)?;
124
125 if bytes.len() != 32 {
126 return Err(KeySerdeError::InvalidKeyLength {
127 expected: 32,
128 actual: bytes.len(),
129 });
130 }
131
132 let mut key = [0u8; 32];
133 key.copy_from_slice(&bytes);
134 Ok(key)
135 }
136
137 pub fn keypair_to_pem(keypair: &KeyPair) -> String {
143 let secret = keypair.secret_key();
144 Self::secret_key_to_pem(&secret)
145 }
146
147 pub fn secret_key_to_pem(key: &SecretKey) -> String {
149 let b64 = base64_encode(key);
150 let wrapped = wrap_base64(&b64, 64);
151 format!(
152 "{}\n{}\n{}",
153 ED25519_PRIVATE_KEY_HEADER, wrapped, ED25519_PRIVATE_KEY_FOOTER
154 )
155 }
156
157 pub fn keypair_from_pem(pem: &str) -> Result<KeyPair, KeySerdeError> {
159 let secret = Self::secret_key_from_pem(pem)?;
160 KeyPair::from_secret_key(&secret).map_err(KeySerdeError::SigningError)
161 }
162
163 pub fn secret_key_from_pem(pem: &str) -> Result<SecretKey, KeySerdeError> {
165 let pem = pem.trim();
166
167 if !pem.starts_with(ED25519_PRIVATE_KEY_HEADER) {
168 return Err(KeySerdeError::InvalidPemFormat);
169 }
170 if !pem.ends_with(ED25519_PRIVATE_KEY_FOOTER) {
171 return Err(KeySerdeError::InvalidPemFormat);
172 }
173
174 let content = pem
176 .strip_prefix(ED25519_PRIVATE_KEY_HEADER)
177 .and_then(|s| s.strip_suffix(ED25519_PRIVATE_KEY_FOOTER))
178 .ok_or(KeySerdeError::InvalidPemFormat)?;
179
180 let b64: String = content.chars().filter(|c| !c.is_whitespace()).collect();
182
183 Self::secret_key_from_base64(&b64)
184 }
185
186 pub fn public_key_to_pem(key: &PublicKey) -> String {
188 let b64 = base64_encode(key);
189 let wrapped = wrap_base64(&b64, 64);
190 format!(
191 "{}\n{}\n{}",
192 ED25519_PUBLIC_KEY_HEADER, wrapped, ED25519_PUBLIC_KEY_FOOTER
193 )
194 }
195
196 pub fn public_key_from_pem(pem: &str) -> Result<PublicKey, KeySerdeError> {
198 let pem = pem.trim();
199
200 if !pem.starts_with(ED25519_PUBLIC_KEY_HEADER) {
201 return Err(KeySerdeError::InvalidPemFormat);
202 }
203 if !pem.ends_with(ED25519_PUBLIC_KEY_FOOTER) {
204 return Err(KeySerdeError::InvalidPemFormat);
205 }
206
207 let content = pem
209 .strip_prefix(ED25519_PUBLIC_KEY_HEADER)
210 .and_then(|s| s.strip_suffix(ED25519_PUBLIC_KEY_FOOTER))
211 .ok_or(KeySerdeError::InvalidPemFormat)?;
212
213 let b64: String = content.chars().filter(|c| !c.is_whitespace()).collect();
215
216 Self::public_key_from_base64(&b64)
217 }
218}
219
220fn base64_encode(data: &[u8]) -> String {
226 const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
227
228 let mut result = String::new();
229 let mut i = 0;
230
231 while i < data.len() {
232 let b0 = data[i];
233 let b1 = if i + 1 < data.len() { data[i + 1] } else { 0 };
234 let b2 = if i + 2 < data.len() { data[i + 2] } else { 0 };
235
236 result.push(ALPHABET[(b0 >> 2) as usize] as char);
237 result.push(ALPHABET[(((b0 & 0x03) << 4) | (b1 >> 4)) as usize] as char);
238
239 if i + 1 < data.len() {
240 result.push(ALPHABET[(((b1 & 0x0f) << 2) | (b2 >> 6)) as usize] as char);
241 } else {
242 result.push('=');
243 }
244
245 if i + 2 < data.len() {
246 result.push(ALPHABET[(b2 & 0x3f) as usize] as char);
247 } else {
248 result.push('=');
249 }
250
251 i += 3;
252 }
253
254 result
255}
256
257fn base64_decode(data: &str) -> Result<Vec<u8>, KeySerdeError> {
259 const DECODE_TABLE: [i8; 128] = [
260 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
261 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
262 -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
263 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1,
264 -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
265 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
266 ];
267
268 let data = data.trim_end_matches('=');
269 let len = data.len();
270
271 if len == 0 {
272 return Ok(Vec::new());
273 }
274
275 let output_len = (len * 3) / 4;
276 let mut result = Vec::with_capacity(output_len);
277
278 let bytes = data.as_bytes();
279 let mut i = 0;
280
281 while i + 3 < len {
282 let b0 = decode_char(bytes[i], &DECODE_TABLE)?;
283 let b1 = decode_char(bytes[i + 1], &DECODE_TABLE)?;
284 let b2 = decode_char(bytes[i + 2], &DECODE_TABLE)?;
285 let b3 = decode_char(bytes[i + 3], &DECODE_TABLE)?;
286
287 result.push((b0 << 2) | (b1 >> 4));
288 result.push((b1 << 4) | (b2 >> 2));
289 result.push((b2 << 6) | b3);
290
291 i += 4;
292 }
293
294 if i < len {
296 let remaining = len - i;
297 let b0 = decode_char(bytes[i], &DECODE_TABLE)?;
298 let b1 = if i + 1 < len {
299 decode_char(bytes[i + 1], &DECODE_TABLE)?
300 } else {
301 0
302 };
303 let b2 = if i + 2 < len {
304 decode_char(bytes[i + 2], &DECODE_TABLE)?
305 } else {
306 0
307 };
308
309 result.push((b0 << 2) | (b1 >> 4));
310 if remaining > 2 {
311 result.push((b1 << 4) | (b2 >> 2));
312 }
313 if remaining > 3 {
314 let b3 = decode_char(bytes[i + 3], &DECODE_TABLE)?;
315 result.push((b2 << 6) | b3);
316 }
317 }
318
319 Ok(result)
320}
321
322fn decode_char(c: u8, table: &[i8; 128]) -> Result<u8, KeySerdeError> {
323 if c >= 128 {
324 return Err(KeySerdeError::InvalidBase64(format!(
325 "Invalid character: {}",
326 c as char
327 )));
328 }
329 let val = table[c as usize];
330 if val < 0 {
331 return Err(KeySerdeError::InvalidBase64(format!(
332 "Invalid character: {}",
333 c as char
334 )));
335 }
336 Ok(val as u8)
337}
338
339fn wrap_base64(b64: &str, line_len: usize) -> String {
341 let mut result = String::new();
342 let mut i = 0;
343
344 while i < b64.len() {
345 let end = (i + line_len).min(b64.len());
346 result.push_str(&b64[i..end]);
347 if end < b64.len() {
348 result.push('\n');
349 }
350 i = end;
351 }
352
353 result
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 #[test]
361 fn test_hex_roundtrip() {
362 let keypair = KeyPair::generate();
363 let secret = keypair.secret_key();
364 let public = keypair.public_key();
365
366 let secret_hex = KeySerializer::secret_key_to_hex(&secret);
367 let public_hex = KeySerializer::public_key_to_hex(&public);
368
369 let secret2 = KeySerializer::secret_key_from_hex(&secret_hex).unwrap();
370 let public2 = KeySerializer::public_key_from_hex(&public_hex).unwrap();
371
372 assert_eq!(secret, secret2);
373 assert_eq!(public, public2);
374 }
375
376 #[test]
377 fn test_base64_roundtrip() {
378 let keypair = KeyPair::generate();
379 let secret = keypair.secret_key();
380 let public = keypair.public_key();
381
382 let secret_b64 = KeySerializer::secret_key_to_base64(&secret);
383 let public_b64 = KeySerializer::public_key_to_base64(&public);
384
385 let secret2 = KeySerializer::secret_key_from_base64(&secret_b64).unwrap();
386 let public2 = KeySerializer::public_key_from_base64(&public_b64).unwrap();
387
388 assert_eq!(secret, secret2);
389 assert_eq!(public, public2);
390 }
391
392 #[test]
393 fn test_pem_roundtrip() {
394 let keypair = KeyPair::generate();
395 let secret = keypair.secret_key();
396 let public = keypair.public_key();
397
398 let secret_pem = KeySerializer::secret_key_to_pem(&secret);
399 let public_pem = KeySerializer::public_key_to_pem(&public);
400
401 let secret2 = KeySerializer::secret_key_from_pem(&secret_pem).unwrap();
402 let public2 = KeySerializer::public_key_from_pem(&public_pem).unwrap();
403
404 assert_eq!(secret, secret2);
405 assert_eq!(public, public2);
406 }
407
408 #[test]
409 fn test_keypair_pem_roundtrip() {
410 let keypair = KeyPair::generate();
411 let pem = KeySerializer::keypair_to_pem(&keypair);
412 let keypair2 = KeySerializer::keypair_from_pem(&pem).unwrap();
413
414 assert_eq!(keypair.public_key(), keypair2.public_key());
415 }
416
417 #[test]
418 fn test_invalid_hex_length() {
419 let result = KeySerializer::secret_key_from_hex("0123456789abcdef");
421 assert!(result.is_err());
422 assert!(matches!(
423 result.unwrap_err(),
424 KeySerdeError::InvalidKeyLength { .. }
425 ));
426
427 let long_hex = "0".repeat(80);
429 let result = KeySerializer::secret_key_from_hex(&long_hex);
430 assert!(result.is_err());
431 }
432
433 #[test]
434 fn test_invalid_hex_characters() {
435 let invalid_hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
436 let result = KeySerializer::secret_key_from_hex(invalid_hex);
437 assert!(result.is_err());
438 assert!(matches!(result.unwrap_err(), KeySerdeError::InvalidHex(_)));
439 }
440
441 #[test]
442 fn test_invalid_base64_characters() {
443 let invalid_b64 = "!!!invalid-base64!!!";
444 let result = KeySerializer::secret_key_from_base64(invalid_b64);
445 assert!(result.is_err());
446 assert!(matches!(
447 result.unwrap_err(),
448 KeySerdeError::InvalidBase64(_)
449 ));
450 }
451
452 #[test]
453 fn test_invalid_base64_length() {
454 let short_b64 = base64_encode(&[0u8; 16]);
456 let result = KeySerializer::secret_key_from_base64(&short_b64);
457 assert!(result.is_err());
458 assert!(matches!(
459 result.unwrap_err(),
460 KeySerdeError::InvalidKeyLength { .. }
461 ));
462 }
463
464 #[test]
465 fn test_invalid_pem_format_missing_header() {
466 let pem = "-----END ED25519 PRIVATE KEY-----";
467 let result = KeySerializer::secret_key_from_pem(pem);
468 assert!(result.is_err());
469 assert!(matches!(
470 result.unwrap_err(),
471 KeySerdeError::InvalidPemFormat
472 ));
473 }
474
475 #[test]
476 fn test_invalid_pem_format_missing_footer() {
477 let pem = "-----BEGIN ED25519 PRIVATE KEY-----\nYWJjZGVm";
478 let result = KeySerializer::secret_key_from_pem(pem);
479 assert!(result.is_err());
480 assert!(matches!(
481 result.unwrap_err(),
482 KeySerdeError::InvalidPemFormat
483 ));
484 }
485
486 #[test]
487 fn test_invalid_pem_format_wrong_header() {
488 let pem = "-----BEGIN RSA PRIVATE KEY-----\nYWJjZGVm\n-----END RSA PRIVATE KEY-----";
489 let result = KeySerializer::secret_key_from_pem(pem);
490 assert!(result.is_err());
491 assert!(matches!(
492 result.unwrap_err(),
493 KeySerdeError::InvalidPemFormat
494 ));
495 }
496
497 #[test]
498 fn test_pem_with_extra_whitespace() {
499 let keypair = KeyPair::generate();
500 let secret = keypair.secret_key();
501
502 let pem = KeySerializer::secret_key_to_pem(&secret);
503 let pem_with_spaces = format!(" {} \n\n", pem);
505
506 let secret2 = KeySerializer::secret_key_from_pem(&pem_with_spaces).unwrap();
507 assert_eq!(secret, secret2);
508 }
509
510 #[test]
511 fn test_hex_case_insensitive() {
512 let keypair = KeyPair::generate();
513 let secret = keypair.secret_key();
514
515 let hex_lower = KeySerializer::secret_key_to_hex(&secret);
516 let hex_upper = hex_lower.to_uppercase();
517
518 let secret_from_lower = KeySerializer::secret_key_from_hex(&hex_lower).unwrap();
519 let secret_from_upper = KeySerializer::secret_key_from_hex(&hex_upper).unwrap();
520
521 assert_eq!(secret, secret_from_lower);
522 assert_eq!(secret, secret_from_upper);
523 }
524
525 #[test]
526 fn test_public_key_hex_roundtrip() {
527 let keypair = KeyPair::generate();
528 let public = keypair.public_key();
529
530 let hex = KeySerializer::public_key_to_hex(&public);
531 let public2 = KeySerializer::public_key_from_hex(&hex).unwrap();
532
533 assert_eq!(public, public2);
534 assert_eq!(hex.len(), 64);
536 }
537
538 #[test]
539 fn test_public_key_invalid_hex_length() {
540 let result = KeySerializer::public_key_from_hex("0123");
541 assert!(result.is_err());
542 assert!(matches!(
543 result.unwrap_err(),
544 KeySerdeError::InvalidKeyLength { .. }
545 ));
546 }
547
548 #[test]
549 fn test_public_key_pem_invalid_format() {
550 let pem = "-----BEGIN ED25519 PRIVATE KEY-----\ndata\n-----END ED25519 PRIVATE KEY-----";
551 let result = KeySerializer::public_key_from_pem(pem);
552 assert!(result.is_err());
553 assert!(matches!(
554 result.unwrap_err(),
555 KeySerdeError::InvalidPemFormat
556 ));
557 }
558
559 #[test]
560 fn test_base64_empty_string() {
561 let result = KeySerializer::secret_key_from_base64("");
562 assert!(result.is_err());
563 assert!(matches!(
564 result.unwrap_err(),
565 KeySerdeError::InvalidKeyLength { .. }
566 ));
567 }
568}