Skip to main content

moq_token/
key.rs

1use crate::error::KeyError;
2use crate::generate::generate;
3use crate::{Algorithm, Claims};
4use base64::Engine;
5use elliptic_curve::SecretKey;
6use elliptic_curve::pkcs8::EncodePrivateKey;
7use jsonwebtoken::{DecodingKey, EncodingKey, Header};
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/// Cryptographic operations that a key can perform.
15#[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/// <https://datatracker.ietf.org/doc/html/rfc7518#section-6>
25#[derive(Clone, Serialize, Deserialize)]
26#[serde(tag = "kty")]
27pub enum KeyType {
28	/// <https://datatracker.ietf.org/doc/html/rfc7518#section-6.2>
29	EC {
30		#[serde(rename = "crv")]
31		curve: EllipticCurve,
32		/// The X-coordinate of an EC key
33		#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
34		x: Vec<u8>,
35		/// The Y-coordinate of an EC key
36		#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
37		y: Vec<u8>,
38		/// The private value of an EC key
39		#[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	/// <https://datatracker.ietf.org/doc/html/rfc7518#section-6.3>
48	RSA {
49		#[serde(flatten)]
50		public: RsaPublicKey,
51		#[serde(flatten, skip_serializing_if = "Option::is_none")]
52		private: Option<RsaPrivateKey>,
53	},
54	/// <https://datatracker.ietf.org/doc/html/rfc7518#section-6.4>
55	#[serde(rename = "oct")]
56	OCT {
57		/// The secret key as base64url (unpadded).
58		#[serde(
59			rename = "k",
60			default,
61			serialize_with = "serialize_base64url",
62			deserialize_with = "deserialize_base64url"
63		)]
64		secret: Vec<u8>,
65	},
66	/// <https://datatracker.ietf.org/doc/html/rfc8037#section-2>
67	OKP {
68		#[serde(rename = "crv")]
69		curve: EllipticCurve,
70		#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
71		x: Vec<u8>,
72		#[serde(
73			rename = "d",
74			default,
75			skip_serializing_if = "Option::is_none",
76			serialize_with = "serialize_base64url_optional",
77			deserialize_with = "deserialize_base64url_optional"
78		)]
79		d: Option<Vec<u8>>,
80	},
81}
82
83/// Supported elliptic curves for EC and OKP key types.
84///
85/// See <https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.1>
86#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
87pub enum EllipticCurve {
88	#[serde(rename = "P-256")]
89	P256,
90	#[serde(rename = "P-384")]
91	P384,
92	// jsonwebtoken doesn't support the ES512 algorithm, so we can't implement this
93	// #[serde(rename = "P-521")]
94	// P521,
95	#[serde(rename = "Ed25519")]
96	Ed25519,
97}
98
99/// RSA public key parameters.
100///
101/// See <https://datatracker.ietf.org/doc/html/rfc7518#section-6.3.1>
102#[derive(Clone, Serialize, Deserialize)]
103pub struct RsaPublicKey {
104	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
105	pub n: Vec<u8>,
106	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
107	pub e: Vec<u8>,
108}
109
110/// RSA private key parameters.
111///
112/// See <https://datatracker.ietf.org/doc/html/rfc7518#section-6.3.2>
113#[derive(Clone, Serialize, Deserialize)]
114pub struct RsaPrivateKey {
115	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
116	pub d: Vec<u8>,
117	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
118	pub p: Vec<u8>,
119	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
120	pub q: Vec<u8>,
121	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
122	pub dp: Vec<u8>,
123	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
124	pub dq: Vec<u8>,
125	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
126	pub qi: Vec<u8>,
127	#[serde(skip_serializing_if = "Option::is_none")]
128	pub oth: Option<Vec<RsaAdditionalPrime>>,
129}
130
131/// Additional prime information for multi-prime RSA keys.
132#[derive(Clone, Serialize, Deserialize)]
133pub struct RsaAdditionalPrime {
134	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
135	pub r: Vec<u8>,
136	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
137	pub d: Vec<u8>,
138	#[serde(serialize_with = "serialize_base64url", deserialize_with = "deserialize_base64url")]
139	pub t: Vec<u8>,
140}
141
142/// JWK, almost to spec (<https://datatracker.ietf.org/doc/html/rfc7517>) but not quite the same
143/// because it's annoying to implement.
144#[derive(Clone, Serialize, Deserialize)]
145#[serde(remote = "Self")]
146pub struct Key {
147	/// The algorithm used by the key.
148	#[serde(rename = "alg")]
149	pub algorithm: Algorithm,
150
151	/// The operations that the key can perform.
152	#[serde(rename = "key_ops")]
153	pub operations: HashSet<KeyOperation>,
154
155	/// Defaults to KeyType::OCT
156	#[serde(flatten)]
157	pub key: KeyType,
158
159	/// The key ID, useful for rotating keys.
160	#[serde(skip_serializing_if = "Option::is_none")]
161	pub kid: Option<crate::KeyId>,
162
163	// Cached for performance reasons, unfortunately.
164	#[serde(skip)]
165	pub(crate) decode: OnceLock<DecodingKey>,
166
167	#[serde(skip)]
168	pub(crate) encode: OnceLock<EncodingKey>,
169}
170
171impl<'de> Deserialize<'de> for Key {
172	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173	where
174		D: Deserializer<'de>,
175	{
176		let mut value = serde_json::Value::deserialize(deserializer)?;
177
178		// Normally the "kty" parameter is required in a JWK: https://datatracker.ietf.org/doc/html/rfc7517#section-4.1
179		// But for backwards compatibility we need to default to "oct" because in a previous
180		// implementation the parameter was omitted, and we want to keep previously generated tokens valid
181		if let Some(obj) = value.as_object_mut()
182			&& !obj.contains_key("kty")
183		{
184			obj.insert("kty".to_string(), serde_json::Value::String("oct".to_string()));
185		}
186
187		Self::deserialize(value).map_err(serde::de::Error::custom)
188	}
189}
190
191impl Serialize for Key {
192	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
193	where
194		S: Serializer,
195	{
196		Self::serialize(self, serializer)
197	}
198}
199
200impl fmt::Debug for Key {
201	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202		f.debug_struct("Key")
203			.field("algorithm", &self.algorithm)
204			.field("operations", &self.operations)
205			.field("kid", &self.kid)
206			.finish()
207	}
208}
209
210impl Key {
211	/// Parse a key from a string, auto-detecting JSON or base64url encoding.
212	#[allow(clippy::should_implement_trait)]
213	pub fn from_str(s: &str) -> crate::Result<Self> {
214		let s = s.trim();
215		if s.starts_with('{') {
216			Ok(serde_json::from_str(s)?)
217		} else {
218			let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(s)?;
219			let json = String::from_utf8(decoded)?;
220			Ok(serde_json::from_str(&json)?)
221		}
222	}
223
224	/// Load a key from a file, auto-detecting JSON or base64url encoding.
225	pub fn from_file<P: AsRef<StdPath>>(path: P) -> crate::Result<Self> {
226		let contents = std::fs::read_to_string(&path)?;
227		Self::from_str(&contents)
228	}
229
230	/// Async version of [`from_file`](Self::from_file), using `tokio::fs`.
231	#[cfg(feature = "tokio")]
232	pub async fn from_file_async<P: AsRef<StdPath>>(path: P) -> crate::Result<Self> {
233		let contents = tokio::fs::read_to_string(path).await?;
234		Self::from_str(&contents)
235	}
236
237	/// Encode the key as base64url-encoded JSON.
238	pub fn to_str(&self) -> crate::Result<String> {
239		let json = serde_json::to_string(self)?;
240		Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(json.as_bytes()))
241	}
242
243	/// Write the key to a file as base64url-encoded JSON.
244	pub fn to_file<P: AsRef<StdPath>>(&self, path: P) -> crate::Result<()> {
245		let encoded = self.to_str()?;
246		std::fs::write(path, encoded)?;
247		Ok(())
248	}
249
250	pub fn to_public(&self) -> crate::Result<Self> {
251		if !self.operations.contains(&KeyOperation::Verify) {
252			return Err(KeyError::VerifyUnsupported.into());
253		}
254
255		let key = match self.key {
256			KeyType::RSA { ref public, .. } => KeyType::RSA {
257				public: public.clone(),
258				private: None,
259			},
260			KeyType::EC {
261				ref x,
262				ref y,
263				ref curve,
264				..
265			} => KeyType::EC {
266				x: x.clone(),
267				y: y.clone(),
268				curve: curve.clone(),
269				d: None,
270			},
271			KeyType::OCT { .. } => return Err(KeyError::NoPublicKey.into()),
272			KeyType::OKP { ref x, ref curve, .. } => KeyType::OKP {
273				x: x.clone(),
274				curve: curve.clone(),
275				d: None,
276			},
277		};
278
279		Ok(Self {
280			algorithm: self.algorithm,
281			operations: [KeyOperation::Verify].into(),
282			key,
283			kid: self.kid.clone(),
284			decode: Default::default(),
285			encode: Default::default(),
286		})
287	}
288
289	fn to_decoding_key(&self) -> crate::Result<&DecodingKey> {
290		if let Some(key) = self.decode.get() {
291			return Ok(key);
292		}
293
294		let decoding_key = match self.key {
295			KeyType::OCT { ref secret } => match self.algorithm {
296				Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => DecodingKey::from_secret(secret),
297				_ => return Err(KeyError::InvalidAlgorithm.into()),
298			},
299			KeyType::EC {
300				ref curve,
301				ref x,
302				ref y,
303				..
304			} => match curve {
305				EllipticCurve::P256 => {
306					if self.algorithm != Algorithm::ES256 {
307						return Err(KeyError::InvalidAlgorithmForCurve("P-256").into());
308					}
309					if x.len() != 32 || y.len() != 32 {
310						return Err(KeyError::InvalidCoordinateLength("P-256").into());
311					}
312
313					DecodingKey::from_ec_components(
314						base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
315						base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(y).as_ref(),
316					)?
317				}
318				EllipticCurve::P384 => {
319					if self.algorithm != Algorithm::ES384 {
320						return Err(KeyError::InvalidAlgorithmForCurve("P-384").into());
321					}
322					if x.len() != 48 || y.len() != 48 {
323						return Err(KeyError::InvalidCoordinateLength("P-384").into());
324					}
325
326					DecodingKey::from_ec_components(
327						base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
328						base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(y).as_ref(),
329					)?
330				}
331				_ => return Err(KeyError::InvalidCurve("EC").into()),
332			},
333			KeyType::OKP { ref curve, ref x, .. } => match curve {
334				EllipticCurve::Ed25519 => {
335					if self.algorithm != Algorithm::EdDSA {
336						return Err(KeyError::InvalidAlgorithmForCurve("Ed25519").into());
337					}
338
339					DecodingKey::from_ed_components(
340						base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(x).as_ref(),
341					)?
342				}
343				_ => return Err(KeyError::InvalidCurve("OKP").into()),
344			},
345			KeyType::RSA { ref public, .. } => {
346				DecodingKey::from_rsa_raw_components(public.n.as_ref(), public.e.as_ref())
347			}
348		};
349
350		Ok(self.decode.get_or_init(|| decoding_key))
351	}
352
353	fn to_encoding_key(&self) -> crate::Result<&EncodingKey> {
354		if let Some(key) = self.encode.get() {
355			return Ok(key);
356		}
357
358		let encoding_key = match self.key {
359			KeyType::OCT { ref secret } => match self.algorithm {
360				Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => EncodingKey::from_secret(secret),
361				_ => return Err(KeyError::InvalidAlgorithm.into()),
362			},
363			KeyType::EC { ref curve, ref d, .. } => {
364				let d = d.as_ref().ok_or(KeyError::MissingPrivateKey)?;
365
366				match curve {
367					EllipticCurve::P256 => {
368						let secret_key = SecretKey::<p256::NistP256>::from_slice(d)?;
369						let doc = secret_key.to_pkcs8_der()?;
370						EncodingKey::from_ec_der(doc.as_bytes())
371					}
372					EllipticCurve::P384 => {
373						let secret_key = SecretKey::<p384::NistP384>::from_slice(d)?;
374						let doc = secret_key.to_pkcs8_der()?;
375						EncodingKey::from_ec_der(doc.as_bytes())
376					}
377					_ => return Err(KeyError::InvalidCurve("EC").into()),
378				}
379			}
380			KeyType::OKP {
381				ref curve,
382				ref d,
383				ref x,
384			} => {
385				let d = d.as_ref().ok_or(KeyError::MissingPrivateKey)?;
386
387				let key_pair =
388					aws_lc_rs::signature::Ed25519KeyPair::from_seed_and_public_key(d.as_slice(), x.as_slice())?;
389
390				match curve {
391					EllipticCurve::Ed25519 => EncodingKey::from_ed_der(key_pair.to_pkcs8()?.as_ref()),
392					_ => return Err(KeyError::InvalidCurve("OKP").into()),
393				}
394			}
395			KeyType::RSA {
396				ref public,
397				ref private,
398			} => {
399				let n = BigUint::from_bytes_be(&public.n);
400				let e = BigUint::from_bytes_be(&public.e);
401				let private = private.as_ref().ok_or(KeyError::MissingPrivateKey)?;
402				let d = BigUint::from_bytes_be(&private.d);
403				let p = BigUint::from_bytes_be(&private.p);
404				let q = BigUint::from_bytes_be(&private.q);
405
406				let rsa = rsa::RsaPrivateKey::from_components(n, e, d, vec![p, q]);
407				let pem = rsa?.to_pkcs1_pem(rsa::pkcs1::LineEnding::LF);
408
409				EncodingKey::from_rsa_pem(pem?.as_bytes())?
410			}
411		};
412
413		Ok(self.encode.get_or_init(|| encoding_key))
414	}
415
416	pub fn decode(&self, token: &str) -> crate::Result<Claims> {
417		if !self.operations.contains(&KeyOperation::Verify) {
418			return Err(KeyError::VerifyUnsupported.into());
419		}
420
421		let decode = self.to_decoding_key()?;
422
423		let mut validation = jsonwebtoken::Validation::new(self.algorithm.into());
424		validation.required_spec_claims = Default::default(); // Don't require exp, but still validate it if present
425		validation.validate_exp = false; // We validate exp ourselves to handle null values
426
427		let token = jsonwebtoken::decode::<Claims>(token, decode, &validation)?;
428
429		if let Some(exp) = token.claims.expires
430			&& exp < std::time::SystemTime::now()
431		{
432			return Err(crate::Error::TokenExpired);
433		}
434
435		token.claims.validate()?;
436
437		Ok(token.claims)
438	}
439
440	pub fn encode(&self, payload: &Claims) -> crate::Result<String> {
441		if !self.operations.contains(&KeyOperation::Sign) {
442			return Err(KeyError::SignUnsupported.into());
443		}
444
445		payload.validate()?;
446
447		let encode = self.to_encoding_key()?;
448
449		let mut header = Header::new(self.algorithm.into());
450		header.kid = self.kid.as_ref().map(|k| k.to_string());
451		let token = jsonwebtoken::encode(&header, &payload, encode)?;
452		Ok(token)
453	}
454
455	/// Generate a key pair for the given algorithm, returning the private and public keys.
456	pub fn generate(algorithm: Algorithm, id: Option<crate::KeyId>) -> crate::Result<Self> {
457		generate(algorithm, id)
458	}
459}
460
461/// Serialize bytes as base64url without padding
462fn serialize_base64url<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
463where
464	S: Serializer,
465{
466	let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes);
467	serializer.serialize_str(&encoded)
468}
469
470fn serialize_base64url_optional<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
471where
472	S: Serializer,
473{
474	match bytes {
475		Some(b) => serialize_base64url(b, serializer),
476		None => serializer.serialize_none(),
477	}
478}
479
480/// Deserialize base64url string to bytes, supporting both padded and unpadded formats for backwards compatibility
481fn deserialize_base64url<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
482where
483	D: Deserializer<'de>,
484{
485	let s = String::deserialize(deserializer)?;
486
487	// Try to decode as unpadded base64url first (preferred format)
488	base64::engine::general_purpose::URL_SAFE_NO_PAD
489		.decode(&s)
490		.or_else(|_| {
491			// Fall back to padded base64url for backwards compatibility
492			base64::engine::general_purpose::URL_SAFE.decode(&s)
493		})
494		.map_err(serde::de::Error::custom)
495}
496
497fn deserialize_base64url_optional<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
498where
499	D: Deserializer<'de>,
500{
501	let s: Option<String> = Option::deserialize(deserializer)?;
502	match s {
503		Some(s) => {
504			let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
505				.decode(&s)
506				.or_else(|_| base64::engine::general_purpose::URL_SAFE.decode(&s))
507				.map_err(serde::de::Error::custom)?;
508			Ok(Some(decoded))
509		}
510		None => Ok(None),
511	}
512}
513
514#[cfg(test)]
515mod tests {
516	use super::*;
517	use std::time::{Duration, SystemTime};
518
519	fn create_test_key() -> Key {
520		Key {
521			algorithm: Algorithm::HS256,
522			operations: [KeyOperation::Sign, KeyOperation::Verify].into(),
523			key: KeyType::OCT {
524				secret: b"test-secret-that-is-long-enough-for-hmac-sha256".to_vec(),
525			},
526			kid: Some(crate::KeyId::decode("test-key-1").unwrap()),
527			decode: Default::default(),
528			encode: Default::default(),
529		}
530	}
531
532	fn create_test_claims() -> Claims {
533		Claims {
534			root: "test-path".to_string(),
535			publish: vec!["test-pub".into()],
536			cluster: false,
537			subscribe: vec!["test-sub".into()],
538			expires: Some(SystemTime::now() + Duration::from_secs(3600)),
539			issued: Some(SystemTime::now()),
540		}
541	}
542
543	#[test]
544	fn test_key_from_str_valid() {
545		let key = create_test_key();
546		let json = key.to_str().unwrap();
547		let loaded_key = Key::from_str(&json).unwrap();
548
549		assert_eq!(loaded_key.algorithm, key.algorithm);
550		assert_eq!(loaded_key.operations, key.operations);
551		match (loaded_key.key, key.key) {
552			(KeyType::OCT { secret: loaded_secret }, KeyType::OCT { secret }) => {
553				assert_eq!(loaded_secret, secret);
554			}
555			_ => panic!("Expected OCT key"),
556		}
557		assert_eq!(loaded_key.kid, key.kid);
558	}
559
560	/// Tests whether Key::from_str() works for keys without a kty value to fall back to OCT
561	#[test]
562	fn test_key_oct_backwards_compatibility() {
563		let json = r#"{"alg":"HS256","key_ops":["sign","verify"],"k":"Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68"}"#;
564		let key = Key::from_str(json);
565
566		assert!(key.is_ok());
567		let key = key.unwrap();
568
569		if let KeyType::OCT { ref secret, .. } = key.key {
570			let base64_key = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(secret);
571			assert_eq!(base64_key, "Fp8kipWUJeUFqeSqWym_tRC_tyI8z-QpqopIGrbrD68");
572		} else {
573			panic!("Expected OCT key");
574		}
575
576		let key_str = key.to_str().unwrap();
577
578		// Round-trip through from_str and verify fields
579		let loaded = Key::from_str(&key_str).unwrap();
580		assert_eq!(loaded.algorithm, Algorithm::HS256);
581		assert!(loaded.operations.contains(&KeyOperation::Sign));
582		assert!(loaded.operations.contains(&KeyOperation::Verify));
583		assert!(matches!(loaded.key, KeyType::OCT { .. }));
584	}
585
586	#[test]
587	fn test_key_from_str_invalid_json() {
588		let result = Key::from_str("invalid json");
589		assert!(result.is_err());
590	}
591
592	#[test]
593	fn test_key_to_str() {
594		let key = create_test_key();
595		let encoded = key.to_str().unwrap();
596
597		// Should be base64url, not raw JSON
598		assert!(!encoded.contains('{'));
599
600		// Round-trip through from_str
601		let loaded = Key::from_str(&encoded).unwrap();
602		assert_eq!(loaded.algorithm, Algorithm::HS256);
603		assert_eq!(loaded.kid, key.kid);
604		assert!(loaded.operations.contains(&KeyOperation::Sign));
605		assert!(loaded.operations.contains(&KeyOperation::Verify));
606	}
607
608	#[test]
609	fn test_key_sign_success() {
610		let key = create_test_key();
611		let claims = create_test_claims();
612		let token = key.encode(&claims).unwrap();
613
614		assert!(!token.is_empty());
615		assert_eq!(token.matches('.').count(), 2); // JWT format: header.payload.signature
616	}
617
618	#[test]
619	fn test_key_sign_no_permission() {
620		let mut key = create_test_key();
621		key.operations = [KeyOperation::Verify].into();
622		let claims = create_test_claims();
623
624		let result = key.encode(&claims);
625		assert!(result.is_err());
626		assert!(result.unwrap_err().to_string().contains("key does not support signing"));
627	}
628
629	#[test]
630	fn test_key_sign_invalid_claims() {
631		let key = create_test_key();
632		let invalid_claims = Claims {
633			root: "test-path".to_string(),
634			publish: vec![],
635			subscribe: vec![],
636			cluster: false,
637			expires: None,
638			issued: None,
639		};
640
641		let result = key.encode(&invalid_claims);
642		assert!(result.is_err());
643		assert!(
644			result
645				.unwrap_err()
646				.to_string()
647				.contains("no publish or subscribe allowed; token is useless")
648		);
649	}
650
651	#[test]
652	fn test_key_verify_success() {
653		let key = create_test_key();
654		let claims = create_test_claims();
655		let token = key.encode(&claims).unwrap();
656
657		let verified_claims = key.decode(&token).unwrap();
658		assert_eq!(verified_claims.root, claims.root);
659		assert_eq!(verified_claims.publish, claims.publish);
660		assert_eq!(verified_claims.subscribe, claims.subscribe);
661		assert_eq!(verified_claims.cluster, claims.cluster);
662	}
663
664	#[test]
665	fn test_key_verify_no_permission() {
666		let mut key = create_test_key();
667		key.operations = [KeyOperation::Sign].into();
668
669		let result = key.decode("some.jwt.token");
670		assert!(result.is_err());
671		assert!(
672			result
673				.unwrap_err()
674				.to_string()
675				.contains("key does not support verification")
676		);
677	}
678
679	#[test]
680	fn test_key_verify_invalid_token() {
681		let key = create_test_key();
682		let result = key.decode("invalid-token");
683		assert!(result.is_err());
684	}
685
686	#[test]
687	fn test_key_verify_path_mismatch() {
688		let key = create_test_key();
689		let claims = create_test_claims();
690		let token = key.encode(&claims).unwrap();
691
692		// This test was expecting a path mismatch error, but now decode succeeds
693		let result = key.decode(&token);
694		assert!(result.is_ok());
695	}
696
697	#[test]
698	fn test_key_verify_expired_token() {
699		let key = create_test_key();
700		let mut claims = create_test_claims();
701		claims.expires = Some(SystemTime::now() - Duration::from_secs(3600)); // 1 hour ago
702		let token = key.encode(&claims).unwrap();
703
704		let result = key.decode(&token);
705		assert!(result.is_err());
706	}
707
708	#[test]
709	fn test_key_verify_token_without_exp() {
710		let key = create_test_key();
711		let claims = Claims {
712			root: "test-path".to_string(),
713			publish: vec!["".to_string()],
714			subscribe: vec!["".to_string()],
715			cluster: false,
716			expires: None,
717			issued: None,
718		};
719		let token = key.encode(&claims).unwrap();
720
721		let verified_claims = key.decode(&token).unwrap();
722		assert_eq!(verified_claims.root, claims.root);
723		assert_eq!(verified_claims.publish, claims.publish);
724		assert_eq!(verified_claims.subscribe, claims.subscribe);
725		assert_eq!(verified_claims.expires, None);
726	}
727
728	#[test]
729	fn test_key_round_trip() {
730		let key = create_test_key();
731		let original_claims = Claims {
732			root: "test-path".to_string(),
733			publish: vec!["test-pub".into()],
734			subscribe: vec!["test-sub".into()],
735			cluster: true,
736			expires: Some(SystemTime::now() + Duration::from_secs(3600)),
737			issued: Some(SystemTime::now()),
738		};
739
740		let token = key.encode(&original_claims).unwrap();
741		let verified_claims = key.decode(&token).unwrap();
742
743		assert_eq!(verified_claims.root, original_claims.root);
744		assert_eq!(verified_claims.publish, original_claims.publish);
745		assert_eq!(verified_claims.subscribe, original_claims.subscribe);
746		assert_eq!(verified_claims.cluster, original_claims.cluster);
747	}
748
749	#[test]
750	fn test_key_generate_hs256() {
751		let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
752		assert!(key.is_ok());
753		let key = key.unwrap();
754
755		assert_eq!(key.algorithm, Algorithm::HS256);
756		assert_eq!(key.kid, Some(crate::KeyId::decode("test-id").unwrap()));
757		assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
758
759		match key.key {
760			KeyType::OCT { ref secret } => assert_eq!(secret.len(), 32),
761			_ => panic!("Expected OCT key"),
762		}
763	}
764
765	#[test]
766	fn test_key_generate_hs384() {
767		let key = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
768		assert!(key.is_ok());
769		let key = key.unwrap();
770
771		assert_eq!(key.algorithm, Algorithm::HS384);
772
773		match key.key {
774			KeyType::OCT { ref secret } => assert_eq!(secret.len(), 48),
775			_ => panic!("Expected OCT key"),
776		}
777	}
778
779	#[test]
780	fn test_key_generate_hs512() {
781		let key = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
782		assert!(key.is_ok());
783		let key = key.unwrap();
784
785		assert_eq!(key.algorithm, Algorithm::HS512);
786
787		match key.key {
788			KeyType::OCT { ref secret } => assert_eq!(secret.len(), 64),
789			_ => panic!("Expected OCT key"),
790		}
791	}
792
793	#[test]
794	fn test_key_generate_rs512() {
795		let key = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
796		assert!(key.is_ok());
797		let key = key.unwrap();
798
799		assert_eq!(key.algorithm, Algorithm::RS512);
800		assert!(matches!(key.key, KeyType::RSA { .. }));
801		match key.key {
802			KeyType::RSA {
803				ref public,
804				ref private,
805			} => {
806				assert!(private.is_some());
807				assert_eq!(public.n.len(), 256);
808				assert_eq!(public.e.len(), 3);
809			}
810			_ => panic!("Expected RSA key"),
811		}
812	}
813
814	#[test]
815	fn test_key_generate_es256() {
816		let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
817		assert!(key.is_ok());
818		let key = key.unwrap();
819
820		assert_eq!(key.algorithm, Algorithm::ES256);
821		assert!(matches!(key.key, KeyType::EC { .. }))
822	}
823
824	#[test]
825	fn test_key_generate_ps512() {
826		let key = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
827		assert!(key.is_ok());
828		let key = key.unwrap();
829
830		assert_eq!(key.algorithm, Algorithm::PS512);
831		assert!(matches!(key.key, KeyType::RSA { .. }));
832	}
833
834	#[test]
835	fn test_key_generate_eddsa() {
836		let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
837		assert!(key.is_ok());
838		let key = key.unwrap();
839
840		assert_eq!(key.algorithm, Algorithm::EdDSA);
841		assert!(matches!(key.key, KeyType::OKP { .. }));
842	}
843
844	#[test]
845	fn test_key_generate_without_id() {
846		let key = Key::generate(Algorithm::HS256, None);
847		assert!(key.is_ok());
848		let key = key.unwrap();
849
850		assert_eq!(key.algorithm, Algorithm::HS256);
851		assert_eq!(key.kid, None);
852		assert_eq!(key.operations, [KeyOperation::Sign, KeyOperation::Verify].into());
853	}
854
855	#[test]
856	fn test_public_key_conversion_hmac() {
857		let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()))
858			.expect("HMAC key generation failed");
859
860		assert!(key.to_public().is_err());
861	}
862
863	#[test]
864	fn test_public_key_conversion_rsa() {
865		let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
866		assert!(key.is_ok());
867		let key = key.unwrap();
868
869		let public_key = key.to_public().unwrap();
870		assert_eq!(key.kid, public_key.kid);
871		assert_eq!(public_key.operations, [KeyOperation::Verify].into());
872		assert!(public_key.encode.get().is_none());
873		assert!(public_key.decode.get().is_none());
874		assert!(matches!(public_key.key, KeyType::RSA { .. }));
875
876		if let KeyType::RSA { public, private } = &public_key.key {
877			assert!(private.is_none());
878
879			if let KeyType::RSA { public: src_public, .. } = &key.key {
880				assert_eq!(public.e, src_public.e);
881				assert_eq!(public.n, src_public.n);
882			} else {
883				unreachable!("Expected RSA key")
884			}
885		} else {
886			unreachable!("Expected RSA key");
887		}
888	}
889
890	#[test]
891	fn test_public_key_conversion_es() {
892		let key = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
893		assert!(key.is_ok());
894		let key = key.unwrap();
895
896		let public_key = key.to_public().unwrap();
897		assert_eq!(key.kid, public_key.kid);
898		assert_eq!(public_key.operations, [KeyOperation::Verify].into());
899		assert!(public_key.encode.get().is_none());
900		assert!(public_key.decode.get().is_none());
901		assert!(matches!(public_key.key, KeyType::EC { .. }));
902
903		if let KeyType::EC { x, y, d, curve } = &public_key.key {
904			assert!(d.is_none());
905
906			if let KeyType::EC {
907				x: src_x,
908				y: src_y,
909				curve: src_curve,
910				..
911			} = &key.key
912			{
913				assert_eq!(x, src_x);
914				assert_eq!(y, src_y);
915				assert_eq!(curve, src_curve);
916			} else {
917				unreachable!("Expected EC key")
918			}
919		} else {
920			unreachable!("Expected EC key");
921		}
922	}
923
924	#[test]
925	fn test_public_key_conversion_ed() {
926		let key = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
927		assert!(key.is_ok());
928		let key = key.unwrap();
929
930		let public_key = key.to_public().unwrap();
931		assert_eq!(key.kid, public_key.kid);
932		assert_eq!(public_key.operations, [KeyOperation::Verify].into());
933		assert!(public_key.encode.get().is_none());
934		assert!(public_key.decode.get().is_none());
935		assert!(matches!(public_key.key, KeyType::OKP { .. }));
936
937		if let KeyType::OKP { x, d, curve } = &public_key.key {
938			assert!(d.is_none());
939
940			if let KeyType::OKP {
941				x: src_x,
942				curve: src_curve,
943				..
944			} = &key.key
945			{
946				assert_eq!(x, src_x);
947				assert_eq!(curve, src_curve);
948			} else {
949				unreachable!("Expected OKP key")
950			}
951		} else {
952			unreachable!("Expected OKP key");
953		}
954	}
955
956	#[test]
957	fn test_key_generate_sign_verify_cycle() {
958		let key = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
959		assert!(key.is_ok());
960		let key = key.unwrap();
961
962		let claims = create_test_claims();
963
964		let token = key.encode(&claims).unwrap();
965		let verified_claims = key.decode(&token).unwrap();
966
967		assert_eq!(verified_claims.root, claims.root);
968		assert_eq!(verified_claims.publish, claims.publish);
969		assert_eq!(verified_claims.subscribe, claims.subscribe);
970		assert_eq!(verified_claims.cluster, claims.cluster);
971	}
972
973	#[test]
974	fn test_key_debug_no_secret() {
975		let key = create_test_key();
976		let debug_str = format!("{key:?}");
977
978		assert!(debug_str.contains("algorithm: HS256"));
979		assert!(debug_str.contains("operations"));
980		assert!(debug_str.contains("kid: Some(KeyId(\"test-key-1\"))"));
981		assert!(!debug_str.contains("secret")); // Should not contain secret
982	}
983
984	#[test]
985	fn test_key_operations_enum() {
986		let sign_op = KeyOperation::Sign;
987		let verify_op = KeyOperation::Verify;
988		let decrypt_op = KeyOperation::Decrypt;
989		let encrypt_op = KeyOperation::Encrypt;
990
991		assert_eq!(sign_op, KeyOperation::Sign);
992		assert_eq!(verify_op, KeyOperation::Verify);
993		assert_eq!(decrypt_op, KeyOperation::Decrypt);
994		assert_eq!(encrypt_op, KeyOperation::Encrypt);
995
996		assert_ne!(sign_op, verify_op);
997		assert_ne!(decrypt_op, encrypt_op);
998	}
999
1000	#[test]
1001	fn test_key_operations_serde() {
1002		let operations = [KeyOperation::Sign, KeyOperation::Verify];
1003		let json = serde_json::to_string(&operations).unwrap();
1004		assert!(json.contains("\"sign\""));
1005		assert!(json.contains("\"verify\""));
1006
1007		let deserialized: Vec<KeyOperation> = serde_json::from_str(&json).unwrap();
1008		assert_eq!(deserialized, operations);
1009	}
1010
1011	#[test]
1012	fn test_key_serde() {
1013		let key = create_test_key();
1014		let json = serde_json::to_string(&key).unwrap();
1015		let deserialized: Key = serde_json::from_str(&json).unwrap();
1016
1017		assert_eq!(deserialized.algorithm, key.algorithm);
1018		assert_eq!(deserialized.operations, key.operations);
1019		assert_eq!(deserialized.kid, key.kid);
1020
1021		if let (
1022			KeyType::OCT {
1023				secret: original_secret,
1024			},
1025			KeyType::OCT {
1026				secret: deserialized_secret,
1027			},
1028		) = (&key.key, &deserialized.key)
1029		{
1030			assert_eq!(deserialized_secret, original_secret);
1031		} else {
1032			panic!("Expected both keys to be OCT variant");
1033		}
1034	}
1035
1036	#[test]
1037	fn test_key_clone() {
1038		let key = create_test_key();
1039		let cloned = key.clone();
1040
1041		assert_eq!(cloned.algorithm, key.algorithm);
1042		assert_eq!(cloned.operations, key.operations);
1043		assert_eq!(cloned.kid, key.kid);
1044
1045		if let (
1046			KeyType::OCT {
1047				secret: original_secret,
1048			},
1049			KeyType::OCT { secret: cloned_secret },
1050		) = (&key.key, &cloned.key)
1051		{
1052			assert_eq!(cloned_secret, original_secret);
1053		} else {
1054			panic!("Expected both keys to be OCT variant");
1055		}
1056	}
1057
1058	#[test]
1059	fn test_hmac_algorithms() {
1060		let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1061		let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1062		let key_512 = Key::generate(Algorithm::HS512, Some(crate::KeyId::decode("test-id").unwrap()));
1063
1064		let claims = create_test_claims();
1065
1066		// Test that each algorithm can sign and verify
1067		for key in [key_256, key_384, key_512] {
1068			assert!(key.is_ok());
1069			let key = key.unwrap();
1070
1071			let token = key.encode(&claims).unwrap();
1072			let verified_claims = key.decode(&token).unwrap();
1073			assert_eq!(verified_claims.root, claims.root);
1074		}
1075	}
1076
1077	#[test]
1078	fn test_rsa_pkcs1_asymmetric_algorithms() {
1079		let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1080		let key_rs384 = Key::generate(Algorithm::RS384, Some(crate::KeyId::decode("test-id").unwrap()));
1081		let key_rs512 = Key::generate(Algorithm::RS512, Some(crate::KeyId::decode("test-id").unwrap()));
1082
1083		for key in [key_rs256, key_rs384, key_rs512] {
1084			test_asymmetric_key(key);
1085		}
1086	}
1087
1088	#[test]
1089	fn test_rsa_pss_asymmetric_algorithms() {
1090		let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1091		let key_ps384 = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1092		let key_ps512 = Key::generate(Algorithm::PS512, Some(crate::KeyId::decode("test-id").unwrap()));
1093
1094		for key in [key_ps256, key_ps384, key_ps512] {
1095			test_asymmetric_key(key);
1096		}
1097	}
1098
1099	#[test]
1100	fn test_ec_asymmetric_algorithms() {
1101		let key_es256 = Key::generate(Algorithm::ES256, Some(crate::KeyId::decode("test-id").unwrap()));
1102		let key_es384 = Key::generate(Algorithm::ES384, Some(crate::KeyId::decode("test-id").unwrap()));
1103
1104		for key in [key_es256, key_es384] {
1105			test_asymmetric_key(key);
1106		}
1107	}
1108
1109	#[test]
1110	fn test_ed_asymmetric_algorithms() {
1111		let key_eddsa = Key::generate(Algorithm::EdDSA, Some(crate::KeyId::decode("test-id").unwrap()));
1112
1113		test_asymmetric_key(key_eddsa);
1114	}
1115
1116	fn test_asymmetric_key(key: crate::Result<Key>) {
1117		assert!(key.is_ok());
1118		let key = key.unwrap();
1119
1120		let claims = create_test_claims();
1121		let token = key.encode(&claims).unwrap();
1122
1123		let private_verified_claims = key.decode(&token).unwrap();
1124		assert_eq!(
1125			private_verified_claims.root, claims.root,
1126			"validation using private key"
1127		);
1128
1129		let public_verified_claims = key.to_public().unwrap().decode(&token).unwrap();
1130		assert_eq!(public_verified_claims.root, claims.root, "validation using public key");
1131	}
1132
1133	#[test]
1134	fn test_cross_algorithm_verification_fails() {
1135		let key_256 = Key::generate(Algorithm::HS256, Some(crate::KeyId::decode("test-id").unwrap()));
1136		assert!(key_256.is_ok());
1137		let key_256 = key_256.unwrap();
1138
1139		let key_384 = Key::generate(Algorithm::HS384, Some(crate::KeyId::decode("test-id").unwrap()));
1140		assert!(key_384.is_ok());
1141		let key_384 = key_384.unwrap();
1142
1143		let claims = create_test_claims();
1144		let token = key_256.encode(&claims).unwrap();
1145
1146		// Different algorithm should fail verification
1147		let result = key_384.decode(&token);
1148		assert!(result.is_err());
1149	}
1150
1151	#[test]
1152	fn test_asymmetric_cross_algorithm_verification_fails() {
1153		let key_rs256 = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1154		assert!(key_rs256.is_ok());
1155		let key_rs256 = key_rs256.unwrap();
1156
1157		let key_ps256 = Key::generate(Algorithm::PS256, Some(crate::KeyId::decode("test-id").unwrap()));
1158		assert!(key_ps256.is_ok());
1159		let key_ps256 = key_ps256.unwrap();
1160
1161		let claims = create_test_claims();
1162		let token = key_rs256.encode(&claims).unwrap();
1163
1164		// Different algorithm should fail verification
1165		let private_result = key_ps256.decode(&token);
1166		let public_result = key_ps256.to_public().unwrap().decode(&token);
1167		assert!(private_result.is_err());
1168		assert!(public_result.is_err());
1169	}
1170
1171	#[test]
1172	fn test_rsa_pkcs1_public_key_conversion() {
1173		let key = Key::generate(Algorithm::RS256, Some(crate::KeyId::decode("test-id").unwrap()));
1174		assert!(key.is_ok());
1175		let key = key.unwrap();
1176
1177		assert!(key.operations.contains(&KeyOperation::Sign));
1178		assert!(key.operations.contains(&KeyOperation::Verify));
1179
1180		let public_key = key.to_public().unwrap();
1181		assert!(!public_key.operations.contains(&KeyOperation::Sign));
1182		assert!(public_key.operations.contains(&KeyOperation::Verify));
1183
1184		match key.key {
1185			KeyType::RSA {
1186				ref public,
1187				ref private,
1188			} => {
1189				assert!(private.is_some());
1190				assert_eq!(public.n.len(), 256);
1191				assert_eq!(public.e.len(), 3);
1192
1193				match public_key.key {
1194					KeyType::RSA {
1195						public: ref guest_public,
1196						private: ref public_private,
1197					} => {
1198						assert!(public_private.is_none());
1199						assert_eq!(public.n, guest_public.n);
1200						assert_eq!(public.e, guest_public.e);
1201					}
1202					_ => panic!("Expected public key to be an RSA key"),
1203				}
1204			}
1205			_ => panic!("Expected private key to be an RSA key"),
1206		}
1207	}
1208
1209	#[test]
1210	fn test_rsa_pss_public_key_conversion() {
1211		let key = Key::generate(Algorithm::PS384, Some(crate::KeyId::decode("test-id").unwrap()));
1212		assert!(key.is_ok());
1213		let key = key.unwrap();
1214
1215		assert!(key.operations.contains(&KeyOperation::Sign));
1216		assert!(key.operations.contains(&KeyOperation::Verify));
1217
1218		let public_key = key.to_public().unwrap();
1219		assert!(!public_key.operations.contains(&KeyOperation::Sign));
1220		assert!(public_key.operations.contains(&KeyOperation::Verify));
1221
1222		match key.key {
1223			KeyType::RSA {
1224				ref public,
1225				ref private,
1226			} => {
1227				assert!(private.is_some());
1228				assert_eq!(public.n.len(), 256);
1229				assert_eq!(public.e.len(), 3);
1230
1231				match public_key.key {
1232					KeyType::RSA {
1233						public: ref guest_public,
1234						private: ref public_private,
1235					} => {
1236						assert!(public_private.is_none());
1237						assert_eq!(public.n, guest_public.n);
1238						assert_eq!(public.e, guest_public.e);
1239					}
1240					_ => panic!("Expected public key to be an RSA key"),
1241				}
1242			}
1243			_ => panic!("Expected private key to be an RSA key"),
1244		}
1245	}
1246
1247	#[test]
1248	fn test_base64url_serialization() {
1249		let key = create_test_key();
1250		let json = serde_json::to_string(&key).unwrap();
1251
1252		// Check that the secret is base64url encoded without padding
1253		let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1254		let k_value = parsed["k"].as_str().unwrap();
1255
1256		// Base64url should not contain padding characters
1257		assert!(!k_value.contains('='));
1258		assert!(!k_value.contains('+'));
1259		assert!(!k_value.contains('/'));
1260
1261		// Verify it decodes correctly
1262		let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1263			.decode(k_value)
1264			.unwrap();
1265
1266		if let KeyType::OCT {
1267			secret: original_secret,
1268		} = &key.key
1269		{
1270			assert_eq!(decoded, *original_secret);
1271		} else {
1272			panic!("Expected both keys to be OCT variant");
1273		}
1274	}
1275
1276	#[test]
1277	fn test_backwards_compatibility_unpadded_base64url() {
1278		// Create a JSON with unpadded base64url (new format)
1279		let unpadded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY","kid":"test-key-1"}"#;
1280
1281		// Should be able to deserialize new format
1282		let key: Key = serde_json::from_str(unpadded_json).unwrap();
1283		assert_eq!(key.algorithm, Algorithm::HS256);
1284		assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1285
1286		if let KeyType::OCT { secret } = &key.key {
1287			assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1288		} else {
1289			panic!("Expected key to be OCT variant");
1290		}
1291	}
1292
1293	#[test]
1294	fn test_backwards_compatibility_padded_base64url() {
1295		// Create a JSON with padded base64url (old format) - same secret but with padding
1296		let padded_json = r#"{"kty":"oct","alg":"HS256","key_ops":["sign","verify"],"k":"dGVzdC1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaC1mb3ItaG1hYy1zaGEyNTY=","kid":"test-key-1"}"#;
1297
1298		// Should be able to deserialize old format for backwards compatibility
1299		let key: Key = serde_json::from_str(padded_json).unwrap();
1300		assert_eq!(key.algorithm, Algorithm::HS256);
1301		assert_eq!(key.kid, Some(crate::KeyId::decode("test-key-1").unwrap()));
1302
1303		if let KeyType::OCT { secret } = &key.key {
1304			assert_eq!(secret, b"test-secret-that-is-long-enough-for-hmac-sha256");
1305		} else {
1306			panic!("Expected key to be OCT variant");
1307		}
1308	}
1309
1310	// Tests that Rust can load keys generated by the JS @moq/token package
1311	// and verify tokens signed by JS.
1312	//
1313	// Generated with: bun -e 'import { generate } from "./js/token/src/generate.ts"; ...'
1314	// See js/token/src/interop.test.ts for the JS-side counterpart.
1315
1316	/// JS-generated HS256 key (from @moq/token generate("HS256", "js-test-key"))
1317	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":[]}"#;
1318
1319	/// JS-generated HS256 token (from @moq/token sign(key, {root:"live", put:["camera1"], get:["camera1","camera2"]}))
1320	const JS_HS256_TOKEN: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImpzLXRlc3Qta2V5In0.eyJyb290IjoibGl2ZSIsInB1dCI6WyJjYW1lcmExIl0sImdldCI6WyJjYW1lcmExIiwiY2FtZXJhMiJdLCJpYXQiOjE3NzUxNzY3NTR9.tHNQtHh_HCIKxXOexDCM7AkjqWzbULLZzjEckfOGRfY";
1321
1322	/// JS-generated EdDSA private key (from @moq/token generate("EdDSA", "js-eddsa-key"))
1323	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":[]}"#;
1324
1325	/// JS-generated EdDSA public key (from @moq/token toPublicKey(key))
1326	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"]}"#;
1327
1328	/// JS-generated EdDSA token (from @moq/token sign(key, {root:"stream", put:["video"]}))
1329	const JS_EDDSA_TOKEN: &str = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImpzLWVkZHNhLWtleSJ9.eyJyb290Ijoic3RyZWFtIiwicHV0IjpbInZpZGVvIl0sImlhdCI6MTc3NTE3Njc1Nn0.l9rUMHjPSXWKSXRP3mmeMgTAywtqpdqQehhViWaPrKxax1Y2D9KRIYTixYz-b6PI-AoHQYusHWeeLu_HRw2cAg";
1330
1331	#[test]
1332	fn test_js_hs256_key_load() {
1333		let key = Key::from_str(JS_HS256_KEY).unwrap();
1334		assert_eq!(key.algorithm, Algorithm::HS256);
1335		assert_eq!(key.kid, Some(crate::KeyId::decode("js-test-key").unwrap()));
1336	}
1337
1338	#[test]
1339	fn test_js_hs256_token_verify() {
1340		let key = Key::from_str(JS_HS256_KEY).unwrap();
1341		let claims = key.decode(JS_HS256_TOKEN).unwrap();
1342		assert_eq!(claims.root, "live");
1343		assert_eq!(claims.publish, vec!["camera1"]);
1344		assert_eq!(claims.subscribe, vec!["camera1", "camera2"]);
1345	}
1346
1347	#[test]
1348	fn test_js_hs256_sign_and_roundtrip() {
1349		let key = Key::from_str(JS_HS256_KEY).unwrap();
1350		let claims = Claims {
1351			root: "rust-test".to_string(),
1352			publish: vec!["pub1".into()],
1353			subscribe: vec!["sub1".into()],
1354			..Default::default()
1355		};
1356		let token = key.encode(&claims).unwrap();
1357		let verified = key.decode(&token).unwrap();
1358		assert_eq!(verified.root, "rust-test");
1359		assert_eq!(verified.publish, vec!["pub1"]);
1360	}
1361
1362	#[test]
1363	fn test_js_eddsa_key_load() {
1364		let private_key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1365		assert_eq!(private_key.algorithm, Algorithm::EdDSA);
1366		assert!(matches!(private_key.key, KeyType::OKP { .. }));
1367
1368		let public_key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1369		assert_eq!(public_key.algorithm, Algorithm::EdDSA);
1370	}
1371
1372	#[test]
1373	fn test_js_eddsa_token_verify_with_private_key() {
1374		let key = Key::from_str(JS_EDDSA_PRIVATE_KEY).unwrap();
1375		let claims = key.decode(JS_EDDSA_TOKEN).unwrap();
1376		assert_eq!(claims.root, "stream");
1377		assert_eq!(claims.publish, vec!["video"]);
1378	}
1379
1380	#[test]
1381	fn test_js_eddsa_token_verify_with_public_key() {
1382		let key = Key::from_str(JS_EDDSA_PUBLIC_KEY).unwrap();
1383		let claims = key.decode(JS_EDDSA_TOKEN).unwrap();
1384		assert_eq!(claims.root, "stream");
1385		assert_eq!(claims.publish, vec!["video"]);
1386	}
1387
1388	#[test]
1389	fn test_js_token_wrong_key_fails() {
1390		// Generate a different HS256 key
1391		let wrong_key = Key::generate(Algorithm::HS256, None).unwrap();
1392		let result = wrong_key.decode(JS_HS256_TOKEN);
1393		assert!(result.is_err());
1394	}
1395
1396	#[test]
1397	fn test_js_eddsa_token_wrong_key_fails() {
1398		// Try verifying EdDSA token with the HS256 key
1399		let wrong_key = Key::from_str(JS_HS256_KEY).unwrap();
1400		let result = wrong_key.decode(JS_EDDSA_TOKEN);
1401		assert!(result.is_err());
1402	}
1403
1404	#[test]
1405	fn test_file_io_base64url() {
1406		let key = create_test_key();
1407		let temp_dir = std::env::temp_dir();
1408		let temp_path = temp_dir.join("test_jwk.key");
1409
1410		// Write key to file as base64url
1411		key.to_file(&temp_path).unwrap();
1412
1413		// Read file contents
1414		let contents = std::fs::read_to_string(&temp_path).unwrap();
1415
1416		// Should be base64url encoded
1417		assert!(!contents.contains('{'));
1418		assert!(!contents.contains('}'));
1419		assert!(!contents.contains('"'));
1420
1421		// Decode and verify it's valid JSON
1422		let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
1423			.decode(&contents)
1424			.unwrap();
1425		let json_str = String::from_utf8(decoded).unwrap();
1426		let _: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1427
1428		// Read key back from file
1429		let loaded_key = Key::from_file(&temp_path).unwrap();
1430		assert_eq!(loaded_key.algorithm, key.algorithm);
1431		assert_eq!(loaded_key.operations, key.operations);
1432		assert_eq!(loaded_key.kid, key.kid);
1433
1434		if let (
1435			KeyType::OCT {
1436				secret: original_secret,
1437			},
1438			KeyType::OCT { secret: loaded_secret },
1439		) = (&key.key, &loaded_key.key)
1440		{
1441			assert_eq!(loaded_secret, original_secret);
1442		} else {
1443			panic!("Expected both keys to be OCT variant");
1444		}
1445
1446		// Clean up
1447		std::fs::remove_file(temp_path).ok();
1448	}
1449
1450	#[test]
1451	fn test_file_io_raw_json() {
1452		let key = create_test_key();
1453		let temp_dir = std::env::temp_dir();
1454		let temp_path = temp_dir.join("test_jwk_raw_json.key");
1455
1456		// Write key as raw JSON (backwards compat format)
1457		let json = serde_json::to_string(&key).unwrap();
1458		std::fs::write(&temp_path, &json).unwrap();
1459
1460		// Verify it looks like JSON
1461		assert!(json.starts_with('{'));
1462
1463		// Load via from_file (should auto-detect JSON)
1464		let loaded_key = Key::from_file(&temp_path).unwrap();
1465		assert_eq!(loaded_key.algorithm, key.algorithm);
1466		assert_eq!(loaded_key.operations, key.operations);
1467		assert_eq!(loaded_key.kid, key.kid);
1468
1469		if let (
1470			KeyType::OCT {
1471				secret: original_secret,
1472			},
1473			KeyType::OCT { secret: loaded_secret },
1474		) = (&key.key, &loaded_key.key)
1475		{
1476			assert_eq!(loaded_secret, original_secret);
1477		} else {
1478			panic!("Expected both keys to be OCT variant");
1479		}
1480
1481		// Clean up
1482		std::fs::remove_file(temp_path).ok();
1483	}
1484}