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:
88
zeroidc/vendor/jwt/src/algorithm/mod.rs
vendored
Normal file
88
zeroidc/vendor/jwt/src/algorithm/mod.rs
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
179
zeroidc/vendor/jwt/src/algorithm/openssl.rs
vendored
Normal file
179
zeroidc/vendor/jwt/src/algorithm/openssl.rs
vendored
Normal 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(())
|
||||
}
|
||||
}
|
||||
138
zeroidc/vendor/jwt/src/algorithm/rust_crypto.rs
vendored
Normal file
138
zeroidc/vendor/jwt/src/algorithm/rust_crypto.rs
vendored
Normal 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(())
|
||||
}
|
||||
}
|
||||
32
zeroidc/vendor/jwt/src/algorithm/store.rs
vendored
Normal file
32
zeroidc/vendor/jwt/src/algorithm/store.rs
vendored
Normal 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
92
zeroidc/vendor/jwt/src/claims.rs
vendored
Normal 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
75
zeroidc/vendor/jwt/src/error.rs
vendored
Normal 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
191
zeroidc/vendor/jwt/src/header.rs
vendored
Normal 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
228
zeroidc/vendor/jwt/src/lib.rs
vendored
Normal 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
18
zeroidc/vendor/jwt/src/token/mod.rs
vendored
Normal 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
190
zeroidc/vendor/jwt/src/token/signed.rs
vendored
Normal 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
289
zeroidc/vendor/jwt/src/token/verified.rs
vendored
Normal 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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user