1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![forbid(unsafe_code)]
3#![warn(missing_docs)]
4#[allow(dead_code)]
107type Bytes<N> = generic_array::GenericArray<u8, N>;
108
109pub mod v4 {
111 use crate::version::V4;
112 pub type SymmetricKey = crate::purpose::local::SymmetricKey<V4>;
114 pub type DecryptedToken<M, F = (), E = crate::Json<()>> =
116 crate::purpose::local::DecryptedToken<V4, M, F, E>;
117 pub type EncryptedToken<F = (), E = crate::Json<()>> =
119 crate::purpose::local::EncryptedToken<V4, F, E>;
120 pub type UnencryptedToken<M, F = (), E = crate::Json<()>> =
122 crate::purpose::local::UnencryptedToken<V4, M, F, E>;
123
124 pub type PublicKey = crate::purpose::public::PublicKey<V4>;
126 pub type SecretKey = crate::purpose::public::SecretKey<V4>;
128 pub type VerifiedToken<M, F = (), E = crate::Json<()>> =
130 crate::purpose::public::VerifiedToken<V4, M, F, E>;
131 pub type SignedToken<F = (), E = crate::Json<()>> =
133 crate::purpose::public::SignedToken<V4, F, E>;
134 pub type UnsignedToken<M, F = (), E = crate::Json<()>> =
136 crate::purpose::public::UnsignedToken<V4, M, F, E>;
137}
138
139pub mod v3 {
141 use crate::version::V3;
142 pub type SymmetricKey = crate::purpose::local::SymmetricKey<V3>;
144 pub type DecryptedToken<M, F = (), E = crate::Json<()>> =
146 crate::purpose::local::DecryptedToken<V3, M, F, E>;
147 pub type EncryptedToken<F = (), E = crate::Json<()>> =
149 crate::purpose::local::EncryptedToken<V3, F, E>;
150 pub type UnencryptedToken<M, F = (), E = crate::Json<()>> =
152 crate::purpose::local::UnencryptedToken<V3, M, F, E>;
153
154 pub type PublicKey = crate::purpose::public::PublicKey<V3>;
156 pub type SecretKey = crate::purpose::public::SecretKey<V3>;
158 pub type VerifiedToken<M, F = (), E = crate::Json<()>> =
160 crate::purpose::public::VerifiedToken<V3, M, F, E>;
161 pub type SignedToken<F = (), E = crate::Json<()>> =
163 crate::purpose::public::SignedToken<V3, F, E>;
164 pub type UnsignedToken<M, F = (), E = crate::Json<()>> =
166 crate::purpose::public::UnsignedToken<V3, M, F, E>;
167}
168
169pub mod purpose {
170 pub mod local;
173 pub mod public;
174
175 pub use local::Local;
176 pub use public::{Public, Secret};
177
178 pub trait Purpose: Default {
183 const HEADER: &'static str;
185 }
186}
187
188pub mod key;
189
190pub mod paserk;
192
193pub mod version {
194 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
198 pub struct V3;
199
200 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
202 pub struct V4;
203
204 pub trait Version: Default + crate::sealed::Sealed {
208 const PASETO_HEADER: &'static str;
210 const PASERK_HEADER: &'static str;
212 }
213
214 impl Version for V3 {
215 const PASETO_HEADER: &'static str = "v3";
216 const PASERK_HEADER: &'static str = "k3";
217 }
218
219 impl Version for V4 {
220 const PASETO_HEADER: &'static str = "v4";
221 const PASERK_HEADER: &'static str = "k4";
222 }
223
224 impl crate::sealed::Sealed for V3 {}
225
226 impl crate::sealed::Sealed for V4 {}
227}
228
229pub mod encodings {
240 use serde::{de::DeserializeOwned, Serialize};
241
242 use crate::Json;
243
244 pub trait Payload: Default {
246 const SUFFIX: &'static str;
248 }
249
250 pub trait MessageEncoding<M>: Payload {
252 fn encode(&self, s: &M) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>>;
254 }
255
256 pub trait MessageDecoding<M>: Payload {
258 fn decode(&self, from: &[u8]) -> Result<M, Box<dyn std::error::Error + Send + Sync>>;
260 }
261
262 impl Payload for Json<()> {
263 const SUFFIX: &'static str = "";
265 }
266
267 impl<M: Serialize> MessageEncoding<M> for Json<()> {
268 fn encode(&self, s: &M) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
269 serde_json::to_vec(s).map_err(From::from)
270 }
271 }
272
273 impl<M: DeserializeOwned> MessageDecoding<M> for Json<()> {
274 fn decode(&self, from: &[u8]) -> Result<M, Box<dyn std::error::Error + Send + Sync>> {
275 serde_json::from_slice(from).map_err(From::from)
276 }
277 }
278}
279
280mod pae;
281
282pub trait Footer: Sized {
289 fn encode(&self) -> Vec<u8>;
291 fn decode(footer: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>;
293}
294
295#[derive(Default)]
307pub struct Json<T>(pub T);
308
309impl<T: serde::Serialize + serde::de::DeserializeOwned> Footer for Json<T> {
310 fn encode(&self) -> Vec<u8> {
311 serde_json::to_vec(&self.0).expect("json serialization should not panic")
312 }
313
314 fn decode(footer: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
315 match footer {
316 x if x.is_empty() => Err("missing footer".into()),
317 x => serde_json::from_slice(x).map(Self).map_err(|e| e.into()),
318 }
319 }
320}
321
322impl Footer for Vec<u8> {
323 fn encode(&self) -> Vec<u8> {
324 self.clone()
325 }
326
327 fn decode(footer: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
328 Ok(footer.to_owned())
329 }
330}
331
332impl Footer for () {
333 fn encode(&self) -> Vec<u8> {
334 Vec::new()
335 }
336
337 fn decode(footer: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
338 match footer {
339 x if x.is_empty() => Ok(()),
340 x => Err(format!("unexpected footer {x:?}").into()),
341 }
342 }
343}
344
345mod sealed {
346 pub trait Sealed {}
347}
348
349#[derive(Default)]
350struct TokenMetadata<V, T, E> {
351 version_header: V,
352 token_type: T,
353 #[allow(dead_code)]
354 encoding: E,
355}
356
357pub mod tokens {
358 use core::fmt;
361
362 use base64ct::Encoding;
363
364 use crate::{encodings::Payload, purpose, version, Footer, Json, PasetoError, TokenMetadata};
365
366 pub struct ValidatedToken<V, T, M, F = (), E = Json<()>> {
375 pub(crate) meta: TokenMetadata<V, T, E>,
376 pub message: M,
378 pub footer: F,
380 }
381
382 impl<V, T, M, E> TokenBuilder<V, T, M, (), E> {
383 pub fn with_footer<F>(self, footer: F) -> TokenBuilder<V, T, M, F, E> {
387 TokenBuilder(ValidatedToken {
388 meta: self.0.meta,
389 message: self.0.message,
390 footer,
391 })
392 }
393 }
394
395 impl<V, T, M, F> TokenBuilder<V, T, M, F, Json<()>> {
396 pub fn with_encoding<E>(self, encoding: E) -> TokenBuilder<V, T, M, F, E> {
400 TokenBuilder(ValidatedToken {
401 message: self.0.message,
402 footer: self.0.footer,
403 meta: TokenMetadata {
404 version_header: self.0.meta.version_header,
405 token_type: self.0.meta.token_type,
406 encoding,
407 },
408 })
409 }
410 }
411
412 pub struct TokenBuilder<V, T, M, F = (), E = Json<()>>(
414 pub(crate) ValidatedToken<V, T, M, F, E>,
415 );
416
417 impl<V: crate::version::Version, T: crate::purpose::Purpose, M> TokenBuilder<V, T, M> {
418 pub fn new(message: M) -> Self {
420 Self(ValidatedToken {
421 meta: TokenMetadata::default(),
422 message,
423 footer: (),
424 })
425 }
426 }
427
428 pub struct SecuredToken<V, T, F = (), E = Json<()>> {
442 #[allow(dead_code)]
443 pub(crate) meta: TokenMetadata<V, T, E>,
444 pub(crate) payload: Vec<u8>,
445 pub(crate) encoded_footer: Vec<u8>,
446 pub(crate) footer: F,
447 }
448
449 impl<V: version::Version, T: purpose::Purpose, F, E: Payload> fmt::Display
450 for SecuredToken<V, T, F, E>
451 {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 f.write_str(V::PASETO_HEADER)?;
454 f.write_str(E::SUFFIX)?;
455 f.write_str(".")?;
456 f.write_str(T::HEADER)?;
457 f.write_str(".")?;
458 f.write_str(&base64ct::Base64UrlUnpadded::encode_string(&self.payload))?;
459
460 if !self.encoded_footer.is_empty() {
461 f.write_str(".")?;
462 f.write_str(&base64ct::Base64UrlUnpadded::encode_string(
463 &self.encoded_footer,
464 ))?;
465 }
466
467 Ok(())
468 }
469 }
470
471 impl<V: version::Version, K: purpose::Purpose, F, E: Payload> serde::Serialize
472 for SecuredToken<V, K, F, E>
473 {
474 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
475 where
476 S: serde::Serializer,
477 {
478 serializer.collect_str(self)
479 }
480 }
481
482 impl<'de, V: version::Version, K: purpose::Purpose, F: Footer, E: Payload>
483 serde::Deserialize<'de> for SecuredToken<V, K, F, E>
484 {
485 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
486 where
487 D: serde::Deserializer<'de>,
488 {
489 struct FromStrVisitor<V, T, F, E>(std::marker::PhantomData<(V, T, F, E)>);
490 impl<'de, V: version::Version, K: purpose::Purpose, F: Footer, E: Payload>
491 serde::de::Visitor<'de> for FromStrVisitor<V, K, F, E>
492 {
493 type Value = SecuredToken<V, K, F, E>;
494
495 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
496 write!(
497 formatter,
498 "a \"{}.{}.\" paseto",
499 V::PASETO_HEADER,
500 K::HEADER,
501 )
502 }
503 fn visit_str<Err>(self, v: &str) -> Result<Self::Value, Err>
504 where
505 Err: serde::de::Error,
506 {
507 v.parse().map_err(Err::custom)
508 }
509 }
510 deserializer.deserialize_str(FromStrVisitor(std::marker::PhantomData))
511 }
512 }
513
514 impl<V: version::Version, T: purpose::Purpose, F: Footer, E: Payload> std::str::FromStr
515 for SecuredToken<V, T, F, E>
516 {
517 type Err = PasetoError;
518
519 fn from_str(s: &str) -> Result<Self, Self::Err> {
520 let s = s
521 .strip_prefix(V::PASETO_HEADER)
522 .ok_or(PasetoError::InvalidToken)?;
523 let s = s.strip_prefix(E::SUFFIX).ok_or(PasetoError::InvalidToken)?;
524 let s = s.strip_prefix('.').ok_or(PasetoError::InvalidToken)?;
525 let s = s.strip_prefix(T::HEADER).ok_or(PasetoError::InvalidToken)?;
526 let s = s.strip_prefix('.').ok_or(PasetoError::InvalidToken)?;
527
528 let (payload, footer) = match s.split_once('.') {
529 Some((payload, footer)) => (payload, Some(footer)),
530 None => (s, None),
531 };
532
533 let payload = base64ct::Base64UrlUnpadded::decode_vec(payload)
534 .map_err(|_| PasetoError::Base64DecodeError)?;
535 let encoded_footer = footer
536 .map(base64ct::Base64UrlUnpadded::decode_vec)
537 .transpose()
538 .map_err(|_| PasetoError::Base64DecodeError)?
539 .unwrap_or_default();
540 let footer = F::decode(&encoded_footer).map_err(PasetoError::PayloadError)?;
541
542 Ok(Self {
543 meta: TokenMetadata::default(),
544 payload,
545 encoded_footer,
546 footer,
547 })
548 }
549 }
550
551 impl<V, T, F, E> SecuredToken<V, T, F, E> {
552 pub fn unverified_footer(&self) -> &F {
554 &self.footer
555 }
556 }
557}
558
559#[derive(Debug)]
560#[non_exhaustive]
561pub enum PasetoError {
563 Base64DecodeError,
565 InvalidKey,
567 InvalidToken,
569 CryptoError,
571 PayloadError(Box<dyn std::error::Error + Send + Sync>),
573}
574
575impl std::error::Error for PasetoError {
576 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
577 match self {
578 PasetoError::PayloadError(x) => Some(&**x),
579 _ => None,
580 }
581 }
582}
583
584impl std::fmt::Display for PasetoError {
585 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
586 match self {
587 PasetoError::Base64DecodeError => f.write_str("The token could not be base64 decoded"),
588 PasetoError::InvalidKey => f.write_str("Could not parse the key"),
589 PasetoError::InvalidToken => f.write_str("Could not parse the token"),
590 PasetoError::CryptoError => f.write_str("Token signature could not be validated"),
591 PasetoError::PayloadError(x) => {
592 write!(f, "there was an error with the payload encoding: {x}")
593 }
594 }
595 }
596}
597
598#[cfg(fuzzing)]
599pub mod fuzzing {
600 use rand::{CryptoRng, RngCore};
601
602 use crate::purpose::local::{DecryptedToken, EncryptedToken, Local, LocalVersion};
603 use crate::purpose::public::{Secret, SignedToken, UnsignedToken, VerifiedToken};
604 use crate::version::{V3, V4};
605 use crate::{
606 key::{Key, KeyType},
607 purpose::local::UnencryptedToken,
608 version::Version,
609 Json,
610 };
611
612 #[derive(Clone, Debug)]
613 pub struct FakeRng<const N: usize> {
615 pub bytes: [u8; N],
616 pub start: usize,
617 }
618
619 impl<'a, const N: usize> arbitrary::Arbitrary<'a> for FakeRng<N>
620 where
621 [u8; N]: arbitrary::Arbitrary<'a>,
622 {
623 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
624 Ok(Self {
625 bytes: <[u8; N]>::arbitrary(u)?,
626 start: 0,
627 })
628 }
629 }
630
631 impl<const N: usize> RngCore for FakeRng<N> {
632 fn next_u32(&mut self) -> u32 {
633 unimplemented!()
634 }
635
636 fn next_u64(&mut self) -> u64 {
637 unimplemented!()
638 }
639
640 fn fill_bytes(&mut self, dest: &mut [u8]) {
641 let remaining = N - self.start;
642 let requested = dest.len();
643 if requested > remaining {
644 panic!("not enough entropy");
645 }
646 dest.copy_from_slice(&self.bytes[self.start..self.start + requested]);
647 self.start += requested;
648 }
649
650 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
651 self.fill_bytes(dest);
652 Ok(())
653 }
654 }
655
656 impl<const N: usize> CryptoRng for FakeRng<N> {}
658
659 #[derive(Debug)]
660 pub struct FuzzInput<V: Version, K: KeyType<V>> {
661 key: Key<V, K>,
662 ephemeral: FakeRng<32>,
663 data1: String,
664 data2: String,
665 data3: String,
666 }
667
668 impl<'a, V: Version, K: KeyType<V>> arbitrary::Arbitrary<'a> for FuzzInput<V, K>
669 where
670 Key<V, K>: arbitrary::Arbitrary<'a>,
671 {
672 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
673 Ok(Self {
674 key: u.arbitrary()?,
675 ephemeral: u.arbitrary()?,
676 data1: u.arbitrary()?,
677 data2: u.arbitrary()?,
678 data3: u.arbitrary()?,
679 })
680 }
681 }
682
683 #[derive(serde::Deserialize, serde::Serialize)]
684 struct Data {
685 data: String,
686 }
687
688 impl<V: LocalVersion> FuzzInput<V, Local> {
689 pub fn run(self) {
690 let token = UnencryptedToken::<V, Data>::new(Data {
691 data: self.data1.clone(),
692 })
693 .with_footer(Json(Data {
694 data: self.data2.clone(),
695 }))
696 .encrypt_with_assertions_and_rng(&self.key, self.data3.as_bytes(), self.ephemeral)
697 .unwrap();
698 let token: EncryptedToken<V, Json<Data>> = token.to_string().parse().unwrap();
699 assert_eq!(token.unverified_footer().0.data, self.data2);
700 let token: DecryptedToken<V, Data, Json<Data>> = token
701 .decrypt_with_assertions(&self.key, self.data3.as_bytes())
702 .unwrap();
703 assert_eq!(token.message.data, self.data1);
704 }
705 }
706 impl FuzzInput<V3, Secret> {
707 pub fn run(self) {
708 let token = UnsignedToken::<V3, Data>::new(Data {
709 data: self.data1.clone(),
710 })
711 .with_footer(Json(Data {
712 data: self.data2.clone(),
713 }))
714 .sign_with_assertions(&self.key, self.data3.as_bytes())
715 .unwrap();
716 let token: SignedToken<V3, Json<Data>> = token.to_string().parse().unwrap();
717 assert_eq!(token.unverified_footer().0.data, self.data2);
718 let token: VerifiedToken<V3, Data, Json<Data>> = token
719 .verify_with_assertions(&self.key.public_key(), self.data3.as_bytes())
720 .unwrap();
721 assert_eq!(token.message.data, self.data1);
722 }
723 }
724 impl FuzzInput<V4, Secret> {
725 pub fn run(self) {
726 let token = UnsignedToken::<V4, Data>::new(Data {
727 data: self.data1.clone(),
728 })
729 .with_footer(Json(Data {
730 data: self.data2.clone(),
731 }))
732 .sign_with_assertions(&self.key, self.data3.as_bytes())
733 .unwrap();
734 let token: SignedToken<V4, Json<Data>> = token.to_string().parse().unwrap();
735 assert_eq!(token.unverified_footer().0.data, self.data2);
736 let token: VerifiedToken<V4, Data, Json<Data>> = token
737 .verify_with_assertions(&self.key.public_key(), self.data3.as_bytes())
738 .unwrap();
739 assert_eq!(token.message.data, self.data1);
740 }
741 }
742}