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)]
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
220#[derive(Clone)]
227pub struct Key {
228 jwk: Jwk,
229
230 decode: OnceLock<DecodingKey>,
232 encode: OnceLock<EncodingKey>,
233}
234
235impl std::ops::Deref for Key {
240 type Target = Jwk;
241
242 fn deref(&self) -> &Self::Target {
243 &self.jwk
244 }
245}
246
247impl TryFrom<Jwk> for Key {
248 type Error = crate::Error;
249
250 fn try_from(jwk: Jwk) -> crate::Result<Self> {
251 jwk.import()
252 }
253}
254
255impl From<&Key> for Jwk {
256 fn from(key: &Key) -> Self {
257 key.export()
258 }
259}
260
261impl<'de> Deserialize<'de> for Key {
262 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
263 where
264 D: Deserializer<'de>,
265 {
266 let jwk = Jwk::deserialize(deserializer)?;
267 Key::try_from(jwk).map_err(serde::de::Error::custom)
268 }
269}
270
271impl Serialize for Key {
272 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
273 where
274 S: Serializer,
275 {
276 Serialize::serialize(&Jwk::from(self), serializer)
277 }
278}
279
280impl fmt::Debug for Key {
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 f.debug_struct("Key")
283 .field("algorithm", &self.algorithm)
284 .field("operations", &self.operations)
285 .field("kid", &self.kid)
286 .field("scope", &self.scope)
287 .finish()
288 }
289}
290
291impl Key {
292 pub fn export(&self) -> Jwk {
297 self.jwk.clone()
298 }
299
300 #[allow(clippy::should_implement_trait)]
302 pub fn from_str(s: &str) -> crate::Result<Self> {
303 let s = s.trim();
304 if s.starts_with('{') {
305 Ok(serde_json::from_str(s)?)
306 } else {
307 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(s)?;
308 let json = String::from_utf8(decoded)?;
309 Ok(serde_json::from_str(&json)?)
310 }
311 }
312
313 pub fn from_file<P: AsRef<StdPath>>(path: P) -> crate::Result<Self> {
315 let contents = std::fs::read_to_string(&path)?;
316 Self::from_str(&contents)
317 }
318
319 #[cfg(feature = "tokio")]
321 pub async fn from_file_async<P: AsRef<StdPath>>(path: P) -> crate::Result<Self> {
322 let contents = tokio::fs::read_to_string(path).await?;
323 Self::from_str(&contents)
324 }
325
326 pub fn to_str(&self) -> crate::Result<String> {
328 let json = serde_json::to_string(self)?;
329 Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(json.as_bytes()))
330 }
331
332 pub fn to_file<P: AsRef<StdPath>>(&self, path: P) -> crate::Result<()> {
337 let encoded = self.to_str()?;
338 crate::fs::write(path.as_ref(), &encoded, self.is_private())?;
339 Ok(())
340 }
341
342 pub fn to_public(&self) -> crate::Result<Self> {
347 if !self.operations.contains(&KeyOperation::Verify) {
348 return Err(KeyError::VerifyUnsupported.into());
349 }
350
351 let material = match self.material {
352 KeyMaterial::RSA { ref public, .. } => KeyMaterial::RSA {
353 public: public.clone(),
354 private: None,
355 },
356 KeyMaterial::EC {
357 ref x,
358 ref y,
359 ref curve,
360 ..
361 } => KeyMaterial::EC {
362 x: x.clone(),
363 y: y.clone(),
364 curve: curve.clone(),
365 d: None,
366 },
367 KeyMaterial::OCT { .. } => return Err(KeyError::NoPublicKey.into()),
368 KeyMaterial::OKP { ref x, ref curve, .. } => KeyMaterial::OKP {
369 x: x.clone(),
370 curve: curve.clone(),
371 d: None,
372 },
373 };
374
375 Ok(Self {
376 jwk: Jwk {
377 algorithm: self.algorithm,
378 operations: [KeyOperation::Verify].into(),
379 material,
380 kid: self.kid.clone(),
381 scope: self.scope.clone(),
382 },
383 decode: Default::default(),
384 encode: Default::default(),
385 })
386 }
387
388 pub(crate) fn is_private(&self) -> bool {
394 match &self.material {
395 KeyMaterial::OCT { .. } => true,
396 KeyMaterial::EC { d, .. } | KeyMaterial::OKP { d, .. } => d.is_some(),
397 KeyMaterial::RSA { private, .. } => private.is_some(),
398 }
399 }
400
401 fn to_decoding_key(&self) -> crate::Result<&DecodingKey> {
402 if let Some(key) = self.decode.get() {
403 return Ok(key);
404 }
405
406 let decoding_key = match self.material {
407 KeyMaterial::OCT { ref secret } => match self.algorithm {
408 Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => DecodingKey::from_secret(secret),
409 _ => return Err(KeyError::InvalidAlgorithm.into()),
410 },
411 KeyMaterial::EC {
412 ref curve,
413 ref x,
414 ref y,
415 ..
416 } => match curve {
417 EllipticCurve::P256 => {
418 if self.algorithm != Algorithm::ES256 {
419 return Err(KeyError::InvalidAlgorithmForCurve("P-256").into());
420 }
421 if x.len() != 32 || y.len() != 32 {
422 return Err(KeyError::InvalidCoordinateLength("P-256").into());
423 }
424
425 DecodingKey::from_ec_components(
426 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
427 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(y).as_ref(),
428 )?
429 }
430 EllipticCurve::P384 => {
431 if self.algorithm != Algorithm::ES384 {
432 return Err(KeyError::InvalidAlgorithmForCurve("P-384").into());
433 }
434 if x.len() != 48 || y.len() != 48 {
435 return Err(KeyError::InvalidCoordinateLength("P-384").into());
436 }
437
438 DecodingKey::from_ec_components(
439 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
440 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(y).as_ref(),
441 )?
442 }
443 _ => return Err(KeyError::InvalidCurve("EC").into()),
444 },
445 KeyMaterial::OKP { ref curve, ref x, .. } => match curve {
446 EllipticCurve::Ed25519 => {
447 if self.algorithm != Algorithm::EdDSA {
448 return Err(KeyError::InvalidAlgorithmForCurve("Ed25519").into());
449 }
450
451 DecodingKey::from_ed_components(
452 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
453 )?
454 }
455 _ => return Err(KeyError::InvalidCurve("OKP").into()),
456 },
457 KeyMaterial::RSA { ref public, .. } => {
458 DecodingKey::from_rsa_raw_components(public.n.as_ref(), public.e.as_ref())
459 }
460 };
461
462 Ok(self.decode.get_or_init(|| decoding_key))
463 }
464
465 fn to_encoding_key(&self) -> crate::Result<&EncodingKey> {
466 if let Some(key) = self.encode.get() {
467 return Ok(key);
468 }
469
470 let encoding_key = match self.material {
471 KeyMaterial::OCT { ref secret } => match self.algorithm {
472 Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => EncodingKey::from_secret(secret),
473 _ => return Err(KeyError::InvalidAlgorithm.into()),
474 },
475 KeyMaterial::EC { ref curve, ref d, .. } => {
476 let d = d.as_ref().ok_or(KeyError::MissingPrivateKey)?;
477
478 match curve {
479 EllipticCurve::P256 => {
480 let secret_key = SecretKey::<p256::NistP256>::from_slice(d)?;
481 let doc = secret_key.to_pkcs8_der()?;
482 EncodingKey::from_ec_der(doc.as_bytes())
483 }
484 EllipticCurve::P384 => {
485 let secret_key = SecretKey::<p384::NistP384>::from_slice(d)?;
486 let doc = secret_key.to_pkcs8_der()?;
487 EncodingKey::from_ec_der(doc.as_bytes())
488 }
489 _ => return Err(KeyError::InvalidCurve("EC").into()),
490 }
491 }
492 KeyMaterial::OKP {
493 ref curve,
494 ref d,
495 ref x,
496 } => {
497 let d = d.as_ref().ok_or(KeyError::MissingPrivateKey)?;
498
499 let key_pair =
500 aws_lc_rs::signature::Ed25519KeyPair::from_seed_and_public_key(d.as_slice(), x.as_slice())?;
501
502 match curve {
503 EllipticCurve::Ed25519 => EncodingKey::from_ed_der(key_pair.to_pkcs8()?.as_ref()),
504 _ => return Err(KeyError::InvalidCurve("OKP").into()),
505 }
506 }
507 KeyMaterial::RSA {
508 ref public,
509 ref private,
510 } => {
511 let n = BigUint::from_bytes_be(&public.n);
512 let e = BigUint::from_bytes_be(&public.e);
513 let private = private.as_ref().ok_or(KeyError::MissingPrivateKey)?;
514 let d = BigUint::from_bytes_be(&private.d);
515 let p = BigUint::from_bytes_be(&private.p);
516 let q = BigUint::from_bytes_be(&private.q);
517
518 let rsa = rsa::RsaPrivateKey::from_components(n, e, d, vec![p, q]);
519 let pem = rsa?.to_pkcs1_pem(rsa::pkcs1::LineEnding::LF);
520
521 EncodingKey::from_rsa_pem(pem?.as_bytes())?
522 }
523 };
524
525 Ok(self.encode.get_or_init(|| encoding_key))
526 }
527
528 pub fn verify(&self, token: &str) -> crate::Result<Claims> {
535 if !self.operations.contains(&KeyOperation::Verify) {
536 return Err(KeyError::VerifyUnsupported.into());
537 }
538
539 let decode = self.to_decoding_key()?;
540
541 let mut validation = jsonwebtoken::Validation::new(self.algorithm.into());
542 validation.required_spec_claims = Default::default(); validation.validate_exp = false; let token = jsonwebtoken::decode::<Claims>(token, decode, &validation)?;
546
547 validate_times(&token.claims, std::time::SystemTime::now())?;
548 token.claims.validate()?;
549 self.validate_scope(&token.claims)?;
550
551 Ok(token.claims)
552 }
553
554 pub fn sign(&self, payload: &Claims) -> crate::Result<String> {
556 if !self.operations.contains(&KeyOperation::Sign) {
557 return Err(KeyError::SignUnsupported.into());
558 }
559
560 payload.validate()?;
561 self.validate_scope(payload)?;
562
563 let encode = self.to_encoding_key()?;
564
565 let mut header = Header::new(self.algorithm.into());
566 header.kid = self.kid.as_ref().map(|k| k.to_string());
567 let token = jsonwebtoken::encode(&header, &payload, encode)?;
568 Ok(token)
569 }
570
571 pub fn generate(algorithm: Algorithm, id: Option<crate::KeyId>) -> crate::Result<Self> {
573 generate(algorithm, id)
574 }
575
576 pub fn with_scope(mut self, scope: crate::Scope) -> crate::Result<Self> {
581 scope.validate()?;
582 self.jwk.scope = Some(scope);
583 Ok(self)
584 }
585
586 pub fn with_operations(mut self, operations: impl IntoIterator<Item = KeyOperation>) -> Self {
588 self.jwk.operations = operations.into_iter().collect();
589 self
590 }
591
592 fn validate_scope(&self, claims: &Claims) -> crate::Result<()> {
593 if let Some(scope) = &self.scope {
594 scope.validate()?;
595 if !scope.allows(claims) {
596 return Err(crate::Error::ScopeExceeded);
597 }
598 }
599 Ok(())
600 }
601}
602
603fn validate_times(claims: &Claims, now: std::time::SystemTime) -> crate::Result<()> {
605 if claims.expires.is_some_and(|exp| exp <= now) {
606 return Err(crate::Error::TokenExpired);
607 }
608 if claims.not_before.is_some_and(|nbf| nbf > now) {
609 return Err(crate::Error::TokenNotYetValid);
610 }
611 Ok(())
612}
613
614fn serialize_base64url<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
616where
617 S: Serializer,
618{
619 let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes);
620 serializer.serialize_str(&encoded)
621}
622
623fn serialize_base64url_optional<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
624where
625 S: Serializer,
626{
627 match bytes {
628 Some(b) => serialize_base64url(b, serializer),
629 None => serializer.serialize_none(),
630 }
631}
632
633fn deserialize_base64url<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
635where
636 D: Deserializer<'de>,
637{
638 let s = String::deserialize(deserializer)?;
639
640 base64::engine::general_purpose::URL_SAFE_NO_PAD
642 .decode(&s)
643 .or_else(|_| {
644 base64::engine::general_purpose::URL_SAFE.decode(&s)
646 })
647 .map_err(serde::de::Error::custom)
648}
649
650fn deserialize_base64url_optional<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
651where
652 D: Deserializer<'de>,
653{
654 let s: Option<String> = Option::deserialize(deserializer)?;
655 match s {
656 Some(s) => {
657 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
658 .decode(&s)
659 .or_else(|_| base64::engine::general_purpose::URL_SAFE.decode(&s))
660 .map_err(serde::de::Error::custom)?;
661 Ok(Some(decoded))
662 }
663 None => Ok(None),
664 }
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670
671 fn patterns(texts: &[&str]) -> crate::Patterns {
672 texts.iter().map(|text| text.parse().unwrap()).collect()
673 }
674 use std::time::{Duration, SystemTime};
675
676 fn create_test_key() -> Key {
677 let mut jwk = Jwk::new(
678 Algorithm::HS256,
679 KeyMaterial::OCT {
680 secret: b"test-secret-that-is-long-enough-for-hmac-sha256".to_vec(),
681 },
682 );
683 jwk.kid = Some(crate::KeyId::decode("test-key-1").unwrap());
684 jwk.import().unwrap()
685 }
686
687 fn create_test_claims() -> Claims {
688 Claims {
689 root: "test-path".to_string(),
690 publish: patterns(&["test-pub/**"]),
691 subscribe: patterns(&["test-sub/**"]),
692 expires: Some(SystemTime::now() + Duration::from_secs(3600)),
693 issued: Some(SystemTime::now()),
694 not_before: None,
695 }
696 }
697
698 #[test]
699 fn test_key_from_str_valid() {
700 let key = create_test_key();
701 let json = key.to_str().unwrap();
702 let loaded_key = Key::from_str(&json).unwrap();
703
704 assert_eq!(loaded_key.algorithm, key.algorithm);
705 assert_eq!(loaded_key.operations, key.operations);
706 match (&loaded_key.material, &key.material) {
707 (KeyMaterial::OCT { secret: loaded_secret }, KeyMaterial::OCT { secret }) => {
708 assert_eq!(loaded_secret, secret);
709 }
710 _ => panic!("Expected OCT key"),
711 }
712 assert_eq!(loaded_key.kid, key.kid);
713 }
714
715 #[test]
717 fn test_key_oct_without_kty_is_refused() {
718 let json = r#"{"alg":"HS256","key_ops":["sign","verify"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#;
719 assert!(Key::from_str(json).is_err());
720 }
721
722 #[test]
723 fn test_key_without_key_ops_defaults_sign_verify() {
724 let json = r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","kid":"no-ops"}"#;
725 let key = Key::from_str(json).unwrap();
726
727 assert_eq!(key.operations, sign_verify());
728
729 let claims = create_test_claims();
730 let token = key.sign(&claims).unwrap();
731 let verified = key.verify(&token).unwrap();
732 assert_eq!(verified.root, claims.root);
733 }
734
735 #[test]
736 fn test_key_without_key_ops_round_trip() {
737 let json = r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#;
738 let key = Key::from_str(json).unwrap();
739
740 let serialized = serde_json::to_string(&key).unwrap();
741 let parsed: serde_json::Value = serde_json::from_str(&serialized).unwrap();
742 let ops = parsed["key_ops"].as_array().unwrap();
743 assert_eq!(ops.len(), 2);
744
745 let reloaded = Key::from_str(&serialized).unwrap();
746 assert_eq!(reloaded.operations, sign_verify());
747 assert_eq!(reloaded.algorithm, key.algorithm);
748 }
749
750 #[test]
752 fn test_key_oct_secret_required_and_min_length() {
753 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256"}"#).is_err());
755 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256","k":""}"#).is_err());
756
757 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256","k":"AAAAAAAAAAAAAAAAAAAAAA"}"#).is_err());
759
760 let short = Jwk::new(Algorithm::HS256, KeyMaterial::OCT { secret: vec![0; 16] });
762 assert!(short.import().is_err());
763
764 let key =
766 Key::from_str(r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#).unwrap();
767 let KeyMaterial::OCT { ref secret } = key.material else {
768 panic!("Expected OCT key");
769 };
770 assert_eq!(secret.len(), 32);
771 }
772
773 #[test]
774 fn test_key_without_key_ops_to_public() {
775 let json = r#"{"kty":"OKP","alg":"EdDSA","crv":"Ed25519","x":"UiU9fT_SdBBpkFtJPRCY0gX1jK_Dr9syYLFuEz4QUM4","d":"lm-L_PV3ksuQ-KrFBgFMDJqAZC3_Z6Z5UC4ZQY5OoDQ","kid":"defaulted"}"#;
776 let key = Key::from_str(json).unwrap();
777 assert_eq!(key.operations, sign_verify());
778
779 let public = key.to_public().unwrap();
780 assert_eq!(public.operations, [KeyOperation::Verify].into());
781 }
782
783 #[test]
784 fn test_key_from_str_invalid_json() {
785 let result = Key::from_str("invalid json");
786 assert!(result.is_err());
787 }
788
789 #[test]
790 fn test_key_to_str() {
791 let key = create_test_key();
792 let encoded = key.to_str().unwrap();
793
794 assert!(!encoded.contains('{'));
796
797 let loaded = Key::from_str(&encoded).unwrap();
799 assert_eq!(loaded.algorithm, Algorithm::HS256);
800 assert_eq!(loaded.kid, key.kid);
801 assert!(loaded.operations.contains(&KeyOperation::Sign));
802 assert!(loaded.operations.contains(&KeyOperation::Verify));
803 }
804
805 #[test]
806 fn test_key_sign_success() {
807 let key = create_test_key();
808 let claims = create_test_claims();
809 let token = key.sign(&claims).unwrap();
810
811 assert!(!token.is_empty());
812 assert_eq!(token.matches('.').count(), 2); }
814
815 #[test]
816 fn test_key_scope_enforced_when_signing_and_verifying() {
817 let unrestricted = create_test_key();
818 let scoped = unrestricted
819 .clone()
820 .with_scope(crate::Scope {
821 root: "test-path".into(),
822 publish: patterns(&["allowed/**"]),
823 subscribe: patterns(&[]),
824 })
825 .unwrap();
826 let allowed = Claims {
827 root: "test-path".into(),
828 publish: patterns(&["allowed/room/**"]),
829 ..Default::default()
830 };
831 let denied = Claims {
832 root: "test-path".into(),
833 publish: patterns(&["other/**"]),
834 ..Default::default()
835 };
836
837 assert!(scoped.sign(&allowed).is_ok());
838 assert!(matches!(scoped.sign(&denied), Err(crate::Error::ScopeExceeded)));
839
840 let forged = unrestricted.sign(&denied).unwrap();
841 assert!(matches!(scoped.verify(&forged), Err(crate::Error::ScopeExceeded)));
842 }
843
844 #[test]
847 fn test_key_derived_material_never_stale() {
848 let claims = Claims {
849 root: "test-path".into(),
850 publish: patterns(&["test-pub/**"]),
851 ..Default::default()
852 };
853
854 let key = create_test_key();
856 let token = key.sign(&claims).unwrap();
857 assert!(key.encode.get().is_some());
858
859 let mut jwk = Jwk::from(&key);
862 jwk.algorithm = Algorithm::HS384;
863 let derived = Key::try_from(jwk).unwrap();
864 assert!(derived.encode.get().is_none());
865
866 let derived_token = derived.sign(&claims).unwrap();
867 assert_ne!(token, derived_token);
868
869 let cold = Key::from_str(&derived.to_str().unwrap()).unwrap();
872 assert_eq!(derived_token, cold.sign(&claims).unwrap());
873 assert!(cold.verify(&derived_token).is_ok());
874 assert!(key.verify(&derived_token).is_err());
875 }
876
877 #[test]
880 fn test_key_scope_requires_validation() {
881 let key = create_test_key();
882 assert!(key.scope.is_none());
883
884 let useless = crate::Scope::default();
885 assert!(matches!(
886 key.clone().with_scope(useless.clone()),
887 Err(crate::Error::UselessScope)
888 ));
889
890 let mut jwk = Jwk::from(&key);
891 jwk.scope = Some(useless);
892 assert!(matches!(Key::try_from(jwk), Err(crate::Error::UselessScope)));
893
894 let json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","scope":{}}"#;
895 assert!(Key::from_str(json).is_err());
896 }
897
898 #[test]
899 fn test_key_sign_no_permission() {
900 let key = create_test_key().with_operations([KeyOperation::Verify]);
901 let claims = create_test_claims();
902
903 let result = key.sign(&claims);
904 assert!(result.is_err());
905 assert!(result.unwrap_err().to_string().contains("key does not support signing"));
906 }
907
908 #[test]
909 fn test_key_sign_invalid_claims() {
910 let key = create_test_key();
911 let invalid_claims = Claims {
912 root: "test-path".to_string(),
913 publish: patterns(&[]),
914 subscribe: patterns(&[]),
915 expires: None,
916 issued: None,
917 not_before: None,
918 };
919
920 let result = key.sign(&invalid_claims);
921 assert!(result.is_err());
922 assert!(
923 result
924 .unwrap_err()
925 .to_string()
926 .contains("no publish or subscribe allowed; token is useless")
927 );
928 }
929
930 #[test]
931 fn test_key_verify_success() {
932 let key = create_test_key();
933 let claims = create_test_claims();
934 let token = key.sign(&claims).unwrap();
935
936 let verified_claims = key.verify(&token).unwrap();
937 assert_eq!(verified_claims.root, claims.root);
938 assert_eq!(verified_claims.publish, claims.publish);
939 assert_eq!(verified_claims.subscribe, claims.subscribe);
940 }
941
942 #[test]
943 fn test_key_verify_no_permission() {
944 let key = create_test_key().with_operations([KeyOperation::Sign]);
945
946 let result = key.verify("some.jwt.token");
947 assert!(result.is_err());
948 assert!(
949 result
950 .unwrap_err()
951 .to_string()
952 .contains("key does not support verification")
953 );
954 }
955
956 #[test]
957 fn test_key_verify_invalid_token() {
958 let key = create_test_key();
959 let result = key.verify("invalid-token");
960 assert!(result.is_err());
961 }
962
963 #[test]
964 fn test_key_verify_path_mismatch() {
965 let key = create_test_key();
966 let claims = create_test_claims();
967 let token = key.sign(&claims).unwrap();
968
969 let result = key.verify(&token);
971 assert!(result.is_ok());
972 }
973
974 fn sign_raw(key: &Key, payload: serde_json::Value) -> String {
976 let header = Header::new(key.algorithm.into());
977 jsonwebtoken::encode(&header, &payload, key.to_encoding_key().unwrap()).unwrap()
978 }
979
980 #[test]
984 fn test_key_verify_only_registered_claims() {
985 let key = create_test_key();
986 let now = SystemTime::now()
987 .duration_since(SystemTime::UNIX_EPOCH)
988 .unwrap()
989 .as_secs();
990
991 let token = sign_raw(
992 &key,
993 serde_json::json!({"root": "room", "publish": ["**"], "iss": "api", "sub": "alice", "jti": "1", "iat": now}),
994 );
995 assert_eq!(key.verify(&token).unwrap().root, "room");
996
997 for (claim, payload) in [
998 ("rooot", serde_json::json!({"rooot": "room/123", "publish": ["**"]})),
999 (
1000 "user_id",
1001 serde_json::json!({"root": "room", "publish": ["**"], "user_id": 7}),
1002 ),
1003 (
1004 "cluster",
1005 serde_json::json!({"root": "room", "put": [""], "cluster": true}),
1006 ),
1007 ] {
1008 let err = key.verify(&sign_raw(&key, payload)).unwrap_err().to_string();
1009 assert!(err.contains(&format!("`{claim}`")), "{claim}: {err}");
1010 }
1011
1012 let token = sign_raw(
1014 &key,
1015 serde_json::json!({"root": "room", "publish": ["**"], "aud": "relay"}),
1016 );
1017 assert!(key.verify(&token).is_err());
1018 }
1019
1020 #[test]
1021 fn test_key_verify_enforces_not_before() {
1022 let key = create_test_key();
1023 let at = |offset: i64| {
1024 let now = SystemTime::now()
1025 .duration_since(SystemTime::UNIX_EPOCH)
1026 .unwrap()
1027 .as_secs() as i64;
1028 sign_raw(
1029 &key,
1030 serde_json::json!({"root": "room", "publish": ["**"], "nbf": now + offset}),
1031 )
1032 };
1033 assert!(key.verify(&at(-60)).is_ok());
1034 assert!(matches!(key.verify(&at(3600)), Err(crate::Error::TokenNotYetValid)));
1035 }
1036
1037 #[test]
1039 fn validate_times_at_the_boundary() {
1040 let now = SystemTime::UNIX_EPOCH + Duration::from_secs(1_000);
1041 let second = Duration::from_secs(1);
1042
1043 let at = |expires: Option<SystemTime>, not_before: Option<SystemTime>| {
1044 let mut claims = create_test_claims();
1045 claims.expires = expires;
1046 claims.not_before = not_before;
1047 validate_times(&claims, now)
1048 };
1049
1050 assert!(at(Some(now + second), None).is_ok());
1051 assert!(matches!(at(Some(now), None), Err(crate::Error::TokenExpired)));
1052 assert!(matches!(at(Some(now - second), None), Err(crate::Error::TokenExpired)));
1053
1054 assert!(at(None, Some(now)).is_ok());
1055 assert!(at(None, Some(now - second)).is_ok());
1056 assert!(matches!(
1057 at(None, Some(now + second)),
1058 Err(crate::Error::TokenNotYetValid)
1059 ));
1060 }
1061
1062 #[test]
1063 fn test_key_verify_expired_token() {
1064 let key = create_test_key();
1065 let mut claims = create_test_claims();
1066 claims.expires = Some(SystemTime::now() - Duration::from_secs(3600)); let token = key.sign(&claims).unwrap();
1068
1069 let result = key.verify(&token);
1070 assert!(result.is_err());
1071 }
1072
1073 #[test]
1074 fn test_key_verify_token_without_exp() {
1075 let key = create_test_key();
1076 let claims = Claims {
1077 root: "test-path".to_string(),
1078 publish: patterns(&["**"]),
1079 subscribe: patterns(&["**"]),
1080 expires: None,
1081 issued: None,
1082 not_before: None,
1083 };
1084 let token = key.sign(&claims).unwrap();
1085
1086 let verified_claims = key.verify(&token).unwrap();
1087 assert_eq!(verified_claims.root, claims.root);
1088 assert_eq!(verified_claims.publish, claims.publish);
1089 assert_eq!(verified_claims.subscribe, claims.subscribe);
1090 assert_eq!(verified_claims.expires, None);
1091 }
1092
1093 #[test]
1094 fn test_key_round_trip() {
1095 let key = create_test_key();
1096 let original_claims = Claims {
1097 root: "test-path".to_string(),
1098 publish: patterns(&["test-pub/**"]),
1099 subscribe: patterns(&["test-sub/**"]),
1100 expires: Some(SystemTime::now() + Duration::from_secs(3600)),
1101 issued: Some(SystemTime::now()),
1102 not_before: None,
1103 };
1104
1105 let token = key.sign(&original_claims).unwrap();
1106 let verified_claims = key.verify(&token).unwrap();
1107
1108 assert_eq!(verified_claims.root, original_claims.root);
1109 assert_eq!(verified_claims.publish, original_claims.publish);
1110 assert_eq!(verified_claims.subscribe, original_claims.subscribe);
1111 }
1112
1113 #[test]
1114 fn test_key_generate_hs256() {
1115 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1116 assert!(key.is_ok());
1117 let key = key.unwrap();
1118
1119 assert_eq!(key.algorithm, Algorithm::HS256);
1120 assert_eq!(key.kid, Some(crate::KeyId::decode("test-id").unwrap()));
1121 assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
1122
1123 match &key.material {
1124 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 32),
1125 _ => panic!("Expected OCT key"),
1126 }
1127 }
1128
1129 #[test]
1130 fn test_key_generate_hs384() {
1131 let key = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1132 assert!(key.is_ok());
1133 let key = key.unwrap();
1134
1135 assert_eq!(key.algorithm, Algorithm::HS384);
1136
1137 match &key.material {
1138 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 48),
1139 _ => panic!("Expected OCT key"),
1140 }
1141 }
1142
1143 #[test]
1144 fn test_key_generate_hs512() {
1145 let key = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
1146 assert!(key.is_ok());
1147 let key = key.unwrap();
1148
1149 assert_eq!(key.algorithm, Algorithm::HS512);
1150
1151 match &key.material {
1152 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 64),
1153 _ => panic!("Expected OCT key"),
1154 }
1155 }
1156
1157 #[test]
1158 fn test_key_generate_rs512() {
1159 let key = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
1160 assert!(key.is_ok());
1161 let key = key.unwrap();
1162
1163 assert_eq!(key.algorithm, Algorithm::RS512);
1164 assert!(matches!(key.material, KeyMaterial::RSA { .. }));
1165 match &key.material {
1166 KeyMaterial::RSA { public, private } => {
1167 assert!(private.is_some());
1168 assert_eq!(public.n.len(), 256);
1169 assert_eq!(public.e.len(), 3);
1170 }
1171 _ => panic!("Expected RSA key"),
1172 }
1173 }
1174
1175 #[test]
1176 fn test_key_generate_es256() {
1177 let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1178 assert!(key.is_ok());
1179 let key = key.unwrap();
1180
1181 assert_eq!(key.algorithm, Algorithm::ES256);
1182 assert!(matches!(key.material, KeyMaterial::EC { .. }))
1183 }
1184
1185 #[test]
1186 fn test_key_generate_ps512() {
1187 let key = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
1188 assert!(key.is_ok());
1189 let key = key.unwrap();
1190
1191 assert_eq!(key.algorithm, Algorithm::PS512);
1192 assert!(matches!(key.material, KeyMaterial::RSA { .. }));
1193 }
1194
1195 #[test]
1196 fn test_key_generate_eddsa() {
1197 let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1198 assert!(key.is_ok());
1199 let key = key.unwrap();
1200
1201 assert_eq!(key.algorithm, Algorithm::EdDSA);
1202 assert!(matches!(key.material, KeyMaterial::OKP { .. }));
1203 }
1204
1205 #[test]
1206 fn test_key_generate_without_id() {
1207 let key = Key::generate(Algorithm::HS256, None);
1208 assert!(key.is_ok());
1209 let key = key.unwrap();
1210
1211 assert_eq!(key.algorithm, Algorithm::HS256);
1212 assert_eq!(key.kid, None);
1213 assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
1214 }
1215
1216 #[test]
1217 fn test_public_key_conversion_hmac() {
1218 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()))
1219 .expect("HMAC key generation failed");
1220
1221 assert!(key.to_public().is_err());
1222 }
1223
1224 #[test]
1225 fn test_public_key_conversion_rsa() {
1226 let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1227 assert!(key.is_ok());
1228 let key = key.unwrap();
1229
1230 let public_key = key.to_public().unwrap();
1231 assert_eq!(key.kid, public_key.kid);
1232 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1233 assert!(public_key.encode.get().is_none());
1234 assert!(public_key.decode.get().is_none());
1235 assert!(matches!(public_key.material, KeyMaterial::RSA { .. }));
1236
1237 if let KeyMaterial::RSA { public, private } = &public_key.material {
1238 assert!(private.is_none());
1239
1240 if let KeyMaterial::RSA { public: src_public, .. } = &key.material {
1241 assert_eq!(public.e, src_public.e);
1242 assert_eq!(public.n, src_public.n);
1243 } else {
1244 unreachable!("Expected RSA key")
1245 }
1246 } else {
1247 unreachable!("Expected RSA key");
1248 }
1249 }
1250
1251 #[test]
1252 fn test_public_key_conversion_es() {
1253 let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1254 assert!(key.is_ok());
1255 let key = key.unwrap();
1256
1257 let public_key = key.to_public().unwrap();
1258 assert_eq!(key.kid, public_key.kid);
1259 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1260 assert!(public_key.encode.get().is_none());
1261 assert!(public_key.decode.get().is_none());
1262 assert!(matches!(public_key.material, KeyMaterial::EC { .. }));
1263
1264 if let KeyMaterial::EC { x, y, d, curve } = &public_key.material {
1265 assert!(d.is_none());
1266
1267 if let KeyMaterial::EC {
1268 x: src_x,
1269 y: src_y,
1270 curve: src_curve,
1271 ..
1272 } = &key.material
1273 {
1274 assert_eq!(x, src_x);
1275 assert_eq!(y, src_y);
1276 assert_eq!(curve, src_curve);
1277 } else {
1278 unreachable!("Expected EC key")
1279 }
1280 } else {
1281 unreachable!("Expected EC key");
1282 }
1283 }
1284
1285 #[test]
1286 fn test_public_key_conversion_ed() {
1287 let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1288 assert!(key.is_ok());
1289 let key = key.unwrap();
1290
1291 let public_key = key.to_public().unwrap();
1292 assert_eq!(key.kid, public_key.kid);
1293 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1294 assert!(public_key.encode.get().is_none());
1295 assert!(public_key.decode.get().is_none());
1296 assert!(matches!(public_key.material, KeyMaterial::OKP { .. }));
1297
1298 if let KeyMaterial::OKP { x, d, curve } = &public_key.material {
1299 assert!(d.is_none());
1300
1301 if let KeyMaterial::OKP {
1302 x: src_x,
1303 curve: src_curve,
1304 ..
1305 } = &key.material
1306 {
1307 assert_eq!(x, src_x);
1308 assert_eq!(curve, src_curve);
1309 } else {
1310 unreachable!("Expected OKP key")
1311 }
1312 } else {
1313 unreachable!("Expected OKP key");
1314 }
1315 }
1316
1317 #[test]
1318 fn test_key_generate_sign_verify_cycle() {
1319 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1320 assert!(key.is_ok());
1321 let key = key.unwrap();
1322
1323 let claims = create_test_claims();
1324
1325 let token = key.sign(&claims).unwrap();
1326 let verified_claims = key.verify(&token).unwrap();
1327
1328 assert_eq!(verified_claims.root, claims.root);
1329 assert_eq!(verified_claims.publish, claims.publish);
1330 assert_eq!(verified_claims.subscribe, claims.subscribe);
1331 }
1332
1333 #[test]
1334 fn test_key_debug_no_secret() {
1335 let key = create_test_key();
1336 let debug_str = format!("{key:?}");
1337
1338 assert!(debug_str.contains("algorithm: HS256"));
1339 assert!(debug_str.contains("operations"));
1340 assert!(debug_str.contains("kid: Some(KeyId(\"test-key-1\"))"));
1341 assert!(!debug_str.contains("secret")); }
1343
1344 #[test]
1345 fn test_key_operations_enum() {
1346 let sign_op = KeyOperation::Sign;
1347 let verify_op = KeyOperation::Verify;
1348 let decrypt_op = KeyOperation::Decrypt;
1349 let encrypt_op = KeyOperation::Encrypt;
1350
1351 assert_eq!(sign_op, KeyOperation::Sign);
1352 assert_eq!(verify_op, KeyOperation::Verify);
1353 assert_eq!(decrypt_op, KeyOperation::Decrypt);
1354 assert_eq!(encrypt_op, KeyOperation::Encrypt);
1355
1356 assert_ne!(sign_op, verify_op);
1357 assert_ne!(decrypt_op, encrypt_op);
1358 }
1359
1360 #[test]
1361 fn test_key_operations_serde() {
1362 let operations = [KeyOperation::Sign, KeyOperation::Verify];
1363 let json = serde_json::to_string(&operations).unwrap();
1364 assert!(json.contains("\"sign\""));
1365 assert!(json.contains("\"verify\""));
1366
1367 let deserialized: Vec<KeyOperation> = serde_json::from_str(&json).unwrap();
1368 assert_eq!(deserialized, operations);
1369 }
1370
1371 #[test]
1372 fn test_key_serde() {
1373 let key = create_test_key();
1374 let json = serde_json::to_string(&key).unwrap();
1375 let deserialized: Key = serde_json::from_str(&json).unwrap();
1376
1377 assert_eq!(deserialized.algorithm, key.algorithm);
1378 assert_eq!(deserialized.operations, key.operations);
1379 assert_eq!(deserialized.kid, key.kid);
1380
1381 if let (
1382 KeyMaterial::OCT {
1383 secret: original_secret,
1384 },
1385 KeyMaterial::OCT {
1386 secret: deserialized_secret,
1387 },
1388 ) = (&key.material, &deserialized.material)
1389 {
1390 assert_eq!(deserialized_secret, original_secret);
1391 } else {
1392 panic!("Expected both keys to be OCT variant");
1393 }
1394 }
1395
1396 #[test]
1397 fn test_key_clone() {
1398 let key = create_test_key();
1399 let cloned = key.clone();
1400
1401 assert_eq!(cloned.algorithm, key.algorithm);
1402 assert_eq!(cloned.operations, key.operations);
1403 assert_eq!(cloned.kid, key.kid);
1404
1405 if let (
1406 KeyMaterial::OCT {
1407 secret: original_secret,
1408 },
1409 KeyMaterial::OCT { secret: cloned_secret },
1410 ) = (&key.material, &cloned.material)
1411 {
1412 assert_eq!(cloned_secret, original_secret);
1413 } else {
1414 panic!("Expected both keys to be OCT variant");
1415 }
1416 }
1417
1418 #[test]
1419 fn test_hmac_algorithms() {
1420 let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1421 let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1422 let key_512 = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
1423
1424 let claims = create_test_claims();
1425
1426 for key in [key_256, key_384, key_512] {
1428 assert!(key.is_ok());
1429 let key = key.unwrap();
1430
1431 let token = key.sign(&claims).unwrap();
1432 let verified_claims = key.verify(&token).unwrap();
1433 assert_eq!(verified_claims.root, claims.root);
1434 }
1435 }
1436
1437 #[test]
1438 fn test_rsa_pkcs1_asymmetric_algorithms() {
1439 let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1440 let key_rs384 = Key::generate(Algorithm::RS384, Some(crate::KeyId::decode("test-id").unwrap()));
1441 let key_rs512 = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
1442
1443 for key in [key_rs256, key_rs384, key_rs512] {
1444 test_asymmetric_key(key);
1445 }
1446 }
1447
1448 #[test]
1449 fn test_rsa_pss_asymmetric_algorithms() {
1450 let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1451 let key_ps384 = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1452 let key_ps512 = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
1453
1454 for key in [key_ps256, key_ps384, key_ps512] {
1455 test_asymmetric_key(key);
1456 }
1457 }
1458
1459 #[test]
1460 fn test_ec_asymmetric_algorithms() {
1461 let key_es256 = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1462 let key_es384 = Key::generate(Algorithm::ES384, Some(crate::KeyId::decode("test-id").unwrap()));
1463
1464 for key in [key_es256, key_es384] {
1465 test_asymmetric_key(key);
1466 }
1467 }
1468
1469 #[test]
1470 fn test_ed_asymmetric_algorithms() {
1471 let key_eddsa = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1472
1473 test_asymmetric_key(key_eddsa);
1474 }
1475
1476 fn test_asymmetric_key(key: crate::Result<Key>) {
1477 assert!(key.is_ok());
1478 let key = key.unwrap();
1479
1480 let claims = create_test_claims();
1481 let token = key.sign(&claims).unwrap();
1482
1483 let private_verified_claims = key.verify(&token).unwrap();
1484 assert_eq!(
1485 private_verified_claims.root, claims.root,
1486 "validation using private key"
1487 );
1488
1489 let public_verified_claims = key.to_public().unwrap().verify(&token).unwrap();
1490 assert_eq!(public_verified_claims.root, claims.root, "validation using public key");
1491 }
1492
1493 #[test]
1494 fn test_cross_algorithm_verification_fails() {
1495 let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1496 assert!(key_256.is_ok());
1497 let key_256 = key_256.unwrap();
1498
1499 let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1500 assert!(key_384.is_ok());
1501 let key_384 = key_384.unwrap();
1502
1503 let claims = create_test_claims();
1504 let token = key_256.sign(&claims).unwrap();
1505
1506 let result = key_384.verify(&token);
1508 assert!(result.is_err());
1509 }
1510
1511 #[test]
1512 fn test_asymmetric_cross_algorithm_verification_fails() {
1513 let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1514 assert!(key_rs256.is_ok());
1515 let key_rs256 = key_rs256.unwrap();
1516
1517 let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1518 assert!(key_ps256.is_ok());
1519 let key_ps256 = key_ps256.unwrap();
1520
1521 let claims = create_test_claims();
1522 let token = key_rs256.sign(&claims).unwrap();
1523
1524 let private_result = key_ps256.verify(&token);
1526 let public_result = key_ps256.to_public().unwrap().verify(&token);
1527 assert!(private_result.is_err());
1528 assert!(public_result.is_err());
1529 }
1530
1531 #[test]
1532 fn test_rsa_pkcs1_public_key_conversion() {
1533 let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1534 assert!(key.is_ok());
1535 let key = key.unwrap();
1536
1537 assert!(key.operations.contains(&KeyOperation::Sign));
1538 assert!(key.operations.contains(&KeyOperation::Verify));
1539
1540 let public_key = key.to_public().unwrap();
1541 assert!(!public_key.operations.contains(&KeyOperation::Sign));
1542 assert!(public_key.operations.contains(&KeyOperation::Verify));
1543
1544 match &key.material {
1545 KeyMaterial::RSA { public, private } => {
1546 assert!(private.is_some());
1547 assert_eq!(public.n.len(), 256);
1548 assert_eq!(public.e.len(), 3);
1549
1550 match &public_key.material {
1551 KeyMaterial::RSA {
1552 public: guest_public,
1553 private: public_private,
1554 } => {
1555 assert!(public_private.is_none());
1556 assert_eq!(public.n, guest_public.n);
1557 assert_eq!(public.e, guest_public.e);
1558 }
1559 _ => panic!("Expected public key to be an RSA key"),
1560 }
1561 }
1562 _ => panic!("Expected private key to be an RSA key"),
1563 }
1564 }
1565
1566 #[test]
1567 fn test_rsa_pss_public_key_conversion() {
1568 let key = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1569 assert!(key.is_ok());
1570 let key = key.unwrap();
1571
1572 assert!(key.operations.contains(&KeyOperation::Sign));
1573 assert!(key.operations.contains(&KeyOperation::Verify));
1574
1575 let public_key = key.to_public().unwrap();
1576 assert!(!public_key.operations.contains(&KeyOperation::Sign));
1577 assert!(public_key.operations.contains(&KeyOperation::Verify));
1578
1579 match &key.material {
1580 KeyMaterial::RSA { public, private } => {
1581 assert!(private.is_some());
1582 assert_eq!(public.n.len(), 256);
1583 assert_eq!(public.e.len(), 3);
1584
1585 match &public_key.material {
1586 KeyMaterial::RSA {
1587 public: guest_public,
1588 private: public_private,
1589 } => {
1590 assert!(public_private.is_none());
1591 assert_eq!(public.n, guest_public.n);
1592 assert_eq!(public.e, guest_public.e);
1593 }
1594 _ => panic!("Expected public key to be an RSA key"),
1595 }
1596 }
1597 _ => panic!("Expected private key to be an RSA key"),
1598 }
1599 }
1600
1601 #[test]
1602 fn test_base64url_serialization() {
1603 let key = create_test_key();
1604 let json = serde_json::to_string(&key).unwrap();
1605
1606 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1608 let k_value = parsed["k"].as_str().unwrap();
1609
1610 assert!(!k_value.contains('='));
1612 assert!(!k_value.contains('+'));
1613 assert!(!k_value.contains('/'));
1614
1615 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1617 .decode(k_value)
1618 .unwrap();
1619
1620 if let KeyMaterial::OCT {
1621 secret: original_secret,
1622 } = &key.material
1623 {
1624 assert_eq!(decoded, *original_secret);
1625 } else {
1626 panic!("Expected both keys to be OCT variant");
1627 }
1628 }
1629
1630 #[test]
1631 fn test_backwards_compatibility_unpadded_base64url() {
1632 let unpadded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY","kid":"test-key-1"}"#;
1634
1635 let key: Key = serde_json::from_str(unpadded_json).unwrap();
1637 assert_eq!(key.algorithm, Algorithm::HS256);
1638 assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1639
1640 if let KeyMaterial::OCT { secret } = &key.material {
1641 assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1642 } else {
1643 panic!("Expected key to be OCT variant");
1644 }
1645 }
1646
1647 #[test]
1648 fn test_backwards_compatibility_padded_base64url() {
1649 let padded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY=","kid":"test-key-1"}"#;
1651
1652 let key: Key = serde_json::from_str(padded_json).unwrap();
1654 assert_eq!(key.algorithm, Algorithm::HS256);
1655 assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1656
1657 if let KeyMaterial::OCT { secret } = &key.material {
1658 assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1659 } else {
1660 panic!("Expected key to be OCT variant");
1661 }
1662 }
1663
1664 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":[]}"#;
1672
1673 const JS_HS256_TOKEN: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImpzLXRlc3Qta2V5In0.eyJyb290IjoibGl2ZSIsInB1Ymxpc2giOlsiY2FtZXJhMSJdLCJzdWJzY3JpYmUiOlsiY2FtZXJhMSIsImNhbWVyYTIiXSwiaWF0IjoxNzc1MTc2NzU0fQ.DxrRkpYDd7Cc215qstY4RfnB7hRvc8RG61YWO4UwyWg";
1675
1676 const JS_HS256_LEGACY_TOKEN: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImpzLXRlc3Qta2V5In0.eyJyb290IjoibGl2ZSIsInB1dCI6WyJjYW1lcmExIl0sImdldCI6WyJjYW1lcmExIiwiY2FtZXJhMiJdLCJpYXQiOjE3NzUxNzY3NTR9.tHNQtHh_HCIKxXOexDCM7AkjqWzbULLZzjEckfOGRfY";
1678
1679 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":[]}"#;
1681
1682 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"]}"#;
1684
1685 const JS_EDDSA_TOKEN: &str = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImpzLWVkZHNhLWtleSJ9.eyJyb290Ijoic3RyZWFtIiwicHVibGlzaCI6WyJ2aWRlby8qKiJdLCJpYXQiOjE3NzUxNzY3NTZ9.Vq00rznemlwxsGLdAR8EJs1J8cgXhaiuEiCysmCxnWbDemIFxS6kTNv3kp6LKnokp-mpHiRpO24Nv7b47jcIAQ";
1687
1688 #[test]
1689 fn test_js_hs256_key_load() {
1690 let key = Key::from_str(JS_HS256_KEY).unwrap();
1691 assert_eq!(key.algorithm, Algorithm::HS256);
1692 assert_eq!(key.kid, Some(crate::KeyId::decode("js-test-key").unwrap()));
1693 }
1694
1695 #[test]
1696 fn test_js_hs256_token_verify() {
1697 let key = Key::from_str(JS_HS256_KEY).unwrap();
1698 let claims = key.verify(JS_HS256_TOKEN).unwrap();
1699 assert_eq!(claims.root, "live");
1700 assert_eq!(claims.publish, patterns(&["camera1"]));
1701 assert_eq!(claims.subscribe, patterns(&["camera1", "camera2"]));
1702 }
1703
1704 #[test]
1705 fn test_js_legacy_prefix_token_verifies_as_subtrees() {
1706 let key = Key::from_str(JS_HS256_KEY).unwrap();
1707 let claims = key.verify(JS_HS256_LEGACY_TOKEN).unwrap();
1708 assert_eq!(claims.root, "live");
1709 assert_eq!(claims.publish, patterns(&["camera1/**"]));
1710 assert_eq!(claims.subscribe, patterns(&["camera1/**", "camera2/**"]));
1711 }
1712
1713 #[test]
1714 fn test_legacy_scoped_key_signs_within_its_prefixes() {
1715 let json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","scope":{"root":"demo","put":["room"]}}"#;
1717 let key = Key::from_str(json).unwrap();
1718 assert_eq!(key.scope.as_ref().unwrap().publish, patterns(&["room/**"]));
1719
1720 let inside = Claims {
1721 root: "demo/room".into(),
1722 publish: patterns(&["alice"]),
1723 ..Default::default()
1724 };
1725 let outside = Claims {
1726 root: "demo".into(),
1727 publish: patterns(&["lobby/**"]),
1728 ..Default::default()
1729 };
1730 assert!(key.verify(&key.sign(&inside).unwrap()).is_ok());
1731 assert!(matches!(key.sign(&outside), Err(crate::Error::ScopeExceeded)));
1732
1733 assert!(
1735 serde_json::to_string(&key)
1736 .unwrap()
1737 .contains(r#""scope":{"root":"demo","put":["room"]}"#)
1738 );
1739 }
1740
1741 #[test]
1742 fn test_js_hs256_sign_and_roundtrip() {
1743 let key = Key::from_str(JS_HS256_KEY).unwrap();
1744 let claims = Claims {
1745 root: "rust-test".to_string(),
1746 publish: patterns(&["pub1/**"]),
1747 subscribe: patterns(&["sub1/**"]),
1748 ..Default::default()
1749 };
1750 let token = key.sign(&claims).unwrap();
1751 let verified = key.verify(&token).unwrap();
1752 assert_eq!(verified.root, "rust-test");
1753 assert_eq!(verified.publish, patterns(&["pub1/**"]));
1754 }
1755
1756 #[test]
1757 fn test_js_eddsa_key_load() {
1758 let private_key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1759 assert_eq!(private_key.algorithm, Algorithm::EdDSA);
1760 assert!(matches!(private_key.material, KeyMaterial::OKP { .. }));
1761
1762 let public_key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1763 assert_eq!(public_key.algorithm, Algorithm::EdDSA);
1764 }
1765
1766 #[test]
1767 fn test_js_eddsa_token_verify_with_private_key() {
1768 let key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1769 let claims = key.verify(JS_EDDSA_TOKEN).unwrap();
1770 assert_eq!(claims.root, "stream");
1771 assert_eq!(claims.publish, patterns(&["video/**"]));
1772 }
1773
1774 #[test]
1775 fn test_js_eddsa_token_verify_with_public_key() {
1776 let key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1777 let claims = key.verify(JS_EDDSA_TOKEN).unwrap();
1778 assert_eq!(claims.root, "stream");
1779 assert_eq!(claims.publish, patterns(&["video/**"]));
1780 }
1781
1782 #[test]
1783 fn test_js_token_wrong_key_fails() {
1784 let wrong_key = Key::generate(Algorithm::HS256, None).unwrap();
1786 let result = wrong_key.verify(JS_HS256_TOKEN);
1787 assert!(result.is_err());
1788 }
1789
1790 #[test]
1791 fn test_js_eddsa_token_wrong_key_fails() {
1792 let wrong_key = Key::from_str(JS_HS256_KEY).unwrap();
1794 let result = wrong_key.verify(JS_EDDSA_TOKEN);
1795 assert!(result.is_err());
1796 }
1797
1798 #[test]
1799 fn test_file_io_base64url() {
1800 let key = create_test_key();
1801 let temp_dir = std::env::temp_dir();
1802 let temp_path = temp_dir.join("test_jwk.key");
1803
1804 key.to_file(&temp_path).unwrap();
1806
1807 let contents = std::fs::read_to_string(&temp_path).unwrap();
1809
1810 assert!(!contents.contains('{'));
1812 assert!(!contents.contains('}'));
1813 assert!(!contents.contains('"'));
1814
1815 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1817 .decode(&contents)
1818 .unwrap();
1819 let json_str = String::from_utf8(decoded).unwrap();
1820 let _: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1821
1822 let loaded_key = Key::from_file(&temp_path).unwrap();
1824 assert_eq!(loaded_key.algorithm, key.algorithm);
1825 assert_eq!(loaded_key.operations, key.operations);
1826 assert_eq!(loaded_key.kid, key.kid);
1827
1828 if let (
1829 KeyMaterial::OCT {
1830 secret: original_secret,
1831 },
1832 KeyMaterial::OCT { secret: loaded_secret },
1833 ) = (&key.material, &loaded_key.material)
1834 {
1835 assert_eq!(loaded_secret, original_secret);
1836 } else {
1837 panic!("Expected both keys to be OCT variant");
1838 }
1839
1840 std::fs::remove_file(temp_path).ok();
1842 }
1843
1844 #[test]
1845 fn test_file_io_raw_json() {
1846 let key = create_test_key();
1847 let temp_dir = std::env::temp_dir();
1848 let temp_path = temp_dir.join("test_jwk_raw_json.key");
1849
1850 let json = serde_json::to_string(&key).unwrap();
1852 std::fs::write(&temp_path, &json).unwrap();
1853
1854 assert!(json.starts_with('{'));
1856
1857 let loaded_key = Key::from_file(&temp_path).unwrap();
1859 assert_eq!(loaded_key.algorithm, key.algorithm);
1860 assert_eq!(loaded_key.operations, key.operations);
1861 assert_eq!(loaded_key.kid, key.kid);
1862
1863 if let (
1864 KeyMaterial::OCT {
1865 secret: original_secret,
1866 },
1867 KeyMaterial::OCT { secret: loaded_secret },
1868 ) = (&key.material, &loaded_key.material)
1869 {
1870 assert_eq!(loaded_secret, original_secret);
1871 } else {
1872 panic!("Expected both keys to be OCT variant");
1873 }
1874
1875 std::fs::remove_file(temp_path).ok();
1877 }
1878
1879 #[cfg(unix)]
1880 mod permissions {
1881 use super::*;
1882 use std::os::unix::fs::PermissionsExt;
1883
1884 fn temp_path(name: &str) -> std::path::PathBuf {
1885 let unique = SystemTime::now()
1886 .duration_since(SystemTime::UNIX_EPOCH)
1887 .unwrap()
1888 .as_nanos();
1889 std::env::temp_dir().join(format!("test_perms_{name}_{unique}.jwk"))
1890 }
1891
1892 fn mode(path: &std::path::Path) -> u32 {
1893 std::fs::metadata(path).unwrap().permissions().mode() & 0o777
1894 }
1895
1896 #[test]
1897 fn private_key_is_owner_only() {
1898 let path = temp_path("private");
1899 create_test_key().to_file(&path).unwrap();
1900 assert_eq!(mode(&path), 0o600);
1901 std::fs::remove_file(&path).ok();
1902 }
1903
1904 #[test]
1905 fn private_key_tightens_existing_file() {
1906 let path = temp_path("existing");
1907 std::fs::write(&path, "stale").unwrap();
1908 std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap();
1909
1910 create_test_key().to_file(&path).unwrap();
1911 assert_eq!(mode(&path), 0o600);
1912
1913 let contents = std::fs::read_to_string(&path).unwrap();
1915 assert!(!contents.contains("stale"));
1916 std::fs::remove_file(&path).ok();
1917 }
1918
1919 #[test]
1920 fn public_key_keeps_default_permissions() {
1921 let path = temp_path("public");
1922 std::fs::write(&path, "").unwrap();
1923 std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap();
1924
1925 let public = Key::generate(Algorithm::ES256, None).unwrap().to_public().unwrap();
1926 public.to_file(&path).unwrap();
1927 assert_eq!(mode(&path), 0o644);
1928 std::fs::remove_file(&path).ok();
1929 }
1930 }
1931}