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