use std::fmt::Display;
use base64::{Engine as _, engine::general_purpose};
use ring::hmac::{self, Key};
use crate::error::{Error, JWTError, KeyError};
#[derive(Clone)]
pub struct HS256 {
key: Key
}
impl HS256 {
pub fn new<A: AsRef<str>>(secret: A) -> Self {
let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_ref().as_bytes());
Self {
key
}
}
pub fn sign_jwt(&self, headers: &str, payload: &str) -> String {
let header_str = general_purpose::URL_SAFE_NO_PAD.encode(headers);
let payload_str = general_purpose::URL_SAFE_NO_PAD.encode(payload);
let unsecure_jwt = format!("{}.{}",header_str,payload_str);
let sign = general_purpose::URL_SAFE_NO_PAD.encode(hmac::sign(&self.key, unsecure_jwt.as_bytes()).as_ref());
format!("{}.{}",unsecure_jwt,sign)
}
pub fn verify_jwt(&self, jwt: &str) -> Result<(),Error> {
let jwt_parts = jwt.split('.').collect::<Vec<&str>>();
if jwt_parts.len() != 3 {
return Err(Error::JWT(JWTError::JWTParts))
}
let headerb64_str = &jwt_parts[0];
let payloadb64_str = &jwt_parts[1];
let signatureb64 = &jwt_parts[2];
let unprotected_jwt = format!("{}.{}",headerb64_str,payloadb64_str);
let signature = general_purpose::URL_SAFE_NO_PAD.decode(signatureb64).map_err(|e| Error::Decode(e,"Unable to decode signature from jwt"))?;
hmac::verify(&self.key, unprotected_jwt.as_bytes(), signature.as_ref()).map_err(|e| Error::Key(KeyError::Verification(e)))
}
}
impl Display for HS256 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"hs256")
}
}
#[cfg(test)]
mod test {
use crate::{Error, sign_algorithms::HS256};
#[test]
fn verify_signing_hs256() -> Result<(),Error> {
let header = String::from("{\"Animal\": \"perrito\"}");
let payload = String::from("{\"Nombre\": \"Milaneso\"}");
let secret = "Doggies";
let sym_key = HS256::new(secret);
let secured_jwt = sym_key.sign_jwt(&header,&payload);
sym_key.verify_jwt(&secured_jwt)
}
}