Update to serde 1.0 (#4)
* Try updating service to 1.0 * Strip bounds from structs * Clean bounds from Default impl * Remove pem files, clean up some documentation * Add badges, categories
This commit is contained in:
parent
a265a703d6
commit
42824a133e
11 changed files with 65 additions and 92 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "medallion"
|
name = "medallion"
|
||||||
version = "2.1.1"
|
version = "2.2.1"
|
||||||
authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
|
authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
|
||||||
description = "JWT library for rust using serde, serde_json and openssl"
|
description = "JWT library for rust using serde, serde_json and openssl"
|
||||||
homepage = "http://github.com/commandline/medallion"
|
homepage = "http://github.com/commandline/medallion"
|
||||||
|
@ -8,6 +8,8 @@ repository = "http://github.com/commandline/medallion"
|
||||||
documentation = "https://commandline.github.io/medallion/"
|
documentation = "https://commandline.github.io/medallion/"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["JWT", "token", "web", "JSON", "RSA"]
|
keywords = ["JWT", "token", "web", "JSON", "RSA"]
|
||||||
|
categories = ["cryptography", "authentication", "web-programming",
|
||||||
|
"data-structures"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -17,3 +19,7 @@ serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
travis-ci = { repository = "https://travis-ci.org/commandline/medallion", branch = "master" }
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,18 @@ struct Custom {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_token(user_id: &str, password: &str) -> Option<String> {
|
fn new_token(user_id: &str, password: &str) -> Option<String> {
|
||||||
// Dummy auth
|
// dummy auth, in a real application using something like openidconnect, this would be some
|
||||||
|
// specific authentication scheme that takes place first then the JWT is generated as part of
|
||||||
|
// sucess and signed with the provider's private key so other services can validate trust for
|
||||||
|
// the claims in the token
|
||||||
if password != "password" {
|
if password != "password" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let header: Header<()> = Default::default();
|
let header: Header<()> = Default::default();
|
||||||
let payload = Payload {
|
let payload = Payload {
|
||||||
|
// custom claims will be application specific, they may come from open standards such as
|
||||||
|
// openidconnect where they may be referred to as registered claims
|
||||||
claims: Some(Custom {
|
claims: Some(Custom {
|
||||||
user_id: user_id.into(),
|
user_id: user_id.into(),
|
||||||
rhino: true,
|
rhino: true,
|
||||||
|
|
|
@ -15,12 +15,18 @@ struct Custom {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_token(sub: &str, password: &str) -> Option<String> {
|
fn new_token(sub: &str, password: &str) -> Option<String> {
|
||||||
// Dummy auth
|
// dummy auth, in a real application using something like openidconnect, this would be some
|
||||||
|
// specific authentication scheme that takes place first then the JWT is generated as part of
|
||||||
|
// sucess and signed with the provider's private key so other services can validate trust for
|
||||||
|
// the claims in the token
|
||||||
if password != "password" {
|
if password != "password" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let header = Header {
|
let header = Header {
|
||||||
|
// customer headers generally are about the token itself, like here describing the type of
|
||||||
|
// token, as opposed to claims which are about the authenticated user or some output of
|
||||||
|
// the authentication process
|
||||||
headers: Some(Custom { typ: "JWT".into(), ..Default::default() }),
|
headers: Some(Custom { typ: "JWT".into(), ..Default::default() }),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,10 @@ use std::default::Default;
|
||||||
use medallion::{Header, DefaultPayload, DefaultToken};
|
use medallion::{Header, DefaultPayload, DefaultToken};
|
||||||
|
|
||||||
fn new_token(user_id: &str, password: &str) -> Option<String> {
|
fn new_token(user_id: &str, password: &str) -> Option<String> {
|
||||||
// Dummy auth
|
// dummy auth, in a real application using something like openidconnect, this would be some
|
||||||
|
// specific authentication scheme that takes place first then the JWT is generated as part of
|
||||||
|
// sucess and signed with the provider's private key so other services can validate trust for
|
||||||
|
// the claims in the token
|
||||||
if password != "password" {
|
if password != "password" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +27,7 @@ fn new_token(user_id: &str, password: &str) -> Option<String> {
|
||||||
fn login(token: &str) -> Option<String> {
|
fn login(token: &str) -> Option<String> {
|
||||||
let token: DefaultToken<()> = DefaultToken::parse(token).unwrap();
|
let token: DefaultToken<()> = DefaultToken::parse(token).unwrap();
|
||||||
|
|
||||||
|
// the key for HMAC is some secret known to trusted/trusting parties
|
||||||
if token.verify(b"secret_key").unwrap() {
|
if token.verify(b"secret_key").unwrap() {
|
||||||
token.payload.sub
|
token.payload.sub
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
-----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-----
|
|
|
@ -1,9 +0,0 @@
|
||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqfGTaNo/PBWs3frGcNpb
|
|
||||||
oXCh2/NV2edFwkXMnEjGVw9tyLEu62Tv/LD+hHGQ/EtRfy+iueamtqwRnpeQDnTh
|
|
||||||
dYgc7a1nfava7j7ecUCJNnjCJSBT8qWX5U7lsFrxm2NEhGIMgO40wQRxFylb4izx
|
|
||||||
rloBvFKr+XtfgIY5CaUTaKdvZ2moOXgzZ5kC8Qr7SASpEJiMRM432N9QDTTg3kv2
|
|
||||||
3htec+FzSucEdJHrkIxr2LjHAmtmXcT8vRikpN2GThbRUwjLEW3YsWJSTn9GGzF2
|
|
||||||
XZ2rRhdDMviPPjjYPH7Sau2ROnk8sgGMtEzZNS7i/Lm5qsAoPw85mnnnTvJ6ArId
|
|
||||||
2QIDAQAB
|
|
||||||
-----END PUBLIC KEY-----
|
|
|
@ -1,24 +1,20 @@
|
||||||
extern crate medallion;
|
extern crate medallion;
|
||||||
|
extern crate openssl;
|
||||||
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fs::File;
|
use openssl::rsa;
|
||||||
use std::io::{Error, Read};
|
|
||||||
use medallion::{Algorithm, Header, DefaultPayload, DefaultToken};
|
use medallion::{Algorithm, Header, DefaultPayload, DefaultToken};
|
||||||
|
|
||||||
fn load_pem(keypath: &str) -> Result<String, Error> {
|
fn new_token(private_key: &[u8], user_id: &str, password: &str) -> Option<String> {
|
||||||
let mut key_file = File::open(keypath)?;
|
// dummy auth, in a real application using something like openidconnect, this would be some
|
||||||
let mut key = String::new();
|
// specific authentication scheme that takes place first then the JWT is generated as part of
|
||||||
key_file.read_to_string(&mut key)?;
|
// sucess and signed with the provider's private key so other services can validate trust for
|
||||||
Ok(key)
|
// the claims in the token
|
||||||
}
|
|
||||||
|
|
||||||
fn new_token(user_id: &str, password: &str) -> Option<String> {
|
|
||||||
// Dummy auth
|
|
||||||
if password != "password" {
|
if password != "password" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can satisfy Header's generic parameter with an empty type
|
// can satisfy Header's type parameter with an empty tuple
|
||||||
let header: Header<()> = Header { alg: Algorithm::RS256, ..Default::default() };
|
let header: Header<()> = Header { alg: Algorithm::RS256, ..Default::default() };
|
||||||
let payload: DefaultPayload = DefaultPayload {
|
let payload: DefaultPayload = DefaultPayload {
|
||||||
iss: Some("example.com".into()),
|
iss: Some("example.com".into()),
|
||||||
|
@ -27,15 +23,13 @@ fn new_token(user_id: &str, password: &str) -> Option<String> {
|
||||||
};
|
};
|
||||||
let token = DefaultToken::new(header, payload);
|
let token = DefaultToken::new(header, payload);
|
||||||
|
|
||||||
// this key was generated explicitly for these examples and is not used anywhere else
|
token.sign(private_key).ok()
|
||||||
token.sign(load_pem("./privateKey.pem").unwrap().as_bytes()).ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn login(token: &str) -> Option<String> {
|
fn login(public_key: &[u8], token: &str) -> Option<String> {
|
||||||
let token: DefaultToken<()> = DefaultToken::parse(token).unwrap();
|
let token: DefaultToken<()> = DefaultToken::parse(token).unwrap();
|
||||||
|
|
||||||
// this key was generated explicitly for these examples and is not used anywhere else
|
if token.verify(public_key).unwrap() {
|
||||||
if token.verify(load_pem("./publicKey.pub").unwrap().as_bytes()).unwrap() {
|
|
||||||
token.payload.sub
|
token.payload.sub
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -43,9 +37,12 @@ fn login(token: &str) -> Option<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let token = new_token("Random User", "password").unwrap();
|
// alternatively can read .pem files from fs or fetch from a server or...
|
||||||
|
let keypair = rsa::Rsa::generate(2048).unwrap();
|
||||||
|
|
||||||
let logged_in_user = login(&*token).unwrap();
|
let token = new_token(&keypair.private_key_to_pem().unwrap(), "Random User", "password").unwrap();
|
||||||
|
|
||||||
|
let logged_in_user = login(&keypair.public_key_to_pem().unwrap(), &*token).unwrap();
|
||||||
|
|
||||||
assert_eq!(logged_in_user, "Random User");
|
assert_eq!(logged_in_user, "Random User");
|
||||||
}
|
}
|
||||||
|
|
31
src/crypt.rs
31
src/crypt.rs
|
@ -73,8 +73,7 @@ fn verify_rsa(signature: &str, data: &str, key: &[u8], digest: MessageDigest) ->
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use header::Algorithm;
|
use header::Algorithm;
|
||||||
use std::io::{Error, Read};
|
use openssl;
|
||||||
use std::fs::File;
|
|
||||||
use super::{sign, verify};
|
use super::{sign, verify};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -90,17 +89,17 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn sign_data_rsa() {
|
pub fn sign_and_verify_data_rsa() {
|
||||||
let header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
|
let header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
|
||||||
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
|
let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
|
||||||
let real_sig = "nXdpIkFQYZXZ0VlJjHmAc5_aewHCCJpT5jP1fpexUCF_9m3NxlC7uYNXAl6NKno520oh9wVT4VV_vmPeEin7BnnoIJNPcImWcUzkYpLTrDBntiF9HCuqFaniuEVzlf8dVlRJgo8QxhmUZEjyDFjPZXZxPlPV1LD6hrtItxMKZbh1qoNY3OL7Mwo-WuSRQ0mmKj-_y3weAmx_9EaTLY639uD8-o5iZxIIf85U4e55Wdp-C9FJ4RxyHpjgoG8p87IbChfleSdWcZL3NZuxjRCHVWgS1uYG0I-LqBWpWyXnJ1zk6-w4tfxOYpZFMOIyq4tY2mxJQ78Kvcu8bTO7UdI7iA";
|
|
||||||
let data = format!("{}.{}", header, claims);
|
let data = format!("{}.{}", header, claims);
|
||||||
|
|
||||||
let key = load_pem("./examples/privateKey.pem").unwrap();
|
let keypair = openssl::rsa::Rsa::generate(2048).unwrap();
|
||||||
|
|
||||||
let sig = sign(&*data, key.as_bytes(), &Algorithm::RS256);
|
let sig = sign(&*data, &keypair.private_key_to_pem().unwrap(), &Algorithm::RS256).unwrap();
|
||||||
|
|
||||||
assert_eq!(sig.unwrap(), real_sig);
|
assert!(verify(&sig, &*data, &keypair.public_key_to_pem().unwrap(), &Algorithm::RS256).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -112,22 +111,4 @@ pub mod tests {
|
||||||
|
|
||||||
assert!(verify(target, &*data, "secret".as_bytes(), &Algorithm::HS256).unwrap());
|
assert!(verify(target, &*data, "secret".as_bytes(), &Algorithm::HS256).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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_pem("./examples/publicKey.pub").unwrap();
|
|
||||||
assert!(verify(&real_sig, &*data, key.as_bytes(), &Algorithm::RS256).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_pem(keypath: &str) -> Result<String, Error> {
|
|
||||||
let mut key_file = File::open(keypath)?;
|
|
||||||
let mut key = String::new();
|
|
||||||
key_file.read_to_string(&mut key)?;
|
|
||||||
Ok(key)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,15 @@ use openssl::error::ErrorStack;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Custom, Medallion specific errors.
|
||||||
Custom(String),
|
Custom(String),
|
||||||
|
/// String encoding errors.
|
||||||
Utf8(FromUtf8Error),
|
Utf8(FromUtf8Error),
|
||||||
|
/// Base64 encoding or decoding errors.
|
||||||
Base64(Base64Error),
|
Base64(Base64Error),
|
||||||
|
/// JSON parsing or stringifying errors.
|
||||||
JSON(serde_json::Error),
|
JSON(serde_json::Error),
|
||||||
|
/// Errors from RSA operations.
|
||||||
Crypto(ErrorStack),
|
Crypto(ErrorStack),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub enum Algorithm {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Serialize + DeserializeOwned> Header<T> {
|
impl<T: Serialize + DeserializeOwned> Header<T> {
|
||||||
|
/// Decode from base64.
|
||||||
pub fn from_base64(raw: &str) -> Result<Header<T>> {
|
pub fn from_base64(raw: &str) -> Result<Header<T>> {
|
||||||
let data = decode_config(raw, URL_SAFE_NO_PAD)?;
|
let data = decode_config(raw, URL_SAFE_NO_PAD)?;
|
||||||
let own: Header<T> = serde_json::from_slice(&data)?;
|
let own: Header<T> = serde_json::from_slice(&data)?;
|
||||||
|
|
20
src/lib.rs
20
src/lib.rs
|
@ -1,6 +1,11 @@
|
||||||
#![crate_name = "medallion"]
|
#![crate_name = "medallion"]
|
||||||
#![crate_type = "lib"]
|
#![crate_type = "lib"]
|
||||||
#![doc(html_root_url = "https://commandline.github.io/medallion/")]
|
#![doc(html_root_url = "https://commandline.github.io/medallion/")]
|
||||||
|
///! A crate for working with JSON WebTokens that use OpenSSL for RSA signing and encryption and
|
||||||
|
///! serde and serde_json for JSON encoding and decoding.
|
||||||
|
///!
|
||||||
|
///! Tries to support the standard uses for JWTs while providing reasonable ways to extend,
|
||||||
|
///! primarily by adding custom headers and claims to tokens.
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
extern crate openssl;
|
extern crate openssl;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
@ -23,8 +28,8 @@ mod crypt;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
/// A convenient type that bins the same type parameter for the custom claims, an empty tuple, as
|
/// A convenient type that binds the same type parameter for the custom claims, an empty tuple, as
|
||||||
/// DefaultPayload so that the two aliases may be used together to reduce boilerplate when not
|
/// DefaultPayload so that the two aliases may be used together to reduce boilerplate when no
|
||||||
/// custom claims are needed.
|
/// custom claims are needed.
|
||||||
pub type DefaultToken<H> = Token<H, ()>;
|
pub type DefaultToken<H> = Token<H, ()>;
|
||||||
|
|
||||||
|
@ -54,7 +59,7 @@ impl<H, C> Token<H, C>
|
||||||
let pieces: Vec<_> = raw.split('.').collect();
|
let pieces: Vec<_> = raw.split('.').collect();
|
||||||
|
|
||||||
Ok(Token {
|
Ok(Token {
|
||||||
raw: Some(raw.into()),
|
raw: Some(raw.to_owned()),
|
||||||
header: Header::from_base64(pieces[0])?,
|
header: Header::from_base64(pieces[0])?,
|
||||||
payload: Payload::from_base64(pieces[1])?,
|
payload: Payload::from_base64(pieces[1])?,
|
||||||
})
|
})
|
||||||
|
@ -98,7 +103,7 @@ impl<H, C> PartialEq for Token<H, C>
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use {DefaultPayload, DefaultToken, Header};
|
use {DefaultPayload, DefaultToken, Header};
|
||||||
use crypt::tests::load_pem;
|
use openssl;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use time::{self, Duration, Tm};
|
use time::{self, Duration, Tm};
|
||||||
use super::Algorithm::{HS256, RS512};
|
use super::Algorithm::{HS256, RS512};
|
||||||
|
@ -158,15 +163,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn roundtrip_rsa() {
|
pub fn roundtrip_rsa() {
|
||||||
|
let rsa_keypair = openssl::rsa::Rsa::generate(2048).unwrap();
|
||||||
let header: Header<()> = Header { alg: RS512, ..Default::default() };
|
let header: Header<()> = Header { alg: RS512, ..Default::default() };
|
||||||
let token = DefaultToken { header: header, ..Default::default() };
|
let token = DefaultToken { header: header, ..Default::default() };
|
||||||
let private_key = load_pem("./examples/privateKey.pem").unwrap();
|
let raw = token.sign(&rsa_keypair.private_key_to_pem().unwrap()).unwrap();
|
||||||
let raw = token.sign(private_key.as_bytes()).unwrap();
|
|
||||||
let same = DefaultToken::parse(&*raw).unwrap();
|
let same = DefaultToken::parse(&*raw).unwrap();
|
||||||
|
|
||||||
assert_eq!(token, same);
|
assert_eq!(token, same);
|
||||||
let public_key = load_pem("./examples/publicKey.pub").unwrap();
|
assert!(same.verify(&rsa_keypair.public_key_to_pem().unwrap()).unwrap());
|
||||||
assert!(same.verify(public_key.as_bytes()).unwrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_for_range(nbf: Tm, exp: Tm) -> DefaultToken<()> {
|
fn create_for_range(nbf: Tm, exp: Tm) -> DefaultToken<()> {
|
||||||
|
|
Loading…
Reference in a new issue