RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.

This commit is contained in:
Adam Ierymenko
2022-06-08 07:32:16 -04:00
parent 373ca30269
commit d5ca4e5f52
12611 changed files with 2898014 additions and 284 deletions

88
zeroidc/vendor/jwt/src/algorithm/mod.rs vendored Normal file
View File

@@ -0,0 +1,88 @@
//! Algorithms capable of signing and verifying tokens. By default only the
//! `hmac` crate's `Hmac` type is supported. For more algorithms, enable the
//! feature `openssl` and see the [openssl](openssl/index.html)
//! module. The `none` algorithm is explicitly not supported.
//! ## Examples
//! ```
//! use hmac::{Hmac, Mac};
//! use sha2::Sha256;
//!
//! let hs256_key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret").unwrap();
//! ```
use serde::{Deserialize, Serialize};
use crate::error::Error;
#[cfg(feature = "openssl")]
pub mod openssl;
pub mod rust_crypto;
pub mod store;
/// The type of an algorithm, corresponding to the
/// [JWA](https://tools.ietf.org/html/rfc7518) specification.
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum AlgorithmType {
Hs256,
Hs384,
Hs512,
Rs256,
Rs384,
Rs512,
Es256,
Es384,
Es512,
Ps256,
Ps384,
Ps512,
#[serde(rename = "none")]
None,
}
impl Default for AlgorithmType {
fn default() -> Self {
AlgorithmType::Hs256
}
}
/// An algorithm capable of signing base64 encoded header and claims strings.
/// strings.
pub trait SigningAlgorithm {
fn algorithm_type(&self) -> AlgorithmType;
fn sign(&self, header: &str, claims: &str) -> Result<String, Error>;
}
/// An algorithm capable of verifying base64 encoded header and claims strings.
pub trait VerifyingAlgorithm {
fn algorithm_type(&self) -> AlgorithmType;
fn verify_bytes(&self, header: &str, claims: &str, signature: &[u8]) -> Result<bool, Error>;
fn verify(&self, header: &str, claims: &str, signature: &str) -> Result<bool, Error> {
let signature_bytes = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?;
self.verify_bytes(header, claims, &*signature_bytes)
}
}
// TODO: investigate if these AsRef impls are necessary
impl<T: AsRef<dyn VerifyingAlgorithm>> VerifyingAlgorithm for T {
fn algorithm_type(&self) -> AlgorithmType {
self.as_ref().algorithm_type()
}
fn verify_bytes(&self, header: &str, claims: &str, signature: &[u8]) -> Result<bool, Error> {
self.as_ref().verify_bytes(header, claims, signature)
}
}
impl<T: AsRef<dyn SigningAlgorithm>> SigningAlgorithm for T {
fn algorithm_type(&self) -> AlgorithmType {
self.as_ref().algorithm_type()
}
fn sign(&self, header: &str, claims: &str) -> Result<String, Error> {
self.as_ref().sign(header, claims)
}
}

View File

@@ -0,0 +1,179 @@
//! OpenSSL support through the openssl crate.
//! Note that private keys can only be used for signing and that public keys
//! can only be used for verification.
//! ## Examples
//! ```
//! use jwt::PKeyWithDigest;
//! use openssl::hash::MessageDigest;
//! use openssl::pkey::PKey;
//! let pem = include_bytes!("../../test/rs256-public.pem");
//! let rs256_public_key = PKeyWithDigest {
//! digest: MessageDigest::sha256(),
//! key: PKey::public_key_from_pem(pem).unwrap(),
//! };
//! ```
use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm};
use crate::error::Error;
use crate::SEPARATOR;
use openssl::bn::BigNum;
use openssl::ecdsa::EcdsaSig;
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::{Id, PKey, Private, Public};
use openssl::sign::{Signer, Verifier};
/// A wrapper class around [PKey](../../../openssl/pkey/struct.PKey.html) that
/// associates the key with a
/// [MessageDigest](../../../openssl/hash/struct.MessageDigest.html).
pub struct PKeyWithDigest<T> {
pub digest: MessageDigest,
pub key: PKey<T>,
}
impl<T> PKeyWithDigest<T> {
fn algorithm_type(&self) -> AlgorithmType {
match (self.key.id(), self.digest.type_()) {
(Id::RSA, Nid::SHA256) => AlgorithmType::Rs256,
(Id::RSA, Nid::SHA384) => AlgorithmType::Rs384,
(Id::RSA, Nid::SHA512) => AlgorithmType::Rs512,
(Id::EC, Nid::SHA256) => AlgorithmType::Es256,
(Id::EC, Nid::SHA384) => AlgorithmType::Es384,
(Id::EC, Nid::SHA512) => AlgorithmType::Es512,
_ => panic!("Invalid algorithm type"),
}
}
}
impl SigningAlgorithm for PKeyWithDigest<Private> {
fn algorithm_type(&self) -> AlgorithmType {
PKeyWithDigest::algorithm_type(self)
}
fn sign(&self, header: &str, claims: &str) -> Result<String, Error> {
let mut signer = Signer::new(self.digest.clone(), &self.key)?;
signer.update(header.as_bytes())?;
signer.update(SEPARATOR.as_bytes())?;
signer.update(claims.as_bytes())?;
let signer_signature = signer.sign_to_vec()?;
let signature = if self.key.id() == Id::EC {
der_to_jose(&signer_signature)?
} else {
signer_signature
};
Ok(base64::encode_config(&signature, base64::URL_SAFE_NO_PAD))
}
}
impl VerifyingAlgorithm for PKeyWithDigest<Public> {
fn algorithm_type(&self) -> AlgorithmType {
PKeyWithDigest::algorithm_type(self)
}
fn verify_bytes(&self, header: &str, claims: &str, signature: &[u8]) -> Result<bool, Error> {
let mut verifier = Verifier::new(self.digest.clone(), &self.key)?;
verifier.update(header.as_bytes())?;
verifier.update(SEPARATOR.as_bytes())?;
verifier.update(claims.as_bytes())?;
let verified = if self.key.id() == Id::EC {
let der = jose_to_der(signature)?;
verifier.verify(&der)?
} else {
verifier.verify(signature)?
};
Ok(verified)
}
}
/// OpenSSL by default signs ECDSA in DER, but JOSE expects them in a concatenated (R, S) format
fn der_to_jose(der: &[u8]) -> Result<Vec<u8>, Error> {
let signature = EcdsaSig::from_der(&der)?;
let r = signature.r().to_vec();
let s = signature.s().to_vec();
Ok([r, s].concat())
}
/// OpenSSL by default verifies ECDSA in DER, but JOSE parses out a concatenated (R, S) format
fn jose_to_der(jose: &[u8]) -> Result<Vec<u8>, Error> {
let (r, s) = jose.split_at(jose.len() / 2);
let ecdsa_signature =
EcdsaSig::from_private_components(BigNum::from_slice(r)?, BigNum::from_slice(s)?)?;
Ok(ecdsa_signature.to_der()?)
}
#[cfg(test)]
mod tests {
use crate::algorithm::openssl::PKeyWithDigest;
use crate::algorithm::AlgorithmType::*;
use crate::algorithm::{SigningAlgorithm, VerifyingAlgorithm};
use crate::error::Error;
use crate::header::PrecomputedAlgorithmOnlyHeader as AlgOnly;
use crate::ToBase64;
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
// {"sub":"1234567890","name":"John Doe","admin":true}
const CLAIMS: &'static str =
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
const RS256_SIGNATURE: &'static str =
"cQsAHF2jHvPGFP5zTD8BgoJrnzEx6JNQCpupebWLFnOc2r_punDDTylI6Ia4JZNkvy2dQP-7W-DEbFQ3oaarHsDndqUgwf9iYlDQxz4Rr2nEZX1FX0-FMEgFPeQpdwveCgjtTYUbVy37ijUySN_rW-xZTrsh_Ug-ica8t-zHRIw";
#[test]
fn rs256_sign() -> Result<(), Error> {
let pem = include_bytes!("../../test/rs256-private.pem");
let algorithm = PKeyWithDigest {
digest: MessageDigest::sha256(),
key: PKey::private_key_from_pem(pem)?,
};
let result = algorithm.sign(&AlgOnly(Rs256).to_base64()?, CLAIMS)?;
assert_eq!(result, RS256_SIGNATURE);
Ok(())
}
#[test]
fn rs256_verify() -> Result<(), Error> {
let pem = include_bytes!("../../test/rs256-public.pem");
let algorithm = PKeyWithDigest {
digest: MessageDigest::sha256(),
key: PKey::public_key_from_pem(pem)?,
};
let verification_result =
algorithm.verify(&AlgOnly(Rs256).to_base64()?, CLAIMS, RS256_SIGNATURE)?;
assert!(verification_result);
Ok(())
}
#[test]
fn es256() -> Result<(), Error> {
let private_pem = include_bytes!("../../test/es256-private.pem");
let private_key = PKeyWithDigest {
digest: MessageDigest::sha256(),
key: PKey::private_key_from_pem(private_pem)?,
};
let signature = private_key.sign(&AlgOnly(Es256).to_base64()?, CLAIMS)?;
let public_pem = include_bytes!("../../test/es256-public.pem");
let public_key = PKeyWithDigest {
digest: MessageDigest::sha256(),
key: PKey::public_key_from_pem(public_pem)?,
};
let verification_result =
public_key.verify(&AlgOnly(Es256).to_base64()?, CLAIMS, &*signature)?;
assert!(verification_result);
Ok(())
}
}

