Simplify header decoding

This commit is contained in:
Vincent Prouillet 2019-11-03 16:13:22 +00:00
parent 417e00780d
commit 73d96357c3
4 changed files with 36 additions and 32 deletions

View File

@ -45,7 +45,7 @@ pub enum ErrorKind {
/// When a key is provided with an invalid format
InvalidKeyFormat,
// validation error
// Validation errors
/// When a tokens `exp` claim indicates that it has expired
ExpiredSignature,
/// When a tokens `iss` claim does not match the expected issuer
@ -91,6 +91,7 @@ impl StdError for Error {
ErrorKind::InvalidSubject => "invalid subject",
ErrorKind::ImmatureSignature => "immature signature",
ErrorKind::InvalidAlgorithm => "algorithms don't match",
ErrorKind::InvalidAlgorithmName => "not a known algorithm",
ErrorKind::Base64(ref err) => err.description(),
ErrorKind::Json(ref err) => err.description(),
ErrorKind::Utf8(ref err) => err.description(),
@ -111,6 +112,7 @@ impl StdError for Error {
ErrorKind::InvalidSubject => None,
ErrorKind::ImmatureSignature => None,
ErrorKind::InvalidAlgorithm => None,
ErrorKind::InvalidAlgorithmName => None,
ErrorKind::Base64(ref err) => Some(err),
ErrorKind::Json(ref err) => Some(err),
ErrorKind::Utf8(ref err) => Some(err),
@ -123,17 +125,17 @@ impl StdError for Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self.0 {
ErrorKind::InvalidToken => write!(f, "invalid token"),
ErrorKind::InvalidSignature => write!(f, "invalid signature"),
ErrorKind::InvalidEcdsaKey => write!(f, "invalid ECDSA key"),
ErrorKind::InvalidRsaKey => write!(f, "invalid RSA key"),
ErrorKind::ExpiredSignature => write!(f, "expired signature"),
ErrorKind::InvalidIssuer => write!(f, "invalid issuer"),
ErrorKind::InvalidAudience => write!(f, "invalid audience"),
ErrorKind::InvalidSubject => write!(f, "invalid subject"),
ErrorKind::ImmatureSignature => write!(f, "immature signature"),
ErrorKind::InvalidAlgorithm => write!(f, "algorithms don't match"),
ErrorKind::Base64(ref err) => write!(f, "base64 error: {}", err),
ErrorKind::InvalidToken
| ErrorKind::InvalidSignature
| ErrorKind::InvalidEcdsaKey
| ErrorKind::InvalidRsaKey
| ErrorKind::ExpiredSignature
| ErrorKind::InvalidIssuer
| ErrorKind::InvalidAudience
| ErrorKind::InvalidSubject
| ErrorKind::ImmatureSignature
| ErrorKind::InvalidAlgorithm
| ErrorKind::InvalidAlgorithmName => write!(f, "{}", self.description()),
ErrorKind::Json(ref err) => write!(f, "JSON error: {}", err),
ErrorKind::Utf8(ref err) => write!(f, "UTF-8 error: {}", err),
ErrorKind::Crypto(ref err) => write!(f, "Crypto error: {}", err),

View File

@ -1,6 +1,8 @@
use crate::algorithms::Algorithm;
use serde::{Deserialize, Serialize};
use crate::algorithms::Algorithm;
use crate::errors::Result;
/// A basic JWT header, the alg defaults to HS256 and typ is automatically
/// set to `JWT`. All the other fields are optional.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -43,7 +45,7 @@ pub struct Header {
impl Header {
/// Returns a JWT header with the algorithm given
pub fn new(algorithm: Algorithm) -> Header {
pub fn new(algorithm: Algorithm) -> Self {
Header {
typ: Some("JWT".to_string()),
alg: algorithm,
@ -54,6 +56,14 @@ impl Header {
x5t: None,
}
}
/// Converts an encoded part into the Header struct if possible
pub(crate) fn from_encoded(encoded_part: &str) -> Result<Self> {
let decoded = base64::decode_config(encoded_part, base64::URL_SAFE_NO_PAD)?;
let s = String::from_utf8(decoded)?;
Ok(serde_json::from_str(&s)?)
}
}
impl Default for Header {

View File

@ -25,7 +25,7 @@ use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use crate::errors::{new_error, ErrorKind, Result};
use crate::serialization::{from_jwt_part, from_jwt_part_claims, to_jwt_part};
use crate::serialization::{encode_part, from_jwt_part_claims};
use crate::validation::validate;
/// Encode the header and claims given and sign the payload using the algorithm from the header and the key
@ -51,8 +51,8 @@ use crate::validation::validate;
/// let token = encode(&Header::default(), &my_claims, Key::Hmac("secret".as_ref())).unwrap();
/// ```
pub fn encode<T: Serialize>(header: &Header, claims: &T, key: Key) -> Result<String> {
let encoded_header = to_jwt_part(&header)?;
let encoded_claims = to_jwt_part(&claims)?;
let encoded_header = encode_part(&header)?;
let encoded_claims = encode_part(&claims)?;
let signing_input = [encoded_header.as_ref(), encoded_claims.as_ref()].join(".");
let signature = sign(&*signing_input, key, header.alg)?;
@ -97,7 +97,7 @@ pub fn decode<T: DeserializeOwned>(
) -> Result<TokenData<T>> {
let (signature, signing_input) = expect_two!(token.rsplitn(2, '.'));
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
let header: Header = from_jwt_part(header)?;
let header = Header::from_encoded(header)?;
if !verify(signature, signing_input, key, header.alg)? {
return Err(new_error(ErrorKind::InvalidSignature));
@ -135,7 +135,7 @@ pub fn decode<T: DeserializeOwned>(
pub fn dangerous_unsafe_decode<T: DeserializeOwned>(token: &str) -> Result<TokenData<T>> {
let (_, signing_input) = expect_two!(token.rsplitn(2, '.'));
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
let header: Header = from_jwt_part(header)?;
let header = Header::from_encoded(header)?;
let (decoded_claims, _): (T, _) = from_jwt_part_claims(claims)?;
@ -156,7 +156,7 @@ pub fn dangerous_unsafe_decode<T: DeserializeOwned>(token: &str) -> Result<Token
pub fn decode_header(token: &str) -> Result<Header> {
let (_, signing_input) = expect_two!(token.rsplitn(2, '.'));
let (_, header) = expect_two!(signing_input.rsplitn(2, '.'));
from_jwt_part(header)
Header::from_encoded(header)
}
/// Decode a PEM string to obtain its key

View File

@ -16,18 +16,10 @@ pub struct TokenData<T> {
pub claims: T,
}
/// Serializes to JSON and encodes to base64
pub fn to_jwt_part<T: Serialize>(input: &T) -> Result<String> {
let encoded = to_string(input)?;
Ok(base64::encode_config(encoded.as_bytes(), base64::URL_SAFE_NO_PAD))
}
/// Decodes from base64 and deserializes from JSON to a struct
pub fn from_jwt_part<B: AsRef<str>, T: DeserializeOwned>(encoded: B) -> Result<T> {
let decoded = base64::decode_config(encoded.as_ref(), base64::URL_SAFE_NO_PAD)?;
let s = String::from_utf8(decoded)?;
Ok(from_str(&s)?)
/// Serializes a struct to JSON and encodes it in base64
pub fn encode_part<T: Serialize>(input: &T) -> Result<String> {
let json = to_string(input)?;
Ok(base64::encode_config(json.as_bytes(), base64::URL_SAFE_NO_PAD))
}
/// Decodes from base64 and deserializes from JSON to a struct AND a hashmap