1use crate::cryptotensors::CryptoTensorsError;
11use crate::encryption::EncryptionAlgorithm;
12use crate::registry;
13use crate::signing::SignatureAlgorithm;
14use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
15use once_cell::sync::OnceCell;
16use ring::rand::{self, SecureRandom};
17use ring::signature::{Ed25519KeyPair, KeyPair};
18use serde::{de::Error, Deserialize, Deserializer, Serialize};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum ValidateMode {
28 ForCreation,
30 FromHeader,
32 FromJwk,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "lowercase")]
39pub enum JwkKeyType {
40 Oct,
42 Okp,
44}
45
46mod key_material_serde {
48 use super::*;
49
50 pub fn deserialize<'de, D>(deserializer: D) -> Result<OnceCell<Option<String>>, D::Error>
51 where
52 D: Deserializer<'de>,
53 {
54 let value: Option<String> = Option::deserialize(deserializer)?;
55 let cell = OnceCell::new();
56 if let Some(v) = value {
57 cell.set(Some(v))
58 .map_err(|_| D::Error::custom("Failed to set OnceCell value"))?;
59 }
60 Ok(cell)
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct KeyMaterial {
67 #[serde(rename = "kty")]
69 pub key_type: JwkKeyType,
70
71 pub alg: String,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub kid: Option<String>,
77
78 #[serde(skip_serializing, default)]
80 #[serde(with = "key_material_serde")]
81 pub k: OnceCell<Option<String>>,
82
83 #[serde(skip_serializing, default)]
85 #[serde(with = "key_material_serde")]
86 #[serde(rename = "x")]
87 pub x_pub: OnceCell<Option<String>>,
88
89 #[serde(skip_serializing, default)]
91 #[serde(with = "key_material_serde")]
92 #[serde(rename = "d")]
93 pub d_priv: OnceCell<Option<String>>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub jku: Option<String>,
98}
99
100impl KeyMaterial {
101 fn new_internal(
103 key_type: JwkKeyType,
104 alg: String,
105 kid: Option<String>,
106 jku: Option<String>,
107 k: Option<Vec<u8>>,
108 x_pub: Option<Vec<u8>>,
109 d_priv: Option<Vec<u8>>,
110 ) -> Result<Self, CryptoTensorsError> {
111 let key_material = Self {
112 key_type,
113 alg,
114 kid,
115 k: OnceCell::new(),
116 x_pub: OnceCell::new(),
117 d_priv: OnceCell::new(),
118 jku,
119 };
120
121 if let Some(k) = k {
122 key_material
123 .k
124 .set(Some(BASE64.encode(&k)))
125 .map_err(|_| CryptoTensorsError::KeyCreation("Failed to set key".to_string()))?;
126 }
127 if let Some(x_pub) = x_pub {
128 key_material
129 .x_pub
130 .set(Some(BASE64.encode(&x_pub)))
131 .map_err(|_| {
132 CryptoTensorsError::KeyCreation("Failed to set public key".to_string())
133 })?;
134 }
135 if let Some(d_priv) = d_priv {
136 key_material
137 .d_priv
138 .set(Some(BASE64.encode(&d_priv)))
139 .map_err(|_| {
140 CryptoTensorsError::KeyCreation("Failed to set private key".to_string())
141 })?;
142 }
143
144 Ok(key_material)
145 }
146
147 pub fn load_key(&self) -> Result<(), CryptoTensorsError> {
190 let jwk = match self.key_type {
192 JwkKeyType::Oct => registry::get_master_key(self.jku.as_deref(), self.kid.as_deref())?,
193 JwkKeyType::Okp => registry::get_verify_key(self.jku.as_deref(), self.kid.as_deref())?,
194 };
195
196 let key = Self::from_jwk(&jwk, false)?;
198
199 self.update_from_key(&key)
201 }
202
203 pub(crate) fn update_from_key(&self, key: &KeyMaterial) -> Result<(), CryptoTensorsError> {
205 match self.key_type {
207 JwkKeyType::Oct => {
208 if let Some(Some(k)) = key.k.get() {
209 self.k
210 .set(Some(k.clone()))
211 .map_err(|_| CryptoTensorsError::MissingMasterKey)?;
212 } else {
213 return Err(CryptoTensorsError::MissingMasterKey);
214 }
215 }
216 JwkKeyType::Okp => {
217 if let Some(Some(x_pub)) = key.x_pub.get() {
218 self.x_pub
219 .set(Some(x_pub.clone()))
220 .map_err(|_| CryptoTensorsError::MissingVerificationKey)?;
221 } else {
222 return Err(CryptoTensorsError::MissingVerificationKey);
223 }
224 }
226 }
227
228 Ok(())
229 }
230
231 pub fn validate(&self, mode: ValidateMode) -> Result<(), CryptoTensorsError> {
233 if self.key_type != JwkKeyType::Oct && self.key_type != JwkKeyType::Okp {
235 return Err(CryptoTensorsError::InvalidKey(
236 "Invalid key type".to_string(),
237 ));
238 }
239
240 match mode {
242 ValidateMode::ForCreation | ValidateMode::FromHeader => {
243 if self.alg.is_empty() {
244 return Err(CryptoTensorsError::InvalidAlgorithm(
245 "Missing alg field".to_string(),
246 ));
247 }
248 }
249 ValidateMode::FromJwk => {
250 }
252 }
253
254 match mode {
256 ValidateMode::ForCreation => {
257 match self.key_type {
258 JwkKeyType::Oct => {
259 if self.k.get().and_then(|k| k.as_ref()).is_none() {
260 return Err(CryptoTensorsError::MissingMasterKey);
261 }
262 }
263 JwkKeyType::Okp => {
264 if self.d_priv.get().and_then(|k| k.as_ref()).is_none() {
267 return Err(CryptoTensorsError::MissingSigningKey);
268 }
269 if self.x_pub.get().and_then(|k| k.as_ref()).is_none() {
270 return Err(CryptoTensorsError::MissingVerificationKey);
271 }
272 }
273 }
274 }
275 ValidateMode::FromJwk => match self.key_type {
276 JwkKeyType::Oct => {
277 if self.k.get().and_then(|k| k.as_ref()).is_none() {
278 return Err(CryptoTensorsError::MissingMasterKey);
279 }
280 }
281 JwkKeyType::Okp => {
282 if self.x_pub.get().and_then(|k| k.as_ref()).is_none() {
283 return Err(CryptoTensorsError::MissingVerificationKey);
284 }
285 }
286 },
287 ValidateMode::FromHeader => {
288 }
290 }
291
292 if !self.alg.is_empty() {
294 match self.key_type {
295 JwkKeyType::Oct => {
296 if self.alg.parse::<EncryptionAlgorithm>().is_err() {
297 return Err(CryptoTensorsError::InvalidAlgorithm(
298 "Invalid encryption algorithm".to_string(),
299 ));
300 }
301 }
302 JwkKeyType::Okp => {
303 if self.alg.parse::<SignatureAlgorithm>().is_err() {
304 return Err(CryptoTensorsError::InvalidAlgorithm(
305 "Invalid signature algorithm".to_string(),
306 ));
307 }
308 }
309 }
310 }
311
312 Ok(())
313 }
314
315 pub fn new_enc_key(
327 key_b64: Option<String>,
328 alg: Option<String>,
329 kid: Option<String>,
330 jku: Option<String>,
331 ) -> Result<Self, CryptoTensorsError> {
332 let alg = alg.unwrap_or_else(|| "aes256gcm".to_string());
333 let enc_alg = alg
334 .parse::<EncryptionAlgorithm>()
335 .map_err(|_| CryptoTensorsError::InvalidAlgorithm(alg.clone()))?;
336 let key_bytes = if let Some(ref b64_str) = key_b64 {
337 let bytes = BASE64.decode(b64_str).map_err(|e| {
338 CryptoTensorsError::InvalidKey(format!("Invalid base64 key: {}", e))
339 })?;
340 if bytes.len() != enc_alg.key_len() {
341 return Err(CryptoTensorsError::InvalidKeyLength {
342 expected: enc_alg.key_len(),
343 actual: bytes.len(),
344 });
345 }
346 bytes
347 } else {
348 let mut key = vec![0u8; enc_alg.key_len()];
350 let rng = rand::SystemRandom::new();
351 rng.fill(&mut key)
352 .map_err(|e| CryptoTensorsError::RandomGeneration(e.to_string()))?;
353 key
354 };
355 KeyMaterial::new_internal(JwkKeyType::Oct, alg, kid, jku, Some(key_bytes), None, None)
356 }
357
358 pub fn new_sign_key(
371 public_b64: Option<String>,
372 private_b64: Option<String>,
373 alg: Option<String>,
374 kid: Option<String>,
375 jku: Option<String>,
376 ) -> Result<Self, CryptoTensorsError> {
377 let alg = alg.unwrap_or_else(|| "ed25519".to_string());
378 let sig_alg = alg
379 .parse::<SignatureAlgorithm>()
380 .map_err(|_| CryptoTensorsError::InvalidAlgorithm(alg.clone()))?;
381 match sig_alg {
382 SignatureAlgorithm::Ed25519 => {
383 let public = if let Some(pub_b64) = public_b64 {
384 let pub_bytes = BASE64.decode(&pub_b64).map_err(|e| {
385 CryptoTensorsError::InvalidKey(format!("Invalid base64 public key: {}", e))
386 })?;
387 if pub_bytes.len() != 32 {
388 return Err(CryptoTensorsError::InvalidKeyLength {
389 expected: 32,
390 actual: pub_bytes.len(),
391 });
392 }
393 Some(pub_bytes)
394 } else {
395 None
396 };
397 let private = if let Some(priv_b64) = private_b64 {
398 let priv_bytes = BASE64.decode(&priv_b64).map_err(|e| {
399 CryptoTensorsError::InvalidKey(format!("Invalid base64 private key: {}", e))
400 })?;
401 if priv_bytes.len() != 32 {
402 return Err(CryptoTensorsError::InvalidKeyLength {
403 expected: 32,
404 actual: priv_bytes.len(),
405 });
406 }
407 Some(priv_bytes)
408 } else {
409 None
410 };
411 let (public, private) = if public.is_none() && private.is_none() {
413 let rng = rand::SystemRandom::new();
414 let mut private_key = [0u8; 32];
415 rng.fill(&mut private_key)
416 .map_err(|e| CryptoTensorsError::RandomGeneration(e.to_string()))?;
417 let key_pair = Ed25519KeyPair::from_seed_unchecked(&private_key)
418 .map_err(|e| CryptoTensorsError::KeyCreation(e.to_string()))?;
419 (
420 Some(key_pair.public_key().as_ref().to_vec()),
421 Some(private_key.to_vec()),
422 )
423 } else if public.is_some() && private.is_some() {
424 Ed25519KeyPair::from_seed_and_public_key(
426 private.clone().unwrap().as_slice(),
427 public.clone().unwrap().as_slice(),
428 )
429 .map_err(|e| CryptoTensorsError::KeyCreation(e.to_string()))?;
430 (public, private)
431 } else if private.is_some() && public.is_none() {
432 let private_key = private.clone().unwrap();
434 let key_pair = Ed25519KeyPair::from_seed_unchecked(&private_key)
435 .map_err(|e| CryptoTensorsError::KeyCreation(e.to_string()))?;
436 (Some(key_pair.public_key().as_ref().to_vec()), private)
437 } else {
438 (public, private)
440 };
441 KeyMaterial::new_internal(JwkKeyType::Okp, alg, kid, jku, None, public, private)
442 }
443 }
444 }
445
446 pub fn to_jwk(&self) -> Result<String, CryptoTensorsError> {
452 #[derive(Serialize)]
454 struct JwkOut<'a> {
455 #[serde(rename = "kty")]
456 key_type: &'a JwkKeyType,
457 alg: &'a String,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 kid: &'a Option<String>,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 jku: &'a Option<String>,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 k: Option<&'a String>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 #[serde(rename = "x")]
466 x_pub: Option<&'a String>,
467 #[serde(skip_serializing_if = "Option::is_none")]
468 #[serde(rename = "d")]
469 d_priv: Option<&'a String>,
470 }
471 let jwk = JwkOut {
472 key_type: &self.key_type,
473 alg: &self.alg,
474 kid: &self.kid,
475 jku: &self.jku,
476 k: self.k.get().and_then(|v| v.as_ref()),
477 x_pub: self.x_pub.get().and_then(|v| v.as_ref()),
478 d_priv: self.d_priv.get().and_then(|v| v.as_ref()),
479 };
480 serde_json::to_string(&jwk)
481 .map_err(|e| CryptoTensorsError::KeyCreation(format!("Failed to serialize JWK: {}", e)))
482 }
483
484 pub fn get_master_key_bytes(&self) -> Result<Vec<u8>, CryptoTensorsError> {
491 let k = self
492 .k
493 .get()
494 .and_then(|v| v.as_ref())
495 .ok_or(CryptoTensorsError::MissingMasterKey)?;
496 BASE64.decode(k).map_err(|e| {
497 CryptoTensorsError::InvalidKey(format!("Invalid base64 master key: {}", e))
498 })
499 }
500
501 pub fn get_public_key_bytes(&self) -> Result<Vec<u8>, CryptoTensorsError> {
508 let x = self
509 .x_pub
510 .get()
511 .and_then(|v| v.as_ref())
512 .ok_or(CryptoTensorsError::MissingVerificationKey)?;
513 BASE64.decode(x).map_err(|e| {
514 CryptoTensorsError::InvalidKey(format!("Invalid base64 public key: {}", e))
515 })
516 }
517
518 pub fn get_private_key_bytes(&self) -> Result<Vec<u8>, CryptoTensorsError> {
525 let d = self
526 .d_priv
527 .get()
528 .and_then(|v| v.as_ref())
529 .ok_or(CryptoTensorsError::MissingSigningKey)?;
530 BASE64.decode(d).map_err(|e| {
531 CryptoTensorsError::InvalidKey(format!("Invalid base64 private key: {}", e))
532 })
533 }
534
535 pub fn from_header(header: &serde_json::Value) -> Result<Self, CryptoTensorsError> {
544 let key: KeyMaterial = serde_json::from_value(header.clone()).map_err(|e| {
545 CryptoTensorsError::InvalidKey(format!("Failed to parse key material: {}", e))
546 })?;
547 key.validate(ValidateMode::FromHeader)?;
548 Ok(key)
549 }
550
551 pub fn from_jwk(
561 jwk_str: &serde_json::Value,
562 is_for_save: bool,
563 ) -> Result<Self, CryptoTensorsError> {
564 let key_material: KeyMaterial = serde_json::from_value(jwk_str.clone())
565 .map_err(|e| CryptoTensorsError::InvalidKey(format!("Failed to parse JWK: {}", e)))?;
566
567 key_material.validate(if is_for_save {
570 ValidateMode::ForCreation
571 } else {
572 ValidateMode::FromJwk
573 })?;
574
575 Ok(key_material)
576 }
577
578 pub fn from_jwk_str(jwk_json: &str, is_for_save: bool) -> Result<Self, CryptoTensorsError> {
588 let jwk_value: serde_json::Value = serde_json::from_str(jwk_json)
589 .map_err(|e| CryptoTensorsError::InvalidKey(format!("Invalid JSON: {}", e)))?;
590 Self::from_jwk(&jwk_value, is_for_save)
591 }
592
593 pub fn kid(&self) -> Option<&str> {
595 self.kid.as_deref()
596 }
597
598 pub fn jku(&self) -> Option<&str> {
600 self.jku.as_deref()
601 }
602
603 pub fn set_kid(&mut self, kid: &str) {
605 self.kid = Some(kid.to_string());
606 }
607
608 pub fn set_jku(&mut self, jku: &str) {
610 self.jku = Some(jku.to_string());
611 }
612}
613
614#[derive(Clone, Copy)]
616pub enum KeyRole {
617 Master,
619 Signing,
621 Verify,
623}
624
625impl KeyRole {
626 fn missing_key_message(&self) -> &'static str {
627 match self {
628 KeyRole::Master => "encryption key required: provide enc_key or use registry (enc_kid)",
629 KeyRole::Signing => "signing key required: provide sign_key or use registry (sign_kid)",
630 KeyRole::Verify => {
631 "verification key required: provide sign_key or use registry (sign_kid)"
632 }
633 }
634 }
635}
636
637pub struct KeyLookupParams<'a> {
640 pub direct: Option<&'a KeyMaterial>,
642 pub jku: Option<&'a str>,
644 pub kid: Option<&'a str>,
646 pub registry_allowed: bool,
648}
649
650pub fn resolve_key(
653 role: KeyRole,
654 params: &KeyLookupParams<'_>,
655 for_creation: bool,
656) -> Result<KeyMaterial, CryptoTensorsError> {
657 if let Some(k) = params.direct {
658 return Ok(k.clone());
659 }
660 let jwk = if params.registry_allowed {
661 match role {
662 KeyRole::Master => registry::get_master_key(params.jku, params.kid)?,
663 KeyRole::Signing => registry::get_signing_key(params.jku, params.kid)?,
664 KeyRole::Verify => registry::get_verify_key(params.jku, params.kid)?,
665 }
666 } else {
667 return Err(CryptoTensorsError::KeyLoad(
668 role.missing_key_message().to_string(),
669 ));
670 };
671 KeyMaterial::from_jwk(&jwk, for_creation)
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677
678 #[test]
679 fn test_keymaterial_convenience_methods() {
680 let jwk_json = r#"{"kty":"oct","alg":"aes256gcm","kid":"test","k":"dGVzdC1rZXktMzItYnl0ZXMtbG9uZy1lbmNyeXB0aW9u"}"#;
682 let key = KeyMaterial::from_jwk_str(jwk_json, false).unwrap();
683 assert_eq!(key.kid(), Some("test"));
684 assert_eq!(key.alg, "aes256gcm");
685
686 let mut key = KeyMaterial::new_enc_key(None, None, None, None).unwrap();
688 key.set_kid("new-kid");
689 key.set_jku("file:///new/path.jwk");
690 assert_eq!(key.kid(), Some("new-kid"));
691 assert_eq!(key.jku(), Some("file:///new/path.jwk"));
692 }
693}