View File

@@ -0,0 +1,138 @@
//! RustCrypto implementations of signing and verifying algorithms.
//! According to that organization, only hmac is safely implemented at the
//! moment.
use digest::{
block_buffer::Eager,
consts::U256,
core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore},
generic_array::typenum::{IsLess, Le, NonZero},
HashMarker,
};
use hmac::{Hmac, Mac};
use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm};
use crate::error::Error;
use crate::SEPARATOR;
/// A trait used to make the implementation of `SigningAlgorithm` and
/// `VerifyingAlgorithm` easier.
/// RustCrypto crates tend to have algorithm types defined at the type level,
/// so they cannot accept a self argument.
pub trait TypeLevelAlgorithmType {
fn algorithm_type() -> AlgorithmType;
}
macro_rules! type_level_algorithm_type {
($rust_crypto_type: ty, $algorithm_type: expr) => {
impl TypeLevelAlgorithmType for $rust_crypto_type {
fn algorithm_type() -> AlgorithmType {
$algorithm_type
}
}
};
}
type_level_algorithm_type!(sha2::Sha256, AlgorithmType::Hs256);
type_level_algorithm_type!(sha2::Sha384, AlgorithmType::Hs384);
type_level_algorithm_type!(sha2::Sha512, AlgorithmType::Hs512);
impl<D> SigningAlgorithm for Hmac<D>
where
D: CoreProxy + TypeLevelAlgorithmType,
D::Core: HashMarker
+ BufferKindUser<BufferKind = Eager>
+ FixedOutputCore
+ digest::Reset
+ Default
+ Clone,
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
fn algorithm_type(&self) -> AlgorithmType {
D::algorithm_type()
}
fn sign(&self, header: &str, claims: &str) -> Result<String, Error> {
let hmac = get_hmac_with_data(self, header, claims);
let mac_result = hmac.finalize();
let code = mac_result.into_bytes();
Ok(base64::encode_config(&code, base64::URL_SAFE_NO_PAD))
}
}
impl<D> VerifyingAlgorithm for Hmac<D>
where
D: CoreProxy + TypeLevelAlgorithmType,
D::Core: HashMarker
+ BufferKindUser<BufferKind = Eager>
+ FixedOutputCore
+ digest::Reset
+ Default
+ Clone,
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
fn algorithm_type(&self) -> AlgorithmType {
D::algorithm_type()
}
fn verify_bytes(&self, header: &str, claims: &str, signature: &[u8]) -> Result<bool, Error> {
let hmac = get_hmac_with_data(self, header, claims);
hmac.verify_slice(signature)?;
Ok(true)
}
}
fn get_hmac_with_data<D>(hmac: &Hmac<D>, header: &str, claims: &str) -> Hmac<D>
where
D: CoreProxy,
D::Core: HashMarker
+ BufferKindUser<BufferKind = Eager>
+ FixedOutputCore
+ digest::Reset
+ Default
+ Clone,
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
let mut hmac = hmac.clone();
hmac.reset();
hmac.update(header.as_bytes());
hmac.update(SEPARATOR.as_bytes());
hmac.update(claims.as_bytes());
hmac
}
#[cfg(test)]
mod tests {
use crate::algorithm::{SigningAlgorithm, VerifyingAlgorithm};
use crate::error::Error;
use hmac::{Hmac, Mac};
use sha2::Sha256;
#[test]
pub fn sign() -> Result<(), Error> {
let header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
let expected_signature = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let signer: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
let computed_signature = SigningAlgorithm::sign(&signer, header, claims)?;
assert_eq!(computed_signature, expected_signature);
Ok(())
}
#[test]
pub fn verify() -> Result<(), Error> {
let header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
let signature = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let verifier: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
assert!(VerifyingAlgorithm::verify(
&verifier, header, claims, signature
)?);
Ok(())
}
}

