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