diff --git a/sasl/src/error.rs b/sasl/src/error.rs index 4192300a..284406bd 100644 --- a/sasl/src/error.rs +++ b/sasl/src/error.rs @@ -1,8 +1,11 @@ use openssl::error::ErrorStack; +/// A wrapper enum for things that could go wrong in this crate. #[derive(Debug)] pub enum Error { + /// An error in OpenSSL. OpenSslErrorStack(ErrorStack), + /// An error in a SASL mechanism. SaslError(String), } diff --git a/sasl/src/lib.rs b/sasl/src/lib.rs index 48a57b4a..fc815050 100644 --- a/sasl/src/lib.rs +++ b/sasl/src/lib.rs @@ -1,14 +1,50 @@ -//! Provides the `SaslMechanism` trait and some implementations. +#![deny(missing_docs)] + +//! This crate provides a framework for SASL authentication and a few authentication mechanisms. +//! +//! # Examples +//! +//! ```rust +//! use sasl::{SaslCredentials, SaslSecret, SaslMechanism, Error}; +//! use sasl::mechanisms::Plain; +//! +//! let creds = SaslCredentials { +//! username: "user".to_owned(), +//! secret: SaslSecret::Password("pencil".to_owned()), +//! channel_binding: None, +//! }; +//! +//! let mut mechanism = Plain::from_credentials(creds).unwrap(); +//! +//! let initial_data = mechanism.initial().unwrap(); +//! +//! assert_eq!(initial_data, b"\0user\0pencil"); +//! ``` +//! +//! You may look at the tests of `mechanisms/scram.rs` for examples of more advanced usage. +//! +//! # Usage +//! +//! You can use this in your crate by adding this under `dependencies` in your `Cargo.toml`: +//! +//! ```toml,ignore +//! sasl = "*" +//! ``` extern crate base64; extern crate openssl; -pub mod error; +mod error; + +pub use error::Error; /// A struct containing SASL credentials. pub struct SaslCredentials { - pub username: String, + /// The requested username. + pub username: String, // TODO: change this since some mechanisms do not use it + /// The secret used to authenticate. pub secret: SaslSecret, + /// Optionally, channel binding data, for *-PLUS mechanisms. pub channel_binding: Option>, } @@ -20,6 +56,7 @@ pub enum SaslSecret { Password(String), } +/// A trait which defines SASL mechanisms. pub trait SaslMechanism { /// The name of the mechanism. fn name(&self) -> &str; diff --git a/sasl/src/mechanisms/anonymous.rs b/sasl/src/mechanisms/anonymous.rs index 31a6f14b..ecfe14df 100644 --- a/sasl/src/mechanisms/anonymous.rs +++ b/sasl/src/mechanisms/anonymous.rs @@ -4,9 +4,14 @@ use SaslCredentials; use SaslMechanism; use SaslSecret; +/// A struct for the SASL ANONYMOUS mechanism. pub struct Anonymous; impl Anonymous { + /// Constructs a new struct for authenticating using the SASL ANONYMOUS mechanism. + /// + /// It is recommended that instead you use a `SaslCredentials` struct and turn it into the + /// requested mechanism using `from_credentials`. pub fn new() -> Anonymous { Anonymous } diff --git a/sasl/src/mechanisms/mod.rs b/sasl/src/mechanisms/mod.rs index 0e4205c8..2ccd1d79 100644 --- a/sasl/src/mechanisms/mod.rs +++ b/sasl/src/mechanisms/mod.rs @@ -1,4 +1,5 @@ -///! Provides a few SASL mechanisms. +//! Provides a few SASL mechanisms. + mod anonymous; mod plain; mod scram; diff --git a/sasl/src/mechanisms/plain.rs b/sasl/src/mechanisms/plain.rs index c273d97f..d77b70cc 100644 --- a/sasl/src/mechanisms/plain.rs +++ b/sasl/src/mechanisms/plain.rs @@ -4,12 +4,17 @@ use SaslCredentials; use SaslMechanism; use SaslSecret; +/// A struct for the SASL PLAIN mechanism. pub struct Plain { username: String, password: String, } impl Plain { + /// Constructs a new struct for authenticating using the SASL PLAIN mechanism. + /// + /// It is recommended that instead you use a `SaslCredentials` struct and turn it into the + /// requested mechanism using `from_credentials`. pub fn new, P: Into>(username: N, password: P) -> Plain { Plain { username: username.into(), diff --git a/sasl/src/mechanisms/scram.rs b/sasl/src/mechanisms/scram.rs index c2fcbc18..757e8cc2 100644 --- a/sasl/src/mechanisms/scram.rs +++ b/sasl/src/mechanisms/scram.rs @@ -66,13 +66,22 @@ fn generate_nonce() -> Result { Ok(base64::encode(&data)) } +/// A trait which defines the needed methods for SCRAM. pub trait ScramProvider { + /// The name of the hash function. fn name() -> &'static str; + + /// A function which hashes the data using the hash function. fn hash(data: &[u8]) -> Vec; + + /// A function which performs an HMAC using the hash function. fn hmac(data: &[u8], key: &[u8]) -> Vec; + + /// A function which does PBKDF2 key derivation using the hash function. fn derive(data: &[u8], salt: &[u8], iterations: usize) -> Vec; } +/// A `ScramProvider` which provides SCRAM-SHA-1 and SCRAM-SHA-1-PLUS pub struct Sha1; impl ScramProvider for Sha1 { @@ -99,6 +108,7 @@ impl ScramProvider for Sha1 { } } +/// A `ScramProvider` which provides SCRAM-SHA-256 and SCRAM-SHA-256-PLUS pub struct Sha256; impl ScramProvider for Sha256 { @@ -136,6 +146,7 @@ enum ScramState { }, } +/// A struct for the SASL SCRAM-* and SCRAM-*-PLUS mechanisms. pub struct Scram { name: String, username: String, @@ -147,6 +158,10 @@ pub struct Scram { } impl Scram { + /// Constructs a new struct for authenticating using the SASL SCRAM-* mechanism. + /// + /// It is recommended that instead you use a `SaslCredentials` struct and turn it into the + /// requested mechanism using `from_credentials`. pub fn new, P: Into>( username: N, password: P, @@ -162,6 +177,12 @@ impl Scram { }) } + /// Constructs a new struct for authenticating using the SASL SCRAM-* mechanism. + /// + /// This one takes a nonce instead of generating it. + /// + /// It is recommended that instead you use a `SaslCredentials` struct and turn it into the + /// requested mechanism using `from_credentials`. pub fn new_with_nonce, P: Into>( username: N, password: P, @@ -178,11 +199,18 @@ impl Scram { } } + /// Constructs a new struct for authenticating using the SASL SCRAM-*-PLUS mechanism. + /// + /// This means that this function will also take the channel binding data. + /// + /// It is recommended that instead you use a `SaslCredentials` struct and turn it into the + /// requested mechanism using `from_credentials`. pub fn new_with_channel_binding, P: Into>( username: N, password: P, channel_binding: Vec, ) -> Result, Error> { + // TODO: channel binding modes other than tls-unique Ok(Scram { name: format!("SCRAM-{}-PLUS", S::name()), username: username.into(),