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