1use crate::error::KeyError;
2use crate::generate::generate;
3use crate::{Algorithm, Claims};
4use base64::Engine;
5use jsonwebtoken::{DecodingKey, EncodingKey, Header};
6use p256::elliptic_curve::SecretKey;
7use p256::elliptic_curve::pkcs8::EncodePrivateKey;
8use rsa::BigUint;
9use rsa::pkcs1::EncodeRsaPrivateKey;
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11use std::sync::OnceLock;
12use std::{collections::HashSet, fmt, path::Path as StdPath};
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
16#[serde(rename_all = "camelCase")]
17pub enum KeyOperation {
18 Sign,
19 Verify,
20 Decrypt,
21 Encrypt,
22}
23
24#[derive(Clone, Serialize, Deserialize)]
26#[serde(tag = "kty")]
27pub enum KeyMaterial {
28 EC {
30 #[serde(rename = "crv")]
31 curve: EllipticCurve,
32 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
34 x: Vec<u8>,
35 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
37 y: Vec<u8>,
38 #[serde(
40 default,
41 skip_serializing_if = "Option::is_none",
42 serialize_with = "serialize_base64url_optional",
43 deserialize_with = "deserialize_base64url_optional"
44 )]
45 d: Option<Vec<u8>>,
46 },
47 RSA {
49 #[serde(flatten)]
50 public: RsaPublicKey,
51 #[serde(flatten, skip_serializing_if = "Option::is_none")]
52 private: Option<RsaPrivateKey>,
53 },
54 #[serde(rename = "oct")]
56 OCT {
57 #[serde(
59 rename = "k",
60 serialize_with = "serialize_base64url",
61 deserialize_with = "deserialize_base64url"
62 )]
63 secret: Vec<u8>,
64 },
65 OKP {
67 #[serde(rename = "crv")]
68 curve: EllipticCurve,
69 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
70 x: Vec<u8>,
71 #[serde(
72 rename = "d",
73 default,
74 skip_serializing_if = "Option::is_none",
75 serialize_with = "serialize_base64url_optional",
76 deserialize_with = "deserialize_base64url_optional"
77 )]
78 d: Option<Vec<u8>>,
79 },
80}
81
82#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
86pub enum EllipticCurve {
87 #[serde(rename = "P-256")]
88 P256,
89 #[serde(rename = "P-384")]
90 P384,
91 #[serde(rename = "Ed25519")]
95 Ed25519,
96}
97
98#[derive(Clone, Serialize, Deserialize)]
102pub struct RsaPublicKey {
103 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
104 pub n: Vec<u8>,
105 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
106 pub e: Vec<u8>,
107}
108
109#[derive(Clone, Serialize, Deserialize)]
113pub struct RsaPrivateKey {
114 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
115 pub d: Vec<u8>,
116 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
117 pub p: Vec<u8>,
118 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
119 pub q: Vec<u8>,
120 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
121 pub dp: Vec<u8>,
122 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
123 pub dq: Vec<u8>,
124 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
125 pub qi: Vec<u8>,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub oth: Option<Vec<RsaAdditionalPrime>>,
128}
129
130#[derive(Clone, Serialize, Deserialize)]
132pub struct RsaAdditionalPrime {
133 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
134 pub r: Vec<u8>,
135 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
136 pub d: Vec<u8>,
137 #[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
138 pub t: Vec<u8>,
139}
140
141#[derive(Clone, Serialize, Deserialize)]
149#[serde(remote = "Self")]
150#[non_exhaustive]
151pub struct Jwk {
152 #[serde(rename = "alg")]
154 pub algorithm: Algorithm,
155
156 #[serde(rename = "key_ops", default = "sign_verify")]
159 pub operations: HashSet<KeyOperation>,
160
161 #[serde(flatten)]
163 pub material: KeyMaterial,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub kid: Option<crate::KeyId>,
168
169 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub scope: Option<crate::Scope>,
172}
173
174fn sign_verify() -> HashSet<KeyOperation> {
175 [KeyOperation::Sign, KeyOperation::Verify].into()
176}
177
178const MIN_OCT_SECRET_BYTES: usize = 32;
180
181impl Jwk {
182 pub fn new(algorithm: Algorithm, material: KeyMaterial) -> Self {
187 Self {
188 algorithm,
189 operations: sign_verify(),
190 material,
191 kid: None,
192 scope: None,
193 }
194 }
195
196 pub fn import(self) -> crate::Result<Key> {
202 if let Some(scope) = &self.scope {
203 scope.validate()?;
204 }
205
206 if let KeyMaterial::OCT { secret } = &self.material
207 && secret.len() < MIN_OCT_SECRET_BYTES
208 {
209 return Err(KeyError::SecretTooShort(MIN_OCT_SECRET_BYTES).into());
210 }
211
212 Ok(Key {
213 jwk: self,
214 decode: Default::default(),
215 encode: Default::default(),
216 })
217 }
218}
219
220impl<'de> Deserialize<'de> for Jwk {
221 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222 where
223 D: Deserializer<'de>,
224 {
225 let mut value = serde_json::Value::deserialize(deserializer)?;
226
227 if let Some(obj) = value.as_object_mut()
231 && !obj.contains_key("kty")
232 {
233 obj.insert("kty".to_string(), serde_json::Value::String("oct".to_string()));
234 }
235
236 Self::deserialize(value).map_err(serde::de::Error::custom)
237 }
238}
239
240impl Serialize for Jwk {
241 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
242 where
243 S: Serializer,
244 {
245 Self::serialize(self, serializer)
246 }
247}
248
249#[derive(Clone)]
256pub struct Key {
257 jwk: Jwk,
258
259 decode: OnceLock<DecodingKey>,
261 encode: OnceLock<EncodingKey>,
262}
263
264impl std::ops::Deref for Key {
269 type Target = Jwk;
270
271 fn deref(&self) -> &Self::Target {
272 &self.jwk
273 }
274}
275
276impl TryFrom<Jwk> for Key {
277 type Error = crate::Error;
278
279 fn try_from(jwk: Jwk) -> crate::Result<Self> {
280 jwk.import()
281 }
282}
283
284impl From<&Key> for Jwk {
285 fn from(key: &Key) -> Self {
286 key.export()
287 }
288}
289
290impl<'de> Deserialize<'de> for Key {
291 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292 where
293 D: Deserializer<'de>,
294 {
295 let jwk = <Jwk as Deserialize>::deserialize(deserializer)?;
298 Key::try_from(jwk).map_err(serde::de::Error::custom)
299 }
300}
301
302impl Serialize for Key {
303 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
304 where
305 S: Serializer,
306 {
307 Serialize::serialize(&Jwk::from(self), serializer)
308 }
309}
310
311impl fmt::Debug for Key {
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313 f.debug_struct("Key")
314 .field("algorithm", &self.algorithm)
315 .field("operations", &self.operations)
316 .field("kid", &self.kid)
317 .field("scope", &self.scope)
318 .finish()
319 }
320}
321
322impl Key {
323 pub fn export(&self) -> Jwk {
328 self.jwk.clone()
329 }
330
331 #[allow(clippy::should_implement_trait)]
333 pub fn from_str(s: &str) -> crate::Result<Self> {
334 let s = s.trim();
335 if s.starts_with('{') {
336 Ok(serde_json::from_str(s)?)
337 } else {
338 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(s)?;
339 let json = String::from_utf8(decoded)?;
340 Ok(serde_json::from_str(&json)?)
341 }
342 }
343
344 pub fn from_file<P: AsRef<StdPath>>(path: P) -> crate::Result<Self> {
346 let contents = std::fs::read_to_string(&path)?;
347 Self::from_str(&contents)
348 }
349
350 #[cfg(feature = "tokio")]
352 pub async fn from_file_async<P: AsRef<StdPath>>(path: P) -> crate::Result<Self> {
353 let contents = tokio::fs::read_to_string(path).await?;
354 Self::from_str(&contents)
355 }
356
357 pub fn to_str(&self) -> crate::Result<String> {
359 let json = serde_json::to_string(self)?;
360 Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(json.as_bytes()))
361 }
362
363 pub fn to_file<P: AsRef<StdPath>>(&self, path: P) -> crate::Result<()> {
365 let encoded = self.to_str()?;
366 std::fs::write(path, encoded)?;
367 Ok(())
368 }
369
370 pub fn to_public(&self) -> crate::Result<Self> {
375 if !self.operations.contains(&KeyOperation::Verify) {
376 return Err(KeyError::VerifyUnsupported.into());
377 }
378
379 let material = match self.material {
380 KeyMaterial::RSA { ref public, .. } => KeyMaterial::RSA {
381 public: public.clone(),
382 private: None,
383 },
384 KeyMaterial::EC {
385 ref x,
386 ref y,
387 ref curve,
388 ..
389 } => KeyMaterial::EC {
390 x: x.clone(),
391 y: y.clone(),
392 curve: curve.clone(),
393 d: None,
394 },
395 KeyMaterial::OCT { .. } => return Err(KeyError::NoPublicKey.into()),
396 KeyMaterial::OKP { ref x, ref curve, .. } => KeyMaterial::OKP {
397 x: x.clone(),
398 curve: curve.clone(),
399 d: None,
400 },
401 };
402
403 Ok(Self {
404 jwk: Jwk {
405 algorithm: self.algorithm,
406 operations: [KeyOperation::Verify].into(),
407 material,
408 kid: self.kid.clone(),
409 scope: self.scope.clone(),
410 },
411 decode: Default::default(),
412 encode: Default::default(),
413 })
414 }
415
416 pub(crate) fn has_signing_material(&self) -> bool {
420 match &self.material {
421 KeyMaterial::OCT { .. } => true,
422 KeyMaterial::EC { d, .. } => d.is_some(),
423 KeyMaterial::OKP { d, .. } => d.is_some(),
424 KeyMaterial::RSA { private, .. } => private.is_some(),
425 }
426 }
427
428 fn to_decoding_key(&self) -> crate::Result<&DecodingKey> {
429 if let Some(key) = self.decode.get() {
430 return Ok(key);
431 }
432
433 let decoding_key = match self.material {
434 KeyMaterial::OCT { ref secret } => match self.algorithm {
435 Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => DecodingKey::from_secret(secret),
436 _ => return Err(KeyError::InvalidAlgorithm.into()),
437 },
438 KeyMaterial::EC {
439 ref curve,
440 ref x,
441 ref y,
442 ..
443 } => match curve {
444 EllipticCurve::P256 => {
445 if self.algorithm != Algorithm::ES256 {
446 return Err(KeyError::InvalidAlgorithmForCurve("P-256").into());
447 }
448 if x.len() != 32 || y.len() != 32 {
449 return Err(KeyError::InvalidCoordinateLength("P-256").into());
450 }
451
452 DecodingKey::from_ec_components(
453 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
454 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(y).as_ref(),
455 )?
456 }
457 EllipticCurve::P384 => {
458 if self.algorithm != Algorithm::ES384 {
459 return Err(KeyError::InvalidAlgorithmForCurve("P-384").into());
460 }
461 if x.len() != 48 || y.len() != 48 {
462 return Err(KeyError::InvalidCoordinateLength("P-384").into());
463 }
464
465 DecodingKey::from_ec_components(
466 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
467 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(y).as_ref(),
468 )?
469 }
470 _ => return Err(KeyError::InvalidCurve("EC").into()),
471 },
472 KeyMaterial::OKP { ref curve, ref x, .. } => match curve {
473 EllipticCurve::Ed25519 => {
474 if self.algorithm != Algorithm::EdDSA {
475 return Err(KeyError::InvalidAlgorithmForCurve("Ed25519").into());
476 }
477
478 DecodingKey::from_ed_components(
479 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
480 )?
481 }
482 _ => return Err(KeyError::InvalidCurve("OKP").into()),
483 },
484 KeyMaterial::RSA { ref public, .. } => {
485 DecodingKey::from_rsa_raw_components(public.n.as_ref(), public.e.as_ref())
486 }
487 };
488
489 Ok(self.decode.get_or_init(|| decoding_key))
490 }
491
492 fn to_encoding_key(&self) -> crate::Result<&EncodingKey> {
493 if let Some(key) = self.encode.get() {
494 return Ok(key);
495 }
496
497 let encoding_key = match self.material {
498 KeyMaterial::OCT { ref secret } => match self.algorithm {
499 Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => EncodingKey::from_secret(secret),
500 _ => return Err(KeyError::InvalidAlgorithm.into()),
501 },
502 KeyMaterial::EC { ref curve, ref d, .. } => {
503 let d = d.as_ref().ok_or(KeyError::MissingPrivateKey)?;
504
505 match curve {
506 EllipticCurve::P256 => {
507 let secret_key = SecretKey::<p256::NistP256>::from_slice(d)?;
508 let doc = secret_key.to_pkcs8_der()?;
509 EncodingKey::from_ec_der(doc.as_bytes())
510 }
511 EllipticCurve::P384 => {
512 let secret_key = SecretKey::<p384::NistP384>::from_slice(d)?;
513 let doc = secret_key.to_pkcs8_der()?;
514 EncodingKey::from_ec_der(doc.as_bytes())
515 }
516 _ => return Err(KeyError::InvalidCurve("EC").into()),
517 }
518 }
519 KeyMaterial::OKP {
520 ref curve,
521 ref d,
522 ref x,
523 } => {
524 let d = d.as_ref().ok_or(KeyError::MissingPrivateKey)?;
525
526 let key_pair =
527 aws_lc_rs::signature::Ed25519KeyPair::from_seed_and_public_key(d.as_slice(), x.as_slice())?;
528
529 match curve {
530 EllipticCurve::Ed25519 => EncodingKey::from_ed_der(key_pair.to_pkcs8()?.as_ref()),
531 _ => return Err(KeyError::InvalidCurve("OKP").into()),
532 }
533 }
534 KeyMaterial::RSA {
535 ref public,
536 ref private,
537 } => {
538 let n = BigUint::from_bytes_be(&public.n);
539 let e = BigUint::from_bytes_be(&public.e);
540 let private = private.as_ref().ok_or(KeyError::MissingPrivateKey)?;
541 let d = BigUint::from_bytes_be(&private.d);
542 let p = BigUint::from_bytes_be(&private.p);
543 let q = BigUint::from_bytes_be(&private.q);
544
545 let rsa = rsa::RsaPrivateKey::from_components(n, e, d, vec![p, q]);
546 let pem = rsa?.to_pkcs1_pem(rsa::pkcs1::LineEnding::LF);
547
548 EncodingKey::from_rsa_pem(pem?.as_bytes())?
549 }
550 };
551
552 Ok(self.encode.get_or_init(|| encoding_key))
553 }
554
555 pub fn verify(&self, token: &str) -> crate::Result<Claims> {
561 if !self.operations.contains(&KeyOperation::Verify) {
562 return Err(KeyError::VerifyUnsupported.into());
563 }
564
565 let decode = self.to_decoding_key()?;
566
567 let mut validation = jsonwebtoken::Validation::new(self.algorithm.into());
568 validation.required_spec_claims = Default::default(); validation.validate_exp = false; let token = jsonwebtoken::decode::<Claims>(token, decode, &validation)?;
572
573 if let Some(exp) = token.claims.expires
574 && exp < std::time::SystemTime::now()
575 {
576 return Err(crate::Error::TokenExpired);
577 }
578
579 token.claims.validate()?;
580 self.validate_scope(&token.claims)?;
581
582 Ok(token.claims)
583 }
584
585 pub fn sign(&self, payload: &Claims) -> crate::Result<String> {
587 if !self.operations.contains(&KeyOperation::Sign) {
588 return Err(KeyError::SignUnsupported.into());
589 }
590
591 payload.validate()?;
592 self.validate_scope(payload)?;
593
594 let encode = self.to_encoding_key()?;
595
596 let mut header = Header::new(self.algorithm.into());
597 header.kid = self.kid.as_ref().map(|k| k.to_string());
598 let token = jsonwebtoken::encode(&header, &payload, encode)?;
599 Ok(token)
600 }
601
602 #[doc(hidden)]
603 #[deprecated(note = "renamed to Key::sign")]
604 pub fn encode(&self, payload: &Claims) -> crate::Result<String> {
605 self.sign(payload)
606 }
607
608 #[doc(hidden)]
609 #[deprecated(note = "renamed to Key::verify")]
610 pub fn decode(&self, token: &str) -> crate::Result<Claims> {
611 self.verify(token)
612 }
613
614 pub fn generate(algorithm: Algorithm, id: Option<crate::KeyId>) -> crate::Result<Self> {
616 generate(algorithm, id)
617 }
618
619 pub fn with_scope(mut self, scope: crate::Scope) -> crate::Result<Self> {
624 scope.validate()?;
625 self.jwk.scope = Some(scope);
626 Ok(self)
627 }
628
629 pub fn with_operations(mut self, operations: impl IntoIterator<Item = KeyOperation>) -> Self {
631 self.jwk.operations = operations.into_iter().collect();
632 self
633 }
634
635 fn validate_scope(&self, claims: &Claims) -> crate::Result<()> {
636 if let Some(scope) = &self.scope {
637 scope.validate()?;
638 if !scope.allows(claims) {
639 return Err(crate::Error::ScopeExceeded);
640 }
641 }
642 Ok(())
643 }
644}
645
646fn serialize_base64url<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
648where
649 S: Serializer,
650{
651 let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes);
652 serializer.serialize_str(&encoded)
653}
654
655fn serialize_base64url_optional<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
656where
657 S: Serializer,
658{
659 match bytes {
660 Some(b) => serialize_base64url(b, serializer),
661 None => serializer.serialize_none(),
662 }
663}
664
665fn deserialize_base64url<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
667where
668 D: Deserializer<'de>,
669{
670 let s = String::deserialize(deserializer)?;
671
672 base64::engine::general_purpose::URL_SAFE_NO_PAD
674 .decode(&s)
675 .or_else(|_| {
676 base64::engine::general_purpose::URL_SAFE.decode(&s)
678 })
679 .map_err(serde::de::Error::custom)
680}
681
682fn deserialize_base64url_optional<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
683where
684 D: Deserializer<'de>,
685{
686 let s: Option<String> = Option::deserialize(deserializer)?;
687 match s {
688 Some(s) => {
689 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
690 .decode(&s)
691 .or_else(|_| base64::engine::general_purpose::URL_SAFE.decode(&s))
692 .map_err(serde::de::Error::custom)?;
693 Ok(Some(decoded))
694 }
695 None => Ok(None),
696 }
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702 use std::time::{Duration, SystemTime};
703
704 fn create_test_key() -> Key {
705 let mut jwk = Jwk::new(
706 Algorithm::HS256,
707 KeyMaterial::OCT {
708 secret: b"test-secret-that-is-long-enough-for-hmac-sha256".to_vec(),
709 },
710 );
711 jwk.kid = Some(crate::KeyId::decode("test-key-1").unwrap());
712 jwk.import().unwrap()
713 }
714
715 fn create_test_claims() -> Claims {
716 Claims {
717 root: "test-path".to_string(),
718 publish: vec!["test-pub".into()],
719 subscribe: vec!["test-sub".into()],
720 expires: Some(SystemTime::now() + Duration::from_secs(3600)),
721 issued: Some(SystemTime::now()),
722 }
723 }
724
725 #[test]
726 fn test_key_from_str_valid() {
727 let key = create_test_key();
728 let json = key.to_str().unwrap();
729 let loaded_key = Key::from_str(&json).unwrap();
730
731 assert_eq!(loaded_key.algorithm, key.algorithm);
732 assert_eq!(loaded_key.operations, key.operations);
733 match (&loaded_key.material, &key.material) {
734 (KeyMaterial::OCT { secret: loaded_secret }, KeyMaterial::OCT { secret }) => {
735 assert_eq!(loaded_secret, secret);
736 }
737 _ => panic!("Expected OCT key"),
738 }
739 assert_eq!(loaded_key.kid, key.kid);
740 }
741
742 #[test]
744 fn test_key_oct_backwards_compatibility() {
745 let json = r#"{"alg":"HS256","key_ops":["sign","verify"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#;
746 let key = Key::from_str(json);
747
748 assert!(key.is_ok());
749 let key = key.unwrap();
750
751 if let KeyMaterial::OCT { secret, .. } = &key.material {
752 let base64_key = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(secret);
753 assert_eq!(base64_key, "Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68");
754 } else {
755 panic!("Expected OCT key");
756 }
757
758 let key_str = key.to_str().unwrap();
759
760 let loaded = Key::from_str(&key_str).unwrap();
762 assert_eq!(loaded.algorithm, Algorithm::HS256);
763 assert!(loaded.operations.contains(&KeyOperation::Sign));
764 assert!(loaded.operations.contains(&KeyOperation::Verify));
765 assert!(matches!(loaded.material, KeyMaterial::OCT { .. }));
766 }
767
768 #[test]
769 fn test_key_without_key_ops_defaults_sign_verify() {
770 let json = r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","kid":"no-ops"}"#;
771 let key = Key::from_str(json).unwrap();
772
773 assert_eq!(key.operations, sign_verify());
774
775 let claims = create_test_claims();
776 let token = key.sign(&claims).unwrap();
777 let verified = key.verify(&token).unwrap();
778 assert_eq!(verified.root, claims.root);
779 }
780
781 #[test]
782 fn test_key_without_key_ops_round_trip() {
783 let json = r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#;
784 let key = Key::from_str(json).unwrap();
785
786 let serialized = serde_json::to_string(&key).unwrap();
787 let parsed: serde_json::Value = serde_json::from_str(&serialized).unwrap();
788 let ops = parsed["key_ops"].as_array().unwrap();
789 assert_eq!(ops.len(), 2);
790
791 let reloaded = Key::from_str(&serialized).unwrap();
792 assert_eq!(reloaded.operations, sign_verify());
793 assert_eq!(reloaded.algorithm, key.algorithm);
794 }
795
796 #[test]
798 fn test_key_oct_secret_required_and_min_length() {
799 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256"}"#).is_err());
801 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256","k":""}"#).is_err());
802
803 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256","k":"AAAAAAAAAAAAAAAAAAAAAA"}"#).is_err());
805
806 let short = Jwk::new(Algorithm::HS256, KeyMaterial::OCT { secret: vec![0; 16] });
808 assert!(short.import().is_err());
809
810 let key =
812 Key::from_str(r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#).unwrap();
813 let KeyMaterial::OCT { ref secret } = key.material else {
814 panic!("Expected OCT key");
815 };
816 assert_eq!(secret.len(), 32);
817 }
818
819 #[test]
820 fn test_key_without_key_ops_to_public() {
821 let json = r#"{"kty":"OKP","alg":"EdDSA","crv":"Ed25519","x":"UiU9fT_SdBBpkFtJPRCY0gX1jK_Dr9syYLFuEz4QUM4","d":"lm-L_PV3ksuQ-KrFBgFMDJqAZC3_Z6Z5UC4ZQY5OoDQ","kid":"defaulted"}"#;
822 let key = Key::from_str(json).unwrap();
823 assert_eq!(key.operations, sign_verify());
824
825 let public = key.to_public().unwrap();
826 assert_eq!(public.operations, [KeyOperation::Verify].into());
827 }
828
829 #[test]
830 fn test_key_from_str_invalid_json() {
831 let result = Key::from_str("invalid json");
832 assert!(result.is_err());
833 }
834
835 #[test]
836 fn test_key_to_str() {
837 let key = create_test_key();
838 let encoded = key.to_str().unwrap();
839
840 assert!(!encoded.contains('{'));
842
843 let loaded = Key::from_str(&encoded).unwrap();
845 assert_eq!(loaded.algorithm, Algorithm::HS256);
846 assert_eq!(loaded.kid, key.kid);
847 assert!(loaded.operations.contains(&KeyOperation::Sign));
848 assert!(loaded.operations.contains(&KeyOperation::Verify));
849 }
850
851 #[test]
852 fn test_key_sign_success() {
853 let key = create_test_key();
854 let claims = create_test_claims();
855 let token = key.sign(&claims).unwrap();
856
857 assert!(!token.is_empty());
858 assert_eq!(token.matches('.').count(), 2); }
860
861 #[test]
862 fn test_key_scope_enforced_when_signing_and_verifying() {
863 let unrestricted = create_test_key();
864 let scoped = unrestricted
865 .clone()
866 .with_scope(crate::Scope {
867 root: "test-path".into(),
868 publish: vec!["allowed".into()],
869 subscribe: vec![],
870 })
871 .unwrap();
872 let allowed = Claims {
873 root: "test-path".into(),
874 publish: vec!["allowed/room".into()],
875 ..Default::default()
876 };
877 let denied = Claims {
878 root: "test-path".into(),
879 publish: vec!["other".into()],
880 ..Default::default()
881 };
882
883 assert!(scoped.sign(&allowed).is_ok());
884 assert!(matches!(scoped.sign(&denied), Err(crate::Error::ScopeExceeded)));
885
886 let forged = unrestricted.sign(&denied).unwrap();
887 assert!(matches!(scoped.verify(&forged), Err(crate::Error::ScopeExceeded)));
888 }
889
890 #[test]
893 fn test_key_derived_material_never_stale() {
894 let claims = Claims {
895 root: "test-path".into(),
896 publish: vec!["test-pub".into()],
897 ..Default::default()
898 };
899
900 let key = create_test_key();
902 let token = key.sign(&claims).unwrap();
903 assert!(key.encode.get().is_some());
904
905 let mut jwk = Jwk::from(&key);
908 jwk.algorithm = Algorithm::HS384;
909 let derived = Key::try_from(jwk).unwrap();
910 assert!(derived.encode.get().is_none());
911
912 let derived_token = derived.sign(&claims).unwrap();
913 assert_ne!(token, derived_token);
914
915 let cold = Key::from_str(&derived.to_str().unwrap()).unwrap();
918 assert_eq!(derived_token, cold.sign(&claims).unwrap());
919 assert!(cold.verify(&derived_token).is_ok());
920 assert!(key.verify(&derived_token).is_err());
921 }
922
923 #[test]
926 fn test_key_scope_requires_validation() {
927 let key = create_test_key();
928 assert!(key.scope.is_none());
929
930 let useless = crate::Scope::default();
931 assert!(matches!(
932 key.clone().with_scope(useless.clone()),
933 Err(crate::Error::UselessScope)
934 ));
935
936 let mut jwk = Jwk::from(&key);
937 jwk.scope = Some(useless);
938 assert!(matches!(Key::try_from(jwk), Err(crate::Error::UselessScope)));
939
940 let json = r#"{"alg":"HS256","key_ops":["sign"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","scope":{}}"#;
941 assert!(Key::from_str(json).is_err());
942 }
943
944 #[test]
945 fn test_key_sign_no_permission() {
946 let key = create_test_key().with_operations([KeyOperation::Verify]);
947 let claims = create_test_claims();
948
949 let result = key.sign(&claims);
950 assert!(result.is_err());
951 assert!(result.unwrap_err().to_string().contains("key does not support signing"));
952 }
953
954 #[test]
955 fn test_key_sign_invalid_claims() {
956 let key = create_test_key();
957 let invalid_claims = Claims {
958 root: "test-path".to_string(),
959 publish: vec![],
960 subscribe: vec![],
961 expires: None,
962 issued: None,
963 };
964
965 let result = key.sign(&invalid_claims);
966 assert!(result.is_err());
967 assert!(
968 result
969 .unwrap_err()
970 .to_string()
971 .contains("no publish or subscribe allowed; token is useless")
972 );
973 }
974
975 #[test]
976 fn test_key_verify_success() {
977 let key = create_test_key();
978 let claims = create_test_claims();
979 let token = key.sign(&claims).unwrap();
980
981 let verified_claims = key.verify(&token).unwrap();
982 assert_eq!(verified_claims.root, claims.root);
983 assert_eq!(verified_claims.publish, claims.publish);
984 assert_eq!(verified_claims.subscribe, claims.subscribe);
985 }
986
987 #[test]
988 fn test_key_verify_no_permission() {
989 let key = create_test_key().with_operations([KeyOperation::Sign]);
990
991 let result = key.verify("some.jwt.token");
992 assert!(result.is_err());
993 assert!(
994 result
995 .unwrap_err()
996 .to_string()
997 .contains("key does not support verification")
998 );
999 }
1000
1001 #[test]
1002 fn test_key_verify_invalid_token() {
1003 let key = create_test_key();
1004 let result = key.verify("invalid-token");
1005 assert!(result.is_err());
1006 }
1007
1008 #[test]
1009 fn test_key_verify_path_mismatch() {
1010 let key = create_test_key();
1011 let claims = create_test_claims();
1012 let token = key.sign(&claims).unwrap();
1013
1014 let result = key.verify(&token);
1016 assert!(result.is_ok());
1017 }
1018
1019 #[test]
1020 fn test_key_verify_expired_token() {
1021 let key = create_test_key();
1022 let mut claims = create_test_claims();
1023 claims.expires = Some(SystemTime::now() - Duration::from_secs(3600)); let token = key.sign(&claims).unwrap();
1025
1026 let result = key.verify(&token);
1027 assert!(result.is_err());
1028 }
1029
1030 #[test]
1031 fn test_key_verify_token_without_exp() {
1032 let key = create_test_key();
1033 let claims = Claims {
1034 root: "test-path".to_string(),
1035 publish: vec!["".to_string()],
1036 subscribe: vec!["".to_string()],
1037 expires: None,
1038 issued: None,
1039 };
1040 let token = key.sign(&claims).unwrap();
1041
1042 let verified_claims = key.verify(&token).unwrap();
1043 assert_eq!(verified_claims.root, claims.root);
1044 assert_eq!(verified_claims.publish, claims.publish);
1045 assert_eq!(verified_claims.subscribe, claims.subscribe);
1046 assert_eq!(verified_claims.expires, None);
1047 }
1048
1049 #[test]
1050 fn test_key_round_trip() {
1051 let key = create_test_key();
1052 let original_claims = Claims {
1053 root: "test-path".to_string(),
1054 publish: vec!["test-pub".into()],
1055 subscribe: vec!["test-sub".into()],
1056 expires: Some(SystemTime::now() + Duration::from_secs(3600)),
1057 issued: Some(SystemTime::now()),
1058 };
1059
1060 let token = key.sign(&original_claims).unwrap();
1061 let verified_claims = key.verify(&token).unwrap();
1062
1063 assert_eq!(verified_claims.root, original_claims.root);
1064 assert_eq!(verified_claims.publish, original_claims.publish);
1065 assert_eq!(verified_claims.subscribe, original_claims.subscribe);
1066 }
1067
1068 #[test]
1069 fn test_key_generate_hs256() {
1070 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1071 assert!(key.is_ok());
1072 let key = key.unwrap();
1073
1074 assert_eq!(key.algorithm, Algorithm::HS256);
1075 assert_eq!(key.kid, Some(crate::KeyId::decode("test-id").unwrap()));
1076 assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
1077
1078 match &key.material {
1079 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 32),
1080 _ => panic!("Expected OCT key"),
1081 }
1082 }
1083
1084 #[test]
1085 fn test_key_generate_hs384() {
1086 let key = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1087 assert!(key.is_ok());
1088 let key = key.unwrap();
1089
1090 assert_eq!(key.algorithm, Algorithm::HS384);
1091
1092 match &key.material {
1093 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 48),
1094 _ => panic!("Expected OCT key"),
1095 }
1096 }
1097
1098 #[test]
1099 fn test_key_generate_hs512() {
1100 let key = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
1101 assert!(key.is_ok());
1102 let key = key.unwrap();
1103
1104 assert_eq!(key.algorithm, Algorithm::HS512);
1105
1106 match &key.material {
1107 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 64),
1108 _ => panic!("Expected OCT key"),
1109 }
1110 }
1111
1112 #[test]
1113 fn test_key_generate_rs512() {
1114 let key = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
1115 assert!(key.is_ok());
1116 let key = key.unwrap();
1117
1118 assert_eq!(key.algorithm, Algorithm::RS512);
1119 assert!(matches!(key.material, KeyMaterial::RSA { .. }));
1120 match &key.material {
1121 KeyMaterial::RSA { public, private } => {
1122 assert!(private.is_some());
1123 assert_eq!(public.n.len(), 256);
1124 assert_eq!(public.e.len(), 3);
1125 }
1126 _ => panic!("Expected RSA key"),
1127 }
1128 }
1129
1130 #[test]
1131 fn test_key_generate_es256() {
1132 let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1133 assert!(key.is_ok());
1134 let key = key.unwrap();
1135
1136 assert_eq!(key.algorithm, Algorithm::ES256);
1137 assert!(matches!(key.material, KeyMaterial::EC { .. }))
1138 }
1139
1140 #[test]
1141 fn test_key_generate_ps512() {
1142 let key = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
1143 assert!(key.is_ok());
1144 let key = key.unwrap();
1145
1146 assert_eq!(key.algorithm, Algorithm::PS512);
1147 assert!(matches!(key.material, KeyMaterial::RSA { .. }));
1148 }
1149
1150 #[test]
1151 fn test_key_generate_eddsa() {
1152 let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1153 assert!(key.is_ok());
1154 let key = key.unwrap();
1155
1156 assert_eq!(key.algorithm, Algorithm::EdDSA);
1157 assert!(matches!(key.material, KeyMaterial::OKP { .. }));
1158 }
1159
1160 #[test]
1161 fn test_key_generate_without_id() {
1162 let key = Key::generate(Algorithm::HS256, None);
1163 assert!(key.is_ok());
1164 let key = key.unwrap();
1165
1166 assert_eq!(key.algorithm, Algorithm::HS256);
1167 assert_eq!(key.kid, None);
1168 assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
1169 }
1170
1171 #[test]
1172 fn test_public_key_conversion_hmac() {
1173 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()))
1174 .expect("HMAC key generation failed");
1175
1176 assert!(key.to_public().is_err());
1177 }
1178
1179 #[test]
1180 fn test_public_key_conversion_rsa() {
1181 let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1182 assert!(key.is_ok());
1183 let key = key.unwrap();
1184
1185 let public_key = key.to_public().unwrap();
1186 assert_eq!(key.kid, public_key.kid);
1187 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1188 assert!(public_key.encode.get().is_none());
1189 assert!(public_key.decode.get().is_none());
1190 assert!(matches!(public_key.material, KeyMaterial::RSA { .. }));
1191
1192 if let KeyMaterial::RSA { public, private } = &public_key.material {
1193 assert!(private.is_none());
1194
1195 if let KeyMaterial::RSA { public: src_public, .. } = &key.material {
1196 assert_eq!(public.e, src_public.e);
1197 assert_eq!(public.n, src_public.n);
1198 } else {
1199 unreachable!("Expected RSA key")
1200 }
1201 } else {
1202 unreachable!("Expected RSA key");
1203 }
1204 }
1205
1206 #[test]
1207 fn test_public_key_conversion_es() {
1208 let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1209 assert!(key.is_ok());
1210 let key = key.unwrap();
1211
1212 let public_key = key.to_public().unwrap();
1213 assert_eq!(key.kid, public_key.kid);
1214 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1215 assert!(public_key.encode.get().is_none());
1216 assert!(public_key.decode.get().is_none());
1217 assert!(matches!(public_key.material, KeyMaterial::EC { .. }));
1218
1219 if let KeyMaterial::EC { x, y, d, curve } = &public_key.material {
1220 assert!(d.is_none());
1221
1222 if let KeyMaterial::EC {
1223 x: src_x,
1224 y: src_y,
1225 curve: src_curve,
1226 ..
1227 } = &key.material
1228 {
1229 assert_eq!(x, src_x);
1230 assert_eq!(y, src_y);
1231 assert_eq!(curve, src_curve);
1232 } else {
1233 unreachable!("Expected EC key")
1234 }
1235 } else {
1236 unreachable!("Expected EC key");
1237 }
1238 }
1239
1240 #[test]
1241 fn test_public_key_conversion_ed() {
1242 let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1243 assert!(key.is_ok());
1244 let key = key.unwrap();
1245
1246 let public_key = key.to_public().unwrap();
1247 assert_eq!(key.kid, public_key.kid);
1248 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1249 assert!(public_key.encode.get().is_none());
1250 assert!(public_key.decode.get().is_none());
1251 assert!(matches!(public_key.material, KeyMaterial::OKP { .. }));
1252
1253 if let KeyMaterial::OKP { x, d, curve } = &public_key.material {
1254 assert!(d.is_none());
1255
1256 if let KeyMaterial::OKP {
1257 x: src_x,
1258 curve: src_curve,
1259 ..
1260 } = &key.material
1261 {
1262 assert_eq!(x, src_x);
1263 assert_eq!(curve, src_curve);
1264 } else {
1265 unreachable!("Expected OKP key")
1266 }
1267 } else {
1268 unreachable!("Expected OKP key");
1269 }
1270 }
1271
1272 #[test]
1273 fn test_key_generate_sign_verify_cycle() {
1274 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1275 assert!(key.is_ok());
1276 let key = key.unwrap();
1277
1278 let claims = create_test_claims();
1279
1280 let token = key.sign(&claims).unwrap();
1281 let verified_claims = key.verify(&token).unwrap();
1282
1283 assert_eq!(verified_claims.root, claims.root);
1284 assert_eq!(verified_claims.publish, claims.publish);
1285 assert_eq!(verified_claims.subscribe, claims.subscribe);
1286 }
1287
1288 #[test]
1289 fn test_key_debug_no_secret() {
1290 let key = create_test_key();
1291 let debug_str = format!("{key:?}");
1292
1293 assert!(debug_str.contains("algorithm: HS256"));
1294 assert!(debug_str.contains("operations"));
1295 assert!(debug_str.contains("kid: Some(KeyId(\"test-key-1\"))"));
1296 assert!(!debug_str.contains("secret")); }
1298
1299 #[test]
1300 fn test_key_operations_enum() {
1301 let sign_op = KeyOperation::Sign;
1302 let verify_op = KeyOperation::Verify;
1303 let decrypt_op = KeyOperation::Decrypt;
1304 let encrypt_op = KeyOperation::Encrypt;
1305
1306 assert_eq!(sign_op, KeyOperation::Sign);
1307 assert_eq!(verify_op, KeyOperation::Verify);
1308 assert_eq!(decrypt_op, KeyOperation::Decrypt);
1309 assert_eq!(encrypt_op, KeyOperation::Encrypt);
1310
1311 assert_ne!(sign_op, verify_op);
1312 assert_ne!(decrypt_op, encrypt_op);
1313 }
1314
1315 #[test]
1316 fn test_key_operations_serde() {
1317 let operations = [KeyOperation::Sign, KeyOperation::Verify];
1318 let json = serde_json::to_string(&operations).unwrap();
1319 assert!(json.contains("\"sign\""));
1320 assert!(json.contains("\"verify\""));
1321
1322 let deserialized: Vec<KeyOperation> = serde_json::from_str(&json).unwrap();
1323 assert_eq!(deserialized, operations);
1324 }
1325
1326 #[test]
1327 fn test_key_serde() {
1328 let key = create_test_key();
1329 let json = serde_json::to_string(&key).unwrap();
1330 let deserialized: Key = serde_json::from_str(&json).unwrap();
1331
1332 assert_eq!(deserialized.algorithm, key.algorithm);
1333 assert_eq!(deserialized.operations, key.operations);
1334 assert_eq!(deserialized.kid, key.kid);
1335
1336 if let (
1337 KeyMaterial::OCT {
1338 secret: original_secret,
1339 },
1340 KeyMaterial::OCT {
1341 secret: deserialized_secret,
1342 },
1343 ) = (&key.material, &deserialized.material)
1344 {
1345 assert_eq!(deserialized_secret, original_secret);
1346 } else {
1347 panic!("Expected both keys to be OCT variant");
1348 }
1349 }
1350
1351 #[test]
1352 fn test_key_clone() {
1353 let key = create_test_key();
1354 let cloned = key.clone();
1355
1356 assert_eq!(cloned.algorithm, key.algorithm);
1357 assert_eq!(cloned.operations, key.operations);
1358 assert_eq!(cloned.kid, key.kid);
1359
1360 if let (
1361 KeyMaterial::OCT {
1362 secret: original_secret,
1363 },
1364 KeyMaterial::OCT { secret: cloned_secret },
1365 ) = (&key.material, &cloned.material)
1366 {
1367 assert_eq!(cloned_secret, original_secret);
1368 } else {
1369 panic!("Expected both keys to be OCT variant");
1370 }
1371 }
1372
1373 #[test]
1374 fn test_hmac_algorithms() {
1375 let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1376 let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1377 let key_512 = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
1378
1379 let claims = create_test_claims();
1380
1381 for key in [key_256, key_384, key_512] {
1383 assert!(key.is_ok());
1384 let key = key.unwrap();
1385
1386 let token = key.sign(&claims).unwrap();
1387 let verified_claims = key.verify(&token).unwrap();
1388 assert_eq!(verified_claims.root, claims.root);
1389 }
1390 }
1391
1392 #[test]
1393 fn test_rsa_pkcs1_asymmetric_algorithms() {
1394 let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1395 let key_rs384 = Key::generate(Algorithm::RS384, Some(crate::KeyId::decode("test-id").unwrap()));
1396 let key_rs512 = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
1397
1398 for key in [key_rs256, key_rs384, key_rs512] {
1399 test_asymmetric_key(key);
1400 }
1401 }
1402
1403 #[test]
1404 fn test_rsa_pss_asymmetric_algorithms() {
1405 let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1406 let key_ps384 = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1407 let key_ps512 = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
1408
1409 for key in [key_ps256, key_ps384, key_ps512] {
1410 test_asymmetric_key(key);
1411 }
1412 }
1413
1414 #[test]
1415 fn test_ec_asymmetric_algorithms() {
1416 let key_es256 = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1417 let key_es384 = Key::generate(Algorithm::ES384, Some(crate::KeyId::decode("test-id").unwrap()));
1418
1419 for key in [key_es256, key_es384] {
1420 test_asymmetric_key(key);
1421 }
1422 }
1423
1424 #[test]
1425 fn test_ed_asymmetric_algorithms() {
1426 let key_eddsa = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1427
1428 test_asymmetric_key(key_eddsa);
1429 }
1430
1431 fn test_asymmetric_key(key: crate::Result<Key>) {
1432 assert!(key.is_ok());
1433 let key = key.unwrap();
1434
1435 let claims = create_test_claims();
1436 let token = key.sign(&claims).unwrap();
1437
1438 let private_verified_claims = key.verify(&token).unwrap();
1439 assert_eq!(
1440 private_verified_claims.root, claims.root,
1441 "validation using private key"
1442 );
1443
1444 let public_verified_claims = key.to_public().unwrap().verify(&token).unwrap();
1445 assert_eq!(public_verified_claims.root, claims.root, "validation using public key");
1446 }
1447
1448 #[test]
1449 fn test_cross_algorithm_verification_fails() {
1450 let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1451 assert!(key_256.is_ok());
1452 let key_256 = key_256.unwrap();
1453
1454 let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1455 assert!(key_384.is_ok());
1456 let key_384 = key_384.unwrap();
1457
1458 let claims = create_test_claims();
1459 let token = key_256.sign(&claims).unwrap();
1460
1461 let result = key_384.verify(&token);
1463 assert!(result.is_err());
1464 }
1465
1466 #[test]
1467 fn test_asymmetric_cross_algorithm_verification_fails() {
1468 let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1469 assert!(key_rs256.is_ok());
1470 let key_rs256 = key_rs256.unwrap();
1471
1472 let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1473 assert!(key_ps256.is_ok());
1474 let key_ps256 = key_ps256.unwrap();
1475
1476 let claims = create_test_claims();
1477 let token = key_rs256.sign(&claims).unwrap();
1478
1479 let private_result = key_ps256.verify(&token);
1481 let public_result = key_ps256.to_public().unwrap().verify(&token);
1482 assert!(private_result.is_err());
1483 assert!(public_result.is_err());
1484 }
1485
1486 #[test]
1487 fn test_rsa_pkcs1_public_key_conversion() {
1488 let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1489 assert!(key.is_ok());
1490 let key = key.unwrap();
1491
1492 assert!(key.operations.contains(&KeyOperation::Sign));
1493 assert!(key.operations.contains(&KeyOperation::Verify));
1494
1495 let public_key = key.to_public().unwrap();
1496 assert!(!public_key.operations.contains(&KeyOperation::Sign));
1497 assert!(public_key.operations.contains(&KeyOperation::Verify));
1498
1499 match &key.material {
1500 KeyMaterial::RSA { public, private } => {
1501 assert!(private.is_some());
1502 assert_eq!(public.n.len(), 256);
1503 assert_eq!(public.e.len(), 3);
1504
1505 match &public_key.material {
1506 KeyMaterial::RSA {
1507 public: guest_public,
1508 private: public_private,
1509 } => {
1510 assert!(public_private.is_none());
1511 assert_eq!(public.n, guest_public.n);
1512 assert_eq!(public.e, guest_public.e);
1513 }
1514 _ => panic!("Expected public key to be an RSA key"),
1515 }
1516 }
1517 _ => panic!("Expected private key to be an RSA key"),
1518 }
1519 }
1520
1521 #[test]
1522 fn test_rsa_pss_public_key_conversion() {
1523 let key = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1524 assert!(key.is_ok());
1525 let key = key.unwrap();
1526
1527 assert!(key.operations.contains(&KeyOperation::Sign));
1528 assert!(key.operations.contains(&KeyOperation::Verify));
1529
1530 let public_key = key.to_public().unwrap();
1531 assert!(!public_key.operations.contains(&KeyOperation::Sign));
1532 assert!(public_key.operations.contains(&KeyOperation::Verify));
1533
1534 match &key.material {
1535 KeyMaterial::RSA { public, private } => {
1536 assert!(private.is_some());
1537 assert_eq!(public.n.len(), 256);
1538 assert_eq!(public.e.len(), 3);
1539
1540 match &public_key.material {
1541 KeyMaterial::RSA {
1542 public: guest_public,
1543 private: public_private,
1544 } => {
1545 assert!(public_private.is_none());
1546 assert_eq!(public.n, guest_public.n);
1547 assert_eq!(public.e, guest_public.e);
1548 }
1549 _ => panic!("Expected public key to be an RSA key"),
1550 }
1551 }
1552 _ => panic!("Expected private key to be an RSA key"),
1553 }
1554 }
1555
1556 #[test]
1557 fn test_base64url_serialization() {
1558 let key = create_test_key();
1559 let json = serde_json::to_string(&key).unwrap();
1560
1561 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1563 let k_value = parsed["k"].as_str().unwrap();
1564
1565 assert!(!k_value.contains('='));
1567 assert!(!k_value.contains('+'));
1568 assert!(!k_value.contains('/'));
1569
1570 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1572 .decode(k_value)
1573 .unwrap();
1574
1575 if let KeyMaterial::OCT {
1576 secret: original_secret,
1577 } = &key.material
1578 {
1579 assert_eq!(decoded, *original_secret);
1580 } else {
1581 panic!("Expected both keys to be OCT variant");
1582 }
1583 }
1584
1585 #[test]
1586 fn test_backwards_compatibility_unpadded_base64url() {
1587 let unpadded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY","kid":"test-key-1"}"#;
1589
1590 let key: Key = serde_json::from_str(unpadded_json).unwrap();
1592 assert_eq!(key.algorithm, Algorithm::HS256);
1593 assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1594
1595 if let KeyMaterial::OCT { secret } = &key.material {
1596 assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1597 } else {
1598 panic!("Expected key to be OCT variant");
1599 }
1600 }
1601
1602 #[test]
1603 fn test_backwards_compatibility_padded_base64url() {
1604 let padded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY=","kid":"test-key-1"}"#;
1606
1607 let key: Key = serde_json::from_str(padded_json).unwrap();
1609 assert_eq!(key.algorithm, Algorithm::HS256);
1610 assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1611
1612 if let KeyMaterial::OCT { secret } = &key.material {
1613 assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1614 } else {
1615 panic!("Expected key to be OCT variant");
1616 }
1617 }
1618
1619 const JS_HS256_KEY: &str = r#"{"kty":"oct","alg":"HS256","k":"xm6xsSkfFqzPU3KfcbAcF2_h0OkStxQ_nNqVPYl0ync","kid":"js-test-key","key_ops":["sign","verify"],"guest":[],"guest_sub":[],"guest_pub":[]}"#;
1627
1628 const JS_HS256_TOKEN: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImpzLXRlc3Qta2V5In0.eyJyb290IjoibGl2ZSIsInB1dCI6WyJjYW1lcmExIl0sImdldCI6WyJjYW1lcmExIiwiY2FtZXJhMiJdLCJpYXQiOjE3NzUxNzY3NTR9.tHNQtHh_HCIKxXOexDCM7AkjqWzbULLZzjEckfOGRfY";
1630
1631 const JS_EDDSA_PRIVATE_KEY: &str = r#"{"kty":"OKP","alg":"EdDSA","crv":"Ed25519","x":"UiU9fT_SdBBpkFtJPRCY0gX1jK_Dr9syYLFuEz4QUM4","d":"lm-L_PV3ksuQ-KrFBgFMDJqAZC3_Z6Z5UC4ZQY5OoDQ","kid":"js-eddsa-key","key_ops":["sign","verify"],"guest":[],"guest_sub":[],"guest_pub":[]}"#;
1633
1634 const JS_EDDSA_PUBLIC_KEY: &str = r#"{"kty":"OKP","alg":"EdDSA","crv":"Ed25519","x":"UiU9fT_SdBBpkFtJPRCY0gX1jK_Dr9syYLFuEz4QUM4","kid":"js-eddsa-key","guest":[],"guest_sub":[],"guest_pub":[],"key_ops":["verify"]}"#;
1636
1637 const JS_EDDSA_TOKEN: &str = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImpzLWVkZHNhLWtleSJ9.eyJyb290Ijoic3RyZWFtIiwicHV0IjpbInZpZGVvIl0sImlhdCI6MTc3NTE3Njc1Nn0.l9rUMHjPSXWKSXRP3mmeMgTAywtqpdqQehhViWaPrKxax1Y2D9KRIYTixYz-b6PI-AoHQYusHWeeLu_HRw2cAg";
1639
1640 #[test]
1641 fn test_js_hs256_key_load() {
1642 let key = Key::from_str(JS_HS256_KEY).unwrap();
1643 assert_eq!(key.algorithm, Algorithm::HS256);
1644 assert_eq!(key.kid, Some(crate::KeyId::decode("js-test-key").unwrap()));
1645 }
1646
1647 #[test]
1648 fn test_js_hs256_token_verify() {
1649 let key = Key::from_str(JS_HS256_KEY).unwrap();
1650 let claims = key.verify(JS_HS256_TOKEN).unwrap();
1651 assert_eq!(claims.root, "live");
1652 assert_eq!(claims.publish, vec!["camera1"]);
1653 assert_eq!(claims.subscribe, vec!["camera1", "camera2"]);
1654 }
1655
1656 #[test]
1657 fn test_js_hs256_sign_and_roundtrip() {
1658 let key = Key::from_str(JS_HS256_KEY).unwrap();
1659 let claims = Claims {
1660 root: "rust-test".to_string(),
1661 publish: vec!["pub1".into()],
1662 subscribe: vec!["sub1".into()],
1663 ..Default::default()
1664 };
1665 let token = key.sign(&claims).unwrap();
1666 let verified = key.verify(&token).unwrap();
1667 assert_eq!(verified.root, "rust-test");
1668 assert_eq!(verified.publish, vec!["pub1"]);
1669 }
1670
1671 #[test]
1672 fn test_js_eddsa_key_load() {
1673 let private_key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1674 assert_eq!(private_key.algorithm, Algorithm::EdDSA);
1675 assert!(matches!(private_key.material, KeyMaterial::OKP { .. }));
1676
1677 let public_key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1678 assert_eq!(public_key.algorithm, Algorithm::EdDSA);
1679 }
1680
1681 #[test]
1682 fn test_js_eddsa_token_verify_with_private_key() {
1683 let key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1684 let claims = key.verify(JS_EDDSA_TOKEN).unwrap();
1685 assert_eq!(claims.root, "stream");
1686 assert_eq!(claims.publish, vec!["video"]);
1687 }
1688
1689 #[test]
1690 fn test_js_eddsa_token_verify_with_public_key() {
1691 let key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1692 let claims = key.verify(JS_EDDSA_TOKEN).unwrap();
1693 assert_eq!(claims.root, "stream");
1694 assert_eq!(claims.publish, vec!["video"]);
1695 }
1696
1697 #[test]
1698 fn test_js_token_wrong_key_fails() {
1699 let wrong_key = Key::generate(Algorithm::HS256, None).unwrap();
1701 let result = wrong_key.verify(JS_HS256_TOKEN);
1702 assert!(result.is_err());
1703 }
1704
1705 #[test]
1706 fn test_js_eddsa_token_wrong_key_fails() {
1707 let wrong_key = Key::from_str(JS_HS256_KEY).unwrap();
1709 let result = wrong_key.verify(JS_EDDSA_TOKEN);
1710 assert!(result.is_err());
1711 }
1712
1713 #[test]
1714 fn test_file_io_base64url() {
1715 let key = create_test_key();
1716 let temp_dir = std::env::temp_dir();
1717 let temp_path = temp_dir.join("test_jwk.key");
1718
1719 key.to_file(&temp_path).unwrap();
1721
1722 let contents = std::fs::read_to_string(&temp_path).unwrap();
1724
1725 assert!(!contents.contains('{'));
1727 assert!(!contents.contains('}'));
1728 assert!(!contents.contains('"'));
1729
1730 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1732 .decode(&contents)
1733 .unwrap();
1734 let json_str = String::from_utf8(decoded).unwrap();
1735 let _: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1736
1737 let loaded_key = Key::from_file(&temp_path).unwrap();
1739 assert_eq!(loaded_key.algorithm, key.algorithm);
1740 assert_eq!(loaded_key.operations, key.operations);
1741 assert_eq!(loaded_key.kid, key.kid);
1742
1743 if let (
1744 KeyMaterial::OCT {
1745 secret: original_secret,
1746 },
1747 KeyMaterial::OCT { secret: loaded_secret },
1748 ) = (&key.material, &loaded_key.material)
1749 {
1750 assert_eq!(loaded_secret, original_secret);
1751 } else {
1752 panic!("Expected both keys to be OCT variant");
1753 }
1754
1755 std::fs::remove_file(temp_path).ok();
1757 }
1758
1759 #[test]
1760 fn test_file_io_raw_json() {
1761 let key = create_test_key();
1762 let temp_dir = std::env::temp_dir();
1763 let temp_path = temp_dir.join("test_jwk_raw_json.key");
1764
1765 let json = serde_json::to_string(&key).unwrap();
1767 std::fs::write(&temp_path, &json).unwrap();
1768
1769 assert!(json.starts_with('{'));
1771
1772 let loaded_key = Key::from_file(&temp_path).unwrap();
1774 assert_eq!(loaded_key.algorithm, key.algorithm);
1775 assert_eq!(loaded_key.operations, key.operations);
1776 assert_eq!(loaded_key.kid, key.kid);
1777
1778 if let (
1779 KeyMaterial::OCT {
1780 secret: original_secret,
1781 },
1782 KeyMaterial::OCT { secret: loaded_secret },
1783 ) = (&key.material, &loaded_key.material)
1784 {
1785 assert_eq!(loaded_secret, original_secret);
1786 } else {
1787 panic!("Expected both keys to be OCT variant");
1788 }
1789
1790 std::fs::remove_file(temp_path).ok();
1792 }
1793}