Clean up error handling in crypt

This commit is contained in:
Thomas Gideon 2017-02-17 11:53:12 -05:00
parent 2442a6f2c0
commit 94b83d12aa
7 changed files with 58 additions and 51 deletions

View file

@ -35,7 +35,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 = Token::<DefaultHeader, Custom>::parse(token).unwrap(); let token = Token::<DefaultHeader, Custom>::parse(token).unwrap();
if token.verify(b"secret_key") { if token.verify(b"secret_key").unwrap() {
Some(token.claims.sub) Some(token.claims.sub)
} else { } else {
None None

View file

@ -27,7 +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 = Token::<DefaultHeader, Registered>::parse(token).unwrap(); let token = Token::<DefaultHeader, Registered>::parse(token).unwrap();
if token.verify(b"secret_key") { if token.verify(b"secret_key").unwrap() {
token.claims.sub token.claims.sub
} else { } else {
None None

View file

@ -40,7 +40,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 = Token::<DefaultHeader, Registered>::parse(token).unwrap(); let token = Token::<DefaultHeader, Registered>::parse(token).unwrap();
if token.verify(load_key("./publicKey.pub").unwrap().as_bytes()) { if token.verify(load_key("./publicKey.pub").unwrap().as_bytes()).unwrap() {
token.claims.sub token.claims.sub
} else { } else {
None None

View file

@ -4,6 +4,7 @@ use error::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json; use serde_json;
use serde_json::value::{Value}; use serde_json::value::{Value};
use super::Result;
/// A default claim set, including the standard, or registered, claims and the ability to specify /// A default claim set, including the standard, or registered, claims and the ability to specify
/// your own as private claims. /// your own as private claims.
@ -38,7 +39,7 @@ impl<T: Serialize + Deserialize> Claims<T>{
impl<T: Serialize + Deserialize> Component for Claims<T> { impl<T: Serialize + Deserialize> Component for Claims<T> {
/// This implementation simply parses the base64 data twice, each time applying it to the /// This implementation simply parses the base64 data twice, each time applying it to the
/// registered and private claims. /// registered and private claims.
fn from_base64(raw: &str) -> Result<Claims<T>, Error> { fn from_base64(raw: &str) -> Result<Claims<T>> {
let data = try!(decode_config(raw, URL_SAFE)); let data = try!(decode_config(raw, URL_SAFE));
let reg_claims: Registered = try!(serde_json::from_slice(&data)); let reg_claims: Registered = try!(serde_json::from_slice(&data));
@ -53,7 +54,7 @@ impl<T: Serialize + Deserialize> Component for Claims<T> {
/// Renders both the registered and private claims into a single consolidated JSON /// Renders both the registered and private claims into a single consolidated JSON
/// representation before encoding. /// representation before encoding.
fn to_base64(&self) -> Result<String, Error> { fn to_base64(&self) -> Result<String> {
if let Value::Object(mut reg_map) = serde_json::to_value(&self.reg)? { if let Value::Object(mut reg_map) = serde_json::to_value(&self.reg)? {
if let Value::Object(pri_map) = serde_json::to_value(&self.private)? { if let Value::Object(pri_map) = serde_json::to_value(&self.private)? {
reg_map.extend(pri_map); reg_map.extend(pri_map);

View file

@ -5,8 +5,9 @@ use openssl::memcmp;
use openssl::pkey::PKey; use openssl::pkey::PKey;
use openssl::rsa::Rsa; use openssl::rsa::Rsa;
use openssl::sign::{Signer, Verifier}; use openssl::sign::{Signer, Verifier};
use super::Result;
pub fn sign(data: &str, key: &[u8], algorithm: &Algorithm) -> String { pub fn sign(data: &str, key: &[u8], algorithm: &Algorithm) -> Result<String> {
match algorithm { match algorithm {
&Algorithm::HS256 => sign_hmac(data, key, MessageDigest::sha256()), &Algorithm::HS256 => sign_hmac(data, key, MessageDigest::sha256()),
&Algorithm::HS384 => sign_hmac(data, key, MessageDigest::sha384()), &Algorithm::HS384 => sign_hmac(data, key, MessageDigest::sha384()),
@ -17,7 +18,7 @@ pub fn sign(data: &str, key: &[u8], algorithm: &Algorithm) -> String {
} }
} }
pub fn verify(target: &str, data: &str, key: &[u8], algorithm: &Algorithm) -> bool { pub fn verify(target: &str, data: &str, key: &[u8], algorithm: &Algorithm) -> Result<bool> {
match algorithm { match algorithm {
&Algorithm::HS256 => verify_hmac(target, data, key, MessageDigest::sha256()), &Algorithm::HS256 => verify_hmac(target, data, key, MessageDigest::sha256()),
&Algorithm::HS384 => verify_hmac(target, data, key, MessageDigest::sha384()), &Algorithm::HS384 => verify_hmac(target, data, key, MessageDigest::sha384()),
@ -28,45 +29,45 @@ pub fn verify(target: &str, data: &str, key: &[u8], algorithm: &Algorithm) -> bo
} }
} }
fn sign_hmac(data: &str, key: &[u8], digest: MessageDigest) -> String { fn sign_hmac(data: &str, key: &[u8], digest: MessageDigest) -> Result<String> {
let secret_key = PKey::hmac(key).unwrap(); let secret_key = try!(PKey::hmac(key));
let mut signer = Signer::new(digest, &secret_key).unwrap(); let mut signer = Signer::new(digest, &secret_key)?;
signer.update(data.as_bytes()).unwrap(); signer.update(data.as_bytes())?;
let mac = signer.finish().unwrap(); let mac = signer.finish()?;
encode_config(&mac, URL_SAFE) Ok(encode_config(&mac, URL_SAFE))
} }
fn sign_rsa(data: &str, key: &[u8], digest: MessageDigest) -> String { fn sign_rsa(data: &str, key: &[u8], digest: MessageDigest) -> Result<String> {
let private_key = Rsa::private_key_from_pem(key).unwrap(); let private_key = Rsa::private_key_from_pem(key)?;
let pkey = PKey::from_rsa(private_key).unwrap(); let pkey = PKey::from_rsa(private_key)?;
let mut signer = Signer::new(digest, &pkey).unwrap(); let mut signer = Signer::new(digest, &pkey)?;
signer.update(data.as_bytes()).unwrap(); signer.update(data.as_bytes())?;
let sig = signer.finish().unwrap(); let sig = signer.finish()?;
encode_config(&sig, URL_SAFE) Ok(encode_config(&sig, URL_SAFE))
} }
fn verify_hmac(target: &str, data: &str, key: &[u8], digest: MessageDigest) -> bool { fn verify_hmac(target: &str, data: &str, key: &[u8], digest: MessageDigest) -> Result<bool> {
let target_bytes: Vec<u8> = decode_config(target, URL_SAFE).unwrap(); let target_bytes: Vec<u8> = decode_config(target, URL_SAFE)?;
let secret_key = PKey::hmac(key).unwrap(); let secret_key = PKey::hmac(key)?;
let mut signer = Signer::new(digest, &secret_key).unwrap(); let mut signer = Signer::new(digest, &secret_key)?;
signer.update(data.as_bytes()).unwrap(); signer.update(data.as_bytes())?;
let mac = signer.finish().unwrap(); let mac = signer.finish()?;
memcmp::eq(&mac, &target_bytes) Ok(memcmp::eq(&mac, &target_bytes))
} }
fn verify_rsa(signature: &str, data: &str, key: &[u8], digest: MessageDigest) -> bool { fn verify_rsa(signature: &str, data: &str, key: &[u8], digest: MessageDigest) -> Result<bool> {
let signature_bytes: Vec<u8> = decode_config(signature, URL_SAFE).unwrap(); let signature_bytes: Vec<u8> = decode_config(signature, URL_SAFE)?;
let public_key = Rsa::public_key_from_pem(key).unwrap(); let public_key = Rsa::public_key_from_pem(key)?;
let pkey = PKey::from_rsa(public_key).unwrap(); let pkey = PKey::from_rsa(public_key)?;
let mut verifier = Verifier::new(digest, &pkey).unwrap(); let mut verifier = Verifier::new(digest, &pkey)?;
verifier.update(data.as_bytes()).unwrap(); verifier.update(data.as_bytes())?;
verifier.finish(&signature_bytes).unwrap() Ok(try!(verifier.finish(&signature_bytes)))
} }
#[cfg(test)] #[cfg(test)]
@ -88,7 +89,7 @@ mod tests {
let sig = sign(&*data, "secret".as_bytes(), &Algorithm::HS256); let sig = sign(&*data, "secret".as_bytes(), &Algorithm::HS256);
assert_eq!(sig, real_sig); assert_eq!(sig.unwrap(), real_sig);
} }
#[test] #[test]
@ -102,7 +103,7 @@ mod tests {
let sig = sign(&*data, key.as_bytes(), &Algorithm::RS256); let sig = sign(&*data, key.as_bytes(), &Algorithm::RS256);
assert_eq!(sig.trim(), real_sig); assert_eq!(sig.unwrap(), real_sig);
} }
#[test] #[test]
@ -112,7 +113,7 @@ mod tests {
let target = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"; let target = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
let data = format!("{}.{}", header, claims); let data = format!("{}.{}", header, claims);
assert!(verify(target, &*data, "secret".as_bytes(), &Algorithm::HS256)); assert!(verify(target, &*data, "secret".as_bytes(), &Algorithm::HS256).unwrap());
} }
#[test] #[test]
@ -123,7 +124,7 @@ mod tests {
let data = format!("{}.{}", header, claims); let data = format!("{}.{}", header, claims);
let key = load_key("./examples/publicKey.pub").unwrap(); let key = load_key("./examples/publicKey.pub").unwrap();
assert!(verify(&real_sig, &*data, key.as_bytes(), &Algorithm::RS256)); assert!(verify(&real_sig, &*data, key.as_bytes(), &Algorithm::RS256).unwrap());
} }
fn load_key(keypath: &str) -> Result<String, Error> { fn load_key(keypath: &str) -> Result<String, Error> {

View file

@ -1,6 +1,7 @@
use base64::Base64Error; use base64::Base64Error;
use serde_json; use serde_json;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use openssl::error::ErrorStack;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -9,6 +10,7 @@ pub enum Error {
Base64(Base64Error), Base64(Base64Error),
JSON(serde_json::Error), JSON(serde_json::Error),
Custom(String), Custom(String),
Crypto(ErrorStack),
} }
macro_rules! error_wrap { macro_rules! error_wrap {
@ -22,3 +24,4 @@ macro_rules! error_wrap {
error_wrap!(FromUtf8Error, Error::Utf8); error_wrap!(FromUtf8Error, Error::Utf8);
error_wrap!(Base64Error, Error::Base64); error_wrap!(Base64Error, Error::Base64);
error_wrap!(serde_json::Error, Error::JSON); error_wrap!(serde_json::Error, Error::JSON);
error_wrap!(ErrorStack, Error::Crypto);

View file

@ -21,6 +21,8 @@ pub mod header;
pub mod claims; pub mod claims;
mod crypt; mod crypt;
pub type Result<T> = std::result::Result<T, Error>;
/// Main struct representing a JSON Web Token, composed of a header and a set of claims. /// Main struct representing a JSON Web Token, composed of a header and a set of claims.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Token<H, C> pub struct Token<H, C>
@ -38,8 +40,8 @@ pub trait Header {
/// Any header or claims type must implement this trait in order to serialize and deserialize /// Any header or claims type must implement this trait in order to serialize and deserialize
/// correctly. /// correctly.
pub trait Component: Sized { pub trait Component: Sized {
fn from_base64(raw: &str) -> Result<Self, Error>; fn from_base64(raw: &str) -> Result<Self>;
fn to_base64(&self) -> Result<String, Error>; fn to_base64(&self) -> Result<String>;
} }
/// Provide a default implementation that should work in almost all cases. /// Provide a default implementation that should work in almost all cases.
@ -47,14 +49,14 @@ impl<T> Component for T
where T: Serialize + Deserialize + Sized { where T: Serialize + Deserialize + Sized {
/// Parse from a string. /// Parse from a string.
fn from_base64(raw: &str) -> Result<T, Error> { fn from_base64(raw: &str) -> Result<T> {
let data = try!(decode_config(raw, URL_SAFE)); let data = try!(decode_config(raw, URL_SAFE));
let s = try!(String::from_utf8(data)); let s = try!(String::from_utf8(data));
Ok(try!(serde_json::from_str(&*s))) Ok(try!(serde_json::from_str(&*s)))
} }
/// Encode to a string. /// Encode to a string.
fn to_base64(&self) -> Result<String, Error> { fn to_base64(&self) -> Result<String> {
let s = try!(serde_json::to_string(&self)); let s = try!(serde_json::to_string(&self));
let enc = encode_config((&*s).as_bytes(), URL_SAFE); let enc = encode_config((&*s).as_bytes(), URL_SAFE);
Ok(enc) Ok(enc)
@ -73,7 +75,7 @@ impl<H, C> Token<H, C>
} }
/// Parse a token from a string. /// Parse a token from a string.
pub fn parse(raw: &str) -> Result<Token<H, C>, Error> { pub fn parse(raw: &str) -> Result<Token<H, C>> {
let pieces: Vec<_> = raw.split('.').collect(); let pieces: Vec<_> = raw.split('.').collect();
Ok(Token { Ok(Token {
@ -84,27 +86,27 @@ impl<H, C> Token<H, C>
} }
/// Verify a token with a key and the token's specific algorithm. /// Verify a token with a key and the token's specific algorithm.
pub fn verify(&self, key: &[u8]) -> bool { pub fn verify(&self, key: &[u8]) -> Result<bool> {
let raw = match self.raw { let raw = match self.raw {
Some(ref s) => s, Some(ref s) => s,
None => return false, None => return Ok(false),
}; };
let pieces: Vec<_> = raw.rsplitn(2, '.').collect(); let pieces: Vec<_> = raw.rsplitn(2, '.').collect();
let sig = pieces[0]; let sig = pieces[0];
let data = pieces[1]; let data = pieces[1];
crypt::verify(sig, data, key, &self.header.alg()) Ok(crypt::verify(sig, data, key, &self.header.alg())?)
} }
/// Generate the signed token from a key with the specific algorithm as a url-safe, base64 /// Generate the signed token from a key with the specific algorithm as a url-safe, base64
/// string. /// string.
pub fn signed(&self, key: &[u8]) -> Result<String, Error> { pub fn signed(&self, key: &[u8]) -> Result<String> {
let header = try!(Component::to_base64(&self.header)); let header = try!(Component::to_base64(&self.header));
let claims = try!(self.claims.to_base64()); let claims = try!(self.claims.to_base64());
let data = format!("{}.{}", header, claims); let data = format!("{}.{}", header, claims);
let sig = crypt::sign(&*data, key, &self.header.alg()); let sig = crypt::sign(&*data, key, &self.header.alg())?;
Ok(format!("{}.{}", data, sig)) Ok(format!("{}.{}", data, sig))
} }
} }
@ -137,7 +139,7 @@ mod tests {
{ {
assert_eq!(token.header.alg, HS256); assert_eq!(token.header.alg, HS256);
} }
assert!(token.verify("secret".as_bytes())); assert!(token.verify("secret".as_bytes()).unwrap());
} }
#[test] #[test]
@ -148,7 +150,7 @@ mod tests {
let same = Token::parse(&*raw).unwrap(); let same = Token::parse(&*raw).unwrap();
assert_eq!(token, same); assert_eq!(token, same);
assert!(same.verify(key)); assert!(same.verify(key).unwrap());
} }
#[test] #[test]
@ -166,7 +168,7 @@ mod tests {
assert_eq!(token, same); assert_eq!(token, same);
let public_key = load_key("./examples/publicKey.pub").unwrap(); let public_key = load_key("./examples/publicKey.pub").unwrap();
assert!(same.verify(public_key.as_bytes())); assert!(same.verify(public_key.as_bytes()).unwrap());
} }
fn load_key(keypath: &str) -> Result<String, Error> { fn load_key(keypath: &str) -> Result<String, Error> {