hs256_bin/
lib.rs

1use base64ct::{Base64UrlUnpadded, Encoding};
2use ring::hmac::{self, HMAC_SHA256};
3
4#[derive(Debug)]
5pub enum DecodeError {
6    InvalidToken,
7    InvalidSignature,
8}
9
10impl From<base64ct::Error> for DecodeError {
11    fn from(_: base64ct::Error) -> Self {
12        Self::InvalidToken
13    }
14}
15
16/// Tokens interface for creating and validating tokens
17pub struct Tokens {
18    key: hmac::Key,
19}
20
21impl Tokens {
22    /// Creates a new tokens interface from the provided
23    /// secret key
24    ///
25    /// `secret` The secret key
26    pub fn new(secret: &[u8]) -> Self {
27        // Create a new HMAC key using the provided secret
28        let key = hmac::Key::new(HMAC_SHA256, secret);
29        Self { key }
30    }
31
32    /// Encodes the value by base64 encoding the value and a
33    /// signature for the value then joining them
34    ///
35    /// `value` The data to encode as the token value
36    pub fn encode(&self, value: &[u8]) -> String {
37        // Encode the message
38        let msg = Base64UrlUnpadded::encode_string(value);
39
40        // Create a signature from the raw message bytes
41        let sig = hmac::sign(&self.key, value);
42        let sig = Base64UrlUnpadded::encode_string(sig.as_ref());
43
44        // Join the message and signature to create the token
45        [msg, sig].join(".")
46    }
47
48    /// Decodes a token claims from the provided token string
49    ///
50    /// `token` The token to decode
51    pub fn decode(&self, token: &str) -> Result<Vec<u8>, DecodeError> {
52        // Split the token parts
53        let (msg, sig) = match token.split_once('.') {
54            Some(value) => value,
55            None => return Err(DecodeError::InvalidToken),
56        };
57
58        // Decode the message signature
59        let msg: Vec<u8> = Base64UrlUnpadded::decode_vec(msg)?;
60        let sig: Vec<u8> = Base64UrlUnpadded::decode_vec(sig)?;
61
62        // Verify the signature
63        if hmac::verify(&self.key, &msg, &sig).is_err() {
64            return Err(DecodeError::InvalidSignature);
65        }
66
67        // Decode the verified token claims
68        Ok(msg)
69    }
70}