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> {
534 if !self.operations.contains(&KeyOperation::Verify) {
535 return Err(KeyError::VerifyUnsupported.into());
536 }
537
538 let decode = self.to_decoding_key()?;
539
540 let mut validation = jsonwebtoken::Validation::new(self.algorithm.into());
541 validation.required_spec_claims = Default::default(); validation.validate_exp = false; let token = jsonwebtoken::decode::<Claims>(token, decode, &validation)?;
545
546 if let Some(exp) = token.claims.expires
547 && exp < std::time::SystemTime::now()
548 {
549 return Err(crate::Error::TokenExpired);
550 }
551
552 token.claims.validate()?;
553 self.validate_scope(&token.claims)?;
554
555 Ok(token.claims)
556 }
557
558 pub fn sign(&self, payload: &Claims) -> crate::Result<String> {
560 if !self.operations.contains(&KeyOperation::Sign) {
561 return Err(KeyError::SignUnsupported.into());
562 }
563
564 payload.validate()?;
565 self.validate_scope(payload)?;
566
567 let encode = self.to_encoding_key()?;
568
569 let mut header = Header::new(self.algorithm.into());
570 header.kid = self.kid.as_ref().map(|k| k.to_string());
571 let token = jsonwebtoken::encode(&header, &payload, encode)?;
572 Ok(token)
573 }
574
575 pub fn generate(algorithm: Algorithm, id: Option<crate::KeyId>) -> crate::Result<Self> {
577 generate(algorithm, id)
578 }
579
580 pub fn with_scope(mut self, scope: crate::Scope) -> crate::Result<Self> {
585 scope.validate()?;
586 self.jwk.scope = Some(scope);
587 Ok(self)
588 }
589
590 pub fn with_operations(mut self, operations: impl IntoIterator<Item = KeyOperation>) -> Self {
592 self.jwk.operations = operations.into_iter().collect();
593 self
594 }
595
596 fn validate_scope(&self, claims: &Claims) -> crate::Result<()> {
597 if let Some(scope) = &self.scope {
598 scope.validate()?;
599 if !scope.allows(claims) {
600 return Err(crate::Error::ScopeExceeded);
601 }
602 }
603 Ok(())
604 }
605}
606
607fn serialize_base64url<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
609where
610 S: Serializer,
611{
612 let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes);
613 serializer.serialize_str(&encoded)
614}
615
616fn serialize_base64url_optional<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
617where
618 S: Serializer,
619{
620 match bytes {
621 Some(b) => serialize_base64url(b, serializer),
622 None => serializer.serialize_none(),
623 }
624}
625
626fn deserialize_base64url<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
628where
629 D: Deserializer<'de>,
630{
631 let s = String::deserialize(deserializer)?;
632
633 base64::engine::general_purpose::URL_SAFE_NO_PAD
635 .decode(&s)
636 .or_else(|_| {
637 base64::engine::general_purpose::URL_SAFE.decode(&s)
639 })
640 .map_err(serde::de::Error::custom)
641}
642
643fn deserialize_base64url_optional<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
644where
645 D: Deserializer<'de>,
646{
647 let s: Option<String> = Option::deserialize(deserializer)?;
648 match s {
649 Some(s) => {
650 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
651 .decode(&s)
652 .or_else(|_| base64::engine::general_purpose::URL_SAFE.decode(&s))
653 .map_err(serde::de::Error::custom)?;
654 Ok(Some(decoded))
655 }
656 None => Ok(None),
657 }
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663
664 fn patterns(texts: &[&str]) -> crate::Patterns {
665 texts.iter().map(|text| text.parse().unwrap()).collect()
666 }
667 use std::time::{Duration, SystemTime};
668
669 fn create_test_key() -> Key {
670 let mut jwk = Jwk::new(
671 Algorithm::HS256,
672 KeyMaterial::OCT {
673 secret: b"test-secret-that-is-long-enough-for-hmac-sha256".to_vec(),
674 },
675 );
676 jwk.kid = Some(crate::KeyId::decode("test-key-1").unwrap());
677 jwk.import().unwrap()
678 }
679
680 fn create_test_claims() -> Claims {
681 Claims {
682 root: "test-path".to_string(),
683 publish: patterns(&["test-pub/**"]),
684 subscribe: patterns(&["test-sub/**"]),
685 expires: Some(SystemTime::now() + Duration::from_secs(3600)),
686 issued: Some(SystemTime::now()),
687 }
688 }
689
690 #[test]
691 fn test_key_from_str_valid() {
692 let key = create_test_key();
693 let json = key.to_str().unwrap();
694 let loaded_key = Key::from_str(&json).unwrap();
695
696 assert_eq!(loaded_key.algorithm, key.algorithm);
697 assert_eq!(loaded_key.operations, key.operations);
698 match (&loaded_key.material, &key.material) {
699 (KeyMaterial::OCT { secret: loaded_secret }, KeyMaterial::OCT { secret }) => {
700 assert_eq!(loaded_secret, secret);
701 }
702 _ => panic!("Expected OCT key"),
703 }
704 assert_eq!(loaded_key.kid, key.kid);
705 }
706
707 #[test]
709 fn test_key_oct_without_kty_is_refused() {
710 let json = r#"{"alg":"HS256","key_ops":["sign","verify"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#;
711 assert!(Key::from_str(json).is_err());
712 }
713
714 #[test]
715 fn test_key_without_key_ops_defaults_sign_verify() {
716 let json = r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","kid":"no-ops"}"#;
717 let key = Key::from_str(json).unwrap();
718
719 assert_eq!(key.operations, sign_verify());
720
721 let claims = create_test_claims();
722 let token = key.sign(&claims).unwrap();
723 let verified = key.verify(&token).unwrap();
724 assert_eq!(verified.root, claims.root);
725 }
726
727 #[test]
728 fn test_key_without_key_ops_round_trip() {
729 let json = r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#;
730 let key = Key::from_str(json).unwrap();
731
732 let serialized = serde_json::to_string(&key).unwrap();
733 let parsed: serde_json::Value = serde_json::from_str(&serialized).unwrap();
734 let ops = parsed["key_ops"].as_array().unwrap();
735 assert_eq!(ops.len(), 2);
736
737 let reloaded = Key::from_str(&serialized).unwrap();
738 assert_eq!(reloaded.operations, sign_verify());
739 assert_eq!(reloaded.algorithm, key.algorithm);
740 }
741
742 #[test]
744 fn test_key_oct_secret_required_and_min_length() {
745 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256"}"#).is_err());
747 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256","k":""}"#).is_err());
748
749 assert!(Key::from_str(r#"{"kty":"oct","alg":"HS256","k":"AAAAAAAAAAAAAAAAAAAAAA"}"#).is_err());
751
752 let short = Jwk::new(Algorithm::HS256, KeyMaterial::OCT { secret: vec![0; 16] });
754 assert!(short.import().is_err());
755
756 let key =
758 Key::from_str(r#"{"kty":"oct","alg":"HS256","k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#).unwrap();
759 let KeyMaterial::OCT { ref secret } = key.material else {
760 panic!("Expected OCT key");
761 };
762 assert_eq!(secret.len(), 32);
763 }
764
765 #[test]
766 fn test_key_without_key_ops_to_public() {
767 let json = r#"{"kty":"OKP","alg":"EdDSA","crv":"Ed25519","x":"UiU9fT_SdBBpkFtJPRCY0gX1jK_Dr9syYLFuEz4QUM4","d":"lm-L_PV3ksuQ-KrFBgFMDJqAZC3_Z6Z5UC4ZQY5OoDQ","kid":"defaulted"}"#;
768 let key = Key::from_str(json).unwrap();
769 assert_eq!(key.operations, sign_verify());
770
771 let public = key.to_public().unwrap();
772 assert_eq!(public.operations, [KeyOperation::Verify].into());
773 }
774
775 #[test]
776 fn test_key_from_str_invalid_json() {
777 let result = Key::from_str("invalid json");
778 assert!(result.is_err());
779 }
780
781 #[test]
782 fn test_key_to_str() {
783 let key = create_test_key();
784 let encoded = key.to_str().unwrap();
785
786 assert!(!encoded.contains('{'));
788
789 let loaded = Key::from_str(&encoded).unwrap();
791 assert_eq!(loaded.algorithm, Algorithm::HS256);
792 assert_eq!(loaded.kid, key.kid);
793 assert!(loaded.operations.contains(&KeyOperation::Sign));
794 assert!(loaded.operations.contains(&KeyOperation::Verify));
795 }
796
797 #[test]
798 fn test_key_sign_success() {
799 let key = create_test_key();
800 let claims = create_test_claims();
801 let token = key.sign(&claims).unwrap();
802
803 assert!(!token.is_empty());
804 assert_eq!(token.matches('.').count(), 2); }
806
807 #[test]
808 fn test_key_scope_enforced_when_signing_and_verifying() {
809 let unrestricted = create_test_key();
810 let scoped = unrestricted
811 .clone()
812 .with_scope(crate::Scope {
813 root: "test-path".into(),
814 publish: patterns(&["allowed/**"]),
815 subscribe: patterns(&[]),
816 })
817 .unwrap();
818 let allowed = Claims {
819 root: "test-path".into(),
820 publish: patterns(&["allowed/room/**"]),
821 ..Default::default()
822 };
823 let denied = Claims {
824 root: "test-path".into(),
825 publish: patterns(&["other/**"]),
826 ..Default::default()
827 };
828
829 assert!(scoped.sign(&allowed).is_ok());
830 assert!(matches!(scoped.sign(&denied), Err(crate::Error::ScopeExceeded)));
831
832 let forged = unrestricted.sign(&denied).unwrap();
833 assert!(matches!(scoped.verify(&forged), Err(crate::Error::ScopeExceeded)));
834 }
835
836 #[test]
839 fn test_key_derived_material_never_stale() {
840 let claims = Claims {
841 root: "test-path".into(),
842 publish: patterns(&["test-pub/**"]),
843 ..Default::default()
844 };
845
846 let key = create_test_key();
848 let token = key.sign(&claims).unwrap();
849 assert!(key.encode.get().is_some());
850
851 let mut jwk = Jwk::from(&key);
854 jwk.algorithm = Algorithm::HS384;
855 let derived = Key::try_from(jwk).unwrap();
856 assert!(derived.encode.get().is_none());
857
858 let derived_token = derived.sign(&claims).unwrap();
859 assert_ne!(token, derived_token);
860
861 let cold = Key::from_str(&derived.to_str().unwrap()).unwrap();
864 assert_eq!(derived_token, cold.sign(&claims).unwrap());
865 assert!(cold.verify(&derived_token).is_ok());
866 assert!(key.verify(&derived_token).is_err());
867 }
868
869 #[test]
872 fn test_key_scope_requires_validation() {
873 let key = create_test_key();
874 assert!(key.scope.is_none());
875
876 let useless = crate::Scope::default();
877 assert!(matches!(
878 key.clone().with_scope(useless.clone()),
879 Err(crate::Error::UselessScope)
880 ));
881
882 let mut jwk = Jwk::from(&key);
883 jwk.scope = Some(useless);
884 assert!(matches!(Key::try_from(jwk), Err(crate::Error::UselessScope)));
885
886 let json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","scope":{}}"#;
887 assert!(Key::from_str(json).is_err());
888 }
889
890 #[test]
891 fn test_key_sign_no_permission() {
892 let key = create_test_key().with_operations([KeyOperation::Verify]);
893 let claims = create_test_claims();
894
895 let result = key.sign(&claims);
896 assert!(result.is_err());
897 assert!(result.unwrap_err().to_string().contains("key does not support signing"));
898 }
899
900 #[test]
901 fn test_key_sign_invalid_claims() {
902 let key = create_test_key();
903 let invalid_claims = Claims {
904 root: "test-path".to_string(),
905 publish: patterns(&[]),
906 subscribe: patterns(&[]),
907 expires: None,
908 issued: None,
909 };
910
911 let result = key.sign(&invalid_claims);
912 assert!(result.is_err());
913 assert!(
914 result
915 .unwrap_err()
916 .to_string()
917 .contains("no publish or subscribe allowed; token is useless")
918 );
919 }
920
921 #[test]
922 fn test_key_verify_success() {
923 let key = create_test_key();
924 let claims = create_test_claims();
925 let token = key.sign(&claims).unwrap();
926
927 let verified_claims = key.verify(&token).unwrap();
928 assert_eq!(verified_claims.root, claims.root);
929 assert_eq!(verified_claims.publish, claims.publish);
930 assert_eq!(verified_claims.subscribe, claims.subscribe);
931 }
932
933 #[test]
934 fn test_key_verify_no_permission() {
935 let key = create_test_key().with_operations([KeyOperation::Sign]);
936
937 let result = key.verify("some.jwt.token");
938 assert!(result.is_err());
939 assert!(
940 result
941 .unwrap_err()
942 .to_string()
943 .contains("key does not support verification")
944 );
945 }
946
947 #[test]
948 fn test_key_verify_invalid_token() {
949 let key = create_test_key();
950 let result = key.verify("invalid-token");
951 assert!(result.is_err());
952 }
953
954 #[test]
955 fn test_key_verify_path_mismatch() {
956 let key = create_test_key();
957 let claims = create_test_claims();
958 let token = key.sign(&claims).unwrap();
959
960 let result = key.verify(&token);
962 assert!(result.is_ok());
963 }
964
965 #[test]
966 fn test_key_verify_expired_token() {
967 let key = create_test_key();
968 let mut claims = create_test_claims();
969 claims.expires = Some(SystemTime::now() - Duration::from_secs(3600)); let token = key.sign(&claims).unwrap();
971
972 let result = key.verify(&token);
973 assert!(result.is_err());
974 }
975
976 #[test]
977 fn test_key_verify_token_without_exp() {
978 let key = create_test_key();
979 let claims = Claims {
980 root: "test-path".to_string(),
981 publish: patterns(&["**"]),
982 subscribe: patterns(&["**"]),
983 expires: None,
984 issued: None,
985 };
986 let token = key.sign(&claims).unwrap();
987
988 let verified_claims = key.verify(&token).unwrap();
989 assert_eq!(verified_claims.root, claims.root);
990 assert_eq!(verified_claims.publish, claims.publish);
991 assert_eq!(verified_claims.subscribe, claims.subscribe);
992 assert_eq!(verified_claims.expires, None);
993 }
994
995 #[test]
996 fn test_key_round_trip() {
997 let key = create_test_key();
998 let original_claims = Claims {
999 root: "test-path".to_string(),
1000 publish: patterns(&["test-pub/**"]),
1001 subscribe: patterns(&["test-sub/**"]),
1002 expires: Some(SystemTime::now() + Duration::from_secs(3600)),
1003 issued: Some(SystemTime::now()),
1004 };
1005
1006 let token = key.sign(&original_claims).unwrap();
1007 let verified_claims = key.verify(&token).unwrap();
1008
1009 assert_eq!(verified_claims.root, original_claims.root);
1010 assert_eq!(verified_claims.publish, original_claims.publish);
1011 assert_eq!(verified_claims.subscribe, original_claims.subscribe);
1012 }
1013
1014 #[test]
1015 fn test_key_generate_hs256() {
1016 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1017 assert!(key.is_ok());
1018 let key = key.unwrap();
1019
1020 assert_eq!(key.algorithm, Algorithm::HS256);
1021 assert_eq!(key.kid, Some(crate::KeyId::decode("test-id").unwrap()));
1022 assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
1023
1024 match &key.material {
1025 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 32),
1026 _ => panic!("Expected OCT key"),
1027 }
1028 }
1029
1030 #[test]
1031 fn test_key_generate_hs384() {
1032 let key = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1033 assert!(key.is_ok());
1034 let key = key.unwrap();
1035
1036 assert_eq!(key.algorithm, Algorithm::HS384);
1037
1038 match &key.material {
1039 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 48),
1040 _ => panic!("Expected OCT key"),
1041 }
1042 }
1043
1044 #[test]
1045 fn test_key_generate_hs512() {
1046 let key = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
1047 assert!(key.is_ok());
1048 let key = key.unwrap();
1049
1050 assert_eq!(key.algorithm, Algorithm::HS512);
1051
1052 match &key.material {
1053 KeyMaterial::OCT { secret } => assert_eq!(secret.len(), 64),
1054 _ => panic!("Expected OCT key"),
1055 }
1056 }
1057
1058 #[test]
1059 fn test_key_generate_rs512() {
1060 let key = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
1061 assert!(key.is_ok());
1062 let key = key.unwrap();
1063
1064 assert_eq!(key.algorithm, Algorithm::RS512);
1065 assert!(matches!(key.material, KeyMaterial::RSA { .. }));
1066 match &key.material {
1067 KeyMaterial::RSA { public, private } => {
1068 assert!(private.is_some());
1069 assert_eq!(public.n.len(), 256);
1070 assert_eq!(public.e.len(), 3);
1071 }
1072 _ => panic!("Expected RSA key"),
1073 }
1074 }
1075
1076 #[test]
1077 fn test_key_generate_es256() {
1078 let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1079 assert!(key.is_ok());
1080 let key = key.unwrap();
1081
1082 assert_eq!(key.algorithm, Algorithm::ES256);
1083 assert!(matches!(key.material, KeyMaterial::EC { .. }))
1084 }
1085
1086 #[test]
1087 fn test_key_generate_ps512() {
1088 let key = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
1089 assert!(key.is_ok());
1090 let key = key.unwrap();
1091
1092 assert_eq!(key.algorithm, Algorithm::PS512);
1093 assert!(matches!(key.material, KeyMaterial::RSA { .. }));
1094 }
1095
1096 #[test]
1097 fn test_key_generate_eddsa() {
1098 let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1099 assert!(key.is_ok());
1100 let key = key.unwrap();
1101
1102 assert_eq!(key.algorithm, Algorithm::EdDSA);
1103 assert!(matches!(key.material, KeyMaterial::OKP { .. }));
1104 }
1105
1106 #[test]
1107 fn test_key_generate_without_id() {
1108 let key = Key::generate(Algorithm::HS256, None);
1109 assert!(key.is_ok());
1110 let key = key.unwrap();
1111
1112 assert_eq!(key.algorithm, Algorithm::HS256);
1113 assert_eq!(key.kid, None);
1114 assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
1115 }
1116
1117 #[test]
1118 fn test_public_key_conversion_hmac() {
1119 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()))
1120 .expect("HMAC key generation failed");
1121
1122 assert!(key.to_public().is_err());
1123 }
1124
1125 #[test]
1126 fn test_public_key_conversion_rsa() {
1127 let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1128 assert!(key.is_ok());
1129 let key = key.unwrap();
1130
1131 let public_key = key.to_public().unwrap();
1132 assert_eq!(key.kid, public_key.kid);
1133 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1134 assert!(public_key.encode.get().is_none());
1135 assert!(public_key.decode.get().is_none());
1136 assert!(matches!(public_key.material, KeyMaterial::RSA { .. }));
1137
1138 if let KeyMaterial::RSA { public, private } = &public_key.material {
1139 assert!(private.is_none());
1140
1141 if let KeyMaterial::RSA { public: src_public, .. } = &key.material {
1142 assert_eq!(public.e, src_public.e);
1143 assert_eq!(public.n, src_public.n);
1144 } else {
1145 unreachable!("Expected RSA key")
1146 }
1147 } else {
1148 unreachable!("Expected RSA key");
1149 }
1150 }
1151
1152 #[test]
1153 fn test_public_key_conversion_es() {
1154 let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1155 assert!(key.is_ok());
1156 let key = key.unwrap();
1157
1158 let public_key = key.to_public().unwrap();
1159 assert_eq!(key.kid, public_key.kid);
1160 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1161 assert!(public_key.encode.get().is_none());
1162 assert!(public_key.decode.get().is_none());
1163 assert!(matches!(public_key.material, KeyMaterial::EC { .. }));
1164
1165 if let KeyMaterial::EC { x, y, d, curve } = &public_key.material {
1166 assert!(d.is_none());
1167
1168 if let KeyMaterial::EC {
1169 x: src_x,
1170 y: src_y,
1171 curve: src_curve,
1172 ..
1173 } = &key.material
1174 {
1175 assert_eq!(x, src_x);
1176 assert_eq!(y, src_y);
1177 assert_eq!(curve, src_curve);
1178 } else {
1179 unreachable!("Expected EC key")
1180 }
1181 } else {
1182 unreachable!("Expected EC key");
1183 }
1184 }
1185
1186 #[test]
1187 fn test_public_key_conversion_ed() {
1188 let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1189 assert!(key.is_ok());
1190 let key = key.unwrap();
1191
1192 let public_key = key.to_public().unwrap();
1193 assert_eq!(key.kid, public_key.kid);
1194 assert_eq!(public_key.operations, [KeyOperation::Verify].into());
1195 assert!(public_key.encode.get().is_none());
1196 assert!(public_key.decode.get().is_none());
1197 assert!(matches!(public_key.material, KeyMaterial::OKP { .. }));
1198
1199 if let KeyMaterial::OKP { x, d, curve } = &public_key.material {
1200 assert!(d.is_none());
1201
1202 if let KeyMaterial::OKP {
1203 x: src_x,
1204 curve: src_curve,
1205 ..
1206 } = &key.material
1207 {
1208 assert_eq!(x, src_x);
1209 assert_eq!(curve, src_curve);
1210 } else {
1211 unreachable!("Expected OKP key")
1212 }
1213 } else {
1214 unreachable!("Expected OKP key");
1215 }
1216 }
1217
1218 #[test]
1219 fn test_key_generate_sign_verify_cycle() {
1220 let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1221 assert!(key.is_ok());
1222 let key = key.unwrap();
1223
1224 let claims = create_test_claims();
1225
1226 let token = key.sign(&claims).unwrap();
1227 let verified_claims = key.verify(&token).unwrap();
1228
1229 assert_eq!(verified_claims.root, claims.root);
1230 assert_eq!(verified_claims.publish, claims.publish);
1231 assert_eq!(verified_claims.subscribe, claims.subscribe);
1232 }
1233
1234 #[test]
1235 fn test_key_debug_no_secret() {
1236 let key = create_test_key();
1237 let debug_str = format!("{key:?}");
1238
1239 assert!(debug_str.contains("algorithm: HS256"));
1240 assert!(debug_str.contains("operations"));
1241 assert!(debug_str.contains("kid: Some(KeyId(\"test-key-1\"))"));
1242 assert!(!debug_str.contains("secret")); }
1244
1245 #[test]
1246 fn test_key_operations_enum() {
1247 let sign_op = KeyOperation::Sign;
1248 let verify_op = KeyOperation::Verify;
1249 let decrypt_op = KeyOperation::Decrypt;
1250 let encrypt_op = KeyOperation::Encrypt;
1251
1252 assert_eq!(sign_op, KeyOperation::Sign);
1253 assert_eq!(verify_op, KeyOperation::Verify);
1254 assert_eq!(decrypt_op, KeyOperation::Decrypt);
1255 assert_eq!(encrypt_op, KeyOperation::Encrypt);
1256
1257 assert_ne!(sign_op, verify_op);
1258 assert_ne!(decrypt_op, encrypt_op);
1259 }
1260
1261 #[test]
1262 fn test_key_operations_serde() {
1263 let operations = [KeyOperation::Sign, KeyOperation::Verify];
1264 let json = serde_json::to_string(&operations).unwrap();
1265 assert!(json.contains("\"sign\""));
1266 assert!(json.contains("\"verify\""));
1267
1268 let deserialized: Vec<KeyOperation> = serde_json::from_str(&json).unwrap();
1269 assert_eq!(deserialized, operations);
1270 }
1271
1272 #[test]
1273 fn test_key_serde() {
1274 let key = create_test_key();
1275 let json = serde_json::to_string(&key).unwrap();
1276 let deserialized: Key = serde_json::from_str(&json).unwrap();
1277
1278 assert_eq!(deserialized.algorithm, key.algorithm);
1279 assert_eq!(deserialized.operations, key.operations);
1280 assert_eq!(deserialized.kid, key.kid);
1281
1282 if let (
1283 KeyMaterial::OCT {
1284 secret: original_secret,
1285 },
1286 KeyMaterial::OCT {
1287 secret: deserialized_secret,
1288 },
1289 ) = (&key.material, &deserialized.material)
1290 {
1291 assert_eq!(deserialized_secret, original_secret);
1292 } else {
1293 panic!("Expected both keys to be OCT variant");
1294 }
1295 }
1296
1297 #[test]
1298 fn test_key_clone() {
1299 let key = create_test_key();
1300 let cloned = key.clone();
1301
1302 assert_eq!(cloned.algorithm, key.algorithm);
1303 assert_eq!(cloned.operations, key.operations);
1304 assert_eq!(cloned.kid, key.kid);
1305
1306 if let (
1307 KeyMaterial::OCT {
1308 secret: original_secret,
1309 },
1310 KeyMaterial::OCT { secret: cloned_secret },
1311 ) = (&key.material, &cloned.material)
1312 {
1313 assert_eq!(cloned_secret, original_secret);
1314 } else {
1315 panic!("Expected both keys to be OCT variant");
1316 }
1317 }
1318
1319 #[test]
1320 fn test_hmac_algorithms() {
1321 let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1322 let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1323 let key_512 = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
1324
1325 let claims = create_test_claims();
1326
1327 for key in [key_256, key_384, key_512] {
1329 assert!(key.is_ok());
1330 let key = key.unwrap();
1331
1332 let token = key.sign(&claims).unwrap();
1333 let verified_claims = key.verify(&token).unwrap();
1334 assert_eq!(verified_claims.root, claims.root);
1335 }
1336 }
1337
1338 #[test]
1339 fn test_rsa_pkcs1_asymmetric_algorithms() {
1340 let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1341 let key_rs384 = Key::generate(Algorithm::RS384, Some(crate::KeyId::decode("test-id").unwrap()));
1342 let key_rs512 = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
1343
1344 for key in [key_rs256, key_rs384, key_rs512] {
1345 test_asymmetric_key(key);
1346 }
1347 }
1348
1349 #[test]
1350 fn test_rsa_pss_asymmetric_algorithms() {
1351 let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1352 let key_ps384 = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1353 let key_ps512 = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
1354
1355 for key in [key_ps256, key_ps384, key_ps512] {
1356 test_asymmetric_key(key);
1357 }
1358 }
1359
1360 #[test]
1361 fn test_ec_asymmetric_algorithms() {
1362 let key_es256 = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1363 let key_es384 = Key::generate(Algorithm::ES384, Some(crate::KeyId::decode("test-id").unwrap()));
1364
1365 for key in [key_es256, key_es384] {
1366 test_asymmetric_key(key);
1367 }
1368 }
1369
1370 #[test]
1371 fn test_ed_asymmetric_algorithms() {
1372 let key_eddsa = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1373
1374 test_asymmetric_key(key_eddsa);
1375 }
1376
1377 fn test_asymmetric_key(key: crate::Result<Key>) {
1378 assert!(key.is_ok());
1379 let key = key.unwrap();
1380
1381 let claims = create_test_claims();
1382 let token = key.sign(&claims).unwrap();
1383
1384 let private_verified_claims = key.verify(&token).unwrap();
1385 assert_eq!(
1386 private_verified_claims.root, claims.root,
1387 "validation using private key"
1388 );
1389
1390 let public_verified_claims = key.to_public().unwrap().verify(&token).unwrap();
1391 assert_eq!(public_verified_claims.root, claims.root, "validation using public key");
1392 }
1393
1394 #[test]
1395 fn test_cross_algorithm_verification_fails() {
1396 let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1397 assert!(key_256.is_ok());
1398 let key_256 = key_256.unwrap();
1399
1400 let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1401 assert!(key_384.is_ok());
1402 let key_384 = key_384.unwrap();
1403
1404 let claims = create_test_claims();
1405 let token = key_256.sign(&claims).unwrap();
1406
1407 let result = key_384.verify(&token);
1409 assert!(result.is_err());
1410 }
1411
1412 #[test]
1413 fn test_asymmetric_cross_algorithm_verification_fails() {
1414 let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1415 assert!(key_rs256.is_ok());
1416 let key_rs256 = key_rs256.unwrap();
1417
1418 let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1419 assert!(key_ps256.is_ok());
1420 let key_ps256 = key_ps256.unwrap();
1421
1422 let claims = create_test_claims();
1423 let token = key_rs256.sign(&claims).unwrap();
1424
1425 let private_result = key_ps256.verify(&token);
1427 let public_result = key_ps256.to_public().unwrap().verify(&token);
1428 assert!(private_result.is_err());
1429 assert!(public_result.is_err());
1430 }
1431
1432 #[test]
1433 fn test_rsa_pkcs1_public_key_conversion() {
1434 let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1435 assert!(key.is_ok());
1436 let key = key.unwrap();
1437
1438 assert!(key.operations.contains(&KeyOperation::Sign));
1439 assert!(key.operations.contains(&KeyOperation::Verify));
1440
1441 let public_key = key.to_public().unwrap();
1442 assert!(!public_key.operations.contains(&KeyOperation::Sign));
1443 assert!(public_key.operations.contains(&KeyOperation::Verify));
1444
1445 match &key.material {
1446 KeyMaterial::RSA { public, private } => {
1447 assert!(private.is_some());
1448 assert_eq!(public.n.len(), 256);
1449 assert_eq!(public.e.len(), 3);
1450
1451 match &public_key.material {
1452 KeyMaterial::RSA {
1453 public: guest_public,
1454 private: public_private,
1455 } => {
1456 assert!(public_private.is_none());
1457 assert_eq!(public.n, guest_public.n);
1458 assert_eq!(public.e, guest_public.e);
1459 }
1460 _ => panic!("Expected public key to be an RSA key"),
1461 }
1462 }
1463 _ => panic!("Expected private key to be an RSA key"),
1464 }
1465 }
1466
1467 #[test]
1468 fn test_rsa_pss_public_key_conversion() {
1469 let key = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1470 assert!(key.is_ok());
1471 let key = key.unwrap();
1472
1473 assert!(key.operations.contains(&KeyOperation::Sign));
1474 assert!(key.operations.contains(&KeyOperation::Verify));
1475
1476 let public_key = key.to_public().unwrap();
1477 assert!(!public_key.operations.contains(&KeyOperation::Sign));
1478 assert!(public_key.operations.contains(&KeyOperation::Verify));
1479
1480 match &key.material {
1481 KeyMaterial::RSA { public, private } => {
1482 assert!(private.is_some());
1483 assert_eq!(public.n.len(), 256);
1484 assert_eq!(public.e.len(), 3);
1485
1486 match &public_key.material {
1487 KeyMaterial::RSA {
1488 public: guest_public,
1489 private: public_private,
1490 } => {
1491 assert!(public_private.is_none());
1492 assert_eq!(public.n, guest_public.n);
1493 assert_eq!(public.e, guest_public.e);
1494 }
1495 _ => panic!("Expected public key to be an RSA key"),
1496 }
1497 }
1498 _ => panic!("Expected private key to be an RSA key"),
1499 }
1500 }
1501
1502 #[test]
1503 fn test_base64url_serialization() {
1504 let key = create_test_key();
1505 let json = serde_json::to_string(&key).unwrap();
1506
1507 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1509 let k_value = parsed["k"].as_str().unwrap();
1510
1511 assert!(!k_value.contains('='));
1513 assert!(!k_value.contains('+'));
1514 assert!(!k_value.contains('/'));
1515
1516 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1518 .decode(k_value)
1519 .unwrap();
1520
1521 if let KeyMaterial::OCT {
1522 secret: original_secret,
1523 } = &key.material
1524 {
1525 assert_eq!(decoded, *original_secret);
1526 } else {
1527 panic!("Expected both keys to be OCT variant");
1528 }
1529 }
1530
1531 #[test]
1532 fn test_backwards_compatibility_unpadded_base64url() {
1533 let unpadded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY","kid":"test-key-1"}"#;
1535
1536 let key: Key = serde_json::from_str(unpadded_json).unwrap();
1538 assert_eq!(key.algorithm, Algorithm::HS256);
1539 assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1540
1541 if let KeyMaterial::OCT { secret } = &key.material {
1542 assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1543 } else {
1544 panic!("Expected key to be OCT variant");
1545 }
1546 }
1547
1548 #[test]
1549 fn test_backwards_compatibility_padded_base64url() {
1550 let padded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY=","kid":"test-key-1"}"#;
1552
1553 let key: Key = serde_json::from_str(padded_json).unwrap();
1555 assert_eq!(key.algorithm, Algorithm::HS256);
1556 assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1557
1558 if let KeyMaterial::OCT { secret } = &key.material {
1559 assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1560 } else {
1561 panic!("Expected key to be OCT variant");
1562 }
1563 }
1564
1565 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":[]}"#;
1573
1574 const JS_HS256_TOKEN: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImpzLXRlc3Qta2V5In0.eyJyb290IjoibGl2ZSIsInB1Ymxpc2giOlsiY2FtZXJhMSJdLCJzdWJzY3JpYmUiOlsiY2FtZXJhMSIsImNhbWVyYTIiXSwiaWF0IjoxNzc1MTc2NzU0fQ.DxrRkpYDd7Cc215qstY4RfnB7hRvc8RG61YWO4UwyWg";
1576
1577 const JS_HS256_LEGACY_TOKEN: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImpzLXRlc3Qta2V5In0.eyJyb290IjoibGl2ZSIsInB1dCI6WyJjYW1lcmExIl0sImdldCI6WyJjYW1lcmExIiwiY2FtZXJhMiJdLCJpYXQiOjE3NzUxNzY3NTR9.tHNQtHh_HCIKxXOexDCM7AkjqWzbULLZzjEckfOGRfY";
1579
1580 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":[]}"#;
1582
1583 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"]}"#;
1585
1586 const JS_EDDSA_TOKEN: &str = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImpzLWVkZHNhLWtleSJ9.eyJyb290Ijoic3RyZWFtIiwicHVibGlzaCI6WyJ2aWRlby8qKiJdLCJpYXQiOjE3NzUxNzY3NTZ9.Vq00rznemlwxsGLdAR8EJs1J8cgXhaiuEiCysmCxnWbDemIFxS6kTNv3kp6LKnokp-mpHiRpO24Nv7b47jcIAQ";
1588
1589 #[test]
1590 fn test_js_hs256_key_load() {
1591 let key = Key::from_str(JS_HS256_KEY).unwrap();
1592 assert_eq!(key.algorithm, Algorithm::HS256);
1593 assert_eq!(key.kid, Some(crate::KeyId::decode("js-test-key").unwrap()));
1594 }
1595
1596 #[test]
1597 fn test_js_hs256_token_verify() {
1598 let key = Key::from_str(JS_HS256_KEY).unwrap();
1599 let claims = key.verify(JS_HS256_TOKEN).unwrap();
1600 assert_eq!(claims.root, "live");
1601 assert_eq!(claims.publish, patterns(&["camera1"]));
1602 assert_eq!(claims.subscribe, patterns(&["camera1", "camera2"]));
1603 }
1604
1605 #[test]
1606 fn test_js_legacy_prefix_token_verifies_as_subtrees() {
1607 let key = Key::from_str(JS_HS256_KEY).unwrap();
1608 let claims = key.verify(JS_HS256_LEGACY_TOKEN).unwrap();
1609 assert_eq!(claims.root, "live");
1610 assert_eq!(claims.publish, patterns(&["camera1/**"]));
1611 assert_eq!(claims.subscribe, patterns(&["camera1/**", "camera2/**"]));
1612 }
1613
1614 #[test]
1615 fn test_legacy_scoped_key_signs_within_its_prefixes() {
1616 let json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68","scope":{"root":"demo","put":["room"]}}"#;
1618 let key = Key::from_str(json).unwrap();
1619 assert_eq!(key.scope.as_ref().unwrap().publish, patterns(&["room/**"]));
1620
1621 let inside = Claims {
1622 root: "demo/room".into(),
1623 publish: patterns(&["alice"]),
1624 ..Default::default()
1625 };
1626 let outside = Claims {
1627 root: "demo".into(),
1628 publish: patterns(&["lobby/**"]),
1629 ..Default::default()
1630 };
1631 assert!(key.verify(&key.sign(&inside).unwrap()).is_ok());
1632 assert!(matches!(key.sign(&outside), Err(crate::Error::ScopeExceeded)));
1633
1634 assert!(
1636 serde_json::to_string(&key)
1637 .unwrap()
1638 .contains(r#""scope":{"root":"demo","put":["room"]}"#)
1639 );
1640 }
1641
1642 #[test]
1643 fn test_js_hs256_sign_and_roundtrip() {
1644 let key = Key::from_str(JS_HS256_KEY).unwrap();
1645 let claims = Claims {
1646 root: "rust-test".to_string(),
1647 publish: patterns(&["pub1/**"]),
1648 subscribe: patterns(&["sub1/**"]),
1649 ..Default::default()
1650 };
1651 let token = key.sign(&claims).unwrap();
1652 let verified = key.verify(&token).unwrap();
1653 assert_eq!(verified.root, "rust-test");
1654 assert_eq!(verified.publish, patterns(&["pub1/**"]));
1655 }
1656
1657 #[test]
1658 fn test_js_eddsa_key_load() {
1659 let private_key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1660 assert_eq!(private_key.algorithm, Algorithm::EdDSA);
1661 assert!(matches!(private_key.material, KeyMaterial::OKP { .. }));
1662
1663 let public_key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1664 assert_eq!(public_key.algorithm, Algorithm::EdDSA);
1665 }
1666
1667 #[test]
1668 fn test_js_eddsa_token_verify_with_private_key() {
1669 let key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1670 let claims = key.verify(JS_EDDSA_TOKEN).unwrap();
1671 assert_eq!(claims.root, "stream");
1672 assert_eq!(claims.publish, patterns(&["video/**"]));
1673 }
1674
1675 #[test]
1676 fn test_js_eddsa_token_verify_with_public_key() {
1677 let key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1678 let claims = key.verify(JS_EDDSA_TOKEN).unwrap();
1679 assert_eq!(claims.root, "stream");
1680 assert_eq!(claims.publish, patterns(&["video/**"]));
1681 }
1682
1683 #[test]
1684 fn test_js_token_wrong_key_fails() {
1685 let wrong_key = Key::generate(Algorithm::HS256, None).unwrap();
1687 let result = wrong_key.verify(JS_HS256_TOKEN);
1688 assert!(result.is_err());
1689 }
1690
1691 #[test]
1692 fn test_js_eddsa_token_wrong_key_fails() {
1693 let wrong_key = Key::from_str(JS_HS256_KEY).unwrap();
1695 let result = wrong_key.verify(JS_EDDSA_TOKEN);
1696 assert!(result.is_err());
1697 }
1698
1699 #[test]
1700 fn test_file_io_base64url() {
1701 let key = create_test_key();
1702 let temp_dir = std::env::temp_dir();
1703 let temp_path = temp_dir.join("test_jwk.key");
1704
1705 key.to_file(&temp_path).unwrap();
1707
1708 let contents = std::fs::read_to_string(&temp_path).unwrap();
1710
1711 assert!(!contents.contains('{'));
1713 assert!(!contents.contains('}'));
1714 assert!(!contents.contains('"'));
1715
1716 let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1718 .decode(&contents)
1719 .unwrap();
1720 let json_str = String::from_utf8(decoded).unwrap();
1721 let _: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1722
1723 let loaded_key = Key::from_file(&temp_path).unwrap();
1725 assert_eq!(loaded_key.algorithm, key.algorithm);
1726 assert_eq!(loaded_key.operations, key.operations);
1727 assert_eq!(loaded_key.kid, key.kid);
1728
1729 if let (
1730 KeyMaterial::OCT {
1731 secret: original_secret,
1732 },
1733 KeyMaterial::OCT { secret: loaded_secret },
1734 ) = (&key.material, &loaded_key.material)
1735 {
1736 assert_eq!(loaded_secret, original_secret);
1737 } else {
1738 panic!("Expected both keys to be OCT variant");
1739 }
1740
1741 std::fs::remove_file(temp_path).ok();
1743 }
1744
1745 #[test]
1746 fn test_file_io_raw_json() {
1747 let key = create_test_key();
1748 let temp_dir = std::env::temp_dir();
1749 let temp_path = temp_dir.join("test_jwk_raw_json.key");
1750
1751 let json = serde_json::to_string(&key).unwrap();
1753 std::fs::write(&temp_path, &json).unwrap();
1754
1755 assert!(json.starts_with('{'));
1757
1758 let loaded_key = Key::from_file(&temp_path).unwrap();
1760 assert_eq!(loaded_key.algorithm, key.algorithm);
1761 assert_eq!(loaded_key.operations, key.operations);
1762 assert_eq!(loaded_key.kid, key.kid);
1763
1764 if let (
1765 KeyMaterial::OCT {
1766 secret: original_secret,
1767 },
1768 KeyMaterial::OCT { secret: loaded_secret },
1769 ) = (&key.material, &loaded_key.material)
1770 {
1771 assert_eq!(loaded_secret, original_secret);
1772 } else {
1773 panic!("Expected both keys to be OCT variant");
1774 }
1775
1776 std::fs::remove_file(temp_path).ok();
1778 }
1779
1780 #[cfg(unix)]
1781 mod permissions {
1782 use super::*;
1783 use std::os::unix::fs::PermissionsExt;
1784
1785 fn temp_path(name: &str) -> std::path::PathBuf {
1786 let unique = SystemTime::now()
1787 .duration_since(SystemTime::UNIX_EPOCH)
1788 .unwrap()
1789 .as_nanos();
1790 std::env::temp_dir().join(format!("test_perms_{name}_{unique}.jwk"))
1791 }
1792
1793 fn mode(path: &std::path::Path) -> u32 {
1794 std::fs::metadata(path).unwrap().permissions().mode() & 0o777
1795 }
1796
1797 #[test]
1798 fn private_key_is_owner_only() {
1799 let path = temp_path("private");
1800 create_test_key().to_file(&path).unwrap();
1801 assert_eq!(mode(&path), 0o600);
1802 std::fs::remove_file(&path).ok();
1803 }
1804
1805 #[test]
1806 fn private_key_tightens_existing_file() {
1807 let path = temp_path("existing");
1808 std::fs::write(&path, "stale").unwrap();
1809 std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap();
1810
1811 create_test_key().to_file(&path).unwrap();
1812 assert_eq!(mode(&path), 0o600);
1813
1814 let contents = std::fs::read_to_string(&path).unwrap();
1816 assert!(!contents.contains("stale"));
1817 std::fs::remove_file(&path).ok();
1818 }
1819
1820 #[test]
1821 fn public_key_keeps_default_permissions() {
1822 let path = temp_path("public");
1823 std::fs::write(&path, "").unwrap();
1824 std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap();
1825
1826 let public = Key::generate(Algorithm::ES256, None).unwrap().to_public().unwrap();
1827 public.to_file(&path).unwrap();
1828 assert_eq!(mode(&path), 0o644);
1829 std::fs::remove_file(&path).ok();
1830 }
1831 }
1832}