1use crate::signing::{KeyPair, PublicKey, SecretKey};
31use serde::{Deserialize, Serialize};
32use thiserror::Error;
33
34#[derive(Debug, Error, Clone, PartialEq, Eq)]
36pub enum KeyFormatError {
37 #[error("Invalid DER encoding: {0}")]
39 InvalidDer(String),
40
41 #[error("Invalid JWK format: {0}")]
43 InvalidJwk(String),
44
45 #[error("Unsupported key type: {0}")]
47 UnsupportedKeyType(String),
48
49 #[error("Invalid key length: expected {expected}, got {actual}")]
51 InvalidKeyLength { expected: usize, actual: usize },
52
53 #[error("Serialization error: {0}")]
55 SerializationError(String),
56
57 #[error("Missing required field: {0}")]
59 MissingField(String),
60}
61
62pub type KeyFormatResult<T> = Result<T, KeyFormatError>;
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
69pub struct JwkKey {
70 pub kty: String,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub crv: Option<String>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub x: Option<String>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub d: Option<String>,
84
85 #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
87 pub key_use: Option<String>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub kid: Option<String>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub alg: Option<String>,
96}
97
98impl JwkKey {
99 pub fn from_ed25519_keypair(keypair: &KeyPair) -> Self {
101 let public_key = keypair.public_key();
102 let secret_key = keypair.secret_key();
103
104 Self {
105 kty: "OKP".to_string(),
106 crv: Some("Ed25519".to_string()),
107 x: Some(base64_url_encode(&public_key)),
108 d: Some(base64_url_encode(&secret_key)),
109 key_use: Some("sig".to_string()),
110 kid: None,
111 alg: Some("EdDSA".to_string()),
112 }
113 }
114
115 pub fn from_ed25519_public_key(public_key: &PublicKey) -> Self {
117 Self {
118 kty: "OKP".to_string(),
119 crv: Some("Ed25519".to_string()),
120 x: Some(base64_url_encode(public_key)),
121 d: None,
122 key_use: Some("sig".to_string()),
123 kid: None,
124 alg: Some("EdDSA".to_string()),
125 }
126 }
127
128 pub fn to_ed25519_keypair(&self) -> KeyFormatResult<KeyPair> {
130 if self.kty != "OKP" {
132 return Err(KeyFormatError::UnsupportedKeyType(self.kty.clone()));
133 }
134
135 if let Some(crv) = &self.crv {
137 if crv != "Ed25519" {
138 return Err(KeyFormatError::UnsupportedKeyType(crv.clone()));
139 }
140 }
141
142 let x = self
144 .x
145 .as_ref()
146 .ok_or_else(|| KeyFormatError::MissingField("x".to_string()))?;
147 let public_bytes =
148 base64_url_decode(x).map_err(|e| KeyFormatError::InvalidJwk(e.to_string()))?;
149
150 if public_bytes.len() != 32 {
151 return Err(KeyFormatError::InvalidKeyLength {
152 expected: 32,
153 actual: public_bytes.len(),
154 });
155 }
156
157 let d = self
159 .d
160 .as_ref()
161 .ok_or_else(|| KeyFormatError::MissingField("d".to_string()))?;
162 let secret_bytes =
163 base64_url_decode(d).map_err(|e| KeyFormatError::InvalidJwk(e.to_string()))?;
164
165 if secret_bytes.len() != 32 {
166 return Err(KeyFormatError::InvalidKeyLength {
167 expected: 32,
168 actual: secret_bytes.len(),
169 });
170 }
171
172 let mut secret_key = [0u8; 32];
174 secret_key.copy_from_slice(&secret_bytes);
175
176 KeyPair::from_secret_key(&secret_key)
177 .map_err(|_| KeyFormatError::InvalidJwk("Invalid secret key".to_string()))
178 }
179
180 pub fn to_ed25519_public_key(&self) -> KeyFormatResult<PublicKey> {
182 if self.kty != "OKP" {
184 return Err(KeyFormatError::UnsupportedKeyType(self.kty.clone()));
185 }
186
187 let x = self
189 .x
190 .as_ref()
191 .ok_or_else(|| KeyFormatError::MissingField("x".to_string()))?;
192 let public_bytes =
193 base64_url_decode(x).map_err(|e| KeyFormatError::InvalidJwk(e.to_string()))?;
194
195 if public_bytes.len() != 32 {
196 return Err(KeyFormatError::InvalidKeyLength {
197 expected: 32,
198 actual: public_bytes.len(),
199 });
200 }
201
202 let mut public_key = [0u8; 32];
203 public_key.copy_from_slice(&public_bytes);
204 Ok(public_key)
205 }
206
207 pub fn to_json(&self) -> KeyFormatResult<String> {
209 serde_json::to_string_pretty(self)
210 .map_err(|e| KeyFormatError::SerializationError(e.to_string()))
211 }
212
213 pub fn from_json(json: &str) -> KeyFormatResult<Self> {
215 serde_json::from_str(json).map_err(|e| KeyFormatError::SerializationError(e.to_string()))
216 }
217
218 pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
220 self.kid = Some(kid.into());
221 self
222 }
223}
224
225pub struct DerKey;
230
231impl DerKey {
232 pub fn encode_ed25519_public_key(public_key: &PublicKey) -> Vec<u8> {
236 let mut der = Vec::with_capacity(44);
239
240 der.push(0x30);
242 der.push(42); der.push(0x30);
246 der.push(5);
247
248 der.push(0x06);
250 der.push(3);
251 der.extend_from_slice(&[0x2B, 0x65, 0x70]);
252
253 der.push(0x03);
255 der.push(33); der.push(0x00); der.extend_from_slice(public_key);
260
261 der
262 }
263
264 pub fn decode_ed25519_public_key(der: &[u8]) -> KeyFormatResult<PublicKey> {
266 if der.len() < 44 {
270 return Err(KeyFormatError::InvalidDer("DER data too short".to_string()));
271 }
272
273 if der[0] != 0x30 {
275 return Err(KeyFormatError::InvalidDer(
276 "Expected SEQUENCE tag".to_string(),
277 ));
278 }
279
280 let key_start = der.len() - 32;
283 if key_start >= der.len() {
284 return Err(KeyFormatError::InvalidDer(
285 "Invalid DER structure".to_string(),
286 ));
287 }
288
289 let mut public_key = [0u8; 32];
290 public_key.copy_from_slice(&der[key_start..]);
291
292 Ok(public_key)
293 }
294
295 pub fn encode_ed25519_private_key(secret_key: &SecretKey) -> Vec<u8> {
299 let mut der = Vec::with_capacity(48);
301
302 der.push(0x30);
304 der.push(46); der.push(0x02);
308 der.push(0x01);
309 der.push(0x00);
310
311 der.push(0x30);
313 der.push(5);
314
315 der.push(0x06);
317 der.push(3);
318 der.extend_from_slice(&[0x2B, 0x65, 0x70]);
319
320 der.push(0x04);
322 der.push(34); der.push(0x04);
326 der.push(32);
327 der.extend_from_slice(secret_key);
328
329 der
330 }
331
332 pub fn decode_ed25519_private_key(der: &[u8]) -> KeyFormatResult<SecretKey> {
334 if der.len() < 48 {
335 return Err(KeyFormatError::InvalidDer(
336 "DER data too short for private key".to_string(),
337 ));
338 }
339
340 let key_start = der.len() - 32;
342 if key_start >= der.len() {
343 return Err(KeyFormatError::InvalidDer(
344 "Invalid DER structure".to_string(),
345 ));
346 }
347
348 let mut secret_key = [0u8; 32];
349 secret_key.copy_from_slice(&der[key_start..]);
350
351 Ok(secret_key)
352 }
353}
354
355fn base64_url_encode(data: &[u8]) -> String {
357 use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
358 URL_SAFE_NO_PAD.encode(data)
359}
360
361fn base64_url_decode(data: &str) -> Result<Vec<u8>, String> {
363 use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
364 URL_SAFE_NO_PAD.decode(data).map_err(|e| e.to_string())
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_jwk_keypair_roundtrip() {
373 let keypair = KeyPair::generate();
374
375 let jwk = JwkKey::from_ed25519_keypair(&keypair);
377 let restored = jwk.to_ed25519_keypair().unwrap();
378
379 assert_eq!(keypair.public_key(), restored.public_key());
381 assert_eq!(keypair.secret_key(), restored.secret_key());
382 }
383
384 #[test]
385 fn test_jwk_public_key_roundtrip() {
386 let keypair = KeyPair::generate();
387 let public_key = keypair.public_key();
388
389 let jwk = JwkKey::from_ed25519_public_key(&public_key);
391 let restored = jwk.to_ed25519_public_key().unwrap();
392
393 assert_eq!(public_key, restored);
394 }
395
396 #[test]
397 fn test_jwk_json_serialization() {
398 let keypair = KeyPair::generate();
399 let jwk = JwkKey::from_ed25519_keypair(&keypair);
400
401 let json = jwk.to_json().unwrap();
403 let restored = JwkKey::from_json(&json).unwrap();
404
405 assert_eq!(jwk, restored);
406 }
407
408 #[test]
409 fn test_jwk_with_kid() {
410 let keypair = KeyPair::generate();
411 let jwk = JwkKey::from_ed25519_keypair(&keypair).with_kid("my-key-id");
412
413 assert_eq!(jwk.kid, Some("my-key-id".to_string()));
414 }
415
416 #[test]
417 fn test_jwk_validation() {
418 let invalid_jwk = JwkKey {
420 kty: "RSA".to_string(),
421 crv: None,
422 x: None,
423 d: None,
424 key_use: None,
425 kid: None,
426 alg: None,
427 };
428
429 assert!(invalid_jwk.to_ed25519_keypair().is_err());
430 }
431
432 #[test]
433 fn test_der_public_key_roundtrip() {
434 let keypair = KeyPair::generate();
435 let public_key = keypair.public_key();
436
437 let der = DerKey::encode_ed25519_public_key(&public_key);
439 let restored = DerKey::decode_ed25519_public_key(&der).unwrap();
440
441 assert_eq!(public_key, restored);
442 }
443
444 #[test]
445 fn test_der_private_key_roundtrip() {
446 let keypair = KeyPair::generate();
447 let secret_key = keypair.secret_key();
448
449 let der = DerKey::encode_ed25519_private_key(&secret_key);
451 let restored = DerKey::decode_ed25519_private_key(&der).unwrap();
452
453 assert_eq!(secret_key, restored);
454 }
455
456 #[test]
457 fn test_der_public_key_structure() {
458 let keypair = KeyPair::generate();
459 let der = DerKey::encode_ed25519_public_key(&keypair.public_key());
460
461 assert_eq!(der[0], 0x30);
463
464 assert!(der.windows(3).any(|w| w == [0x2B, 0x65, 0x70]));
466 }
467
468 #[test]
469 fn test_base64_url_encoding() {
470 let data = b"Hello, World!";
471 let encoded = base64_url_encode(data);
472 let decoded = base64_url_decode(&encoded).unwrap();
473
474 assert_eq!(data, &decoded[..]);
475
476 assert!(!encoded.contains('+'));
478 assert!(!encoded.contains('/'));
479 assert!(!encoded.contains('='));
480 }
481
482 #[test]
483 fn test_jwk_missing_fields() {
484 let jwk = JwkKey {
485 kty: "OKP".to_string(),
486 crv: Some("Ed25519".to_string()),
487 x: None, d: Some("test".to_string()),
489 key_use: None,
490 kid: None,
491 alg: None,
492 };
493
494 assert!(jwk.to_ed25519_keypair().is_err());
495 }
496}