Initial re-factor

This commit is contained in:
Thomas Gideon 2017-02-13 18:40:07 -05:00
commit 29ad0d721d
14 changed files with 743 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target
Cargo.lock

17
Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "medallion"
version = "0.0.1"
authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
description = "JWT library for rust using serde, serde_json and openssl"
homepage = "http://github.com/commandline/meiallion"
repository = "http://github.com/commandline/medallion"
readme = "README.md"
keywords = ["JWT", "token", "web"]
license = "MIT"
[dependencies]
base64 = "0.4"
openssl = "0.9"
serde = "0.9"
serde_json = "0.9"
serde_derive = "0.9"

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2015 Michael Yang <mikkyangg@gmail.com>
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:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
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.

10
README.md Normal file
View file

@ -0,0 +1,10 @@
# JWT
A JWT library for rust using serde, serde_json and openssl.
## Usage
The library provides a `Token` type that wraps a header and claims. The claims can be any type that implements the `Component` trait, which is automatically implemented for types that implement the `Sized`, `Encodable`,
and `Decodable` traits. Header can be any type that implements `Component` and `Header`. `Header` ensures that the required algorithm is available for signing and verification. `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, and `RS512` are supported. See the examples.
This library was originally forked from @mikkyang's rust-jwt.

50
examples/custom_claims.rs Normal file
View file

@ -0,0 +1,50 @@
extern crate crypto;
extern crate jwt;
extern crate rustc_serialize;
use std::default::Default;
use jwt::{
DefaultHeader,
Token,
};
#[derive(Default, RustcDecodable, RustcEncodable)]
struct Custom {
sub: String,
rhino: bool,
}
fn new_token(user_id: &str, password: &str) -> Option<String> {
// Dummy auth
if password != "password" {
return None
}
let header: DefaultHeader = Default::default();
let claims = Custom {
sub: user_id.into(),
rhino: true,
..Default::default()
};
let token = Token::new(header, claims);
token.signed(b"secret_key").ok()
}
fn login(token: &str) -> Option<String> {
let token = Token::<DefaultHeader, Custom>::parse(token).unwrap();
if token.verify(b"secret_key") {
Some(token.claims.sub)
} else {
None
}
}
fn main() {
let token = new_token("Michael Yang", "password").unwrap();
let logged_in_user = login(&*token).unwrap();
assert_eq!(logged_in_user, "Michael Yang");
}

44
examples/hs256.rs Normal file
View file

@ -0,0 +1,44 @@
extern crate crypto;
extern crate jwt;
use std::default::Default;
use jwt::{
DefaultHeader,
Registered,
Token,
};
fn new_token(user_id: &str, password: &str) -> Option<String> {
// Dummy auth
if password != "password" {
return None
}
let header: DefaultHeader = Default::default();
let claims = Registered {
iss: Some("mikkyang.com".into()),
sub: Some(user_id.into()),
..Default::default()
};
let token = Token::new(header, claims);
token.signed(b"secret_key").ok()
}
fn login(token: &str) -> Option<String> {
let token = Token::<DefaultHeader, Registered>::parse(token).unwrap();
if token.verify(b"secret_key") {
token.claims.sub
} else {
None
}
}
fn main() {
let token = new_token("Michael Yang", "password").unwrap();
let logged_in_user = login(&*token).unwrap();
assert_eq!(logged_in_user, "Michael Yang");
}

27
examples/privateKey.pem Normal file
View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAqfGTaNo/PBWs3frGcNpboXCh2/NV2edFwkXMnEjGVw9tyLEu
62Tv/LD+hHGQ/EtRfy+iueamtqwRnpeQDnThdYgc7a1nfava7j7ecUCJNnjCJSBT
8qWX5U7lsFrxm2NEhGIMgO40wQRxFylb4izxrloBvFKr+XtfgIY5CaUTaKdvZ2mo
OXgzZ5kC8Qr7SASpEJiMRM432N9QDTTg3kv23htec+FzSucEdJHrkIxr2LjHAmtm
XcT8vRikpN2GThbRUwjLEW3YsWJSTn9GGzF2XZ2rRhdDMviPPjjYPH7Sau2ROnk8
sgGMtEzZNS7i/Lm5qsAoPw85mnnnTvJ6ArId2QIDAQABAoIBADPz4S+lwL0alz1J
Q88OQgLpjuHR0wYJeL76XaHNcaz9z38SA5j8w40JgtV0bnFiiSiLpICWbZLcqYpF
JUn2G1K16LoUT9YQap5448HVi9z2L8vvxRoh23zDkN5H/yKUx0Z9PvtPVxtGw1fk
Ue2j9cJqS6uJzn83YyvEXL2BFJziS1mQ7W6raD9fWTBFOlcNwR2+djlECEvxoMcf
SQqYa5oBUFWFmaJdCjOxqQxNRgWFxPEVlAz62PUoC1WUoP3uKAg5mvKhH/PjNSGl
9K6JhpVgvlBfVUu7dWGAmaUZOLu8l6EUO9MWXDfk10qFKo1bZT9orrARHESPzL2H
PPzd3J0CgYEA0TyJxaSVVfWqoSm8yYTbczaRuJ89S104pOzV3UjpLS8DWClFhbl0
fYhXGgWIbu3QKksmJ2m9fWm7WbkcYzBjSJOuMFi3actA/glMzZ+DRP+v/154gDR0
4/5Etwv+mI+xJqDFGThelC1I7qKtwiyiz85zslW/zDDrn2Yz9OE01S8CgYEAz+zm
Qtp88FiSnllzZSM8Hnzey3j4CDk73R2izBrECTI4FE+EPHu6w7wmNiRRTSNjc1F2
4qPz/95fUWqwH3ES0UgArFN8vBaoWaoQDmWZGG0ao9Pr6tWDl0DZKrz63jCtCKn7
7/bhcxZXYKIPzWgRQhEsZndmcsvARqPVrecmC3cCgYEAofGdIJ/2BYYS/pHzUHXH
9DB0MNTu9/m68cts68yWzSXqDL5E1O9pPg/ceoN1yYW+7D0l1rN8uiivnQ4s7ohx
D6dd1oWT0ApEz1obW7ruOuU7LwfLdE8leaE/Rf2+nA37Ks6cPpzmdwFlxW2b1wH9
MaG04n6D6GKku8a6x/nWjnkCgYEAwSnAMNNxxodCjsFjJs45B8nR4Q2cv2cMajsi
BqPHAxQYbSYCH36C31xn01yh+xupRHSmEZ9nCom325dVz5/ob2yI048sDkCuXb5T
9EwGkl6ppRE31o5NFbM1DTNLjCeEWMwyNZgRki1rN2bXb2gCwHHb4cWC85q+IeIK
nOhku7kCgYAfj3vj/Wc/xTKkZREevgqLm4+B2Sgl5lLaV7OF8jtXEv2mmmmun3Xd
r5V2dvvsBQ1D5DQmGw+ObICgdox9BViqG+2PYBWAUfAWyDZaaSEfo72L/1RDdsAR
ldUh5fpbdCNl4cz9I2Tysl54pTKMCCH+zj10w+0g5TuNlEZCX/p7qA==
-----END RSA PRIVATE KEY-----

9
examples/publicKey.pub Normal file
View file

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqfGTaNo/PBWs3frGcNpb
oXCh2/NV2edFwkXMnEjGVw9tyLEu62Tv/LD+hHGQ/EtRfy+iueamtqwRnpeQDnTh
dYgc7a1nfava7j7ecUCJNnjCJSBT8qWX5U7lsFrxm2NEhGIMgO40wQRxFylb4izx
rloBvFKr+XtfgIY5CaUTaKdvZ2moOXgzZ5kC8Qr7SASpEJiMRM432N9QDTTg3kv2
3htec+FzSucEdJHrkIxr2LjHAmtmXcT8vRikpN2GThbRUwjLEW3YsWJSTn9GGzF2
XZ2rRhdDMviPPjjYPH7Sau2ROnk8sgGMtEzZNS7i/Lm5qsAoPw85mnnnTvJ6ArId
2QIDAQAB
-----END PUBLIC KEY-----

57
examples/rs256.rs Normal file
View file

@ -0,0 +1,57 @@
extern crate crypto;
extern crate jwt;
use std::default::Default;
use std::fs::File;
use std::io::{Error, Read};
use jwt::{
Algorithm,
DefaultHeader,
Registered,
Token,
};
fn load_key(keypath: &str) -> Result<String, Error> {
let mut key_file = try!(File::open(keypath));
let mut key = String::new();
try!(key_file.read_to_string(&mut key));
Ok(key)
}
fn new_token(user_id: &str, password: &str) -> Option<String> {
// Dummy auth
if password != "password" {
return None
}
let header: DefaultHeader = DefaultHeader {
alg: Algorithm::RS256,
..Default::default()
};
let claims = Registered {
iss: Some("mikkyang.com".into()),
sub: Some(user_id.into()),
..Default::default()
};
let token = Token::new(header, claims);
token.signed(load_key("./privateKey.pem").unwrap().as_bytes()).ok()
}
fn login(token: &str) -> Option<String> {
let token = Token::<DefaultHeader, Registered>::parse(token).unwrap();
if token.verify(load_key("./publicKey.pub").unwrap().as_bytes()) {
token.claims.sub
} else {
None
}
}
fn main() {
let token = new_token("Michael Yang", "password").unwrap();
let logged_in_user = login(&*token).unwrap();
assert_eq!(logged_in_user, "Michael Yang");
}

97
src/claims.rs Normal file
View file

@ -0,0 +1,97 @@
use base64::{decode, encode_config, URL_SAFE};
use Component;
use error::Error;
use serde::{Deserialize, Serialize};
use serde_json;
use serde_json::value::{from_value, to_value, Map, Value};
#[derive(Debug, Default, PartialEq)]
pub struct Claims {
pub reg: Registered,
pub private: Value
}
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Registered {
pub iss: Option<String>,
pub sub: Option<String>,
pub aud: Option<String>,
pub exp: Option<u64>,
pub nbf: Option<u64>,
pub iat: Option<u64>,
pub jti: Option<String>,
}
/// JWT Claims. Registered claims are directly accessible via the `Registered`
/// struct embedded, while private fields are a map that contains `Json`
/// values.
impl Claims {
pub fn new(reg: Registered, private: Value) -> Claims {
Claims {
reg: reg,
private: private
}
}
}
impl Component for Claims {
fn from_base64(raw: &str) -> Result<Claims, Error> {
let data = try!(decode(raw));
let reg_claims: Registered = try!(serde_json::from_slice(&data));
let pri_claims: Value = try!(serde_json::from_slice(&data));
Ok(Claims{
reg: reg_claims,
private: pri_claims
})
}
fn to_base64(&self) -> Result<String, Error> {
let mut value = try!(serde_json::to_value(&self.reg));
let mut obj_value = &value.as_object_mut().unwrap();
// TODO iterate private claims and add to JSON Map
//let mut pri_value = self.private.as_object_mut().unwrap();
//obj_value.extend(pri_value.into_iter());
let s = try!(serde_json::to_string(&obj_value));
let enc = encode_config((&*s).as_bytes(), URL_SAFE);
Ok(enc)
}
}
#[cfg(test)]
mod tests {
use std::default::Default;
use claims::{Claims, Registered};
use Component;
#[test]
fn from_base64() {
let enc = "ew0KICAiaXNzIjogIm1pa2t5YW5nLmNvbSIsDQogICJleHAiOiAxMzAyMzE5MTAwLA0KICAibmFtZSI6ICJNaWNoYWVsIFlhbmciLA0KICAiYWRtaW4iOiB0cnVlDQp9";
let claims = Claims::from_base64(enc).unwrap();
assert_eq!(claims.reg.iss.unwrap(), "mikkyang.com");
assert_eq!(claims.reg.exp.unwrap(), 1302319100);
}
#[test]
fn multiple_types() {
let enc = "ew0KICAiaXNzIjogIm1pa2t5YW5nLmNvbSIsDQogICJleHAiOiAxMzAyMzE5MTAwLA0KICAibmFtZSI6ICJNaWNoYWVsIFlhbmciLA0KICAiYWRtaW4iOiB0cnVlDQp9";
let claims = Registered::from_base64(enc).unwrap();
assert_eq!(claims.iss.unwrap(), "mikkyang.com");
assert_eq!(claims.exp.unwrap(), 1302319100);
}
#[test]
fn roundtrip() {
let mut claims: Claims = Default::default();
claims.reg.iss = Some("mikkyang.com".into());
claims.reg.exp = Some(1302319100);
let enc = claims.to_base64().unwrap();
assert_eq!(claims, Claims::from_base64(&*enc).unwrap());
}
}

47
src/crypt.rs Normal file
View file

@ -0,0 +1,47 @@
use base64::{decode, encode_config, URL_SAFE};
use openssl::hash::MessageDigest;
use openssl::memcmp;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::sign::{Signer, Verifier};
pub fn sign(data: &str, key: &[u8], digest: MessageDigest) -> String {
let secret_key = PKey::hmac(key).unwrap();
let mut signer = Signer::new(digest, &secret_key).unwrap();
signer.update(data.as_bytes()).unwrap();
let mac = signer.finish().unwrap();
encode_config(&mac, URL_SAFE)
}
pub fn sign_rsa(data: &str, key: &[u8], digest: MessageDigest) -> String {
let private_key = Rsa::private_key_from_pem(key).unwrap();
let pkey = PKey::from_rsa(private_key).unwrap();
let mut signer = Signer::new(digest, &pkey).unwrap();
signer.update(data.as_bytes()).unwrap();
let sig = signer.finish().unwrap();
encode_config(&sig, URL_SAFE)
}
pub fn verify(target: &str, data: &str, key: &[u8], digest: MessageDigest) -> bool {
let target_bytes: Vec<u8> = decode(target).unwrap();
let secret_key = PKey::hmac(key).unwrap();
let mut signer = Signer::new(digest, &secret_key).unwrap();
signer.update(data.as_bytes()).unwrap();
let mac = signer.finish().unwrap();
memcmp::eq(&mac, &target_bytes)
}
pub fn verify_rsa(signature: &str, data: &str, key: &[u8], digest: MessageDigest) -> bool {
let signature_bytes: Vec<u8> = decode(signature).unwrap();
let public_key = Rsa::public_key_from_pem(key).unwrap();
let pkey = PKey::from_rsa(public_key).unwrap();
let mut verifier = Verifier::new(digest, &pkey).unwrap();
verifier.update(data.as_bytes()).unwrap();
verifier.finish(&signature_bytes).unwrap()
}

23
src/error.rs Normal file
View file

@ -0,0 +1,23 @@
use base64::Base64Error;
use serde_json;
use std::string::FromUtf8Error;
#[derive(Debug)]
pub enum Error {
Format,
Utf8(FromUtf8Error),
Base64(Base64Error),
JSON(serde_json::Error),
}
macro_rules! error_wrap {
($f: ty, $e: expr) => {
impl From<$f> for Error {
fn from(f: $f) -> Error { $e(f) }
}
}
}
error_wrap!(FromUtf8Error, Error::Utf8);
error_wrap!(Base64Error, Error::Base64);
error_wrap!(serde_json::Error, Error::JSON);

74
src/header.rs Normal file
View file

@ -0,0 +1,74 @@
use std::default::Default;
use Header;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct DefaultHeader {
pub typ: Option<HeaderType>,
pub kid: Option<String>,
pub alg: Algorithm,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum HeaderType {
JWT,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Algorithm {
HS256,
HS384,
HS512,
RS256,
RS384,
RS512
}
impl Default for DefaultHeader {
fn default() -> DefaultHeader {
DefaultHeader {
typ: Some(HeaderType::JWT),
kid: None,
alg: Algorithm::HS256,
}
}
}
impl Header for DefaultHeader {
fn alg(&self) -> &Algorithm {
&(self.alg)
}
}
#[cfg(test)]
mod tests {
use Component;
use header::{
Algorithm,
DefaultHeader,
HeaderType,
};
#[test]
fn from_base64() {
let enc = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
let header = DefaultHeader::from_base64(enc).unwrap();
assert_eq!(header.typ.unwrap(), HeaderType::JWT);
assert_eq!(header.alg, Algorithm::HS256);
let enc = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFLU0YzZyJ9";
let header = DefaultHeader::from_base64(enc).unwrap();
assert_eq!(header.kid.unwrap(), "1KSF3g".to_string());
assert_eq!(header.alg, Algorithm::RS256);
}
#[test]
fn roundtrip() {
let header: DefaultHeader = Default::default();
let enc = Component::to_base64(&header).unwrap();
assert_eq!(header, DefaultHeader::from_base64(&*enc).unwrap());
}
}

264
src/lib.rs Normal file
View file

@ -0,0 +1,264 @@
extern crate base64;
extern crate openssl;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use base64::{decode, encode_config, URL_SAFE};
use openssl::hash::MessageDigest;
use serde::{Serialize, Deserialize};
pub use error::Error;
pub use header::DefaultHeader;
pub use header::Algorithm;
pub use claims::Claims;
pub use claims::Registered;
pub mod error;
pub mod header;
pub mod claims;
mod crypt;
#[derive(Debug, Default)]
pub struct Token<H, C>
where H: Component, C: Component {
raw: Option<String>,
pub header: H,
pub claims: C,
}
pub trait Header {
fn alg(&self) -> &header::Algorithm;
}
pub trait Component: Sized {
fn from_base64(raw: &str) -> Result<Self, Error>;
fn to_base64(&self) -> Result<String, Error>;
}
impl<T> Component for T
where T: Serialize + Deserialize + Sized {
/// Parse from a string.
fn from_base64(raw: &str) -> Result<T, Error> {
let data = try!(decode(raw));
let s = try!(String::from_utf8(data));
Ok(try!(serde_json::from_str(&*s)))
}
/// Encode to a string.
fn to_base64(&self) -> Result<String, Error> {
let s = try!(serde_json::to_string(&self));
let enc = encode_config((&*s).as_bytes(), URL_SAFE);
Ok(enc)
}
}
impl<H, C> Token<H, C>
where H: Component + Header, C: Component {
pub fn new(header: H, claims: C) -> Token<H, C> {
Token {
raw: None,
header: header,
claims: claims,
}
}
/// Parse a token from a string.
pub fn parse(raw: &str) -> Result<Token<H, C>, Error> {
let pieces: Vec<_> = raw.split('.').collect();
Ok(Token {
raw: Some(raw.into()),
header: try!(Component::from_base64(pieces[0])),
claims: try!(Component::from_base64(pieces[1])),
})
}
/// Verify a from_base64 token with a key and the token's specific algorithm
pub fn verify(&self, key: &[u8]) -> bool {
match self.header.alg() {
&Algorithm::HS256 => self.verify_hmac(key, MessageDigest::sha256()),
&Algorithm::HS384 => self.verify_hmac(key, MessageDigest::sha384()),
&Algorithm::HS512 => self.verify_hmac(key, MessageDigest::sha512()),
&Algorithm::RS256 => self.verify_rsa(key, MessageDigest::sha256()),
&Algorithm::RS384 => self.verify_rsa(key, MessageDigest::sha384()),
&Algorithm::RS512 => self.verify_rsa(key, MessageDigest::sha512()),
}
}
fn verify_hmac(&self, key: &[u8], digest: MessageDigest) -> bool {
let raw = match self.raw {
Some(ref s) => s,
None => return false,
};
let pieces: Vec<_> = raw.rsplitn(2, '.').collect();
let sig = pieces[0];
let data = pieces[1];
crypt::verify(sig, data, key, digest)
}
fn verify_rsa(&self, key: &[u8], digest: MessageDigest) -> bool {
let raw = match self.raw {
Some(ref s) => s,
None => return false,
};
let pieces: Vec<_> = raw.rsplitn(2, '.').collect();
let sig = pieces[0];
let data = pieces[1];
crypt::verify_rsa(sig, data, key, digest)
}
/// Generate the signed token from a key and the specific algorithm
pub fn signed(&self, key: &[u8]) -> Result<String, Error> {
match self.header.alg() {
&Algorithm::HS256 => self.signed_hmac(key, MessageDigest::sha256()),
&Algorithm::HS384 => self.signed_hmac(key, MessageDigest::sha384()),
&Algorithm::HS512 => self.signed_hmac(key, MessageDigest::sha512()),
&Algorithm::RS256 => self.signed_rsa(key, MessageDigest::sha256()),
&Algorithm::RS384 => self.signed_rsa(key, MessageDigest::sha384()),
&Algorithm::RS512 => self.signed_rsa(key, MessageDigest::sha512()),
}
}
fn signed_hmac(&self, key: &[u8], digest: MessageDigest) -> Result<String, Error> {
let header = try!(Component::to_base64(&self.header));
let claims = try!(self.claims.to_base64());
let data = format!("{}.{}", header, claims);
let sig = crypt::sign(&*data, key, digest);
Ok(format!("{}.{}", data, sig))
}
fn signed_rsa(&self, key: &[u8], digest: MessageDigest) -> Result<String, Error> {
let header = try!(Component::to_base64(&self.header));
let claims = try!(self.claims.to_base64());
let data = format!("{}.{}", header, claims);
let sig = crypt::sign_rsa(&*data, key, digest);
Ok(format!("{}.{}", data, sig))
}
}
impl<H, C> PartialEq for Token<H, C>
where H: Component + PartialEq, C: Component + PartialEq{
fn eq(&self, other: &Token<H, C>) -> bool {
self.header == other.header &&
self.claims == other.claims
}
}
#[cfg(test)]
mod tests {
use crypt::{
sign,
sign_rsa,
verify,
verify_rsa
};
use Claims;
use Token;
use header::Algorithm::{HS256,RS512};
use header::DefaultHeader;
use std::io::{Error, Read};
use std::fs::File;
use openssl::hash::MessageDigest;
#[test]
pub fn sign_data() {
let header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
let real_sig = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let data = format!("{}.{}", header, claims);
let sig = sign(&*data, "secret".as_bytes(), MessageDigest::sha256());
assert_eq!(sig, real_sig);
}
#[test]
pub fn sign_data_rsa() {
let header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
let real_sig = "nXdpIkFQYZXZ0VlJjHmAc5/aewHCCJpT5jP1fpexUCF/9m3NxlC7uYNXAl6NKno520oh9wVT4VV/vmPeEin7BnnoIJNPcImWcUzkYpLTrDBntiF9HCuqFaniuEVzlf8dVlRJgo8QxhmUZEjyDFjPZXZxPlPV1LD6hrtItxMKZbh1qoNY3OL7Mwo+WuSRQ0mmKj+/y3weAmx/9EaTLY639uD8+o5iZxIIf85U4e55Wdp+C9FJ4RxyHpjgoG8p87IbChfleSdWcZL3NZuxjRCHVWgS1uYG0I+LqBWpWyXnJ1zk6+w4tfxOYpZFMOIyq4tY2mxJQ78Kvcu8bTO7UdI7iA";
let data = format!("{}.{}", header, claims);
let key = load_key("./examples/privateKey.pem").unwrap();
let sig = sign_rsa(&*data, key.as_bytes(), MessageDigest::sha256());
assert_eq!(sig.trim(), real_sig);
}
#[test]
pub fn verify_data() {
let header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
let target = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let data = format!("{}.{}", header, claims);
assert!(verify(target, &*data, "secret".as_bytes(), MessageDigest::sha256()));
}
#[test]
pub fn verify_data_rsa() {
let header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
let real_sig = "nXdpIkFQYZXZ0VlJjHmAc5/aewHCCJpT5jP1fpexUCF/9m3NxlC7uYNXAl6NKno520oh9wVT4VV/vmPeEin7BnnoIJNPcImWcUzkYpLTrDBntiF9HCuqFaniuEVzlf8dVlRJgo8QxhmUZEjyDFjPZXZxPlPV1LD6hrtItxMKZbh1qoNY3OL7Mwo+WuSRQ0mmKj+/y3weAmx/9EaTLY639uD8+o5iZxIIf85U4e55Wdp+C9FJ4RxyHpjgoG8p87IbChfleSdWcZL3NZuxjRCHVWgS1uYG0I+LqBWpWyXnJ1zk6+w4tfxOYpZFMOIyq4tY2mxJQ78Kvcu8bTO7UdI7iA";
let data = format!("{}.{}", header, claims);
let key = load_key("./examples/publicKey.pub").unwrap();
assert!(verify_rsa(&real_sig, &*data, key.as_bytes(), MessageDigest::sha256()));
}
#[test]
pub fn raw_data() {
let raw = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let token = Token::<DefaultHeader, Claims>::parse(raw).unwrap();
{
assert_eq!(token.header.alg, HS256);
}
assert!(token.verify("secret".as_bytes()));
}
#[test]
pub fn roundtrip() {
let token: Token<DefaultHeader, Claims> = Default::default();
let key = "secret".as_bytes();
let raw = token.signed(key).unwrap();
let same = Token::parse(&*raw).unwrap();
assert_eq!(token, same);
assert!(same.verify(key));
}
#[test]
pub fn roundtrip_rsa() {
let token: Token<DefaultHeader, Claims> = Token {
header: DefaultHeader {
alg: RS512,
..Default::default()
},
..Default::default()
};
let private_key = load_key("./examples/privateKey.pem").unwrap();
let raw = token.signed(private_key.as_bytes()).unwrap();
let same = Token::parse(&*raw).unwrap();
assert_eq!(token, same);
let public_key = load_key("./examples/publicKey.pub").unwrap();
assert!(same.verify(public_key.as_bytes()));
}
fn load_key(keypath: &str) -> Result<String, Error> {
let mut key_file = try!(File::open(keypath));
let mut key = String::new();
try!(key_file.read_to_string(&mut key));
Ok(key)
}
}