1#![forbid(unsafe_code)]
2
3#![cfg_attr(feature = "rsa", doc = "```")]
21#![cfg_attr(not(feature = "rsa"), doc = "```ignore")]
22#![cfg_attr(feature = "ecdsa", doc = "```")]
51#![cfg_attr(not(feature = "ecdsa"), doc = "```ignore")]
52#![cfg_attr(feature = "ed25519", doc = "```")]
79#![cfg_attr(not(feature = "ed25519"), doc = "```ignore")]
80#![cfg_attr(feature = "hmac", doc = "```")]
107#![cfg_attr(not(feature = "hmac"), doc = "```ignore")]
108use jsonwebtoken::{DecodingKey, EncodingKey};
133
134pub trait JwtKeyExt {
139 fn encoding_key(&self) -> EncodingKey;
145
146 fn decoding_key(&self) -> DecodingKey;
152}
153
154#[cfg(feature = "rsa")]
155impl JwtKeyExt for uselesskey_rsa::RsaKeyPair {
156 fn encoding_key(&self) -> EncodingKey {
157 EncodingKey::from_rsa_pem(self.private_key_pkcs8_pem().as_bytes())
158 .expect("failed to create EncodingKey from RSA PEM")
159 }
160
161 fn decoding_key(&self) -> DecodingKey {
162 DecodingKey::from_rsa_pem(self.public_key_spki_pem().as_bytes())
163 .expect("failed to create DecodingKey from RSA PEM")
164 }
165}
166
167#[cfg(feature = "ecdsa")]
168impl JwtKeyExt for uselesskey_ecdsa::EcdsaKeyPair {
169 fn encoding_key(&self) -> EncodingKey {
170 EncodingKey::from_ec_pem(self.private_key_pkcs8_pem().as_bytes())
171 .expect("failed to create EncodingKey from EC PEM")
172 }
173
174 fn decoding_key(&self) -> DecodingKey {
175 DecodingKey::from_ec_pem(self.public_key_spki_pem().as_bytes())
176 .expect("failed to create DecodingKey from EC PEM")
177 }
178}
179
180#[cfg(feature = "ed25519")]
181impl JwtKeyExt for uselesskey_ed25519::Ed25519KeyPair {
182 fn encoding_key(&self) -> EncodingKey {
183 EncodingKey::from_ed_pem(self.private_key_pkcs8_pem().as_bytes())
184 .expect("failed to create EncodingKey from Ed25519 PEM")
185 }
186
187 fn decoding_key(&self) -> DecodingKey {
188 DecodingKey::from_ed_pem(self.public_key_spki_pem().as_bytes())
189 .expect("failed to create DecodingKey from Ed25519 PEM")
190 }
191}
192
193#[cfg(feature = "hmac")]
194impl JwtKeyExt for uselesskey_hmac::HmacSecret {
195 fn encoding_key(&self) -> EncodingKey {
196 EncodingKey::from_secret(self.secret_bytes())
197 }
198
199 fn decoding_key(&self) -> DecodingKey {
200 DecodingKey::from_secret(self.secret_bytes())
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use std::sync::OnceLock;
207 use uselesskey_core::{Factory, Seed};
208
209 static FX: OnceLock<Factory> = OnceLock::new();
210
211 fn fx() -> Factory {
212 FX.get_or_init(|| {
213 let seed = Seed::from_env_value("uselesskey-jsonwebtoken-inline-test-seed-v1")
214 .expect("test seed should always parse");
215 Factory::deterministic(seed)
216 })
217 .clone()
218 }
219
220 #[cfg(feature = "rsa")]
221 mod rsa_tests {
222 use crate::JwtKeyExt;
223 use jsonwebtoken::{Algorithm, Header, Validation, decode, encode};
224 use serde::{Deserialize, Serialize};
225 use uselesskey_core::Factory;
226 use uselesskey_rsa::{RsaFactoryExt, RsaSpec};
227
228 #[derive(Debug, Serialize, Deserialize, PartialEq)]
229 struct TestClaims {
230 sub: String,
231 exp: usize,
232 }
233
234 #[test]
235 fn test_rsa_sign_and_verify() {
236 let fx = super::fx();
237 let keypair = fx.rsa("test-issuer", RsaSpec::rs256());
238
239 let claims = TestClaims {
240 sub: "user123".to_string(),
241 exp: 2_000_000_000,
242 };
243
244 let header = Header::new(Algorithm::RS256);
245 let token = encode(&header, &claims, &keypair.encoding_key()).unwrap();
246
247 let validation = Validation::new(Algorithm::RS256);
248 let decoded =
249 decode::<TestClaims>(&token, &keypair.decoding_key(), &validation).unwrap();
250
251 assert_eq!(decoded.claims, claims);
252 }
253
254 #[test]
255 fn test_rsa_deterministic_keys_work() {
256 use uselesskey_core::Seed;
257
258 let seed = Seed::from_env_value("test-seed").unwrap();
259 let fx = Factory::deterministic(seed);
260 let keypair = fx.rsa("deterministic-issuer", RsaSpec::rs256());
261
262 let claims = TestClaims {
263 sub: "det-user".to_string(),
264 exp: 2_000_000_000,
265 };
266
267 let header = Header::new(Algorithm::RS256);
268 let token = encode(&header, &claims, &keypair.encoding_key()).unwrap();
269
270 let validation = Validation::new(Algorithm::RS256);
271 let decoded =
272 decode::<TestClaims>(&token, &keypair.decoding_key(), &validation).unwrap();
273
274 assert_eq!(decoded.claims, claims);
275 }
276 }
277
278 #[cfg(feature = "ecdsa")]
279 mod ecdsa_tests {
280 use crate::JwtKeyExt;
281 use jsonwebtoken::{Algorithm, Header, Validation, decode, encode};
282 use serde::{Deserialize, Serialize};
283 use uselesskey_core::Factory;
284 use uselesskey_ecdsa::{EcdsaFactoryExt, EcdsaSpec};
285
286 #[derive(Debug, Serialize, Deserialize, PartialEq)]
287 struct TestClaims {
288 sub: String,
289 exp: usize,
290 }
291
292 #[test]
293 fn test_ecdsa_es256_sign_and_verify() {
294 let fx = Factory::random();
295 let keypair = fx.ecdsa("test-issuer", EcdsaSpec::es256());
296
297 let claims = TestClaims {
298 sub: "user123".to_string(),
299 exp: 2_000_000_000,
300 };
301
302 let header = Header::new(Algorithm::ES256);
303 let token = encode(&header, &claims, &keypair.encoding_key()).unwrap();
304
305 let validation = Validation::new(Algorithm::ES256);
306 let decoded =
307 decode::<TestClaims>(&token, &keypair.decoding_key(), &validation).unwrap();
308
309 assert_eq!(decoded.claims, claims);
310 }
311
312 #[test]
313 fn test_ecdsa_es384_sign_and_verify() {
314 let fx = Factory::random();
315 let keypair = fx.ecdsa("test-issuer", EcdsaSpec::es384());
316
317 let claims = TestClaims {
318 sub: "user123".to_string(),
319 exp: 2_000_000_000,
320 };
321
322 let header = Header::new(Algorithm::ES384);
323 let token = encode(&header, &claims, &keypair.encoding_key()).unwrap();
324
325 let validation = Validation::new(Algorithm::ES384);
326 let decoded =
327 decode::<TestClaims>(&token, &keypair.decoding_key(), &validation).unwrap();
328
329 assert_eq!(decoded.claims, claims);
330 }
331 }
332
333 #[cfg(feature = "ed25519")]
334 mod ed25519_tests {
335 use crate::JwtKeyExt;
336 use jsonwebtoken::{Algorithm, Header, Validation, decode, encode};
337 use serde::{Deserialize, Serialize};
338 use uselesskey_core::Factory;
339 use uselesskey_ed25519::{Ed25519FactoryExt, Ed25519Spec};
340
341 #[derive(Debug, Serialize, Deserialize, PartialEq)]
342 struct TestClaims {
343 sub: String,
344 exp: usize,
345 }
346
347 #[test]
348 fn test_ed25519_sign_and_verify() {
349 let fx = Factory::random();
350 let keypair = fx.ed25519("test-issuer", Ed25519Spec::new());
351
352 let claims = TestClaims {
353 sub: "user123".to_string(),
354 exp: 2_000_000_000,
355 };
356
357 let header = Header::new(Algorithm::EdDSA);
358 let token = encode(&header, &claims, &keypair.encoding_key()).unwrap();
359
360 let validation = Validation::new(Algorithm::EdDSA);
361 let decoded =
362 decode::<TestClaims>(&token, &keypair.decoding_key(), &validation).unwrap();
363
364 assert_eq!(decoded.claims, claims);
365 }
366 }
367
368 #[cfg(feature = "ecdsa")]
369 mod cross_key_tests {
370 use crate::JwtKeyExt;
371 use jsonwebtoken::{Algorithm, Header, Validation, decode, encode};
372 use serde::{Deserialize, Serialize};
373 use uselesskey_core::Factory;
374 use uselesskey_ecdsa::{EcdsaFactoryExt, EcdsaSpec};
375
376 #[derive(Debug, Serialize, Deserialize, PartialEq)]
377 struct TestClaims {
378 sub: String,
379 exp: usize,
380 }
381
382 #[test]
383 fn test_cross_key_decode_fails() {
384 let fx = Factory::random();
385 let key_a = fx.ecdsa("issuer-a", EcdsaSpec::es256());
386 let key_b = fx.ecdsa("issuer-b", EcdsaSpec::es256());
387
388 let claims = TestClaims {
389 sub: "user".to_string(),
390 exp: 2_000_000_000,
391 };
392
393 let token = encode(
394 &Header::new(Algorithm::ES256),
395 &claims,
396 &key_a.encoding_key(),
397 )
398 .unwrap();
399
400 let result = decode::<TestClaims>(
401 &token,
402 &key_b.decoding_key(),
403 &Validation::new(Algorithm::ES256),
404 );
405 assert!(result.is_err(), "decoding with wrong key should fail");
406 }
407 }
408
409 #[cfg(feature = "hmac")]
410 mod hmac_tests {
411 use crate::JwtKeyExt;
412 use jsonwebtoken::{Algorithm, Header, Validation, decode, encode};
413 use serde::{Deserialize, Serialize};
414 use uselesskey_core::Factory;
415 use uselesskey_hmac::{HmacFactoryExt, HmacSpec};
416
417 #[derive(Debug, Serialize, Deserialize, PartialEq)]
418 struct TestClaims {
419 sub: String,
420 exp: usize,
421 }
422
423 #[test]
424 fn test_hmac_hs256_sign_and_verify() {
425 let fx = Factory::random();
426 let secret = fx.hmac("test-secret", HmacSpec::hs256());
427
428 let claims = TestClaims {
429 sub: "user123".to_string(),
430 exp: 2_000_000_000,
431 };
432
433 let header = Header::new(Algorithm::HS256);
434 let token = encode(&header, &claims, &secret.encoding_key()).unwrap();
435
436 let validation = Validation::new(Algorithm::HS256);
437 let decoded =
438 decode::<TestClaims>(&token, &secret.decoding_key(), &validation).unwrap();
439
440 assert_eq!(decoded.claims, claims);
441 }
442
443 #[test]
444 fn test_hmac_hs384_sign_and_verify() {
445 let fx = Factory::random();
446 let secret = fx.hmac("test-secret", HmacSpec::hs384());
447
448 let claims = TestClaims {
449 sub: "user123".to_string(),
450 exp: 2_000_000_000,
451 };
452
453 let header = Header::new(Algorithm::HS384);
454 let token = encode(&header, &claims, &secret.encoding_key()).unwrap();
455
456 let validation = Validation::new(Algorithm::HS384);
457 let decoded =
458 decode::<TestClaims>(&token, &secret.decoding_key(), &validation).unwrap();
459
460 assert_eq!(decoded.claims, claims);
461 }
462
463 #[test]
464 fn test_hmac_hs512_sign_and_verify() {
465 let fx = Factory::random();
466 let secret = fx.hmac("test-secret", HmacSpec::hs512());
467
468 let claims = TestClaims {
469 sub: "user123".to_string(),
470 exp: 2_000_000_000,
471 };
472
473 let header = Header::new(Algorithm::HS512);
474 let token = encode(&header, &claims, &secret.encoding_key()).unwrap();
475
476 let validation = Validation::new(Algorithm::HS512);
477 let decoded =
478 decode::<TestClaims>(&token, &secret.decoding_key(), &validation).unwrap();
479
480 assert_eq!(decoded.claims, claims);
481 }
482 }
483}