diff --git a/sasl/Cargo.toml b/sasl/Cargo.toml index 46e5717..79483d7 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 4be4d87..6c5c7de 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::{Secret, Credentials}; +use crate::client::Mechanism; +use crate::common::{Secret, Credentials}; /// 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 d4eeadf..213fb78 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::{Secret, Credentials, Password, Identity}; +use crate::client::Mechanism; +use crate::common::{Secret, Credentials, Password, Identity}; /// 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 0c1ef3f..58eb2be 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::{ChannelBinding, parse_frame, xor, Password, Credentials, Secret, Identity}; -use common::scram::{ScramProvider, generate_nonce}; +use crate::client::Mechanism; +use crate::common::{ChannelBinding, parse_frame, xor, Password, Credentials, Secret, Identity}; +use crate::common::scram::{ScramProvider, generate_nonce}; -use error::Error; +use crate::error::Error; use std::marker::PhantomData; @@ -118,8 +118,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','); @@ -127,9 +127,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="); @@ -168,9 +168,9 @@ impl Mechanism for Scram { #[cfg(test)] mod tests { - use client::Mechanism; - use client::mechanisms::Scram; - use common::scram::{Sha1, Sha256}; + use crate::client::Mechanism; + use crate::client::mechanisms::Scram; + use crate::common::scram::{Sha1, Sha256}; #[test] fn scram_sha1_works() { // Source: https://wiki.xmpp.org/web/SASLandSCRAM-SHA-1 diff --git a/sasl/src/client/mod.rs b/sasl/src/client/mod.rs index 6e69e47..a6cd093 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 f9bae91..975c95b 100644 --- a/sasl/src/common/scram.rs +++ b/sasl/src/common/scram.rs @@ -1,21 +1,20 @@ -use openssl::pkcs5::pbkdf2_hmac; -use openssl::hash::hash; -use openssl::hash::MessageDigest; -use openssl::sign::Signer; -use openssl::pkey::PKey; -use openssl::rand::rand_bytes; -use openssl::error::ErrorStack; +use rand_os::{OsRng, rand_core::{RngCore, Error as RngError}}; +use sha1::{Sha1 as Sha1_hash, Digest}; +use sha2::Sha256 as Sha256_hash; +use hmac::{Hmac, Mac}; +use pbkdf2::pbkdf2; -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 +30,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>; @@ -40,27 +39,36 @@ pub trait ScramProvider { /// A `ScramProvider` which provides SCRAM-SHA-1 and SCRAM-SHA-1-PLUS pub struct Sha1; -impl ScramProvider for Sha1 { // TODO: look at all these unwraps +impl ScramProvider for Sha1 { type Secret = secret::Pbkdf2Sha1; fn name() -> &'static str { "SHA-1" } 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 { ref method, salt: ref my_salt, iterations: my_iterations, ref data } => { @@ -84,27 +92,36 @@ impl ScramProvider for Sha1 { // TODO: look at all these unwraps /// A `ScramProvider` which provides SCRAM-SHA-256 and SCRAM-SHA-256-PLUS pub struct Sha256; -impl ScramProvider for Sha256 { // TODO: look at all these unwraps +impl ScramProvider for Sha256 { type Secret = secret::Pbkdf2Sha256; fn name() -> &'static str { "SHA-256" } 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 { ref method, salt: ref my_salt, iterations: my_iterations, ref data } => { diff --git a/sasl/src/error.rs b/sasl/src/error.rs index 6d79df2..b528707 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 87ce976..4aa8265 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; @@ -175,4 +170,4 @@ pub mod client; 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 dc6c112..fe329ca 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(), @@ -48,10 +48,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 95b16b5..48d6a69 100644 --- a/sasl/src/server/mechanisms/plain.rs +++ b/sasl/src/server/mechanisms/plain.rs @@ -1,6 +1,6 @@ -use server::{Validator, Response, Mechanism}; -use common::Identity; -use secret; +use crate::server::{Validator, Response, Mechanism}; +use crate::common::Identity; +use crate::secret; pub struct Plain> { validator: V, diff --git a/sasl/src/server/mechanisms/scram.rs b/sasl/src/server/mechanisms/scram.rs index f6c8f8e..920b095 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 server::{Provider, Response, Mechanism}; -use common::{Identity, ChannelBinding, parse_frame, xor}; -use common::scram::{ScramProvider, generate_nonce}; -use secret; -use secret::Pbkdf2Secret; +use crate::server::{Provider, Response, Mechanism}; +use crate::common::{Identity, ChannelBinding, parse_frame, xor}; +use crate::common::scram::{ScramProvider, generate_nonce}; +use crate::secret; +use crate::secret::Pbkdf2Secret; enum ScramState { Init, @@ -134,8 +134,8 @@ impl Mechanism for Scram 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","); @@ -143,14 +143,14 @@ impl Mechanism for Scram 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 = base64::decode(sent_proof).map_err(|_| "can't decode proof".to_owned())?; 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 926ed98..68fff98 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 {