diff --git a/sasl/Cargo.toml b/sasl/Cargo.toml index 46e5717b..79483d7b 100644 --- a/sasl/Cargo.toml +++ b/sasl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sasl" -version = "0.4.2" +version = "0.4.3" authors = ["lumi "] description = "A crate for SASL authentication. Currently only does the client side." homepage = "https://gitlab.com/lumi/sasl-rs" @@ -9,17 +9,19 @@ documentation = "https://docs.rs/sasl" readme = "README.md" keywords = ["sasl", "authentication"] license = "LGPL-3.0+" +edition = "2018" [badges] gitlab = { repository = "lumi/sasl-rs" } [features] default = ["scram"] -scram = ["openssl"] +scram = [] [dependencies] -base64 = "0.9.1" - -[dependencies.openssl] -version = "0.10.7" -optional = true +base64 = "0.10" +rand_os = "0.1" +sha-1 = "0.8" +sha2 = "0.8" +hmac = "0.7" +pbkdf2 = { version = "0.3", default-features = false } diff --git a/sasl/src/client/mechanisms/anonymous.rs b/sasl/src/client/mechanisms/anonymous.rs index d95245a8..45361377 100644 --- a/sasl/src/client/mechanisms/anonymous.rs +++ b/sasl/src/client/mechanisms/anonymous.rs @@ -1,7 +1,7 @@ //! Provides the SASL "ANONYMOUS" mechanism. -use client::Mechanism; -use common::{Credentials, Secret}; +use crate::client::Mechanism; +use crate::common::{Credentials, Secret}; /// A struct for the SASL ANONYMOUS mechanism. pub struct Anonymous; diff --git a/sasl/src/client/mechanisms/plain.rs b/sasl/src/client/mechanisms/plain.rs index 786978da..1c5bd1a3 100644 --- a/sasl/src/client/mechanisms/plain.rs +++ b/sasl/src/client/mechanisms/plain.rs @@ -1,7 +1,7 @@ //! Provides the SASL "PLAIN" mechanism. -use client::Mechanism; -use common::{Credentials, Identity, Password, Secret}; +use crate::client::Mechanism; +use crate::common::{Credentials, Identity, Password, Secret}; /// A struct for the SASL PLAIN mechanism. pub struct Plain { diff --git a/sasl/src/client/mechanisms/scram.rs b/sasl/src/client/mechanisms/scram.rs index b19ed48c..a3bd35c9 100644 --- a/sasl/src/client/mechanisms/scram.rs +++ b/sasl/src/client/mechanisms/scram.rs @@ -2,11 +2,11 @@ use base64; -use client::Mechanism; -use common::scram::{generate_nonce, ScramProvider}; -use common::{parse_frame, xor, ChannelBinding, Credentials, Identity, Password, Secret}; +use crate::client::Mechanism; +use crate::common::scram::{generate_nonce, ScramProvider}; +use crate::common::{parse_frame, xor, ChannelBinding, Credentials, Identity, Password, Secret}; -use error::Error; +use crate::error::Error; use std::marker::PhantomData; @@ -137,8 +137,8 @@ impl Mechanism for Scram { client_final_message_bare.extend(b",r="); client_final_message_bare.extend(server_nonce.bytes()); let salted_password = S::derive(&self.password, &salt, iterations)?; - let client_key = S::hmac(b"Client Key", &salted_password); - let server_key = S::hmac(b"Server Key", &salted_password); + let client_key = S::hmac(b"Client Key", &salted_password)?; + let server_key = S::hmac(b"Server Key", &salted_password)?; let mut auth_message = Vec::new(); auth_message.extend(initial_message); auth_message.push(b','); @@ -146,9 +146,9 @@ impl Mechanism for Scram { auth_message.push(b','); auth_message.extend(&client_final_message_bare); let stored_key = S::hash(&client_key); - let client_signature = S::hmac(&auth_message, &stored_key); + let client_signature = S::hmac(&auth_message, &stored_key)?; let client_proof = xor(&client_key, &client_signature); - let server_signature = S::hmac(&auth_message, &server_key); + let server_signature = S::hmac(&auth_message, &server_key)?; let mut client_final_message = Vec::new(); client_final_message.extend(&client_final_message_bare); client_final_message.extend(b",p="); @@ -189,9 +189,9 @@ impl Mechanism for Scram { #[cfg(test)] mod tests { - use client::mechanisms::Scram; - use client::Mechanism; - use common::scram::{Sha1, Sha256}; + use crate::client::mechanisms::Scram; + use crate::client::Mechanism; + use crate::common::scram::{Sha1, Sha256}; #[test] fn scram_sha1_works() { diff --git a/sasl/src/client/mod.rs b/sasl/src/client/mod.rs index d8655d54..2acf9cff 100644 --- a/sasl/src/client/mod.rs +++ b/sasl/src/client/mod.rs @@ -1,4 +1,4 @@ -use common::Credentials; +use crate::common::Credentials; /// A trait which defines SASL mechanisms. pub trait Mechanism { diff --git a/sasl/src/common/scram.rs b/sasl/src/common/scram.rs index 6833a899..860e441d 100644 --- a/sasl/src/common/scram.rs +++ b/sasl/src/common/scram.rs @@ -1,21 +1,23 @@ -use openssl::error::ErrorStack; -use openssl::hash::hash; -use openssl::hash::MessageDigest; -use openssl::pkcs5::pbkdf2_hmac; -use openssl::pkey::PKey; -use openssl::rand::rand_bytes; -use openssl::sign::Signer; +use hmac::{Hmac, Mac}; +use pbkdf2::pbkdf2; +use rand_os::{ + rand_core::{Error as RngError, RngCore}, + OsRng, +}; +use sha1::{Digest, Sha1 as Sha1_hash}; +use sha2::Sha256 as Sha256_hash; -use common::Password; +use crate::common::Password; -use secret; +use crate::secret; use base64; /// Generate a nonce for SCRAM authentication. -pub fn generate_nonce() -> Result { - let mut data = vec![0; 32]; - rand_bytes(&mut data)?; +pub fn generate_nonce() -> Result { + let mut data = [0u8; 32]; + let mut rng = OsRng::new()?; + rng.fill_bytes(&mut data); Ok(base64::encode(&data)) } @@ -31,7 +33,7 @@ pub trait ScramProvider { fn hash(data: &[u8]) -> Vec; /// A function which performs an HMAC using the hash function. - fn hmac(data: &[u8], key: &[u8]) -> Vec; + fn hmac(data: &[u8], key: &[u8]) -> Result, String>; /// A function which does PBKDF2 key derivation using the hash function. fn derive(data: &Password, salt: &[u8], iterations: usize) -> Result, String>; @@ -41,7 +43,6 @@ pub trait ScramProvider { pub struct Sha1; impl ScramProvider for Sha1 { - // TODO: look at all these unwraps type Secret = secret::Pbkdf2Sha1; fn name() -> &'static str { @@ -49,28 +50,30 @@ impl ScramProvider for Sha1 { } fn hash(data: &[u8]) -> Vec { - hash(MessageDigest::sha1(), data).unwrap().to_vec() + let hash = Sha1_hash::digest(data); + let mut vec = Vec::with_capacity(Sha1_hash::output_size()); + vec.extend_from_slice(hash.as_slice()); + vec } - fn hmac(data: &[u8], key: &[u8]) -> Vec { - let pkey = PKey::hmac(key).unwrap(); - let mut signer = Signer::new(MessageDigest::sha1(), &pkey).unwrap(); - signer.update(data).unwrap(); - signer.sign_to_vec().unwrap() + fn hmac(data: &[u8], key: &[u8]) -> Result, String> { + type HmacSha1 = Hmac; + let mut mac = match HmacSha1::new_varkey(key) { + Ok(mac) => mac, + Err(err) => return Err(format!("{}", err)), + }; + mac.input(data); + let result = mac.result(); + let mut vec = Vec::with_capacity(Sha1_hash::output_size()); + vec.extend_from_slice(result.code().as_slice()); + Ok(vec) } fn derive(password: &Password, salt: &[u8], iterations: usize) -> Result, String> { match *password { Password::Plain(ref plain) => { let mut result = vec![0; 20]; - pbkdf2_hmac( - plain.as_bytes(), - salt, - iterations, - MessageDigest::sha1(), - &mut result, - ) - .unwrap(); + pbkdf2::>(plain.as_bytes(), salt, iterations, &mut result); Ok(result) } Password::Pbkdf2 { @@ -104,7 +107,6 @@ impl ScramProvider for Sha1 { pub struct Sha256; impl ScramProvider for Sha256 { - // TODO: look at all these unwraps type Secret = secret::Pbkdf2Sha256; fn name() -> &'static str { @@ -112,28 +114,30 @@ impl ScramProvider for Sha256 { } fn hash(data: &[u8]) -> Vec { - hash(MessageDigest::sha256(), data).unwrap().to_vec() + let hash = Sha256_hash::digest(data); + let mut vec = Vec::with_capacity(Sha256_hash::output_size()); + vec.extend_from_slice(hash.as_slice()); + vec } - fn hmac(data: &[u8], key: &[u8]) -> Vec { - let pkey = PKey::hmac(key).unwrap(); - let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); - signer.update(data).unwrap(); - signer.sign_to_vec().unwrap() + fn hmac(data: &[u8], key: &[u8]) -> Result, String> { + type HmacSha256 = Hmac; + let mut mac = match HmacSha256::new_varkey(key) { + Ok(mac) => mac, + Err(err) => return Err(format!("{}", err)), + }; + mac.input(data); + let result = mac.result(); + let mut vec = Vec::with_capacity(Sha256_hash::output_size()); + vec.extend_from_slice(result.code().as_slice()); + Ok(vec) } fn derive(password: &Password, salt: &[u8], iterations: usize) -> Result, String> { match *password { Password::Plain(ref plain) => { let mut result = vec![0; 32]; - pbkdf2_hmac( - plain.as_bytes(), - salt, - iterations, - MessageDigest::sha256(), - &mut result, - ) - .unwrap(); + pbkdf2::>(plain.as_bytes(), salt, iterations, &mut result); Ok(result) } Password::Pbkdf2 { diff --git a/sasl/src/error.rs b/sasl/src/error.rs index 6d79df25..b5287073 100644 --- a/sasl/src/error.rs +++ b/sasl/src/error.rs @@ -1,19 +1,19 @@ #[cfg(feature = "scram")] -use openssl::error::ErrorStack; +use rand_os::rand_core::Error as RngError; /// A wrapper enum for things that could go wrong in this crate. #[derive(Debug)] pub enum Error { #[cfg(feature = "scram")] - /// An error in OpenSSL. - OpenSslErrorStack(ErrorStack), + /// An error while initializing the Rng. + RngError(RngError), /// An error in a SASL mechanism. SaslError(String), } #[cfg(feature = "scram")] -impl From for Error { - fn from(err: ErrorStack) -> Error { - Error::OpenSslErrorStack(err) +impl From for Error { + fn from(err: RngError) -> Error { + Error::RngError(err) } } diff --git a/sasl/src/lib.rs b/sasl/src/lib.rs index 9e137971..2a64e762 100644 --- a/sasl/src/lib.rs +++ b/sasl/src/lib.rs @@ -163,11 +163,6 @@ //! sasl = "*" //! ``` -extern crate base64; - -#[cfg(feature = "scram")] -extern crate openssl; - mod error; pub mod client; @@ -176,4 +171,4 @@ pub mod server; pub mod common; pub mod secret; -pub use error::Error; +pub use crate::error::Error; diff --git a/sasl/src/secret.rs b/sasl/src/secret.rs index 54c94c84..31f42dd9 100644 --- a/sasl/src/secret.rs +++ b/sasl/src/secret.rs @@ -19,10 +19,10 @@ pub struct Pbkdf2Sha1 { } impl Pbkdf2Sha1 { - #[cfg(feature = "openssl")] + #[cfg(feature = "scram")] pub fn derive(password: &str, salt: &[u8], iterations: usize) -> Result { - use common::scram::{ScramProvider, Sha1}; - use common::Password; + use crate::common::scram::{ScramProvider, Sha1}; + use crate::common::Password; let digest = Sha1::derive(&Password::Plain(password.to_owned()), salt, iterations)?; Ok(Pbkdf2Sha1 { salt: salt.to_vec(), @@ -54,10 +54,10 @@ pub struct Pbkdf2Sha256 { } impl Pbkdf2Sha256 { - #[cfg(feature = "openssl")] + #[cfg(feature = "scram")] pub fn derive(password: &str, salt: &[u8], iterations: usize) -> Result { - use common::scram::{ScramProvider, Sha256}; - use common::Password; + use crate::common::scram::{ScramProvider, Sha256}; + use crate::common::Password; let digest = Sha256::derive(&Password::Plain(password.to_owned()), salt, iterations)?; Ok(Pbkdf2Sha256 { salt: salt.to_vec(), diff --git a/sasl/src/server/mechanisms/plain.rs b/sasl/src/server/mechanisms/plain.rs index 1deebbe5..8df0e76f 100644 --- a/sasl/src/server/mechanisms/plain.rs +++ b/sasl/src/server/mechanisms/plain.rs @@ -1,6 +1,6 @@ -use common::Identity; -use secret; -use server::{Mechanism, Response, Validator}; +use crate::common::Identity; +use crate::secret; +use crate::server::{Mechanism, Response, Validator}; pub struct Plain> { validator: V, diff --git a/sasl/src/server/mechanisms/scram.rs b/sasl/src/server/mechanisms/scram.rs index 5f6feaec..a53e97ed 100644 --- a/sasl/src/server/mechanisms/scram.rs +++ b/sasl/src/server/mechanisms/scram.rs @@ -2,11 +2,11 @@ use std::marker::PhantomData; use base64; -use common::scram::{generate_nonce, ScramProvider}; -use common::{parse_frame, xor, ChannelBinding, Identity}; -use secret; -use secret::Pbkdf2Secret; -use server::{Mechanism, Provider, Response}; +use crate::common::scram::{generate_nonce, ScramProvider}; +use crate::common::{parse_frame, xor, ChannelBinding, Identity}; +use crate::secret; +use crate::secret::Pbkdf2Secret; +use crate::server::{Mechanism, Provider, Response}; enum ScramState { Init, @@ -150,8 +150,8 @@ where client_final_message_bare.extend(base64::encode(&cb_data).bytes()); client_final_message_bare.extend(b",r="); client_final_message_bare.extend(server_nonce.bytes()); - let client_key = S::hmac(b"Client Key", &salted_password); - let server_key = S::hmac(b"Server Key", &salted_password); + let client_key = S::hmac(b"Client Key", &salted_password)?; + let server_key = S::hmac(b"Server Key", &salted_password)?; let mut auth_message = Vec::new(); auth_message.extend(initial_client_message); auth_message.extend(b","); @@ -159,7 +159,7 @@ where auth_message.extend(b","); auth_message.extend(client_final_message_bare.clone()); let stored_key = S::hash(&client_key); - let client_signature = S::hmac(&auth_message, &stored_key); + let client_signature = S::hmac(&auth_message, &stored_key)?; let client_proof = xor(&client_key, &client_signature); let sent_proof = frame.get("p").ok_or_else(|| "no proof".to_owned())?; let sent_proof = @@ -167,7 +167,7 @@ where if client_proof != sent_proof { return Err("authentication failed".to_owned()); } - let server_signature = S::hmac(&auth_message, &server_key); + let server_signature = S::hmac(&auth_message, &server_key)?; let mut buf = Vec::new(); buf.extend(b"v="); buf.extend(base64::encode(&server_signature).bytes()); diff --git a/sasl/src/server/mod.rs b/sasl/src/server/mod.rs index 87b4b07b..020e88b2 100644 --- a/sasl/src/server/mod.rs +++ b/sasl/src/server/mod.rs @@ -1,5 +1,5 @@ -use common::Identity; -use secret::Secret; +use crate::common::Identity; +use crate::secret::Secret; #[macro_export] macro_rules! impl_validator_using_provider {