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