diff --git a/sasl/Cargo.toml b/sasl/Cargo.toml index 46e5717b7ac5fa26fe43a5ca317a08a2c45b7ccd..79483d7bd62ab77998b94cbd4d4901dbd6ed5fb3 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 d95245a8b05bb09dbbdd78c72573f32e55892ffd..45361377669803f30b95a1cd58662cc36a7f1c66 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 786978da1f3eaee817ece5a624d818f0f17a9672..1c5bd1a310db0acf62a5bd0b43da5d28462a0f22 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 b19ed48c8b2f58cdec96626e4ce408177c7a0607..a3bd35c97e8d8db6e649291e90abe14389b005c0 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 d8655d543cea5816c55d63317dca9ffbdfb94efa..2acf9cff2d7b941f8a6100205301c99d5d08c369 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 6833a8994fbf52c8ed7ce487be04384f7ccf66d2..860e441d8f42908e1eabe9581c7c52aa8b9c5357 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 6d79df25b8e7bf130a3b6fd6a9219e142558025c..b5287073753dccf6de443bc0835961aaa24ab9f6 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 9e1379715c77e1cf9df3e659faca72815c43f3ba..2a64e7621d873abdc29ca6130e070e0b96aab961 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 54c94c8494b14e84ce30d93bb4efa230a4468232..31f42dd9e00123d325baee445361dc3f836d522e 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 1deebbe5a7408667cfca74bd7de0ccedd89e5227..8df0e76fdab603de5091ec4220befff49ead4324 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 5f6feaec6d57b3cdc4711aa9dd59cf3ca9ed808e..a53e97ed3c88bdb0d3c0d05b1fb1a49f64f1a356 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 87b4b07be1725886bb3384c74b892da91a13d585..020e88b208d0363b80562bd4436d8367f4e5a984 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 {