From 94b83d12aa8e2802206e7b701b6d7f1043e94b39 Mon Sep 17 00:00:00 2001 From: Thomas Gideon Date: Fri, 17 Feb 2017 11:53:12 -0500 Subject: [PATCH] Clean up error handling in crypt --- examples/custom_claims.rs | 2 +- examples/hs256.rs | 2 +- examples/rs256.rs | 2 +- src/claims.rs | 5 +-- src/crypt.rs | 67 ++++++++++++++++++++------------------- src/error.rs | 3 ++ src/lib.rs | 28 ++++++++-------- 7 files changed, 58 insertions(+), 51 deletions(-) diff --git a/examples/custom_claims.rs b/examples/custom_claims.rs index 05fcc29..8432110 100644 --- a/examples/custom_claims.rs +++ b/examples/custom_claims.rs @@ -35,7 +35,7 @@ fn new_token(user_id: &str, password: &str) -> Option { fn login(token: &str) -> Option { let token = Token::::parse(token).unwrap(); - if token.verify(b"secret_key") { + if token.verify(b"secret_key").unwrap() { Some(token.claims.sub) } else { None diff --git a/examples/hs256.rs b/examples/hs256.rs index aabc21b..cd417f7 100644 --- a/examples/hs256.rs +++ b/examples/hs256.rs @@ -27,7 +27,7 @@ fn new_token(user_id: &str, password: &str) -> Option { fn login(token: &str) -> Option { let token = Token::::parse(token).unwrap(); - if token.verify(b"secret_key") { + if token.verify(b"secret_key").unwrap() { token.claims.sub } else { None diff --git a/examples/rs256.rs b/examples/rs256.rs index 5b70994..7b13cf0 100644 --- a/examples/rs256.rs +++ b/examples/rs256.rs @@ -40,7 +40,7 @@ fn new_token(user_id: &str, password: &str) -> Option { fn login(token: &str) -> Option { let token = Token::::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 diff --git a/src/claims.rs b/src/claims.rs index dfe9b8a..626d108 100644 --- a/src/claims.rs +++ b/src/claims.rs @@ -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 Claims{ impl Component for Claims { /// This implementation simply parses the base64 data twice, each time applying it to the /// registered and private claims. - fn from_base64(raw: &str) -> Result, Error> { + fn from_base64(raw: &str) -> Result> { let data = try!(decode_config(raw, URL_SAFE)); let reg_claims: Registered = try!(serde_json::from_slice(&data)); @@ -53,7 +54,7 @@ impl Component for Claims { /// Renders both the registered and private claims into a single consolidated JSON /// representation before encoding. - fn to_base64(&self) -> Result { + fn to_base64(&self) -> Result { 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); diff --git a/src/crypt.rs b/src/crypt.rs index 407d9d7..18cbe75 100644 --- a/src/crypt.rs +++ b/src/crypt.rs @@ -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 { 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 { 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 { + 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 { + 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 = 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 { + let target_bytes: Vec = 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 = 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 { + let signature_bytes: Vec = 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 { diff --git a/src/error.rs b/src/error.rs index ec9856c..ac6905e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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); diff --git a/src/lib.rs b/src/lib.rs index 3b8f1f3..538e0df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub mod header; pub mod claims; mod crypt; +pub type Result = std::result::Result; + /// Main struct representing a JSON Web Token, composed of a header and a set of claims. #[derive(Debug, Default)] pub struct Token @@ -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; - fn to_base64(&self) -> Result; + fn from_base64(raw: &str) -> Result; + fn to_base64(&self) -> Result; } /// Provide a default implementation that should work in almost all cases. @@ -47,14 +49,14 @@ impl Component for T where T: Serialize + Deserialize + Sized { /// Parse from a string. - fn from_base64(raw: &str) -> Result { + fn from_base64(raw: &str) -> Result { 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 { + fn to_base64(&self) -> Result { let s = try!(serde_json::to_string(&self)); let enc = encode_config((&*s).as_bytes(), URL_SAFE); Ok(enc) @@ -73,7 +75,7 @@ impl Token } /// Parse a token from a string. - pub fn parse(raw: &str) -> Result, Error> { + pub fn parse(raw: &str) -> Result> { let pieces: Vec<_> = raw.split('.').collect(); Ok(Token { @@ -84,27 +86,27 @@ impl Token } /// 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 { 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 { + pub fn signed(&self, key: &[u8]) -> Result { 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 {