Detailed changes
@@ -1,6 +1,6 @@
//! Provides the SASL "ANONYMOUS" mechanism.
-use crate::client::Mechanism;
+use crate::client::{Mechanism, MechanismError};
use crate::common::{Credentials, Secret};
/// A struct for the SASL ANONYMOUS mechanism.
@@ -21,11 +21,11 @@ impl Mechanism for Anonymous {
"ANONYMOUS"
}
- fn from_credentials(credentials: Credentials) -> Result<Anonymous, String> {
+ fn from_credentials(credentials: Credentials) -> Result<Anonymous, MechanismError> {
if let Secret::None = credentials.secret {
Ok(Anonymous)
} else {
- Err("the anonymous sasl mechanism requires no credentials".to_owned())
+ Err(MechanismError::AnonymousRequiresNoCredentials)
}
}
}
@@ -1,6 +1,6 @@
//! Provides the SASL "PLAIN" mechanism.
-use crate::client::Mechanism;
+use crate::client::{Mechanism, MechanismError};
use crate::common::{Credentials, Identity, Password, Secret};
/// A struct for the SASL PLAIN mechanism.
@@ -27,15 +27,15 @@ impl Mechanism for Plain {
"PLAIN"
}
- fn from_credentials(credentials: Credentials) -> Result<Plain, String> {
+ fn from_credentials(credentials: Credentials) -> Result<Plain, MechanismError> {
if let Secret::Password(Password::Plain(password)) = credentials.secret {
if let Identity::Username(username) = credentials.identity {
Ok(Plain::new(username, password))
} else {
- Err("PLAIN requires a username".to_owned())
+ Err(MechanismError::PlainRequiresUsername)
}
} else {
- Err("PLAIN requires a plaintext password".to_owned())
+ Err(MechanismError::PlainRequiresPlaintextPassword)
}
}
@@ -2,7 +2,7 @@
use base64;
-use crate::client::Mechanism;
+use crate::client::{Mechanism, MechanismError};
use crate::common::scram::{generate_nonce, ScramProvider};
use crate::common::{parse_frame, xor, ChannelBinding, Credentials, Identity, Password, Secret};
@@ -80,16 +80,16 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
&self.name
}
- fn from_credentials(credentials: Credentials) -> Result<Scram<S>, String> {
+ fn from_credentials(credentials: Credentials) -> Result<Scram<S>, MechanismError> {
if let Secret::Password(password) = credentials.secret {
if let Identity::Username(username) = credentials.identity {
Scram::new(username, password, credentials.channel_binding)
- .map_err(|_| "can't generate nonce".to_owned())
+ .map_err(|_| MechanismError::CannotGenerateNonce)
} else {
- Err("SCRAM requires a username".to_owned())
+ Err(MechanismError::ScramRequiresUsername)
}
} else {
- Err("SCRAM requires a password".to_owned())
+ Err(MechanismError::ScramRequiresPassword)
}
}
@@ -111,7 +111,7 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
data
}
- fn response(&mut self, challenge: &[u8]) -> Result<Vec<u8>, String> {
+ fn response(&mut self, challenge: &[u8]) -> Result<Vec<u8>, MechanismError> {
let next_state;
let ret;
match self.state {
@@ -120,13 +120,13 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
ref gs2_header,
} => {
let frame =
- parse_frame(challenge).map_err(|_| "can't decode challenge".to_owned())?;
+ parse_frame(challenge).map_err(|_| MechanismError::CannotDecodeChallenge)?;
let server_nonce = frame.get("r");
let salt = frame.get("s").and_then(|v| base64::decode(v).ok());
let iterations = frame.get("i").and_then(|v| v.parse().ok());
- let server_nonce = server_nonce.ok_or_else(|| "no server nonce".to_owned())?;
- let salt = salt.ok_or_else(|| "no server salt".to_owned())?;
- let iterations = iterations.ok_or_else(|| "no server iterations".to_owned())?;
+ let server_nonce = server_nonce.ok_or_else(|| MechanismError::NoServerNonce)?;
+ let salt = salt.ok_or_else(|| MechanismError::NoServerSalt)?;
+ let iterations = iterations.ok_or_else(|| MechanismError::NoServerIterations)?;
// TODO: SASLprep
let mut client_final_message_bare = Vec::new();
client_final_message_bare.extend(b"c=");
@@ -159,15 +159,15 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
ret = client_final_message;
}
_ => {
- return Err("not in the right state to receive this response".to_owned());
+ return Err(MechanismError::InvalidState);
}
}
self.state = next_state;
Ok(ret)
}
- fn success(&mut self, data: &[u8]) -> Result<(), String> {
- let frame = parse_frame(data).map_err(|_| "can't decode success response".to_owned())?;
+ fn success(&mut self, data: &[u8]) -> Result<(), MechanismError> {
+ let frame = parse_frame(data).map_err(|_| MechanismError::CannotDecodeSuccessResponse)?;
match self.state {
ScramState::GotServerData {
ref server_signature,
@@ -176,13 +176,13 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
if sig == *server_signature {
Ok(())
} else {
- Err("invalid signature in success response".to_owned())
+ Err(MechanismError::InvalidSignatureInSuccessResponse)
}
} else {
- Err("no signature in success response".to_owned())
+ Err(MechanismError::NoSignatureInSuccessResponse)
}
}
- _ => Err("not in the right state to get a success response".to_owned()),
+ _ => Err(MechanismError::InvalidState),
}
}
}
@@ -1,4 +1,80 @@
+use crate::common::scram::DeriveError;
use crate::common::Credentials;
+use hmac::crypto_mac::InvalidKeyLength;
+use std::fmt;
+
+#[derive(Debug, PartialEq)]
+pub enum MechanismError {
+ AnonymousRequiresNoCredentials,
+
+ PlainRequiresUsername,
+ PlainRequiresPlaintextPassword,
+
+ CannotGenerateNonce,
+ ScramRequiresUsername,
+ ScramRequiresPassword,
+
+ CannotDecodeChallenge,
+ NoServerNonce,
+ NoServerSalt,
+ NoServerIterations,
+ DeriveError(DeriveError),
+ InvalidKeyLength(InvalidKeyLength),
+ InvalidState,
+
+ CannotDecodeSuccessResponse,
+ InvalidSignatureInSuccessResponse,
+ NoSignatureInSuccessResponse,
+}
+
+impl From<DeriveError> for MechanismError {
+ fn from(err: DeriveError) -> MechanismError {
+ MechanismError::DeriveError(err)
+ }
+}
+
+impl From<InvalidKeyLength> for MechanismError {
+ fn from(err: InvalidKeyLength) -> MechanismError {
+ MechanismError::InvalidKeyLength(err)
+ }
+}
+
+impl fmt::Display for MechanismError {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ fmt,
+ "{}",
+ match self {
+ MechanismError::AnonymousRequiresNoCredentials =>
+ "ANONYMOUS mechanism requires no credentials",
+
+ MechanismError::PlainRequiresUsername => "PLAIN requires a username",
+ MechanismError::PlainRequiresPlaintextPassword =>
+ "PLAIN requires a plaintext password",
+
+ MechanismError::CannotGenerateNonce => "can't generate nonce",
+ MechanismError::ScramRequiresUsername => "SCRAM requires a username",
+ MechanismError::ScramRequiresPassword => "SCRAM requires a password",
+
+ MechanismError::CannotDecodeChallenge => "can't decode challenge",
+ MechanismError::NoServerNonce => "no server nonce",
+ MechanismError::NoServerSalt => "no server salt",
+ MechanismError::NoServerIterations => "no server iterations",
+ MechanismError::DeriveError(err) => return write!(fmt, "derive error: {}", err),
+ MechanismError::InvalidKeyLength(err) =>
+ return write!(fmt, "invalid key length: {}", err),
+ MechanismError::InvalidState => "not in the right state to receive this response",
+
+ MechanismError::CannotDecodeSuccessResponse => "can't decode success response",
+ MechanismError::InvalidSignatureInSuccessResponse =>
+ "invalid signature in success response",
+ MechanismError::NoSignatureInSuccessResponse => "no signature in success response",
+ }
+ )
+ }
+}
+
+impl std::error::Error for MechanismError {}
/// A trait which defines SASL mechanisms.
pub trait Mechanism {
@@ -6,7 +82,7 @@ pub trait Mechanism {
fn name(&self) -> &str;
/// Creates this mechanism from `Credentials`.
- fn from_credentials(credentials: Credentials) -> Result<Self, String>
+ fn from_credentials(credentials: Credentials) -> Result<Self, MechanismError>
where
Self: Sized;
@@ -16,12 +92,12 @@ pub trait Mechanism {
}
/// Creates a response to the SASL challenge.
- fn response(&mut self, _challenge: &[u8]) -> Result<Vec<u8>, String> {
+ fn response(&mut self, _challenge: &[u8]) -> Result<Vec<u8>, MechanismError> {
Ok(Vec::new())
}
/// Verifies the server success response, if there is one.
- fn success(&mut self, _data: &[u8]) -> Result<(), String> {
+ fn success(&mut self, _data: &[u8]) -> Result<(), MechanismError> {
Ok(())
}
}
@@ -1,4 +1,4 @@
-use hmac::{Hmac, Mac};
+use hmac::{crypto_mac::InvalidKeyLength, Hmac, Mac};
use pbkdf2::pbkdf2;
use rand_os::{
rand_core::{Error as RngError, RngCore},
@@ -21,6 +21,29 @@ pub fn generate_nonce() -> Result<String, RngError> {
Ok(base64::encode(&data))
}
+#[derive(Debug, PartialEq)]
+pub enum DeriveError {
+ IncompatibleHashingMethod(String, String),
+ IncorrectSalt,
+ IncompatibleIterationCount(usize, usize),
+}
+
+impl std::fmt::Display for DeriveError {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ DeriveError::IncompatibleHashingMethod(one, two) => {
+ write!(fmt, "incompatible hashing method, {} is not {}", one, two)
+ }
+ DeriveError::IncorrectSalt => write!(fmt, "incorrect salt"),
+ DeriveError::IncompatibleIterationCount(one, two) => {
+ write!(fmt, "incompatible iteration count, {} is not {}", one, two)
+ }
+ }
+ }
+}
+
+impl std::error::Error for DeriveError {}
+
/// A trait which defines the needed methods for SCRAM.
pub trait ScramProvider {
/// The kind of secret this `ScramProvider` requires.
@@ -33,10 +56,10 @@ pub trait ScramProvider {
fn hash(data: &[u8]) -> Vec<u8>;
/// A function which performs an HMAC using the hash function.
- fn hmac(data: &[u8], key: &[u8]) -> Result<Vec<u8>, String>;
+ fn hmac(data: &[u8], key: &[u8]) -> Result<Vec<u8>, InvalidKeyLength>;
/// A function which does PBKDF2 key derivation using the hash function.
- fn derive(data: &Password, salt: &[u8], iterations: usize) -> Result<Vec<u8>, String>;
+ fn derive(data: &Password, salt: &[u8], iterations: usize) -> Result<Vec<u8>, DeriveError>;
}
/// A `ScramProvider` which provides SCRAM-SHA-1 and SCRAM-SHA-1-PLUS
@@ -56,12 +79,9 @@ impl ScramProvider for Sha1 {
vec
}
- fn hmac(data: &[u8], key: &[u8]) -> Result<Vec<u8>, String> {
+ fn hmac(data: &[u8], key: &[u8]) -> Result<Vec<u8>, InvalidKeyLength> {
type HmacSha1 = Hmac<Sha1_hash>;
- let mut mac = match HmacSha1::new_varkey(key) {
- Ok(mac) => mac,
- Err(err) => return Err(format!("{}", err)),
- };
+ let mut mac = HmacSha1::new_varkey(key)?;
mac.input(data);
let result = mac.result();
let mut vec = Vec::with_capacity(Sha1_hash::output_size());
@@ -69,7 +89,7 @@ impl ScramProvider for Sha1 {
Ok(vec)
}
- fn derive(password: &Password, salt: &[u8], iterations: usize) -> Result<Vec<u8>, String> {
+ fn derive(password: &Password, salt: &[u8], iterations: usize) -> Result<Vec<u8>, DeriveError> {
match *password {
Password::Plain(ref plain) => {
let mut result = vec![0; 20];
@@ -83,17 +103,16 @@ impl ScramProvider for Sha1 {
ref data,
} => {
if method != Self::name() {
- Err(format!(
- "incompatible hashing method, {} is not {}",
- method,
- Self::name()
+ Err(DeriveError::IncompatibleHashingMethod(
+ method.to_string(),
+ Self::name().to_string(),
))
} else if my_salt == &salt {
- Err(format!("incorrect salt"))
+ Err(DeriveError::IncorrectSalt)
} else if my_iterations == iterations {
- Err(format!(
- "incompatible iteration count, {} is not {}",
- my_iterations, iterations
+ Err(DeriveError::IncompatibleIterationCount(
+ my_iterations,
+ iterations,
))
} else {
Ok(data.to_vec())
@@ -120,12 +139,9 @@ impl ScramProvider for Sha256 {
vec
}
- fn hmac(data: &[u8], key: &[u8]) -> Result<Vec<u8>, String> {
+ fn hmac(data: &[u8], key: &[u8]) -> Result<Vec<u8>, InvalidKeyLength> {
type HmacSha256 = Hmac<Sha256_hash>;
- let mut mac = match HmacSha256::new_varkey(key) {
- Ok(mac) => mac,
- Err(err) => return Err(format!("{}", err)),
- };
+ let mut mac = HmacSha256::new_varkey(key)?;
mac.input(data);
let result = mac.result();
let mut vec = Vec::with_capacity(Sha256_hash::output_size());
@@ -133,7 +149,7 @@ impl ScramProvider for Sha256 {
Ok(vec)
}
- fn derive(password: &Password, salt: &[u8], iterations: usize) -> Result<Vec<u8>, String> {
+ fn derive(password: &Password, salt: &[u8], iterations: usize) -> Result<Vec<u8>, DeriveError> {
match *password {
Password::Plain(ref plain) => {
let mut result = vec![0; 32];
@@ -147,17 +163,16 @@ impl ScramProvider for Sha256 {
ref data,
} => {
if method != Self::name() {
- Err(format!(
- "incompatible hashing method, {} is not {}",
- method,
- Self::name()
+ Err(DeriveError::IncompatibleHashingMethod(
+ method.to_string(),
+ Self::name().to_string(),
))
} else if my_salt == &salt {
- Err(format!("incorrect salt"))
+ Err(DeriveError::IncorrectSalt)
} else if my_iterations == iterations {
- Err(format!(
- "incompatible iteration count, {} is not {}",
- my_iterations, iterations
+ Err(DeriveError::IncompatibleIterationCount(
+ my_iterations,
+ iterations,
))
} else {
Ok(data.to_vec())
@@ -17,7 +17,7 @@
//!
//! let mut mechanism = Plain::from_credentials(creds).unwrap();
//!
-//! let initial_data = mechanism.initial().unwrap();
+//! let initial_data = mechanism.initial();
//!
//! assert_eq!(initial_data, b"\0user\0pencil");
//! ```
@@ -28,8 +28,9 @@
//! #[macro_use] extern crate sasl;
//!
//! use sasl::server::{Validator, Provider, Mechanism as ServerMechanism, Response};
+//! use sasl::server::{ValidatorError, ProviderError, MechanismError as ServerMechanismError};
//! use sasl::server::mechanisms::{Plain as ServerPlain, Scram as ServerScram};
-//! use sasl::client::Mechanism as ClientMechanism;
+//! use sasl::client::{Mechanism as ClientMechanism, MechanismError as ClientMechanismError};
//! use sasl::client::mechanisms::{Plain as ClientPlain, Scram as ClientScram};
//! use sasl::common::{Identity, Credentials, Password, ChannelBinding};
//! use sasl::common::scram::{ScramProvider, Sha1, Sha256};
@@ -43,13 +44,13 @@
//! struct MyValidator;
//!
//! impl Validator<secret::Plain> for MyValidator {
-//! fn validate(&self, identity: &Identity, value: &secret::Plain) -> Result<(), String> {
+//! fn validate(&self, identity: &Identity, value: &secret::Plain) -> Result<(), ValidatorError> {
//! let &secret::Plain(ref password) = value;
//! if identity != &Identity::Username(USERNAME.to_owned()) {
-//! Err("authentication failed".to_owned())
+//! Err(ValidatorError::AuthenticationFailed)
//! }
//! else if password != PASSWORD {
-//! Err("authentication failed".to_owned())
+//! Err(ValidatorError::AuthenticationFailed)
//! }
//! else {
//! Ok(())
@@ -58,9 +59,9 @@
//! }
//!
//! impl Provider<secret::Pbkdf2Sha1> for MyValidator {
-//! fn provide(&self, identity: &Identity) -> Result<secret::Pbkdf2Sha1, String> {
+//! fn provide(&self, identity: &Identity) -> Result<secret::Pbkdf2Sha1, ProviderError> {
//! if identity != &Identity::Username(USERNAME.to_owned()) {
-//! Err("authentication failed".to_owned())
+//! Err(ProviderError::AuthenticationFailed)
//! }
//! else {
//! let digest = sasl::common::scram::Sha1::derive
@@ -79,9 +80,9 @@
//! impl_validator_using_provider!(MyValidator, secret::Pbkdf2Sha1);
//!
//! impl Provider<secret::Pbkdf2Sha256> for MyValidator {
-//! fn provide(&self, identity: &Identity) -> Result<secret::Pbkdf2Sha256, String> {
+//! fn provide(&self, identity: &Identity) -> Result<secret::Pbkdf2Sha256, ProviderError> {
//! if identity != &Identity::Username(USERNAME.to_owned()) {
-//! Err("authentication failed".to_owned())
+//! Err(ProviderError::AuthenticationFailed)
//! }
//! else {
//! let digest = sasl::common::scram::Sha256::derive
@@ -99,10 +100,28 @@
//!
//! impl_validator_using_provider!(MyValidator, secret::Pbkdf2Sha256);
//!
-//! fn finish<CM, SM>(cm: &mut CM, sm: &mut SM) -> Result<Identity, String>
+//! #[derive(Debug, PartialEq)]
+//! enum MechanismError {
+//! Client(ClientMechanismError),
+//! Server(ServerMechanismError),
+//! }
+//!
+//! impl From<ClientMechanismError> for MechanismError {
+//! fn from(err: ClientMechanismError) -> MechanismError {
+//! MechanismError::Client(err)
+//! }
+//! }
+//!
+//! impl From<ServerMechanismError> for MechanismError {
+//! fn from(err: ServerMechanismError) -> MechanismError {
+//! MechanismError::Server(err)
+//! }
+//! }
+//!
+//! fn finish<CM, SM>(cm: &mut CM, sm: &mut SM) -> Result<Identity, MechanismError>
//! where CM: ClientMechanism,
//! SM: ServerMechanism {
-//! let init = cm.initial()?;
+//! let init = cm.initial();
//! println!("C: {}", String::from_utf8_lossy(&init));
//! let mut resp = sm.respond(&init)?;
//! loop {
@@ -133,7 +152,7 @@
//! 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!(mech.respond(b"\0user\0marker"), Err(ServerMechanismError::ValidatorError(ValidatorError::AuthenticationFailed)));
//!
//! let creds = Credentials::default()
//! .with_username(USERNAME)
@@ -1,3 +1,6 @@
+#[cfg(feature = "scram")]
+use crate::common::scram::DeriveError;
+
pub trait Secret {}
pub trait Pbkdf2Secret {
@@ -20,7 +23,11 @@ pub struct Pbkdf2Sha1 {
impl Pbkdf2Sha1 {
#[cfg(feature = "scram")]
- pub fn derive(password: &str, salt: &[u8], iterations: usize) -> Result<Pbkdf2Sha1, String> {
+ pub fn derive(
+ password: &str,
+ salt: &[u8],
+ iterations: usize,
+ ) -> Result<Pbkdf2Sha1, DeriveError> {
use crate::common::scram::{ScramProvider, Sha1};
use crate::common::Password;
let digest = Sha1::derive(&Password::Plain(password.to_owned()), salt, iterations)?;
@@ -55,7 +62,11 @@ pub struct Pbkdf2Sha256 {
impl Pbkdf2Sha256 {
#[cfg(feature = "scram")]
- pub fn derive(password: &str, salt: &[u8], iterations: usize) -> Result<Pbkdf2Sha256, String> {
+ pub fn derive(
+ password: &str,
+ salt: &[u8],
+ iterations: usize,
+ ) -> Result<Pbkdf2Sha256, DeriveError> {
use crate::common::scram::{ScramProvider, Sha256};
use crate::common::Password;
let digest = Sha256::derive(&Password::Plain(password.to_owned()), salt, iterations)?;
@@ -1,6 +1,6 @@
use crate::common::Identity;
use crate::secret;
-use crate::server::{Mechanism, Response, Validator};
+use crate::server::{Mechanism, MechanismError, Response, Validator};
pub struct Plain<V: Validator<secret::Plain>> {
validator: V,
@@ -19,19 +19,19 @@ impl<V: Validator<secret::Plain>> Mechanism for Plain<V> {
"PLAIN"
}
- fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
+ fn respond(&mut self, payload: &[u8]) -> Result<Response, MechanismError> {
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")?;
+ .ok_or_else(|| MechanismError::NoUsernameSpecified)?;
+ let username = String::from_utf8(username.to_vec())
+ .map_err(|_| MechanismError::ErrorDecodingUsername)?;
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")?;
+ .ok_or_else(|| MechanismError::NoPasswordSpecified)?;
+ let password = String::from_utf8(password.to_vec())
+ .map_err(|_| MechanismError::ErrorDecodingPassword)?;
let ident = Identity::Username(username);
self.validator.validate(&ident, &secret::Plain(password))?;
Ok(Response::Success(ident, Vec::new()))
@@ -6,7 +6,7 @@ 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};
+use crate::server::{Mechanism, MechanismError, Provider, Response};
enum ScramState {
Init,
@@ -61,7 +61,7 @@ where
&self.name
}
- fn respond(&mut self, payload: &[u8]) -> Result<Response, String> {
+ fn respond(&mut self, payload: &[u8]) -> Result<Response, MechanismError> {
let next_state;
let ret;
match self.state {
@@ -82,7 +82,7 @@ where
}
}
if commas < 2 {
- return Err("failed to decode message".to_owned());
+ return Err(MechanismError::FailedToDecodeMessage);
}
let gs2_header = payload[..idx].to_vec();
let rest = payload[idx..].to_vec();
@@ -92,29 +92,29 @@ where
// Not supported.
if gs2_header[0] != 0x79 {
// ord("y")
- return Err("channel binding not supported".to_owned());
+ return Err(MechanismError::ChannelBindingNotSupported);
}
}
ref other => {
// Supported.
if gs2_header[0] == 0x79 {
// ord("y")
- return Err("channel binding is supported".to_owned());
+ return Err(MechanismError::ChannelBindingIsSupported);
} else if !other.supports("tls-unique") {
// TODO: grab the data
- return Err("channel binding mechanism incorrect".to_owned());
+ return Err(MechanismError::ChannelBindingMechanismIncorrect);
}
}
}
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())?;
+ parse_frame(&rest).map_err(|_| MechanismError::CannotDecodeInitialMessage)?;
+ let username = frame.get("n").ok_or_else(|| MechanismError::NoUsername)?;
let identity = Identity::Username(username.to_owned());
- let client_nonce = frame.get("r").ok_or_else(|| "no nonce".to_owned())?;
+ let client_nonce = frame.get("r").ok_or_else(|| MechanismError::NoNonce)?;
let mut server_nonce = String::new();
server_nonce += client_nonce;
server_nonce +=
- &generate_nonce().map_err(|_| "failed to generate nonce".to_owned())?;
+ &generate_nonce().map_err(|_| MechanismError::FailedToGenerateNonce)?;
let pbkdf2 = self.provider.provide(&identity)?;
let mut buf = Vec::new();
buf.extend(b"r=");
@@ -141,7 +141,8 @@ where
ref initial_client_message,
ref initial_server_message,
} => {
- let frame = parse_frame(payload).map_err(|_| "can't decode response".to_owned())?;
+ let frame =
+ parse_frame(payload).map_err(|_| MechanismError::CannotDecodeResponse)?;
let mut cb_data: Vec<u8> = Vec::new();
cb_data.extend(gs2_header);
cb_data.extend(self.channel_binding.data());
@@ -161,11 +162,11 @@ where
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 = frame.get("p").ok_or_else(|| MechanismError::NoProof)?;
let sent_proof =
- base64::decode(sent_proof).map_err(|_| "can't decode proof".to_owned())?;
+ base64::decode(sent_proof).map_err(|_| MechanismError::CannotDecodeProof)?;
if client_proof != sent_proof {
- return Err("authentication failed".to_owned());
+ return Err(MechanismError::AuthenticationFailed);
}
let server_signature = S::hmac(&auth_message, &server_key)?;
let mut buf = Vec::new();
@@ -175,7 +176,7 @@ where
next_state = ScramState::Done;
}
ScramState::Done => {
- return Err("sasl session is already over".to_owned());
+ return Err(MechanismError::SaslSessionAlreadyOver);
}
}
self.state = next_state;
@@ -1,5 +1,7 @@
+use crate::common::scram::DeriveError;
use crate::common::Identity;
use crate::secret::Secret;
+use std::fmt;
#[macro_export]
macro_rules! impl_validator_using_provider {
@@ -9,11 +11,11 @@ macro_rules! impl_validator_using_provider {
&self,
identity: &$crate::common::Identity,
value: &$secret,
- ) -> Result<(), String> {
+ ) -> Result<(), ValidatorError> {
if &(self as &$crate::server::Provider<$secret>).provide(identity)? == value {
Ok(())
} else {
- Err("authentication failure".to_owned())
+ Err(ValidatorError::AuthenticationFailed)
}
}
}
@@ -21,16 +23,150 @@ macro_rules! impl_validator_using_provider {
}
pub trait Provider<S: Secret>: Validator<S> {
- fn provide(&self, identity: &Identity) -> Result<S, String>;
+ fn provide(&self, identity: &Identity) -> Result<S, ProviderError>;
}
pub trait Validator<S: Secret> {
- fn validate(&self, identity: &Identity, value: &S) -> Result<(), String>;
+ fn validate(&self, identity: &Identity, value: &S) -> Result<(), ValidatorError>;
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ProviderError {
+ AuthenticationFailed,
+ DeriveError(DeriveError),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ValidatorError {
+ AuthenticationFailed,
+ ProviderError(ProviderError),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum MechanismError {
+ NoUsernameSpecified,
+ ErrorDecodingUsername,
+ NoPasswordSpecified,
+ ErrorDecodingPassword,
+ ValidatorError(ValidatorError),
+
+ FailedToDecodeMessage,
+ ChannelBindingNotSupported,
+ ChannelBindingIsSupported,
+ ChannelBindingMechanismIncorrect,
+ CannotDecodeInitialMessage,
+ NoUsername,
+ NoNonce,
+ FailedToGenerateNonce,
+ ProviderError(ProviderError),
+
+ CannotDecodeResponse,
+ InvalidKeyLength(hmac::crypto_mac::InvalidKeyLength),
+ NoProof,
+ CannotDecodeProof,
+ AuthenticationFailed,
+ SaslSessionAlreadyOver,
+}
+
+impl From<DeriveError> for ProviderError {
+ fn from(err: DeriveError) -> ProviderError {
+ ProviderError::DeriveError(err)
+ }
+}
+
+impl From<ProviderError> for ValidatorError {
+ fn from(err: ProviderError) -> ValidatorError {
+ ValidatorError::ProviderError(err)
+ }
+}
+
+impl From<ProviderError> for MechanismError {
+ fn from(err: ProviderError) -> MechanismError {
+ MechanismError::ProviderError(err)
+ }
+}
+
+impl From<ValidatorError> for MechanismError {
+ fn from(err: ValidatorError) -> MechanismError {
+ MechanismError::ValidatorError(err)
+ }
+}
+
+impl From<hmac::crypto_mac::InvalidKeyLength> for MechanismError {
+ fn from(err: hmac::crypto_mac::InvalidKeyLength) -> MechanismError {
+ MechanismError::InvalidKeyLength(err)
+ }
+}
+
+impl fmt::Display for ProviderError {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "provider error")
+ }
+}
+
+impl fmt::Display for ValidatorError {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "validator error")
+ }
+}
+
+impl fmt::Display for MechanismError {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ MechanismError::NoUsernameSpecified => write!(fmt, "no username specified"),
+ MechanismError::ErrorDecodingUsername => write!(fmt, "error decoding username"),
+ MechanismError::NoPasswordSpecified => write!(fmt, "no password specified"),
+ MechanismError::ErrorDecodingPassword => write!(fmt, "error decoding password"),
+ MechanismError::ValidatorError(err) => write!(fmt, "validator error: {}", err),
+
+ MechanismError::FailedToDecodeMessage => write!(fmt, "failed to decode message"),
+ MechanismError::ChannelBindingNotSupported => {
+ write!(fmt, "channel binding not supported")
+ }
+ MechanismError::ChannelBindingIsSupported => {
+ write!(fmt, "channel binding is supported")
+ }
+ MechanismError::ChannelBindingMechanismIncorrect => {
+ write!(fmt, "channel binding mechanism is incorrect")
+ }
+ MechanismError::CannotDecodeInitialMessage => {
+ write!(fmt, "canβt decode initial message")
+ }
+ MechanismError::NoUsername => write!(fmt, "no username"),
+ MechanismError::NoNonce => write!(fmt, "no nonce"),
+ MechanismError::FailedToGenerateNonce => write!(fmt, "failed to generate nonce"),
+ MechanismError::ProviderError(err) => write!(fmt, "provider error: {}", err),
+
+ MechanismError::CannotDecodeResponse => write!(fmt, "canβt decode response"),
+ MechanismError::InvalidKeyLength(err) => write!(fmt, "invalid key length: {}", err),
+ MechanismError::NoProof => write!(fmt, "no proof"),
+ MechanismError::CannotDecodeProof => write!(fmt, "canβt decode proof"),
+ MechanismError::AuthenticationFailed => write!(fmt, "authentication failed"),
+ MechanismError::SaslSessionAlreadyOver => write!(fmt, "SASL session already over"),
+ }
+ }
+}
+
+impl Error for ProviderError {}
+
+impl Error for ValidatorError {}
+
+use std::error::Error;
+impl Error for MechanismError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ MechanismError::ValidatorError(err) => Some(err),
+ MechanismError::ProviderError(err) => Some(err),
+ // TODO: figure out how to enable the std feature on this crate.
+ //MechanismError::InvalidKeyLength(err) => Some(err),
+ _ => None,
+ }
+ }
}
pub trait Mechanism {
fn name(&self) -> &str;
- fn respond(&mut self, payload: &[u8]) -> Result<Response, String>;
+ fn respond(&mut self, payload: &[u8]) -> Result<Response, MechanismError>;
}
#[derive(Debug, Clone, PartialEq, Eq)]