View File

@@ -0,0 +1,32 @@
use std::borrow::Borrow;
use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
/// A store of keys that can be retrieved by key id.
pub trait Store {
type Algorithm: ?Sized;
fn get(&self, key_id: &str) -> Option<&Self::Algorithm>;
}
impl<K, A> Store for BTreeMap<K, A>
where
K: Borrow<str> + Ord,
{
type Algorithm = A;
fn get(&self, key_id: &str) -> Option<&A> {
BTreeMap::get(self, key_id)
}
}
impl<K, A> Store for HashMap<K, A>
where
K: Borrow<str> + Ord + Hash,
{
type Algorithm = A;
fn get(&self, key_id: &str) -> Option<&A> {
HashMap::get(self, key_id)
}
}

92
zeroidc/vendor/jwt/src/claims.rs vendored Normal file
View File

@@ -0,0 +1,92 @@
//! Convenience structs for commonly defined fields in claims.
use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
/// Generic [JWT claims](https://tools.ietf.org/html/rfc7519#page-8) with
/// defined fields for registered and private claims.
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Claims {
#[serde(flatten)]
pub registered: RegisteredClaims,
#[serde(flatten)]
pub private: BTreeMap<String, serde_json::Value>,
}
impl Claims {
pub fn new(registered: RegisteredClaims) -> Self {
Claims {
registered,
private: BTreeMap::new(),
}
}
}
pub type SecondsSinceEpoch = u64;
/// Registered claims according to the
/// [JWT specification](https://tools.ietf.org/html/rfc7519#page-9).
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct RegisteredClaims {
#[serde(rename = "iss", skip_serializing_if = "Option::is_none")]
pub issuer: Option<String>,
#[serde(rename = "sub", skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(rename = "aud", skip_serializing_if = "Option::is_none")]
pub audience: Option<String>,
#[serde(rename = "exp", skip_serializing_if = "Option::is_none")]
pub expiration: Option<SecondsSinceEpoch>,
#[serde(rename = "nbf", skip_serializing_if = "Option::is_none")]
pub not_before: Option<SecondsSinceEpoch>,
#[serde(rename = "iat", skip_serializing_if = "Option::is_none")]
pub issued_at: Option<SecondsSinceEpoch>,
#[serde(rename = "jti", skip_serializing_if = "Option::is_none")]
pub json_web_token_id: Option<String>,
}
#[cfg(test)]
mod tests {
use crate::claims::Claims;
use crate::error::Error;
use crate::{FromBase64, ToBase64};
use serde_json::Value;
use std::default::Default;
// {"iss":"mikkyang.com","exp":1302319100,"custom_claim":true}
const ENCODED_PAYLOAD: &str =
"eyJpc3MiOiJtaWtreWFuZy5jb20iLCJleHAiOjEzMDIzMTkxMDAsImN1c3RvbV9jbGFpbSI6dHJ1ZX0K";
#[test]
fn registered_claims() -> Result<(), Error> {
let claims = Claims::from_base64(ENCODED_PAYLOAD)?;
assert_eq!(claims.registered.issuer.unwrap(), "mikkyang.com");
assert_eq!(claims.registered.expiration.unwrap(), 1302319100);
Ok(())
}
#[test]
fn private_claims() -> Result<(), Error> {
let claims = Claims::from_base64(ENCODED_PAYLOAD)?;
assert_eq!(claims.private["custom_claim"], Value::Bool(true));
Ok(())
}
#[test]
fn roundtrip() -> Result<(), Error> {
let mut claims: Claims = Default::default();
claims.registered.issuer = Some("mikkyang.com".into());
claims.registered.expiration = Some(1302319100);
let enc = claims.to_base64()?;
assert_eq!(claims, Claims::from_base64(&*enc)?);
Ok(())
}
}

75
zeroidc/vendor/jwt/src/error.rs vendored Normal file
View File

@@ -0,0 +1,75 @@
use std::fmt;
use std::string::FromUtf8Error;
use base64::DecodeError;
use crypto_common::InvalidLength;
use digest::MacError;
use serde_json::Error as JsonError;
use self::Error::*;
use crate::algorithm::AlgorithmType;
#[derive(Debug)]
pub enum Error {
AlgorithmMismatch(AlgorithmType, AlgorithmType),
Base64(DecodeError),
Format,
InvalidSignature,
Json(JsonError),
NoClaimsComponent,
NoHeaderComponent,
NoKeyId,
NoKeyWithKeyId(String),
NoSignatureComponent,
RustCryptoMac(MacError),
RustCryptoMacKeyLength(InvalidLength),
TooManyComponents,
Utf8(FromUtf8Error),
#[cfg(feature = "openssl")]
OpenSsl(openssl::error::ErrorStack),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
AlgorithmMismatch(a, b) => {
write!(f, "Expected algorithm type {:?} but found {:?}", a, b)
}
NoKeyId => write!(f, "No key id found"),
NoKeyWithKeyId(ref kid) => write!(f, "Key with key id {} not found", kid),
NoHeaderComponent => write!(f, "No header component found in token string"),
NoClaimsComponent => write!(f, "No claims component found in token string"),
NoSignatureComponent => write!(f, "No signature component found in token string"),
TooManyComponents => write!(f, "Too many components found in token string"),
Format => write!(f, "Format"),
InvalidSignature => write!(f, "Invalid signature"),
Base64(ref x) => write!(f, "{}", x),
Json(ref x) => write!(f, "{}", x),
Utf8(ref x) => write!(f, "{}", x),
RustCryptoMac(ref x) => write!(f, "{}", x),
RustCryptoMacKeyLength(ref x) => write!(f, "{}", x),
#[cfg(feature = "openssl")]
OpenSsl(ref x) => write!(f, "{}", x),
}
}
}
impl std::error::Error for Error {}
macro_rules! error_wrap {
($f:ty, $e:expr) => {
impl From<$f> for Error {
fn from(f: $f) -> Error {
$e(f)
}
}
};
}
error_wrap!(DecodeError, Base64);
error_wrap!(JsonError, Json);
error_wrap!(FromUtf8Error, Utf8);
error_wrap!(MacError, RustCryptoMac);
error_wrap!(InvalidLength, RustCryptoMacKeyLength);
#[cfg(feature = "openssl")]
error_wrap!(openssl::error::ErrorStack, Error::OpenSsl);

