tenet/
lib.rs

1//! # JWT Watchflow
2//! A godawful jwt implementation
3//!
4//! ```toml
5//! [dependencies.jwt]
6//! package="jwt_watchflow"
7//! version = "*"
8//! features = ["HS256"]
9//! ```
10//! ### Don't use this
11//! This library is slow, baldly implemented and took a total of 20 minutes to write and bug check.
12//!
13//! You are much better off using another tried and tested JWT implementation:
14//! - [`frank_jwt`](https://crates.io/crates/frank_jwt)
15//! - [`jsonwebtoken`](https://crates.io/crates/jsonwebtoken)
16//! - [`biscuit`](https://crates.io/crates/biscuit)
17//! - [`jsonwebtokens`](https://crates.io/crates/jsonwebtokens)
18//!
19//!
20//! **Why does it exist?**
21//! I got fed up with shitty API design around signing and verifying tokens.
22//!
23//! - This library has only two separate token types.
24//! - This library does not do any verification on a token payload.
25//!
26//! ### Types
27//!
28//! This library has two token types:
29//! - A unsigned token with a given type (`Token<T>`)
30//! - A signed token (`SignedToken`)
31//!
32//! Token algorithms are decided by a enum: `TokenAlgorithm`
33//!
34//! ### Supported Algorithms
35//! - `HS256`
36//! - `HS512`
37
38//! ### Example
39//!
40//! ```
41//! use serde::{Serialize, Deserialize};
42//! use jwt_watchflow::{Token, TokenAlgorithm, SignedToken};
43//!
44//! // Create a new token type.
45//! // This uses serde's Serialize and Deserialize procedural macros.
46//! #[derive(Serialize, Deserialize, Debug)]
47//! struct MyToken {
48//!     foo: u8,
49//!     bar: bool,
50//!     baz: String
51//! }
52//!
53//! // create a key to sign and verify tokens.
54//! let token_key = b"VERY_SECURE_KEY".to_vec();
55//!
56//! // create a new token, sign it, and get the string representation
57//! let token = Token::create(
58//!     TokenAlgorithm::HS256,
59//!     MyToken { foo: 10, bar: true, baz: "Hello World".to_string() }
60//! )
61//! .sign(&token_key).unwrap().string();
62//!
63//! println!("{}", token);
64//!
65//! // verify a token input
66//! let payload = Token::<MyToken>::verify(token, &token_key).unwrap();
67//!
68//! println!("{:?}", payload);
69//! ```
70
71
72use std::fmt::{Debug, Formatter};
73use std::ops::{Deref, DerefMut};
74use serde::{Serialize, Deserialize, Serializer, Deserializer};
75use serde::de::{DeserializeOwned, Visitor};
76
77#[cfg(feature="hmac")]
78use hmac::Mac;
79
80const DOT: u8 = '.' as u8;
81static TYPE: &'static str = "JWT";
82
83/// JWT Token Header
84#[derive(Serialize, Deserialize)]
85pub struct TokenHeader {
86    #[serde(skip_serializing_if="Option::is_none")]
87    #[serde(rename="type")]
88    pub _type: Option<String>,
89
90    #[serde(rename="alg")]
91    pub algorithm: TokenAlgorithm,
92}
93
94
95#[derive(Serialize, Deserialize, Clone, Debug)]
96/// Token Signing and Verifying Algorithm
97pub enum TokenAlgorithm {
98    #[cfg(feature="HS256")]
99    HS256,
100    #[cfg(feature="HS512")]
101    HS512,
102    #[serde(other)]
103    Other
104}
105
106/// Unsigned JWT Token
107///
108/// Stores payload object and header.
109pub struct Token<T> {
110    pub header: TokenHeader,
111    pub payload: T
112}
113
114#[derive(Clone)]
115/// Signed JWT Token
116///
117/// Stores:
118/// - algorithm
119/// - body (encoding header & payload)
120/// - signature
121pub struct SignedToken {
122    algorithm: TokenAlgorithm,
123    body: Vec<u8>,
124    signature: Vec<u8>
125}
126
127#[derive(Debug)]
128/// JWT Error
129pub enum TokenError {
130    /// algorithm is not recognised or unsupported
131    UnsupportedAlgorithm,
132    /// invalid signing/verify key when creating algorithm
133    InvalidKey,
134    /// invalid header while parsing/decoding
135    InvalidHeader,
136    /// payload failed to serialise or deserialise
137    InvalidPayload(serde_json::Error),
138
139    /// token was invalid
140    InvalidToken,
141    /// signature was invalid
142    InvalidSignature
143}
144
145fn b64_encode<S: AsRef<[u8]>>(input: S) -> Vec<u8> {
146    let mut buffer = Vec::new();
147    buffer.resize(input.as_ref().len() * 4 / 3 + 4, 0);
148    let decode = base64::encode_config_slice(
149        input.as_ref(),
150        base64::Config::new(base64::CharacterSet::UrlSafe, false),
151        &mut buffer
152    );
153    buffer.truncate(decode);
154    buffer
155}
156
157
158fn b64_decode<S: AsRef<[u8]>>(input: S) -> Result<Vec<u8>, TokenError> {
159    let input = input.as_ref();
160    let mut buffer = vec![0; (input.len() + 3) / 4 * 3];
161    let decode = base64::decode_config_slice(
162        input,
163        base64::Config::new(base64::CharacterSet::UrlSafe, false),
164        &mut buffer
165    );
166
167    match decode {
168        Err(_) => Err(TokenError::InvalidToken),
169        Ok(size) => {
170            buffer.truncate(size);
171            Ok(buffer)
172        }
173    }
174}
175
176impl TokenAlgorithm {
177    fn name(&self) -> &'static str {
178        match &self {
179            #[cfg(feature="HS256")]
180            TokenAlgorithm::HS256 => "HS256",
181            #[cfg(feature="HS512")]
182            TokenAlgorithm::HS512 => "HS512",
183
184            TokenAlgorithm::Other => "Unsupported",
185        }
186    }
187
188    /// sign a given payload with a key
189    pub fn sign<P: AsRef<[u8]>>(&self, payload: P, key: &[u8]) -> Result<Vec<u8>, TokenError> {
190        match &self {
191            #[cfg(feature="HS256")]
192            TokenAlgorithm::HS256 => {
193                let mut hash = match hmac::Hmac::<sha2::Sha256>::new_from_slice(&key) {
194                    Ok(x) => x,
195                    Err(_) => return Err(TokenError::InvalidKey)
196                };
197                hash.update(payload.as_ref());
198                return Ok(hash.finalize().into_bytes().as_slice().to_vec())
199            }
200            #[cfg(feature="HS512")]
201            TokenAlgorithm::HS512 => {
202                let mut hash = match hmac::Hmac::<sha2::Sha256>::new_from_slice(&key) {
203                    Ok(x) => x,
204                    Err(_) => return Err(TokenError::InvalidKey)
205                };
206                hash.update(payload.as_ref());
207                return Ok(hash.finalize().into_bytes().as_slice().to_vec())
208            }
209
210            TokenAlgorithm::Other => Err(TokenError::UnsupportedAlgorithm)
211        }
212    }
213
214    /// verify a given payload and signature with a key
215    pub fn verify<P: AsRef<[u8]>>(&self, payload: P, sig: &[u8], key: &[u8]) -> Result<bool, TokenError> {
216        match &self {
217            #[cfg(feature="HS256")]
218            TokenAlgorithm::HS256 => {
219                let mut hash = match hmac::Hmac::<sha2::Sha256>::new_from_slice(&key) {
220                    Ok(x) => x,
221                    Err(_) => return Err(TokenError::InvalidKey)
222                };
223                hash.update(payload.as_ref());
224                Ok(hash.verify_slice(sig).is_ok())
225            }
226
227            #[cfg(feature="HS512")]
228            TokenAlgorithm::HS512 => {
229                let mut hash = match hmac::Hmac::<sha2::Sha512>::new_from_slice(&key) {
230                    Ok(x) => x,
231                    Err(_) => return Err(TokenError::InvalidKey)
232                };
233                hash.update(payload.as_ref());
234                Ok(hash.verify_slice(sig).is_ok())
235            }
236
237            TokenAlgorithm::Other => Err(TokenError::UnsupportedAlgorithm),
238        }
239    }
240}
241
242impl TokenHeader {
243    /// Create a new TokenHeader object
244    ///
245    /// - `algorithm: TokenAlgorithm` - the signing algorithm to use
246    /// - `include_type: bool` - include the `"type": "JWT"` string in the header. (default false)
247    pub fn new(algorithm: TokenAlgorithm, include_type: bool) -> Self {
248        Self {
249            algorithm,
250            _type: if include_type { Some(TYPE.to_string()) } else { None }
251        }
252    }
253
254    fn serialize(&self) -> Result<Vec<u8>, TokenError> {
255        match serde_json::to_vec(&self) {
256            Ok(x) => Ok(x),
257            Err(_) => Err(TokenError::InvalidHeader)
258        }
259    }
260
261    fn deserialize(input: &[u8]) -> Result<TokenHeader, TokenError> {
262        match serde_json::from_slice(input) {
263            Ok(x) => Ok(x),
264            Err(_) => Err(TokenError::InvalidHeader)
265        }
266    }
267}
268
269impl<T> Token<T> {
270    /// Create a new token wiht a given algorithm and payload
271    ///
272    /// ```
273    /// # use jwt_watchflow::{Token, TokenAlgorithm};
274    /// # struct Foo { a: u8, b: bool };
275    /// let token = Token::create(TokenAlgorithm::HS256, Foo { a: 69, b: false });
276    /// ```
277    pub fn create(algorithm: TokenAlgorithm, payload: T) -> Self {
278        Self {
279            header: TokenHeader::new(algorithm, false),
280            payload
281        }
282    }
283
284    /// Create a new token with a token header and payload.
285    ///
286    /// ```
287    /// # use jwt_watchflow::{Token, TokenAlgorithm, TokenHeader};
288    /// # struct Foo { a: u8, b: bool };
289    /// let token = Token::new(
290    ///     TokenHeader::new(TokenAlgorithm::HS256, true),
291    ///     Foo { a: 69, b: false }
292    /// );
293    /// ```
294    pub fn new(header: TokenHeader, payload: T) -> Self {
295        Self {
296            header,
297            payload
298        }
299    }
300}
301
302impl<T: Serialize> Token<T>  {
303    fn serialize(x: &T) -> Result<Vec<u8>, TokenError> {
304        match serde_json::to_vec(x) {
305            Ok(x) => Ok(x),
306            Err(e) => Err(TokenError::InvalidPayload(e))
307        }
308    }
309
310    fn build(&self) -> Result<Vec<u8>, TokenError> {
311        let mut buffer = b64_encode(&self.header.serialize()?);
312        buffer.push(DOT);
313        buffer.append(&mut b64_encode(Self::serialize(&self.payload)?));
314        Ok(buffer)
315    }
316
317    /// Sign a `Token` to a `SignedToken` with a given key
318    pub fn sign(&self, key: &[u8]) -> Result<SignedToken, TokenError> {
319        let buffer = self.build()?;
320        let sign = self.header.algorithm.sign(&buffer, &key)?;
321
322        Ok(SignedToken {
323            algorithm: self.header.algorithm.clone(),
324            body: buffer ,
325            signature: sign
326        })
327    }
328}
329
330impl<T: DeserializeOwned> Token<T> {
331    fn deserialize(x: &[u8]) -> Result<T, TokenError> {
332        match serde_json::from_slice(x) {
333            Ok(x) => Ok(x),
334            Err(e) => Err(TokenError::InvalidPayload(e))
335        }
336    }
337
338    /// decode, verify and deserialise a token input
339    pub fn verify<S: AsRef<[u8]>>(input: S, key: &[u8]) -> Result<Self, TokenError>{
340        let token = SignedToken::decode(input)?;
341        if !token.verify(key)? {
342            return Err(TokenError::InvalidSignature)
343        }
344        token.parse()
345    }
346}
347
348
349impl SignedToken {
350    /// get the byte representation (by copying bytes) of a signed token
351    pub fn bytes(&self) -> Vec<u8> {
352        let mut body = self.body.clone();
353        body.push(DOT);
354        body.append(&mut b64_encode(&self.signature));
355        body
356    }
357
358    /// convert a signed token (by consuming it) to the byte representation
359    pub fn into_bytes(self) -> Vec<u8> {
360        let mut body = self.body;
361        body.push(DOT);
362        body.append(&mut b64_encode(&self.signature));
363        body
364    }
365
366    /// get the string representation of a signed token
367    pub fn string(&self) -> String {
368        String::from_utf8_lossy(&self.bytes()).to_string()
369    }
370
371
372    /// Decode a string/bytes into a signed token
373    pub fn decode<S: AsRef<[u8]>>(input: S) -> Result<Self, TokenError> {
374        let mut parts = input.as_ref().split(|x| x == &DOT).collect::<Vec<&[u8]>>();
375
376        if parts.len() != 3 { return Err(TokenError::InvalidToken) }
377        let sig = b64_decode(&parts[2])?;
378        let header = TokenHeader::deserialize(&b64_decode(&parts[0])?)?;
379
380        let mut buffer = Vec::new();
381        buffer.extend_from_slice(&mut parts[0]);
382        buffer.push(DOT);
383        buffer.extend_from_slice(&mut parts[1]);
384
385        Ok(SignedToken {
386            algorithm: header.algorithm.clone(),
387            body: buffer,
388            signature: sig
389        })
390    }
391
392    /// deserialize a signed token into a typed token.
393    pub fn parse<T: DeserializeOwned>(&self) -> Result<Token<T>, TokenError> {
394        let parts = self.body.split(|x| x == &DOT).collect::<Vec<&[u8]>>();
395
396        let header = TokenHeader::deserialize(&b64_decode(&parts[0])?)?;
397        let body = Token::<T>::deserialize(&b64_decode(&parts[1])?)?;
398
399        Ok(Token {
400            header,
401            payload: body
402        })
403    }
404
405    /// verify a signed token.
406    pub fn verify(&self, key: &[u8]) -> Result<bool, TokenError> {
407        self.algorithm.verify(&self.body, &self.signature, key)
408    }
409
410    /// verify a signed token and then deserialize into a typed token
411    pub fn parse_verify<T: DeserializeOwned>(&self, key: &[u8]) -> Result<Token<T>, TokenError> {
412        if !self.verify(key)? {
413            return Err(TokenError::InvalidSignature)
414        }
415        self.parse()
416    }
417}
418
419impl<T: Debug> Debug for Token<T> {
420    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
421        f.write_str("Token(")?;
422        f.write_str(self.header.algorithm.name())?;
423        f.write_str(", ")?;
424        self.payload.fmt(f)?;
425        f.write_str(" )")?;
426        Ok(())
427    }
428}
429
430impl Debug for SignedToken {
431    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
432        f.write_str("SignedToken(")?;
433        f.write_str(self.algorithm.name())?;
434        f.write_str(", ")?;
435        for byte in &self.signature {
436            f.write_str(&format!("{:x?}", byte))?;
437        }
438        f.write_str(" )")?;
439        Ok(())
440    }
441}
442
443impl<T> Deref for Token<T> {
444    type Target = T;
445
446    fn deref(&self) -> &Self::Target {
447        &self.payload
448    }
449}
450
451impl<T> DerefMut for Token<T> {
452    fn deref_mut(&mut self) -> &mut Self::Target {
453        &mut self.payload
454    }
455}
456
457impl Serialize for SignedToken {
458    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
459        serializer.serialize_str(&self.string())
460    }
461}
462
463impl<'de> Deserialize<'de> for SignedToken {
464    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
465        deserializer.deserialize_str(SignedTokenVisitor)
466    }
467}
468
469struct SignedTokenVisitor;
470
471impl<'de> Visitor<'de> for SignedTokenVisitor {
472    type Value = SignedToken;
473
474    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
475        formatter.write_str("a valid token string")
476    }
477
478    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: serde::de::Error {
479        match SignedToken::decode(v) {
480            Ok(token) => Ok(token),
481            Err(error) => Err(serde::de::Error::custom(match error {
482                TokenError::InvalidPayload(e) => return Err(E::custom(e.to_string())),
483
484                TokenError::UnsupportedAlgorithm => "unsupported algorithm",
485                TokenError::InvalidKey => "invalid key",
486                TokenError::InvalidHeader => "invalid header",
487                TokenError::InvalidToken => "invalid token",
488                TokenError::InvalidSignature => "invalid signature",
489            }))
490        }
491    }
492}
493
494#[cfg(feature="HS256")]
495impl Default for TokenAlgorithm {
496    fn default() -> Self {
497        TokenAlgorithm::HS256
498    }
499}
500
501#[cfg(feature="http")]
502impl Into<http::StatusCode> for TokenError {
503    fn into(self) -> http::StatusCode {
504        match self {
505            TokenError::UnsupportedAlgorithm => http::StatusCode::BAD_REQUEST,
506            TokenError::InvalidKey => http::StatusCode::BAD_REQUEST,
507            TokenError::InvalidHeader => http::StatusCode::BAD_REQUEST,
508            TokenError::InvalidPayload(_) => http::StatusCode::BAD_REQUEST,
509            TokenError::InvalidToken => http::StatusCode::BAD_REQUEST,
510            TokenError::InvalidSignature => http::StatusCode::UNAUTHORIZED,
511        }
512    }
513}