clean up lots of things, server-side API improved

lumi created

Change summary

sasl/src/common/scram.rs            |   9 +
sasl/src/lib.rs                     | 111 +++++++++----
sasl/src/mechanisms/mod.rs          |   5 
sasl/src/secret.rs                  |  74 ++++++++
sasl/src/server/mechanisms/mod.rs   |   7 
sasl/src/server/mechanisms/plain.rs |  41 ++++
sasl/src/server/mechanisms/scram.rs | 185 ++++++++++++++++++++++
sasl/src/server/mod.rs              | 259 +++---------------------------
8 files changed, 419 insertions(+), 272 deletions(-)

Detailed changes

sasl/src/common/scram.rs 🔗

@@ -8,6 +8,8 @@ use openssl::sign::Signer;
 
 use common::Password;
 
+use secret;
+
 use base64;
 
 /// Generate a nonce for SCRAM authentication.
@@ -19,6 +21,9 @@ pub fn generate_nonce() -> Result<String, ErrorStack> {
 
 /// A trait which defines the needed methods for SCRAM.
 pub trait ScramProvider {
+    /// The kind of secret this `ScramProvider` requires.
+    type SecretKind: secret::SecretKind;
+
     /// The name of the hash function.
     fn name() -> &'static str;
 
@@ -37,6 +42,8 @@ pub struct Sha1;
 
 impl ScramProvider for Sha1 {
     // TODO: look at all these unwraps
+    type SecretKind = secret::Pbkdf2Sha1;
+
     fn name() -> &'static str {
         "SHA-1"
     }
@@ -98,6 +105,8 @@ pub struct Sha256;
 
 impl ScramProvider for Sha256 {
     // TODO: look at all these unwraps
+    type SecretKind = secret::Pbkdf2Sha256;
+
     fn name() -> &'static str {
         "SHA-256"
     }

sasl/src/lib.rs 🔗

@@ -25,12 +25,15 @@
 //! ## More complex usage
 //!
 //! ```rust
-//! use sasl::server::{Validator, Mechanism as ServerMechanism, Response};
+//! #[macro_use] extern crate sasl;
+//!
+//! use sasl::server::{Validator, Provider, Mechanism as ServerMechanism, Response};
 //! use sasl::server::mechanisms::{Plain as ServerPlain, Scram as ServerScram};
 //! use sasl::client::Mechanism as ClientMechanism;
 //! use sasl::client::mechanisms::{Plain as ClientPlain, Scram as ClientScram};
-//! use sasl::common::{Identity, Credentials, Secret, Password, ChannelBinding};
+//! use sasl::common::{Identity, Credentials, Password, ChannelBinding};
 //! use sasl::common::scram::{ScramProvider, Sha1, Sha256};
+//! use sasl::secret;
 //!
 //! const USERNAME: &'static str = "user";
 //! const PASSWORD: &'static str = "pencil";
@@ -39,41 +42,67 @@
 //!
 //! struct MyValidator;
 //!
-//! impl Validator for MyValidator {
-//!     fn validate_credentials(&self, creds: &Credentials) -> Result<Identity, String> {
-//!         if creds.identity != Identity::Username(USERNAME.to_owned()) {
-//!             Err("authentication failure".to_owned())
+//! impl Validator<secret::Plain> for MyValidator {
+//!     fn validate(&self, identity: &Identity, value: &secret::PlainValue) -> Result<(), String> {
+//!         let &secret::PlainValue(ref password) = value;
+//!         if identity != &Identity::Username(USERNAME.to_owned()) {
+//!             Err("authentication failed".to_owned())
 //!         }
-//!         else if creds.secret != Secret::password_plain(PASSWORD) {
-//!             Err("authentication failure".to_owned())
+//!         else if password != PASSWORD {
+//!             Err("authentication failed".to_owned())
 //!         }
 //!         else {
-//!             Ok(creds.identity.clone())
+//!             Ok(())
 //!         }
 //!     }
+//! }
 //!
-//!     fn request_pbkdf2<S: ScramProvider>(&self) -> Result<(Vec<u8>, usize, Vec<u8>), String> {
-//!         Ok( ( SALT.to_vec()
-//!             , ITERATIONS
-//!             , S::derive(&Password::Plain(PASSWORD.to_owned()), &SALT, ITERATIONS)? ) )
+//! impl Provider<secret::Pbkdf2Sha1> for MyValidator {
+//!     fn provide(&self, identity: &Identity) -> Result<secret::Pbkdf2Sha1Value, String> {
+//!         if identity != &Identity::Username(USERNAME.to_owned()) {
+//!             Err("authentication failed".to_owned())
+//!         }
+//!         else {
+//!             let digest = sasl::common::scram::Sha1::derive
+//!                 ( &Password::Plain((PASSWORD.to_owned()))
+//!                 , &SALT[..]
+//!                 , ITERATIONS )?;
+//!             Ok(secret::Pbkdf2Sha1Value {
+//!                 salt: SALT.to_vec(),
+//!                 iterations: ITERATIONS,
+//!                 digest: digest,
+//!             })
+//!         }
 //!     }
 //! }
 //!
-//! let mut mech = ServerPlain::new(MyValidator);
-//! let expected_response = Response::Success(Identity::Username("user".to_owned()), Vec::new());
-//! assert_eq!(mech.respond(b"\0user\0pencil"), Ok(expected_response));
+//! impl_validator_using_provider!(MyValidator, secret::Pbkdf2Sha1);
 //!
-//! let mut mech = ServerPlain::new(MyValidator);
-//! assert_eq!(mech.respond(b"\0user\0marker"), Err("authentication failure".to_owned()));
+//! impl Provider<secret::Pbkdf2Sha256> for MyValidator {
+//!     fn provide(&self, identity: &Identity) -> Result<secret::Pbkdf2Sha256Value, String> {
+//!         if identity != &Identity::Username(USERNAME.to_owned()) {
+//!             Err("authentication failed".to_owned())
+//!         }
+//!         else {
+//!             let digest = sasl::common::scram::Sha256::derive
+//!                 ( &Password::Plain((PASSWORD.to_owned()))
+//!                 , &SALT[..]
+//!                 , ITERATIONS )?;
+//!             Ok(secret::Pbkdf2Sha256Value {
+//!                 salt: SALT.to_vec(),
+//!                 iterations: ITERATIONS,
+//!                 digest: digest,
+//!             })
+//!         }
+//!     }
+//! }
 //!
-//! let creds = Credentials::default()
-//!                         .with_username(USERNAME)
-//!                         .with_password(PASSWORD);
+//! impl_validator_using_provider!(MyValidator, secret::Pbkdf2Sha256);
+
 //!
-//! fn finish<CM, SM, V>(cm: &mut CM, sm: &mut SM) -> Result<Identity, String>
+//! fn finish<CM, SM>(cm: &mut CM, sm: &mut SM) -> Result<Identity, String>
 //!     where CM: ClientMechanism,
-//!           SM: ServerMechanism<V>,
-//!           V: Validator {
+//!           SM: ServerMechanism {
 //!     let init = cm.initial()?;
 //!     println!("C: {}", String::from_utf8_lossy(&init));
 //!     let mut resp = sm.respond(&init)?;
@@ -99,20 +128,32 @@
 //!     }
 //! }
 //!
-//! let mut client_mech = ClientPlain::from_credentials(creds.clone()).unwrap();
-//! let mut server_mech = ServerPlain::new(MyValidator);
+//! fn main() {
+//!     let mut mech = ServerPlain::new(MyValidator);
+//!     let expected_response = Response::Success(Identity::Username("user".to_owned()), Vec::new());
+//!     assert_eq!(mech.respond(b"\0user\0pencil"), Ok(expected_response));
+//!
+//!     let mut mech = ServerPlain::new(MyValidator);
+//!     assert_eq!(mech.respond(b"\0user\0marker"), Err("authentication failed".to_owned()));
 //!
-//! assert_eq!(finish(&mut client_mech, &mut server_mech), Ok(Identity::Username(USERNAME.to_owned())));
+//!     let creds = Credentials::default()
+//!                             .with_username(USERNAME)
+//!                             .with_password(PASSWORD);
+//!     let mut client_mech = ClientPlain::from_credentials(creds.clone()).unwrap();
+//!     let mut server_mech = ServerPlain::new(MyValidator);
 //!
-//! let mut client_mech = ClientScram::<Sha1>::from_credentials(creds.clone()).unwrap();
-//! let mut server_mech = ServerScram::<Sha1, _>::new(MyValidator, ChannelBinding::Unsupported);
+//!     assert_eq!(finish(&mut client_mech, &mut server_mech), Ok(Identity::Username(USERNAME.to_owned())));
 //!
-//! assert_eq!(finish(&mut client_mech, &mut server_mech), Ok(Identity::Username(USERNAME.to_owned())));
+//!     let mut client_mech = ClientScram::<Sha1>::from_credentials(creds.clone()).unwrap();
+//!     let mut server_mech = ServerScram::<Sha1, _>::new(MyValidator, ChannelBinding::Unsupported);
 //!
-//! let mut client_mech = ClientScram::<Sha256>::from_credentials(creds.clone()).unwrap();
-//! let mut server_mech = ServerScram::<Sha256, _>::new(MyValidator, ChannelBinding::Unsupported);
+//!     assert_eq!(finish(&mut client_mech, &mut server_mech), Ok(Identity::Username(USERNAME.to_owned())));
 //!
-//! assert_eq!(finish(&mut client_mech, &mut server_mech), Ok(Identity::Username(USERNAME.to_owned())));
+//!     let mut client_mech = ClientScram::<Sha256>::from_credentials(creds.clone()).unwrap();
+//!     let mut server_mech = ServerScram::<Sha256, _>::new(MyValidator, ChannelBinding::Unsupported);
+//!
+//!     assert_eq!(finish(&mut client_mech, &mut server_mech), Ok(Identity::Username(USERNAME.to_owned())));
+//! }
 //! ```
 //!
 //! # Usage
@@ -131,7 +172,9 @@ extern crate openssl;
 mod error;
 
 pub mod client;
-pub mod common;
+#[macro_use]
 pub mod server;
+pub mod common;
+pub mod secret;
 
 pub use error::Error;

sasl/src/secret.rs 🔗

@@ -0,0 +1,74 @@
+pub trait SecretKind {
+    type Value: PartialEq;
+}
+
+pub trait Pbkdf2SecretValue {
+    fn salt(&self) -> &[u8];
+    fn iterations(&self) -> usize;
+    fn digest(&self) -> &[u8];
+}
+
+pub struct Plain;
+
+#[derive(PartialEq)]
+pub struct PlainValue(pub String);
+
+impl SecretKind for Plain {
+    type Value = PlainValue;
+}
+
+pub struct Pbkdf2Sha1 {
+    pub salt: Vec<u8>,
+    pub iterations: usize,
+}
+
+#[derive(PartialEq)]
+pub struct Pbkdf2Sha1Value {
+    pub salt: Vec<u8>,
+    pub iterations: usize,
+    pub digest: Vec<u8>,
+}
+
+impl SecretKind for Pbkdf2Sha1 {
+    type Value = Pbkdf2Sha1Value;
+}
+
+impl Pbkdf2SecretValue for Pbkdf2Sha1Value {
+    fn salt(&self) -> &[u8] {
+        &self.salt
+    }
+    fn iterations(&self) -> usize {
+        self.iterations
+    }
+    fn digest(&self) -> &[u8] {
+        &self.digest
+    }
+}
+
+pub struct Pbkdf2Sha256 {
+    pub salt: Vec<u8>,
+    pub iterations: usize,
+}
+
+#[derive(PartialEq)]
+pub struct Pbkdf2Sha256Value {
+    pub salt: Vec<u8>,
+    pub iterations: usize,
+    pub digest: Vec<u8>,
+}
+
+impl SecretKind for Pbkdf2Sha256 {
+    type Value = Pbkdf2Sha256Value;
+}
+
+impl Pbkdf2SecretValue for Pbkdf2Sha256Value {
+    fn salt(&self) -> &[u8] {
+        &self.salt
+    }
+    fn iterations(&self) -> usize {
+        self.iterations
+    }
+    fn digest(&self) -> &[u8] {
+        &self.digest
+    }
+}

sasl/src/server/mechanisms/mod.rs 🔗

@@ -0,0 +1,7 @@
+mod plain;
+#[cfg(feature = "scram")]
+mod scram;
+
+pub use self::plain::Plain;
+#[cfg(feature = "scram")]
+pub use self::scram::Scram;

sasl/src/server/mechanisms/plain.rs 🔗

@@ -1,13 +1,40 @@
-use server::Mechanism;
-use common::{Secret, Credentials, Password};
+use common::Identity;
+use secret;
+use server::{Mechanism, Response, Validator};
 
-pub struct Plain {
-    password: String,
+pub struct Plain<V: Validator<secret::Plain>> {
+    validator: V,
 }
 
-impl<V: Validator> Mechanism<V> for Plain {
-    fn name(&self) -> &str { "PLAIN" }
+impl<V: Validator<secret::Plain>> Plain<V> {
+    pub fn new(validator: V) -> Plain<V> {
+        Plain {
+            validator: validator,
+        }
+    }
+}
+
+impl<V: Validator<secret::Plain>> Mechanism for Plain<V> {
+    fn name(&self) -> &str {
+        "PLAIN"
+    }
 
-    fn from_initial_message(validator: &V, msg: &[u8]) -> Result<(Self, String), String> {
+    fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
+        let mut sp = payload.split(|&b| b == 0);
+        sp.next();
+        let username = sp
+            .next()
+            .ok_or_else(|| "no username specified".to_owned())?;
+        let username =
+            String::from_utf8(username.to_vec()).map_err(|_| "error decoding username")?;
+        let password = sp
+            .next()
+            .ok_or_else(|| "no password specified".to_owned())?;
+        let password =
+            String::from_utf8(password.to_vec()).map_err(|_| "error decoding password")?;
+        let ident = Identity::Username(username);
+        self.validator
+            .validate(&ident, &secret::PlainValue(password))?;
+        Ok(Response::Success(ident, Vec::new()))
     }
 }

sasl/src/server/mechanisms/scram.rs 🔗

@@ -0,0 +1,185 @@
+use std::marker::PhantomData;
+
+use base64;
+
+use common::scram::{generate_nonce, ScramProvider};
+use common::{parse_frame, xor, ChannelBinding, Identity};
+use secret;
+use secret::Pbkdf2SecretValue;
+use server::{Mechanism, Provider, Response};
+
+enum ScramState {
+    Init,
+    SentChallenge {
+        initial_client_message: Vec<u8>,
+        initial_server_message: Vec<u8>,
+        gs2_header: Vec<u8>,
+        server_nonce: String,
+        identity: Identity,
+        salted_password: Vec<u8>,
+    },
+    Done,
+}
+
+pub struct Scram<S, P>
+where
+    S: ScramProvider,
+    P: Provider<S::SecretKind>,
+    <S::SecretKind as secret::SecretKind>::Value: secret::Pbkdf2SecretValue,
+{
+    name: String,
+    state: ScramState,
+    channel_binding: ChannelBinding,
+    provider: P,
+    _marker: PhantomData<S>,
+}
+
+impl<S, P> Scram<S, P>
+where
+    S: ScramProvider,
+    P: Provider<S::SecretKind>,
+    <S::SecretKind as secret::SecretKind>::Value: secret::Pbkdf2SecretValue,
+{
+    pub fn new(provider: P, channel_binding: ChannelBinding) -> Scram<S, P> {
+        Scram {
+            name: format!("SCRAM-{}", S::name()),
+            state: ScramState::Init,
+            channel_binding: channel_binding,
+            provider: provider,
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl<S, P> Mechanism for Scram<S, P>
+where
+    S: ScramProvider,
+    P: Provider<S::SecretKind>,
+    <S::SecretKind as secret::SecretKind>::Value: secret::Pbkdf2SecretValue,
+{
+    fn name(&self) -> &str {
+        &self.name
+    }
+
+    fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
+        let next_state;
+        let ret;
+        match self.state {
+            ScramState::Init => {
+                // TODO: really ugly, mostly because parse_frame takes a &[u8] and i don't
+                //       want to double validate utf-8
+                //
+                //       NEED TO CHANGE THIS THOUGH. IT'S AWFUL.
+                let mut commas = 0;
+                let mut idx = 0;
+                for &b in payload {
+                    idx += 1;
+                    if b == 0x2C {
+                        commas += 1;
+                        if commas >= 2 {
+                            break;
+                        }
+                    }
+                }
+                if commas < 2 {
+                    return Err("failed to decode message".to_owned());
+                }
+                let gs2_header = payload[..idx].to_vec();
+                let rest = payload[idx..].to_vec();
+                // TODO: process gs2 header properly, not this ugly stuff
+                match self.channel_binding {
+                    ChannelBinding::None | ChannelBinding::Unsupported => {
+                        // Not supported.
+                        if gs2_header[0] != 0x79 {
+                            // ord("y")
+                            return Err("channel binding not supported".to_owned());
+                        }
+                    }
+                    ref other => {
+                        // Supported.
+                        if gs2_header[0] == 0x79 {
+                            // ord("y")
+                            return Err("channel binding is supported".to_owned());
+                        } else if !other.supports("tls-unique") {
+                            // TODO: grab the data
+                            return Err("channel binding mechanism incorrect".to_owned());
+                        }
+                    }
+                }
+                let frame =
+                    parse_frame(&rest).map_err(|_| "can't decode initial message".to_owned())?;
+                let username = frame.get("n").ok_or_else(|| "no username".to_owned())?;
+                let identity = Identity::Username(username.to_owned());
+                let client_nonce = frame.get("r").ok_or_else(|| "no nonce".to_owned())?;
+                let mut server_nonce = String::new();
+                server_nonce += client_nonce;
+                server_nonce +=
+                    &generate_nonce().map_err(|_| "failed to generate nonce".to_owned())?;
+                let pbkdf2 = self.provider.provide(&identity)?;
+                let mut buf = Vec::new();
+                buf.extend(b"r=");
+                buf.extend(server_nonce.bytes());
+                buf.extend(b",s=");
+                buf.extend(base64::encode(pbkdf2.salt()).bytes());
+                buf.extend(b",i=");
+                buf.extend(pbkdf2.iterations().to_string().bytes());
+                ret = Response::Proceed(buf.clone());
+                next_state = ScramState::SentChallenge {
+                    server_nonce: server_nonce,
+                    identity: identity,
+                    salted_password: pbkdf2.digest().to_vec(),
+                    initial_client_message: rest,
+                    initial_server_message: buf,
+                    gs2_header: gs2_header,
+                };
+            }
+            ScramState::SentChallenge {
+                ref server_nonce,
+                ref identity,
+                ref salted_password,
+                ref gs2_header,
+                ref initial_client_message,
+                ref initial_server_message,
+            } => {
+                let frame = parse_frame(payload).map_err(|_| "can't decode response".to_owned())?;
+                let mut cb_data: Vec<u8> = Vec::new();
+                cb_data.extend(gs2_header);
+                cb_data.extend(self.channel_binding.data());
+                let mut client_final_message_bare = Vec::new();
+                client_final_message_bare.extend(b"c=");
+                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 stored_key = S::hash(&client_key);
+                let mut auth_message = Vec::new();
+                auth_message.extend(initial_client_message);
+                auth_message.extend(b",");
+                auth_message.extend(initial_server_message);
+                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_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 mut buf = Vec::new();
+                buf.extend(b"v=");
+                buf.extend(base64::encode(&server_signature).bytes());
+                ret = Response::Success(identity.clone(), buf);
+                next_state = ScramState::Done;
+            }
+            ScramState::Done => {
+                return Err("sasl session is already over".to_owned());
+            }
+        }
+        self.state = next_state;
+        Ok(ret)
+    }
+}

sasl/src/server/mod.rs 🔗

@@ -1,16 +1,34 @@
-use common::{Credentials, Identity};
-
-#[cfg(feature = "scram")]
-use common::scram::ScramProvider;
+use common::Identity;
+use secret::SecretKind;
+
+#[macro_export]
+macro_rules! impl_validator_using_provider {
+    ( $type:ty, $secret:ty ) => {
+        impl $crate::server::Validator<$secret> for $type {
+            fn validate(
+                &self,
+                identity: &$crate::common::Identity,
+                value: &<$secret as sasl::secret::SecretKind>::Value,
+            ) -> Result<(), String> {
+                if &(self as &$crate::server::Provider<$secret>).provide(identity)? == value {
+                    Ok(())
+                } else {
+                    Err("authentication failure".to_owned())
+                }
+            }
+        }
+    };
+}
 
-pub trait Validator {
-    fn validate_credentials(&self, credentials: &Credentials) -> Result<Identity, String>;
+pub trait Provider<S: SecretKind>: Validator<S> {
+    fn provide(&self, identity: &Identity) -> Result<S::Value, String>;
+}
 
-    #[cfg(feature = "scram")]
-    fn request_pbkdf2<S: ScramProvider>(&self) -> Result<(Vec<u8>, usize, Vec<u8>), String>;
+pub trait Validator<S: SecretKind> {
+    fn validate(&self, identity: &Identity, value: &S::Value) -> Result<(), String>;
 }
 
-pub trait Mechanism<V: Validator> {
+pub trait Mechanism {
     fn name(&self) -> &str;
     fn respond(&mut self, payload: &[u8]) -> Result<Response, String>;
 }
@@ -21,225 +39,4 @@ pub enum Response {
     Proceed(Vec<u8>),
 }
 
-pub mod mechanisms {
-    mod plain {
-        use common::{ChannelBinding, Credentials, Identity, Secret};
-        use server::{Mechanism, Response, Validator};
-
-        pub struct Plain<V: Validator> {
-            validator: V,
-        }
-
-        impl<V: Validator> Plain<V> {
-            pub fn new(validator: V) -> Plain<V> {
-                Plain {
-                    validator: validator,
-                }
-            }
-        }
-
-        impl<V: Validator> Mechanism<V> for Plain<V> {
-            fn name(&self) -> &str {
-                "PLAIN"
-            }
-
-            fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
-                let mut sp = payload.split(|&b| b == 0);
-                sp.next();
-                let username = sp
-                    .next()
-                    .ok_or_else(|| "no username specified".to_owned())?;
-                let username =
-                    String::from_utf8(username.to_vec()).map_err(|_| "error decoding username")?;
-                let password = sp
-                    .next()
-                    .ok_or_else(|| "no password specified".to_owned())?;
-                let password =
-                    String::from_utf8(password.to_vec()).map_err(|_| "error decoding password")?;
-                let creds = Credentials {
-                    identity: Identity::Username(username),
-                    secret: Secret::password_plain(password),
-                    channel_binding: ChannelBinding::None,
-                };
-                let ret = self.validator.validate_credentials(&creds)?;
-                Ok(Response::Success(ret, Vec::new()))
-            }
-        }
-    }
-
-    #[cfg(feature = "scram")]
-    mod scram {
-        use std::marker::PhantomData;
-
-        use base64;
-
-        use common::scram::{generate_nonce, ScramProvider};
-        use common::{parse_frame, xor, ChannelBinding, Credentials, Identity, Secret};
-        use server::{Mechanism, Response, Validator};
-
-        enum ScramState {
-            Init,
-            SentChallenge {
-                initial_client_message: Vec<u8>,
-                initial_server_message: Vec<u8>,
-                gs2_header: Vec<u8>,
-                server_nonce: String,
-                username: String,
-                salted_password: Vec<u8>,
-            },
-            Done,
-        }
-
-        pub struct Scram<S: ScramProvider, V: Validator> {
-            name: String,
-            state: ScramState,
-            channel_binding: ChannelBinding,
-            validator: V,
-            _marker: PhantomData<S>,
-        }
-
-        impl<S: ScramProvider, V: Validator> Scram<S, V> {
-            pub fn new(validator: V, channel_binding: ChannelBinding) -> Scram<S, V> {
-                Scram {
-                    name: format!("SCRAM-{}", S::name()),
-                    state: ScramState::Init,
-                    channel_binding: channel_binding,
-                    validator: validator,
-                    _marker: PhantomData,
-                }
-            }
-        }
-
-        impl<S: ScramProvider, V: Validator> Mechanism<V> for Scram<S, V> {
-            fn name(&self) -> &str {
-                &self.name
-            }
-
-            fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
-                let next_state;
-                let ret;
-                match self.state {
-                    ScramState::Init => {
-                        // TODO: really ugly, mostly because parse_frame takes a &[u8] and i don't
-                        //       want to double validate utf-8
-                        //
-                        //       NEED TO CHANGE THIS THOUGH. IT'S AWFUL.
-                        let mut commas = 0;
-                        let mut idx = 0;
-                        for &b in payload {
-                            idx += 1;
-                            if b == 0x2C {
-                                commas += 1;
-                                if commas >= 2 {
-                                    break;
-                                }
-                            }
-                        }
-                        if commas < 2 {
-                            return Err("failed to decode message".to_owned());
-                        }
-                        let gs2_header = payload[..idx].to_vec();
-                        let rest = payload[idx..].to_vec();
-                        // TODO: process gs2 header properly, not this ugly stuff
-                        match self.channel_binding {
-                            ChannelBinding::None | ChannelBinding::Unsupported => {
-                                // Not supported.
-                                if gs2_header[0] != 0x79 {
-                                    // ord("y")
-                                    return Err("channel binding not supported".to_owned());
-                                }
-                            }
-                            ref other => {
-                                // Supported.
-                                if gs2_header[0] == 0x79 {
-                                    // ord("y")
-                                    return Err("channel binding is supported".to_owned());
-                                } else if !other.supports("tls-unique") {
-                                    // TODO: grab the data
-                                    return Err("channel binding mechanism incorrect".to_owned());
-                                }
-                            }
-                        }
-                        let frame = parse_frame(&rest)
-                            .map_err(|_| "can't decode initial message".to_owned())?;
-                        let username = frame.get("n").ok_or_else(|| "no username".to_owned())?;
-                        let client_nonce = frame.get("r").ok_or_else(|| "no nonce".to_owned())?;
-                        let mut server_nonce = String::new();
-                        server_nonce += client_nonce;
-                        server_nonce +=
-                            &generate_nonce().map_err(|_| "failed to generate nonce".to_owned())?;
-                        let (salt, iterations, data) = self.validator.request_pbkdf2::<S>()?;
-                        let mut buf = Vec::new();
-                        buf.extend(b"r=");
-                        buf.extend(server_nonce.bytes());
-                        buf.extend(b",s=");
-                        buf.extend(base64::encode(&salt).bytes());
-                        buf.extend(b",i=");
-                        buf.extend(iterations.to_string().bytes());
-                        ret = Response::Proceed(buf.clone());
-                        next_state = ScramState::SentChallenge {
-                            server_nonce: server_nonce,
-                            username: username.to_owned(),
-                            salted_password: data,
-                            initial_client_message: rest,
-                            initial_server_message: buf,
-                            gs2_header: gs2_header,
-                        };
-                    }
-                    ScramState::SentChallenge {
-                        server_nonce: ref server_nonce,
-                        username: ref username,
-                        salted_password: ref salted_password,
-                        gs2_header: ref gs2_header,
-                        initial_client_message: ref initial_client_message,
-                        initial_server_message: ref initial_server_message,
-                    } => {
-                        let frame =
-                            parse_frame(payload).map_err(|_| "can't decode response".to_owned())?;
-                        let mut cb_data: Vec<u8> = Vec::new();
-                        cb_data.extend(gs2_header);
-                        cb_data.extend(self.channel_binding.data());
-                        let mut client_final_message_bare = Vec::new();
-                        client_final_message_bare.extend(b"c=");
-                        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 stored_key = S::hash(&client_key);
-                        let mut auth_message = Vec::new();
-                        auth_message.extend(initial_client_message);
-                        auth_message.extend(b",");
-                        auth_message.extend(initial_server_message);
-                        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_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 mut buf = Vec::new();
-                        buf.extend(b"v=");
-                        buf.extend(base64::encode(&server_signature).bytes());
-                        ret = Response::Success(Identity::Username(username.to_owned()), buf);
-                        next_state = ScramState::Done;
-                    }
-                    ScramState::Done => {
-                        return Err("sasl session is already over".to_owned());
-                    }
-                }
-                self.state = next_state;
-                Ok(ret)
-            }
-        }
-    }
-
-    pub use self::plain::Plain;
-    #[cfg(feature = "scram")]
-    pub use self::scram::Scram;
-}
+pub mod mechanisms;