191
zeroidc/vendor/jwt/src/header.rs vendored Normal file
View File

@@ -0,0 +1,191 @@
//! Convenience structs for commonly defined fields in headers.
use std::borrow::Cow;
use serde::{Deserialize, Serialize};
use crate::algorithm::AlgorithmType;
use crate::error::Error;
use crate::ToBase64;
/// A trait for any header than can conform to the
/// [JWT specification](https://tools.ietf.org/html/rfc7519#page-11).
pub trait JoseHeader {
fn algorithm_type(&self) -> AlgorithmType;
fn key_id(&self) -> Option<&str> {
None
}
fn type_(&self) -> Option<HeaderType> {
None
}
fn content_type(&self) -> Option<HeaderContentType> {
None
}
}
/// Generic [JWT header](https://tools.ietf.org/html/rfc7519#page-11) with
/// defined fields for common fields.
#[derive(Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct Header {
#[serde(rename = "alg")]
pub algorithm: AlgorithmType,
#[serde(rename = "kid", skip_serializing_if = "Option::is_none")]
pub key_id: Option<String>,
#[serde(rename = "typ", skip_serializing_if = "Option::is_none")]
pub type_: Option<HeaderType>,
#[serde(rename = "cty", skip_serializing_if = "Option::is_none")]
pub content_type: Option<HeaderContentType>,
}
impl JoseHeader for Header {
fn algorithm_type(&self) -> AlgorithmType {
self.algorithm
}
fn key_id(&self) -> Option<&str> {
self.key_id.as_deref()
}
fn type_(&self) -> Option<HeaderType> {
self.type_
}
fn content_type(&self) -> Option<HeaderContentType> {
self.content_type
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum HeaderType {
#[serde(rename = "JWT")]
JsonWebToken,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum HeaderContentType {
#[serde(rename = "JWT")]
JsonWebToken,
}
/// A header that only contains the algorithm type. The `ToBase64`
/// implementation uses static strings for faster serialization.
pub struct PrecomputedAlgorithmOnlyHeader(pub AlgorithmType);
impl JoseHeader for PrecomputedAlgorithmOnlyHeader {
fn algorithm_type(&self) -> AlgorithmType {
let PrecomputedAlgorithmOnlyHeader(algorithm_type) = *self;
algorithm_type
}
}
impl ToBase64 for PrecomputedAlgorithmOnlyHeader {
fn to_base64(&self) -> Result<Cow<'static, str>, Error> {
let precomputed_str = match self.algorithm_type() {
AlgorithmType::Hs256 => "eyJhbGciOiAiSFMyNTYifQ",
AlgorithmType::Hs384 => "eyJhbGciOiAiSFMzODQifQ",
AlgorithmType::Hs512 => "eyJhbGciOiAiSFM1MTIifQ",
AlgorithmType::Rs256 => "eyJhbGciOiAiUlMyNTYifQ",
AlgorithmType::Rs384 => "eyJhbGciOiAiUlMzODQifQ",
AlgorithmType::Rs512 => "eyJhbGciOiAiUlM1MTIifQ",
AlgorithmType::Es256 => "eyJhbGciOiAiRVMyNTYifQ",
AlgorithmType::Es384 => "eyJhbGciOiAiRVMzODQifQ",
AlgorithmType::Es512 => "eyJhbGciOiAiRVM1MTIifQ",
AlgorithmType::Ps256 => "eyJhbGciOiAiUFMyNTYifQ",
AlgorithmType::Ps384 => "eyJhbGciOiAiUFMzODQifQ",
AlgorithmType::Ps512 => "eyJhbGciOiAiUFM1MTIifQ",
AlgorithmType::None => "eyJhbGciOiAibm9uZSJ9Cg",
};
Ok(Cow::Borrowed(precomputed_str))
}
}
/// A header with a borrowed key. Used for signing claims with a Store
/// conveniently.
#[derive(Serialize)]
pub(crate) struct BorrowedKeyHeader<'a> {
#[serde(rename = "alg")]
pub algorithm: AlgorithmType,
#[serde(rename = "kid")]
pub key_id: &'a str,
}
impl<'a> JoseHeader for BorrowedKeyHeader<'a> {
fn algorithm_type(&self) -> AlgorithmType {
self.algorithm
}
fn key_id(&self) -> Option<&str> {
Some(self.key_id)
}
}
#[cfg(test)]
mod tests {
use crate::algorithm::AlgorithmType;
use crate::error::Error;
use crate::header::{Header, HeaderType, PrecomputedAlgorithmOnlyHeader};
use crate::{FromBase64, ToBase64};
#[test]
fn from_base64() -> Result<(), Error> {
let enc = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
let header = Header::from_base64(enc)?;
assert_eq!(header.type_.unwrap(), HeaderType::JsonWebToken);
assert_eq!(header.algorithm, AlgorithmType::Hs256);
let enc = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFLU0YzZyJ9";
let header = Header::from_base64(enc)?;
assert_eq!(header.key_id.unwrap(), "1KSF3g".to_string());
assert_eq!(header.algorithm, AlgorithmType::Rs256);
Ok(())
}
#[test]
fn roundtrip() -> Result<(), Error> {
let header: Header = Default::default();
let enc = header.to_base64()?;
assert_eq!(header, Header::from_base64(&*enc)?);
Ok(())
}
#[test]
fn precomputed_headers() -> Result<(), Error> {
let algorithms = [
AlgorithmType::Hs256,
AlgorithmType::Hs384,
AlgorithmType::Hs512,
AlgorithmType::Rs256,
AlgorithmType::Rs384,
AlgorithmType::Rs512,
AlgorithmType::Es256,
AlgorithmType::Es384,
AlgorithmType::Es512,
AlgorithmType::Ps256,
AlgorithmType::Ps384,
AlgorithmType::Ps512,
AlgorithmType::None,
];
for algorithm in algorithms.iter() {
let precomputed = PrecomputedAlgorithmOnlyHeader(*algorithm);
let precomputed_str = precomputed.to_base64()?;
let header = Header::from_base64(&*precomputed_str)?;
assert_eq!(*algorithm, header.algorithm);
}
Ok(())
}
}

228
zeroidc/vendor/jwt/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,228 @@
//! ### Only Claims
//! If you don't care about that header as long as the header is verified, signing
//! and verification can be done with just a few traits.
//! #### Signing
//! Claims can be any `serde::Serialize` type, usually derived with
//! `serde_derive`.
//! ```rust
//! use hmac::{Hmac, Mac};
//! use jwt::SignWithKey;
//! use sha2::Sha256;
//! use std::collections::BTreeMap;
//!
//! # use jwt::Error;
//! # fn try_main() -> Result<(), Error> {
//! let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret")?;
//! let mut claims = BTreeMap::new();
//! claims.insert("sub", "someone");
//! let token_str = claims.sign_with_key(&key)?;
//! assert_eq!(token_str, "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0");
//! # Ok(())
//! # }
//! # try_main().unwrap()
//! ```
//! #### Verification
//! Claims can be any `serde::Deserialize` type, usually derived with
//! `serde_derive`.
//! ```rust
//! use hmac::{Hmac, Mac};
//! use jwt::VerifyWithKey;
//! use sha2::Sha256;
//! use std::collections::BTreeMap;
//!
//! # use jwt::Error;
//! # fn try_main() -> Result<(), Error> {
//! let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret")?;
//! let token_str = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0";
//! let claims: BTreeMap<String, String> = token_str.verify_with_key(&key)?;
//! assert_eq!(claims["sub"], "someone");
//! # Ok(())
//! # }
//! # try_main().unwrap()
//! ```
//! ### Header and Claims
//! If you need to customize the header, you can use the `Token` struct. For
//! convenience, a `Header` struct is provided for all of the commonly defined
//! fields, but any type that implements `JoseHeader` can be used.
//! #### Signing
//! Both header and claims have to implement `serde::Serialize`.
//! ```rust
//! use hmac::{Hmac, Mac};
//! use jwt::{AlgorithmType, Header, SignWithKey, Token};
//! use sha2::Sha384;
//! use std::collections::BTreeMap;
//!
//! # use jwt::Error;
//! # fn try_main() -> Result<(), Error> {
//! let key: Hmac<Sha384> = Hmac::new_from_slice(b"some-secret")?;
//! let header = Header {
//! algorithm: AlgorithmType::Hs384,
//! ..Default::default()
//! };
//! let mut claims = BTreeMap::new();
//! claims.insert("sub", "someone");
//! let token = Token::new(header, claims).sign_with_key(&key)?;
//! assert_eq!(token.as_str(), "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_");
//! # Ok(())
//! # }
//! # try_main().unwrap()
//! ```
//! #### Verification
//! Both header and claims have to implement `serde::Deserialize`.
//! ```rust
//! use hmac::{Hmac, Mac};
//! use jwt::{AlgorithmType, Header, Token, VerifyWithKey};
//! use sha2::Sha384;
//! use std::collections::BTreeMap;
//!
//! # use jwt::Error;
//! # fn try_main() -> Result<(), Error> {
//! let key: Hmac<Sha384> = Hmac::new_from_slice(b"some-secret")?;
//! let token_str = "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_";
//! let token: Token<Header, BTreeMap<String, String>, _> = token_str.verify_with_key(&key)?;
//! let header = token.header();
//! let claims = token.claims();
//! assert_eq!(header.algorithm, AlgorithmType::Hs384);
//! assert_eq!(claims["sub"], "someone");
//! # Ok(())
//! # }
//! # try_main().unwrap()
//! ```
#[cfg(doctest)]
doctest!("../README.md");
use std::borrow::Cow;
#[cfg(doctest)]
use doc_comment::doctest;
use serde::{Deserialize, Serialize};
#[cfg(feature = "openssl")]
pub use crate::algorithm::openssl::PKeyWithDigest;
pub use crate::algorithm::store::Store;
pub use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm};
pub use crate::claims::Claims;
pub use crate::claims::RegisteredClaims;
pub use crate::error::Error;
pub use crate::header::{Header, JoseHeader};
pub use crate::token::signed::{SignWithKey, SignWithStore};
pub use crate::token::verified::{VerifyWithKey, VerifyWithStore};
pub use crate::token::{Unsigned, Unverified, Verified};
pub mod algorithm;
pub mod claims;
pub mod error;
pub mod header;
pub mod token;
const SEPARATOR: &str = ".";
/// Representation of a structured JWT. Methods vary based on the signature
/// type `S`.
pub struct Token<H, C, S> {
header: H,
claims: C,
signature: S,
}
impl<H, C, S> Token<H, C, S> {
pub fn header(&self) -> &H {
&self.header
}
pub fn claims(&self) -> &C {
&self.claims
}
pub fn remove_signature(self) -> Token<H, C, Unsigned> {
Token {
header: self.header,
claims: self.claims,
signature: Unsigned,
}
}
}
impl<H, C, S> From<Token<H, C, S>> for (H, C) {
fn from(token: Token<H, C, S>) -> Self {
(token.header, token.claims)
}
}
/// A trait used to convert objects in base64 encoding. The return type can
/// be either owned if the header is dynamic, or it can be borrowed if the
/// header is a static, pre-computed value. It is implemented automatically
/// for every type that implements
/// [Serialize](../../serde/trait.Serialize.html). as a base64 encoding of
/// the object's JSON representation.
pub trait ToBase64 {
fn to_base64(&self) -> Result<Cow<str>, Error>;
}
impl<T: Serialize> ToBase64 for T {
fn to_base64(&self) -> Result<Cow<str>, Error> {
let json_bytes = serde_json::to_vec(&self)?;
let encoded_json_bytes = base64::encode_config(&json_bytes, base64::URL_SAFE_NO_PAD);
Ok(Cow::Owned(encoded_json_bytes))
}
}
/// A trait used to parse objects from base64 encoding. The return type can
/// be either owned if the header is dynamic, or it can be borrowed if the
/// header is a static, pre-computed value. It is implemented automatically
/// for every type that implements
/// [DeserializeOwned](../../serde/trait.Deserialize.html) for
/// the base64 encoded JSON representation.
pub trait FromBase64: Sized {
fn from_base64<Input: ?Sized + AsRef<[u8]>>(raw: &Input) -> Result<Self, Error>;
}
impl<T: for<'de> Deserialize<'de> + Sized> FromBase64 for T {
fn from_base64<Input: ?Sized + AsRef<[u8]>>(raw: &Input) -> Result<Self, Error> {
let json_bytes = base64::decode_config(raw, base64::URL_SAFE_NO_PAD)?;
Ok(serde_json::from_slice(&json_bytes)?)
}
}
#[cfg(test)]
mod tests {
use crate::algorithm::AlgorithmType::Hs256;
use crate::error::Error;
use crate::header::Header;
use crate::token::signed::SignWithKey;
use crate::token::verified::VerifyWithKey;
use crate::Claims;
use crate::Token;
use hmac::Hmac;
use hmac::Mac;
use sha2::Sha256;
#[test]
pub fn raw_data() -> Result<(), Error> {
let raw = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let token: Token<Header, Claims, _> = Token::parse_unverified(raw)?;
assert_eq!(token.header.algorithm, Hs256);
let verifier: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
assert!(token.verify_with_key(&verifier).is_ok());
Ok(())
}
#[test]
pub fn roundtrip() -> Result<(), Error> {
let token: Token<Header, Claims, _> = Default::default();
let key: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
let signed_token = token.sign_with_key(&key)?;
let signed_token_str = signed_token.as_str();
let recreated_token: Token<Header, Claims, _> = Token::parse_unverified(signed_token_str)?;
assert_eq!(signed_token.header(), recreated_token.header());
assert_eq!(signed_token.claims(), recreated_token.claims());
recreated_token.verify_with_key(&key)?;
Ok(())
}
}

