#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(
rust_2018_idioms,
missing_docs,
missing_debug_implementations,
unused_lifetimes,
unused_qualifications
)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc;
use base64ct::{Base64UrlUnpadded, Encoding};
use core::convert::TryFrom;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::{string::String, vec, vec::Vec};
#[cfg(feature = "std")]
use std::{string::String, vec, vec::Vec};
pub use error::Error;
pub mod algorithm;
pub mod error;
pub mod sign;
pub mod time;
pub mod verify;
use error::Result;
#[derive(Debug)]
pub struct UnverifiedJwt<'a> {
header: &'a str,
claims: &'a str,
signed_data: &'a str,
signature: &'a str,
}
#[derive(Debug)]
struct SplitJwt<'a> {
header: &'a str,
claims: &'a str,
signed_data: &'a str,
signature: &'a str,
}
impl<'a> TryFrom<&'a str> for UnverifiedJwt<'a> {
type Error = Error;
fn try_from(value: &'a str) -> Result<Self> {
let split_jwt = Self::split(value)?;
Ok(UnverifiedJwt {
header: split_jwt.header,
claims: split_jwt.claims,
signed_data: split_jwt.signed_data,
signature: split_jwt.signature,
})
}
}
impl<'a> UnverifiedJwt<'a> {
pub fn with_str(jwt: &str) -> Result<UnverifiedJwt<'_>> {
let split_jwt = Self::split(jwt)?;
Ok(UnverifiedJwt {
header: split_jwt.header,
claims: split_jwt.claims,
signed_data: split_jwt.signed_data,
signature: split_jwt.signature,
})
}
pub fn decode_header(&self) -> Result<Vec<u8>> {
Ok(Base64UrlUnpadded::decode_vec(self.header)?)
}
fn decode_claims(&self) -> Result<Vec<u8>> {
Ok(Base64UrlUnpadded::decode_vec(self.claims)?)
}
pub fn decode_signature(&self) -> Result<Vec<u8>> {
Ok(Base64UrlUnpadded::decode_vec(self.signature)?)
}
#[must_use]
pub fn signed_data(&self) -> &'a str {
self.signed_data
}
#[must_use]
pub fn encoded_header(&self) -> &'a str {
self.header
}
#[must_use]
pub fn encoded_signature(&self) -> &'a str {
self.signature
}
fn split(jwt: &str) -> Result<SplitJwt<'_>> {
let mut parts = jwt.rsplitn(2, '.');
let signature = parts.next().ok_or_else(Error::malformed_jwt)?;
let signed_data = parts.next().ok_or_else(Error::malformed_jwt)?;
if parts.next().is_some() {
return Err(Error::malformed_jwt());
}
let mut parts = signed_data.rsplitn(3, '.');
let claims = parts.next().ok_or_else(Error::malformed_jwt)?;
let header = parts.next().ok_or_else(Error::malformed_jwt)?;
if parts.next().is_some() {
return Err(Error::malformed_jwt());
}
Ok(SplitJwt {
header,
claims,
signed_data,
signature,
})
}
}
#[derive(Debug)]
pub struct SignatureVerifiedJwt<'a> {
unverified_jwt: UnverifiedJwt<'a>,
}
impl<'a> SignatureVerifiedJwt<'a> {
#[inline]
pub fn decode_header(&self) -> Result<Vec<u8>> {
self.unverified_jwt.decode_header()
}
#[inline]
pub fn decode_claims(&self) -> Result<Vec<u8>> {
self.unverified_jwt.decode_claims()
}
#[inline]
pub fn decode_signature(&self) -> Result<Vec<u8>> {
self.unverified_jwt.decode_signature()
}
#[inline]
#[must_use]
pub fn signed_data(&self) -> &'a str {
self.unverified_jwt.signed_data()
}
#[inline]
#[must_use]
pub fn encoded_header(&self) -> &'a str {
self.unverified_jwt.encoded_header()
}
#[inline]
#[must_use]
pub fn encoded_claims(&self) -> &'a str {
self.unverified_jwt.claims
}
#[inline]
#[must_use]
pub fn encoded_signature(&self) -> &'a str {
self.unverified_jwt.encoded_signature()
}
}
pub trait Header {}
pub trait Claims {}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub struct BasicHeader<'a> {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub alg: Option<&'a str>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub kid: Option<&'a str>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub typ: Option<&'a str>,
}
impl<'a> Header for BasicHeader<'a> {}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub struct BasicClaims<'a> {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub iss: Option<&'a str>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub iat: Option<u64>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub exp: Option<u64>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub aud: Option<&'a str>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub sub: Option<&'a str>,
}
impl<'a> Claims for BasicClaims<'a> {}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
#[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))]
#[cfg(all(feature = "serde", feature = "serde_json"))]
pub fn serialize_encode_and_sign<H, C, S>(header: &H, claims: &C, signing_key: S) -> Result<String>
where
H: Header + Serialize,
C: Claims + Serialize,
S: sign::Signer,
{
let header = serde_json::to_vec(&header).map_err(|_| Error::unspecified())?;
let claims = serde_json::to_vec(&claims).map_err(|_| Error::unspecified())?;
encode_and_sign(&header, &claims, signing_key)
}
pub fn encode_and_sign<S>(header: &[u8], claims: &[u8], signing_key: S) -> Result<String>
where
S: sign::Signer,
{
let encoded_header_len = Base64UrlUnpadded::encoded_len(header);
let signed_data_len = encoded_header_len + Base64UrlUnpadded::encoded_len(claims) + 1;
let mut output = vec![0; signed_data_len];
Base64UrlUnpadded::encode(header, &mut output[..encoded_header_len])?;
output[encoded_header_len] = b'.';
Base64UrlUnpadded::encode(claims, &mut output[encoded_header_len + 1..])?;
let signature = signing_key
.sign(&output)
.map_err(|_| Error::unspecified())?;
let signature = signature.as_ref();
let final_len = signed_data_len + 1 + Base64UrlUnpadded::encoded_len(signature);
output.reserve_exact(final_len);
output.resize(final_len, 0);
output[signed_data_len] = b'.';
Base64UrlUnpadded::encode(signature, &mut output[signed_data_len + 1..])?;
Ok(String::from_utf8(output).map_err(|_| base64ct::InvalidEncodingError)?)
}
pub fn verify<V>(unverified_jwt: &str, verifying_key: V) -> Result<SignatureVerifiedJwt<'_>>
where
V: verify::Verifier,
{
let unverified_jwt = UnverifiedJwt::with_str(unverified_jwt)?;
let signed_data = unverified_jwt.signed_data().as_bytes();
let decoded_signature = unverified_jwt.decode_signature()?;
verifying_key
.verify(signed_data, &decoded_signature)
.map(|_| SignatureVerifiedJwt { unverified_jwt })
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(any(feature = "ring", feature = "p256"))]
pub(crate) fn jwt_claims_str() -> String {
String::from(
"{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"admin\":true,\"iat\":1516239022}",
)
}
#[test]
fn split_unverified_jwt_normal_parts() {
let jwt = String::from("abc.defg.vwxyz");
let SplitJwt {
header,
claims,
signed_data,
signature,
} = UnverifiedJwt::split(&jwt).unwrap();
assert_eq!("abc", header);
assert_eq!("defg", claims);
assert_eq!("abc.defg", signed_data);
assert_eq!("vwxyz", signature);
}
#[test]
fn with_str_unverified_jwt_normal_parts() {
let jwt = String::from("abc.defg.vwxyz");
let unverified_jwt = UnverifiedJwt::with_str(&jwt).unwrap();
assert_eq!("abc", unverified_jwt.encoded_header());
assert_eq!("abc.defg", unverified_jwt.signed_data());
assert_eq!("vwxyz", unverified_jwt.encoded_signature());
}
#[test]
fn split_unverified_jwt_no_data_in_parts() {
let jwt = String::from("..");
let SplitJwt {
header,
claims,
signed_data,
signature,
} = UnverifiedJwt::split(&jwt).unwrap();
assert_eq!("", header);
assert_eq!("", claims);
assert_eq!(".", signed_data);
assert_eq!("", signature);
}
#[test]
fn with_str_unverified_jwt_no_data_in_parts() {
let jwt = String::from("..");
let unverified_jwt = UnverifiedJwt::with_str(&jwt).unwrap();
assert_eq!("", unverified_jwt.encoded_header());
assert_eq!(".", unverified_jwt.signed_data());
assert_eq!("", unverified_jwt.encoded_signature());
}
#[test]
fn split_unverified_jwt_too_many_parts() {
let jwt = String::from("abc.defg.lmnop.vwxyz");
let error = UnverifiedJwt::split(&jwt).unwrap_err();
assert!(error.is_malformed_jwt());
}
#[test]
fn with_str_unverified_jwt_too_many_parts() {
let jwt = String::from("abc.defg.lmnop.vwxyz");
let error = UnverifiedJwt::with_str(&jwt).unwrap_err();
assert!(error.is_malformed_jwt());
}
#[test]
fn split_unverified_jwt_too_few_parts() {
let jwt = String::from("abc.defg");
let error = UnverifiedJwt::split(&jwt).unwrap_err();
assert!(error.is_malformed_jwt());
}
#[test]
fn with_str_unverified_jwt_too_few_parts() {
let jwt = String::from("abc.defg");
let error = UnverifiedJwt::with_str(&jwt).unwrap_err();
assert!(error.is_malformed_jwt());
}
}
#[cfg(feature = "p256")]
#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
mod p256;
#[cfg(feature = "ring")]
#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
mod ring;
#[cfg(feature = "rsa")]
#[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
mod rsa;