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> {
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)
} else {
None

View file

@ -27,7 +27,7 @@ fn new_token(user_id: &str, password: &str) -> Option<String> {
fn login(token: &str) -> Option<String> {
let token = Token::<DefaultHeader, Registered>::parse(token).unwrap();
if token.verify(b"secret_key") {
if token.verify(b"secret_key").unwrap() {
token.claims.sub
} else {
None

View file

@ -40,7 +40,7 @@ fn new_token(user_id: &str, password: &str) -> Option<String> {
fn login(token: &str) -> Option<String> {
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
} else {
None

View file

@ -4,6 +4,7 @@ use error::Error;
use serde::{Deserialize, Serialize};
use serde_json;
use serde_json::value::{Value};
use super::Result;
/// A default claim set, including the standard, or registered, claims and the ability to specify
/// your own as private claims.
@ -38,7 +39,7 @@ impl<T: Serialize + Deserialize> Claims<T>{
impl<T: Serialize + Deserialize> Component for Claims<T> {
/// This implementation simply parses the base64 data twice, each time applying it to the
/// 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 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
/// 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(pri_map) = serde_json::to_value(&self.private)? {
reg_map.extend(pri_map);

View file

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

View file

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

View file

@ -21,6 +21,8 @@ pub mod header;
pub mod claims;
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.
#[derive(Debug, Default)]
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
/// correctly.
pub trait Component: Sized {
fn from_base64(raw: &str) -> Result<Self, Error>;
fn to_base64(&self) -> Result<String, Error>;
fn from_base64(raw: &str) -> Result<Self>;
fn to_base64(&self) -> Result<String>;
}
/// 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 {
/// 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 s = try!(String::from_utf8(data));
Ok(try!(serde_json::from_str(&*s)))
}
/// 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 enc = encode_config((&*s).as_bytes(), URL_SAFE);
Ok(enc)
@ -73,7 +75,7 @@ impl<H, C> Token<H, C>
}
/// 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();
Ok(Token {
@ -84,27 +86,27 @@ impl<H, C> Token<H, C>
}
/// 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 {
Some(ref s) => s,
None => return false,
None => return Ok(false),
};
let pieces: Vec<_> = raw.rsplitn(2, '.').collect();
let sig = pieces[0];
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
/// 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 claims = try!(self.claims.to_base64());
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))
}
}
@ -137,7 +139,7 @@ mod tests {
{
assert_eq!(token.header.alg, HS256);
}
assert!(token.verify("secret".as_bytes()));
assert!(token.verify("secret".as_bytes()).unwrap());
}
#[test]
@ -148,7 +150,7 @@ mod tests {
let same = Token::parse(&*raw).unwrap();
assert_eq!(token, same);
assert!(same.verify(key));
assert!(same.verify(key).unwrap());
}
#[test]
@ -166,7 +168,7 @@ mod tests {
assert_eq!(token, same);
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> {