18
zeroidc/vendor/jwt/src/token/mod.rs vendored Normal file
View File

@@ -0,0 +1,18 @@
//! A structured representation of a JWT.
pub mod signed;
pub mod verified;
pub struct Unsigned;
pub struct Signed {
pub token_string: String,
}
pub struct Verified;
pub struct Unverified<'a> {
pub header_str: &'a str,
pub claims_str: &'a str,
pub signature_str: &'a str,
}

190
zeroidc/vendor/jwt/src/token/signed.rs vendored Normal file
View File

@@ -0,0 +1,190 @@
use crate::algorithm::store::Store;
use crate::algorithm::SigningAlgorithm;
use crate::error::Error;
use crate::header::{BorrowedKeyHeader, Header, JoseHeader};
use crate::token::{Signed, Unsigned};
use crate::{ToBase64, Token, SEPARATOR};
/// Allow objects to be signed with a key.
pub trait SignWithKey<T> {
fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<T, Error>;
}
/// Allow objects to be signed with a store.
pub trait SignWithStore<T> {
fn sign_with_store<S, A>(self, store: &S) -> Result<T, Error>
where
S: Store<Algorithm = A>,
A: SigningAlgorithm;
}
impl<H, C> Token<H, C, Unsigned> {
/// Create a new unsigned token, with mutable headers and claims.
pub fn new(header: H, claims: C) -> Self {
Token {
header,
claims,
signature: Unsigned,
}
}
pub fn header_mut(&mut self) -> &mut H {
&mut self.header
}
pub fn claims_mut(&mut self) -> &mut C {
&mut self.claims
}
}
impl<H, C> Default for Token<H, C, Unsigned>
where
H: Default,
C: Default,
{
fn default() -> Self {
Token::new(H::default(), C::default())
}
}
impl<C: ToBase64> SignWithKey<String> for C {
fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<String, Error> {
let header = Header {
algorithm: key.algorithm_type(),
..Default::default()
};
let token = Token::new(header, self).sign_with_key(key)?;
Ok(token.signature.token_string)
}
}
impl<'a, C: ToBase64> SignWithStore<String> for (&'a str, C) {
fn sign_with_store<S, A>(self, store: &S) -> Result<String, Error>
where
S: Store<Algorithm = A>,
A: SigningAlgorithm,
{
let (key_id, claims) = self;
let key = store
.get(key_id)
.ok_or_else(|| Error::NoKeyWithKeyId(key_id.to_owned()))?;
let header = BorrowedKeyHeader {
algorithm: key.algorithm_type(),
key_id,
};
let token = Token::new(header, claims).sign_with_key(key)?;
Ok(token.signature.token_string)
}
}
impl<H, C> SignWithKey<Token<H, C, Signed>> for Token<H, C, Unsigned>
where
H: ToBase64 + JoseHeader,
C: ToBase64,
{
fn sign_with_key(self, key: &impl SigningAlgorithm) -> Result<Token<H, C, Signed>, Error> {
let header_algorithm = self.header.algorithm_type();
let key_algorithm = key.algorithm_type();
if header_algorithm != key_algorithm {
return Err(Error::AlgorithmMismatch(header_algorithm, key_algorithm));
}
let header = self.header.to_base64()?;
let claims = self.claims.to_base64()?;
let signature = key.sign(&header, &claims)?;
let token_string = [&*header, &*claims, &signature].join(SEPARATOR);
Ok(Token {
header: self.header,
claims: self.claims,
signature: Signed { token_string },
})
}
}
impl<H, C> SignWithStore<Token<H, C, Signed>> for Token<H, C, Unsigned>
where
H: ToBase64 + JoseHeader,
C: ToBase64,
{
fn sign_with_store<S, A>(self, store: &S) -> Result<Token<H, C, Signed>, Error>
where
S: Store<Algorithm = A>,
A: SigningAlgorithm,
{
let key_id = self.header.key_id().ok_or(Error::NoKeyId)?;
let key = store
.get(key_id)
.ok_or_else(|| Error::NoKeyWithKeyId(key_id.to_owned()))?;
self.sign_with_key(key)
}
}
impl<'a, H, C> Token<H, C, Signed> {
/// Get the string representation of the token.
pub fn as_str(&self) -> &str {
&self.signature.token_string
}
}
impl<H, C> From<Token<H, C, Signed>> for String {
fn from(token: Token<H, C, Signed>) -> Self {
token.signature.token_string
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use hmac::{Hmac, Mac};
use serde::Serialize;
use sha2::{Sha256, Sha512};
use crate::algorithm::AlgorithmType;
use crate::error::Error;
use crate::header::Header;
use crate::token::signed::{SignWithKey, SignWithStore};
use crate::Token;
#[derive(Serialize)]
struct Claims<'a> {
name: &'a str,
}
#[test]
pub fn sign_claims() -> Result<(), Error> {
let claims = Claims { name: "John Doe" };
let key: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
let signed_token = claims.sign_with_key(&key)?;
assert_eq!(signed_token, "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.LlTGHPZRXbci-y349jXXN0byQniQQqwKGybzQCFIgY0");
Ok(())
}
#[test]
pub fn sign_unsigned_with_store() -> Result<(), Error> {
let mut key_store = BTreeMap::new();
let key1: Hmac<Sha512> = Hmac::new_from_slice(b"first")?;
let key2: Hmac<Sha512> = Hmac::new_from_slice(b"second")?;
key_store.insert("first_key".to_owned(), key1);
key_store.insert("second_key".to_owned(), key2);
let header = Header {
algorithm: AlgorithmType::Hs512,
key_id: Some(String::from("second_key")),
..Default::default()
};
let claims = Claims { name: "Jane Doe" };
let token = Token::new(header, claims);
let signed_token = token.sign_with_store(&key_store)?;
assert_eq!(signed_token.as_str(), "eyJhbGciOiJIUzUxMiIsImtpZCI6InNlY29uZF9rZXkifQ.eyJuYW1lIjoiSmFuZSBEb2UifQ.t2ON5s8DDb2hefBIWAe0jaEcp-T7b2Wevmj0kKJ8BFxKNQURHpdh4IA-wbmBmqtiCnqTGoRdqK45hhW0AOtz0A");
Ok(())
}
}

