libjwt/
lib.rs

1// Copyright 2018 LightDiscord
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4//
5// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6//
7// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
9//! # Jwt
10//!
11//! Another JWT implementation written in Rust
12
13#![deny(missing_docs, unsafe_code, unused_extern_crates, warnings)]
14
15extern crate openssl;
16extern crate base64;
17#[macro_use] extern crate error_chain;
18#[macro_use] extern crate serde_json;
19
20use std::borrow::Cow;
21use std::str::FromStr;
22use std::fmt;
23use std::time::{ SystemTime, UNIX_EPOCH };
24
25pub mod error;
26pub mod algorithm;
27pub mod signature;
28pub mod claims;
29pub mod verification;
30
31pub use error::Error;
32pub use algorithm::Algorithm;
33pub use claims::RegisteredClaims;
34pub use verification::Verifications;
35use signature::{ AsKey, Sign, HMAC, RSA, ECDSA, BindSignature };
36
37use serde_json::Value;
38
39/// A Simple Jwt
40#[derive(Debug, Clone)]
41pub struct Jwt<'jwt>(Cow<'jwt, str>);
42
43impl<'jwt> Jwt<'jwt> {
44    /// Create a Jwt from any type who can be turned into a `Cow<'jwt, str>`
45    pub fn new<S>(raw: S) -> Self where S: Into<Cow<'jwt, str>> {
46        Jwt(raw.into())
47    }
48
49    /// Encode header and payload into a valid JWT
50    pub fn encode<K: AsKey>(header: &Header, payload: &Payload, key: &K, algorithm: Option<Algorithm>) -> error::Result<Self> {
51        let algorithm = match algorithm {
52            Some(algorithm) => algorithm,
53            None => header.as_algorithm()?
54        };
55
56        let header = header.from_base64()?;
57        let mut header: Value = serde_json::from_slice(&header)?;
58        header["alg"] = Value::String(algorithm.to_string());
59        header["typ"] = Value::String("JWT".to_owned());
60        let header = header.as_base64()?;
61        let header = Header::new(header);
62
63        let to_sign = format!("{}.{}", header, payload);
64
65        let signature = match algorithm {
66            Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => HMAC::sign(&to_sign, key, algorithm)?,
67            Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => RSA::sign(&to_sign, key, algorithm)?,
68            Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES512 => ECDSA::sign(&to_sign, key, algorithm)?,
69        };
70
71        let token = Jwt::new(format!("{}.{}", to_sign, signature.0));
72
73        Ok(token)
74    }
75
76    /// Decode a Jwt token and check if signature is valid
77    pub fn decode<K: AsKey>(&self, key: &K, algorithm: Option<Algorithm>) -> error::Result<Parts> {
78        let parts = self.into_parts()?;
79        let Parts { header, payload, signature } = parts.clone();
80
81        let algorithm = match algorithm {
82            Some(algorithm) => algorithm,
83            None => header.as_algorithm()?
84        };
85
86        let data = format!("{}.{}", header, payload);
87        let signature = BindSignature(signature, algorithm);
88
89        let verification = match algorithm {
90            Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => HMAC::verify(signature, &data, key)?,
91            Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => RSA::verify(signature, &data, key)?,
92            Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES512 => ECDSA::verify(signature, &data, key)?,
93        };
94
95        if !verification {
96            bail!(error::ErrorKind::InvalidSignature);
97        } else {
98            Ok(parts)
99        }
100    }
101}
102
103/// Transform something into Jwt's parts
104pub trait IntoParts<'c> {
105    /// Error type from a convertion
106    type Error;
107
108    /// Convert it!
109    fn into_parts (&'c self) -> Result<Parts, Self::Error>;
110}
111
112impl<'jwt> IntoParts<'jwt> for Jwt<'jwt> {
113    type Error = error::Error;
114
115    fn into_parts (&'jwt self) -> error::Result<Parts> {
116        let parts: Vec<&'jwt str> = self.0.split(".").collect();
117
118        if parts.len() != 3 {
119            unimplemented!()
120        }
121
122        let parts = Parts {
123            header: Header::new(parts[0]),
124            payload: Payload::new(parts[1]),
125            signature: Signature::new(parts[2])
126        };
127
128        Ok(parts)
129    }
130}
131
132/// Transform something into base64
133pub trait AsBase64 {
134
135    /// Convert it!
136    fn as_base64 (&self) -> error::Result<String>;
137}
138
139impl AsBase64 for Value {
140    fn as_base64 (&self) -> error::Result<String> {
141        let value = serde_json::to_string(&self)?;
142        Ok(base64::encode_config(value.as_bytes(), base64::URL_SAFE))
143    }
144}
145
146/// Transform something from base64
147pub trait FromBase64 {
148
149    /// Convert it!
150    fn from_base64 (&self) -> error::Result<Vec<u8>>;
151}
152
153/// Jwt's parts
154#[derive(Debug, Clone)]
155pub struct Parts<'h, 'p, 's> {
156    header: Header<'h>,
157    payload: Payload<'p>,
158    signature: Signature<'s>
159}
160
161impl<'h, 'p, 's, 'jwt> Into<Jwt<'jwt>> for Parts<'h, 'p, 's> {
162    fn into (self) -> Jwt<'jwt> {
163        let jwt = format!("{}.{}.{}", self.header.0, self.payload.0, self.signature.0);
164        Jwt::new(jwt)
165    }
166}
167
168/// Jwt's header
169#[derive(Debug, Clone)]
170pub struct Header<'h>(Cow<'h, str>);
171
172impl<'h> Header<'h> {
173    /// Create a Jwt's header from any type who can be turned into a `Cow<'h, str>`
174    pub fn new<S>(raw: S) -> Self where S: Into<Cow<'h, str>> {
175        Header(raw.into())
176    }
177
178    /// Convert a base64 transformable into a header.
179    pub fn convert<T>(base: T) -> error::Result<Self> where T: AsBase64 {
180        Ok(Header::new(base.as_base64()?))
181    }
182
183    /// In JWT's header you can have the field "alg" who provide which algorithm is used to sign your token.
184    /// We get this field and convert it into an algorithm.
185    pub fn as_algorithm(&self) -> error::Result<Algorithm> {
186        let header = self.from_base64()?;
187        let header = header.as_slice();
188        let header: Value = serde_json::from_slice(header)?;
189        let header = header["alg"].as_str().ok_or(error::ErrorKind::MissingAlgorithm)?;
190        let algorithm = Algorithm::from_str(header)?;
191
192        Ok(algorithm)
193    }
194}
195
196impl<'h> FromBase64 for Header<'h> {
197    fn from_base64(&self) -> error::Result<Vec<u8>> {
198        let convertion = &*self.0;
199        let convertion = base64::decode_config(&convertion, base64::URL_SAFE)?;
200        Ok(convertion)
201    }
202}
203
204impl<'h> fmt::Display for Header<'h> {
205    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206        write!(f, "{}", self.0)
207    }
208}
209
210/// Jwt's payload
211#[derive(Debug, Clone)]
212pub struct Payload<'p>(Cow<'p, str>);
213
214impl<'p> Payload<'p> {
215    /// Create a Jwt's payload from any type who can be turned into a `Cow<'p, str>`
216    pub fn new<S>(raw: S) -> Self where S: Into<Cow<'p, str>> {
217        Payload(raw.into())
218    }
219
220    /// Convert a base64 transformable into a payload.
221    pub fn convert<T>(base: T) -> error::Result<Self> where T: AsBase64 {
222        Ok(Payload::new(base.as_base64()?))
223    }
224
225    /// Apply claims on payload
226    pub fn apply(self, claims: Vec<RegisteredClaims>) -> error::Result<Payload<'p>> {
227        let payload = self.from_base64()?;
228        let mut payload: Value = serde_json::from_slice(&payload)?;
229
230        for claim in claims {
231            payload[claim.to_string()] = claim.clone().into();
232        }
233
234        let payload = payload.as_base64()?;
235        let payload = Payload::new(payload);
236
237        Ok(payload)
238    }
239
240    /// Verify if a payload is valid
241    pub fn verify(&self, verification: Vec<Verifications>) -> error::Result<()> {
242        let payload = self.from_base64()?;
243        let payload: Value = serde_json::from_slice(&payload)?;
244
245        for verification in verification {
246            match verification {
247                Verifications::SameClaim(claim) => {
248                    let payload = &payload[claim.to_string()];
249                    let claim: Value = claim.clone().into();
250
251                    if *payload != claim {
252                        bail!(error::ErrorKind::VerificationFailed("same_claim".to_string()))
253                    }
254                },
255                Verifications::Expired => {
256                    let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
257                    let exp = payload["exp"].as_u64();
258
259                    if exp <= Some(now) {
260                        bail!(error::ErrorKind::VerificationFailed("token_expired".to_string()))
261                    }
262                }
263            }
264        }
265
266        Ok(())
267    }
268}
269
270impl<'p> FromBase64 for Payload<'p> {
271    fn from_base64(&self) -> error::Result<Vec<u8>> {
272        let convertion = &*self.0;
273        let convertion = base64::decode_config(&convertion, base64::URL_SAFE)?;
274        Ok(convertion)
275    }
276}
277
278impl<'p> fmt::Display for Payload<'p> {
279    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280        write!(f, "{}", self.0)
281    }
282}
283
284/// Jwt's signature
285#[derive(Debug, Clone, PartialEq)]
286pub struct Signature<'s>(Cow<'s, str>);
287
288impl<'s> Signature<'s> {
289    /// Create a Jwt's signature from any type who can be turned into a `Cow<'s, str>`
290    pub fn new<S>(raw: S) -> Self where S: Into<Cow<'s, str>> {
291        Signature(raw.into())
292    }
293}
294
295impl<'s> fmt::Display for Signature<'s> {
296    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297        write!(f, "{}", self.0)
298    }
299}
300
301#[cfg(test)]
302mod tests {
303    use super::{ Jwt, Algorithm, IntoParts, Header, Payload, Value, RegisteredClaims, Verifications, SystemTime, UNIX_EPOCH };
304
305    #[test]
306    fn jwt_from_str () {
307        let s = test_jwt();
308        let jwt = Jwt::new(s);
309
310        assert_eq!(jwt.0.into_owned(), String::from(s));
311    }
312
313    #[test]
314    fn jwt_from_string () {
315        let s = test_jwt();
316        let s = String::from(s);
317        let jwt = Jwt::new(s.clone());
318
319        assert_eq!(jwt.0.into_owned(), s);
320    }
321
322    #[test]
323    fn jwt_into_parts () {
324        let s = test_jwt();
325        let jwt = Jwt::new(s);
326        let parts = jwt.into_parts();
327
328        println!("{:?}", parts);
329    }
330
331    #[test]
332    fn jwt_creation () {
333        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
334
335        let header = json!({});
336        let header = Header::convert(header).unwrap();
337
338        let payload = json!({
339            "light": "discord"
340        });
341        let payload = Payload::convert(payload).unwrap();
342        let payload = payload.apply(vec![
343            RegisteredClaims::Audience("FBI!".to_string()),
344            RegisteredClaims::Issuer("Test-man".to_string()),
345            RegisteredClaims::IssuedAt(now),
346            RegisteredClaims::ExpirationTime(now + 10),
347            RegisteredClaims::Custom("hello".to_string(), Value::String("world".to_string()))
348        ]).unwrap();
349
350        let key = "This is super mega secret!".to_string();
351        let jwt = Jwt::encode(&header, &payload, &key, Some(Algorithm::HS256)).unwrap();
352        let jwt = jwt.into_parts().unwrap();
353
354        let payload = jwt.payload.clone();
355        let _ = payload.verify(vec![
356            Verifications::SameClaim(RegisteredClaims::Issuer("Test-man".to_string())),
357            Verifications::SameClaim(RegisteredClaims::Custom("hello".to_string(), Value::String("world".to_string()))),
358            Verifications::Expired
359        ]);
360
361        let jwt: Jwt = jwt.into();
362
363        println!("{:?}", jwt);
364        assert!(jwt.decode(&key, None).is_ok());
365    }
366
367    fn test_jwt () -> &'static str {
368        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJGQkkhIGN1eicgaXQncyBzZWNyZXQhIHNodXQhIiwiZXhwIjoxNTE5OTk0NTAxLCJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNTE5OTk0NDkxLCJpc3MiOiJUZXN0LW1hbiEiLCJsaWdodCI6ImRpc2NvcmQifQ==.gS76BWOStsnrG9nMacQQE7ThHM1UIR2omB6YkBaQjZ0="
369    }
370}