1#![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#[derive(Debug, Clone)]
41pub struct Jwt<'jwt>(Cow<'jwt, str>);
42
43impl<'jwt> Jwt<'jwt> {
44 pub fn new<S>(raw: S) -> Self where S: Into<Cow<'jwt, str>> {
46 Jwt(raw.into())
47 }
48
49 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 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
103pub trait IntoParts<'c> {
105 type Error;
107
108 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
132pub trait AsBase64 {
134
135 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
146pub trait FromBase64 {
148
149 fn from_base64 (&self) -> error::Result<Vec<u8>>;
151}
152
153#[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#[derive(Debug, Clone)]
170pub struct Header<'h>(Cow<'h, str>);
171
172impl<'h> Header<'h> {
173 pub fn new<S>(raw: S) -> Self where S: Into<Cow<'h, str>> {
175 Header(raw.into())
176 }
177
178 pub fn convert<T>(base: T) -> error::Result<Self> where T: AsBase64 {
180 Ok(Header::new(base.as_base64()?))
181 }
182
183 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#[derive(Debug, Clone)]
212pub struct Payload<'p>(Cow<'p, str>);
213
214impl<'p> Payload<'p> {
215 pub fn new<S>(raw: S) -> Self where S: Into<Cow<'p, str>> {
217 Payload(raw.into())
218 }
219
220 pub fn convert<T>(base: T) -> error::Result<Self> where T: AsBase64 {
222 Ok(Payload::new(base.as_base64()?))
223 }
224
225 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 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#[derive(Debug, Clone, PartialEq)]
286pub struct Signature<'s>(Cow<'s, str>);
287
288impl<'s> Signature<'s> {
289 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}