289
zeroidc/vendor/jwt/src/token/verified.rs vendored Normal file
View File

@@ -0,0 +1,289 @@
use crate::algorithm::store::Store;
use crate::algorithm::VerifyingAlgorithm;
use crate::error::Error;
use crate::header::{Header, JoseHeader};
use crate::token::{Unverified, Verified};
use crate::{FromBase64, Token, SEPARATOR};
/// Allow objects to be verified with a key.
pub trait VerifyWithKey<T> {
fn verify_with_key(self, key: &impl VerifyingAlgorithm) -> Result<T, Error>;
}
/// Allow objects to be verified with a store.
pub trait VerifyWithStore<T> {
fn verify_with_store<S, A>(self, store: &S) -> Result<T, Error>
where
S: Store<Algorithm = A>,
A: VerifyingAlgorithm;
}
impl<'a, H: JoseHeader, C> VerifyWithKey<Token<H, C, Verified>> for Token<H, C, Unverified<'a>> {
fn verify_with_key(
self,
key: &impl VerifyingAlgorithm,
) -> Result<Token<H, C, Verified>, Error> {
let header = self.header();
let header_algorithm = header.algorithm_type();
let key_algorithm = key.algorithm_type();
if header_algorithm != key_algorithm {
return Err(Error::AlgorithmMismatch(header_algorithm, key_algorithm));
}
let Unverified {
header_str,
claims_str,
signature_str,
} = self.signature;
if key.verify(header_str, claims_str, signature_str)? {
Ok(Token {
header: self.header,
claims: self.claims,
signature: Verified,
})
} else {
Err(Error::InvalidSignature)
}
}
}
impl<'a, H: JoseHeader, C> VerifyWithStore<Token<H, C, Verified>> for Token<H, C, Unverified<'a>> {
fn verify_with_store<S, A>(self, store: &S) -> Result<Token<H, C, Verified>, Error>
where
S: Store<Algorithm = A>,
A: VerifyingAlgorithm,
{
let header = self.header();
let key_id = header.key_id().ok_or(Error::NoKeyId)?;
let key = store
.get(key_id)
.ok_or_else(|| Error::NoKeyWithKeyId(key_id.to_owned()))?;
self.verify_with_key(key)
}
}
impl<'a, H, C> VerifyWithKey<Token<H, C, Verified>> for &'a str
where
H: FromBase64 + JoseHeader,
C: FromBase64,
{
fn verify_with_key(
self,
key: &impl VerifyingAlgorithm,
) -> Result<Token<H, C, Verified>, Error> {
let unverified = Token::parse_unverified(self)?;
unverified.verify_with_key(key)
}
}
impl<'a, H, C> VerifyWithStore<Token<H, C, Verified>> for &'a str
where
H: FromBase64 + JoseHeader,
C: FromBase64,
{
fn verify_with_store<S, A>(self, store: &S) -> Result<Token<H, C, Verified>, Error>
where
S: Store<Algorithm = A>,
A: VerifyingAlgorithm,
{
let unverified: Token<H, C, _> = Token::parse_unverified(self)?;
unverified.verify_with_store(store)
}
}
impl<'a, C: FromBase64> VerifyWithKey<C> for &'a str {
fn verify_with_key(self, key: &impl VerifyingAlgorithm) -> Result<C, Error> {
let token: Token<Header, C, _> = self.verify_with_key(key)?;
Ok(token.claims)
}
}
impl<'a, C: FromBase64> VerifyWithStore<C> for &'a str {
fn verify_with_store<S, A>(self, store: &S) -> Result<C, Error>
where
S: Store<Algorithm = A>,
A: VerifyingAlgorithm,
{
let token: Token<Header, C, _> = self.verify_with_store(store)?;
Ok(token.claims)
}
}
impl<'a, H: FromBase64, C: FromBase64> Token<H, C, Unverified<'a>> {
/// Not recommended. Parse the header and claims without checking the validity of the signature.
pub fn parse_unverified(token_str: &str) -> Result<Token<H, C, Unverified>, Error> {
let [header_str, claims_str, signature_str] = split_components(token_str)?;
let header = H::from_base64(header_str)?;
let claims = C::from_base64(claims_str)?;
let signature = Unverified {
header_str,
claims_str,
signature_str,
};
Ok(Token {
header,
claims,
signature,
})
}
}
pub(crate) fn split_components(token: &str) -> Result<[&str; 3], Error> {
let mut components = token.split(SEPARATOR);
let header = components.next().ok_or(Error::NoHeaderComponent)?;
let claims = components.next().ok_or(Error::NoClaimsComponent)?;
let signature = components.next().ok_or(Error::NoSignatureComponent)?;
if components.next().is_some() {
return Err(Error::TooManyComponents);
}
Ok([header, claims, signature])
}
#[cfg(test)]
mod tests {
use std::collections::{BTreeMap, HashMap};
use std::iter::FromIterator;
use hmac::{Hmac, Mac};
use serde::Deserialize;
use sha2::{Sha256, Sha512};
use crate::algorithm::VerifyingAlgorithm;
use crate::error::Error;
use crate::token::verified::{VerifyWithKey, VerifyWithStore};
#[derive(Debug, Deserialize)]
struct Claims {
name: String,
}
#[test]
#[cfg(feature = "openssl")]
pub fn token_can_not_be_verified_with_a_wrong_key() -> Result<(), Error> {
use crate::{token::signed::SignWithKey, AlgorithmType, Header, PKeyWithDigest, Token};
use openssl::{hash::MessageDigest, pkey::PKey};
let private_pem = include_bytes!("../../test/rs256-private.pem");
let public_pem = include_bytes!("../../test/rs256-public-2.pem");
let rs256_private_key = PKeyWithDigest {
digest: MessageDigest::sha256(),
key: PKey::private_key_from_pem(private_pem).unwrap(),
};
let rs256_public_key = PKeyWithDigest {
digest: MessageDigest::sha256(),
key: PKey::public_key_from_pem(public_pem).unwrap(),
};
let header = Header {
algorithm: AlgorithmType::Rs256,
..Default::default()
};
let mut claims = BTreeMap::new();
claims.insert("sub", "someone");
let signed_token = Token::new(header, claims).sign_with_key(&rs256_private_key)?;
let token_str = signed_token.as_str();
let unverified_token: Token<Header, BTreeMap<String, String>, _> =
Token::parse_unverified(token_str)?;
let verified_token_result = unverified_token.verify_with_key(&rs256_public_key);
assert!(verified_token_result.is_err());
match verified_token_result.err().unwrap() {
Error::InvalidSignature => Ok(()),
other => panic!("Wrong error type: {:?}", other),
}
}
#[test]
pub fn component_errors() {
let key: Hmac<Sha256> = Hmac::new_from_slice(b"first").unwrap();
let no_claims = "header";
match VerifyWithKey::<String>::verify_with_key(no_claims, &key) {
Err(Error::NoClaimsComponent) => (),
Ok(s) => panic!("Verify should not have succeeded with output {:?}", s),
x => panic!("Incorrect error type {:?}", x),
}
let no_signature = "header.claims";
match VerifyWithKey::<String>::verify_with_key(no_signature, &key) {
Err(Error::NoSignatureComponent) => (),
Ok(s) => panic!("Verify should not have succeeded with output {:?}", s),
x => panic!("Incorrect error type {:?}", x),
}
let too_many = "header.claims.signature.";
match VerifyWithKey::<String>::verify_with_key(too_many, &key) {
Err(Error::TooManyComponents) => (),
Ok(s) => panic!("Verify should not have succeeded with output {:?}", s),
x => panic!("Incorrect error type {:?}", x),
}
}
// Test stores
fn create_test_data<T>() -> Result<T, Error>
where
T: FromIterator<(&'static str, Box<dyn VerifyingAlgorithm>)>,
{
// Test two different algorithms in the same store
let key1: Hmac<Sha256> = Hmac::new_from_slice(b"first")?;
let key2: Hmac<Sha512> = Hmac::new_from_slice(b"second")?;
let name_to_key_tuples = vec![
("first_key", Box::new(key1) as Box<dyn VerifyingAlgorithm>),
("second_key", Box::new(key2) as Box<dyn VerifyingAlgorithm>),
]
.into_iter()
.collect();
Ok(name_to_key_tuples)
}
// Header {"alg":"HS512","kid":"second_key"}
// Claims {"name":"Jane Doe"}
const JANE_DOE_SECOND_KEY_TOKEN: &str = "eyJhbGciOiJIUzUxMiIsImtpZCI6InNlY29uZF9rZXkifQ.eyJuYW1lIjoiSmFuZSBEb2UifQ.t2ON5s8DDb2hefBIWAe0jaEcp-T7b2Wevmj0kKJ8BFxKNQURHpdh4IA-wbmBmqtiCnqTGoRdqK45hhW0AOtz0A";
#[test]
pub fn verify_claims_with_b_tree_map() -> Result<(), Error> {
let key_store: BTreeMap<_, _> = create_test_data()?;
let claims: Claims = JANE_DOE_SECOND_KEY_TOKEN.verify_with_store(&key_store)?;
assert_eq!(claims.name, "Jane Doe");
Ok(())
}
#[test]
pub fn verify_claims_with_hash_map() -> Result<(), Error> {
let key_store: HashMap<_, _> = create_test_data()?;
let claims: Claims = JANE_DOE_SECOND_KEY_TOKEN.verify_with_store(&key_store)?;
assert_eq!(claims.name, "Jane Doe");
Ok(())
}
#[test]
pub fn verify_claims_with_missing_key() -> Result<(), Error> {
let key_store: BTreeMap<_, _> = create_test_data()?;
let missing_key_token = "eyJhbGciOiJIUzUxMiIsImtpZCI6Im1pc3Npbmdfa2V5In0.eyJuYW1lIjoiSmFuZSBEb2UifQ.MC9hmBjv9OABdv5bsjVdwUgPOhvpe6a924KU-U7PjVWF2N-f_HXa1PVWtDVJ-dqt1GKutVwixrz7hgVvE_G5_w";
let should_fail_claims: Result<Claims, _> = missing_key_token.verify_with_store(&key_store);
match should_fail_claims {
Err(Error::NoKeyWithKeyId(key_id)) => assert_eq!(key_id, "missing_key"),
_ => panic!(
"Missing key should have triggered specific error but returned {:?}",
should_fail_claims
),
}
Ok(())
}
}