diff --git a/src/errors.rs b/src/errors.rs index 95226a8..2ee0e72 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -4,12 +4,17 @@ // LICENSE file in the root directory of this source tree. //! A list of error types which are produced during an execution of the protocol +use std::convert::Infallible; +use std::error::Error; +use std::fmt::Debug; + use displaydoc::Display; -use thiserror::Error; /// Represents an error in the manipulation of internal cryptographic data -#[derive(Clone, Debug, Display, Error, Eq, Hash, PartialEq)] -pub enum InternalPakeError { +#[derive(Clone, Display, Eq, Hash, PartialEq)] +pub enum InternalPakeError { + /// Custom [`SecretKey`](crate::keypair::SecretKey) error type + Custom(T), /// Deserializing from a byte sequence failed InvalidByteSequence, /// Invalid length for {name}: expected {len}, but is actually {actual_len}. @@ -55,13 +60,86 @@ pub enum InternalPakeError { UnexpectedEnvelopeContentsError, } +impl Debug for InternalPakeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Custom(custom) => f.debug_tuple("InvalidByteSequence").field(custom).finish(), + Self::InvalidByteSequence => f.debug_tuple("InvalidByteSequence").finish(), + Self::SizeError { + name, + len, + actual_len, + } => f + .debug_struct("SizeError") + .field("name", name) + .field("len", len) + .field("actual_len", actual_len) + .finish(), + Self::PointError => f.debug_tuple("PointError").finish(), + Self::SubGroupError => f.debug_tuple("SubGroupError").finish(), + Self::HashingFailure => f.debug_tuple("HashingFailure").finish(), + Self::HashToCurveError => f.debug_tuple("HashToCurveError").finish(), + Self::HkdfError => f.debug_tuple("HkdfError").finish(), + Self::HmacError => f.debug_tuple("HmacError").finish(), + Self::SlowHashError => f.debug_tuple("SlowHashError").finish(), + Self::SealError => f.debug_tuple("SealError").finish(), + Self::SealOpenError => f.debug_tuple("SealOpenError").finish(), + Self::SealOpenHmacError => f.debug_tuple("SealOpenHmacError").finish(), + Self::InvalidEnvelopeStructureError => { + f.debug_tuple("InvalidEnvelopeStructureError").finish() + } + Self::IncompatibleEnvelopeModeError => { + f.debug_tuple("IncompatibleEnvelopeModeError").finish() + } + Self::UnexpectedEnvelopeContentsError => { + f.debug_tuple("UnexpectedEnvelopeContentsError").finish() + } + } + } +} + +impl Error for InternalPakeError {} + +impl InternalPakeError { + pub fn into_custom(self) -> InternalPakeError { + match self { + Self::Custom(_) => unreachable!(), + Self::InvalidByteSequence => InternalPakeError::InvalidByteSequence, + Self::SizeError { + name, + len, + actual_len, + } => InternalPakeError::SizeError { + name, + len, + actual_len, + }, + Self::PointError => InternalPakeError::PointError, + Self::SubGroupError => InternalPakeError::SubGroupError, + Self::HashingFailure => InternalPakeError::HashingFailure, + Self::HashToCurveError => InternalPakeError::HashToCurveError, + Self::HkdfError => InternalPakeError::HkdfError, + Self::HmacError => InternalPakeError::HmacError, + Self::SlowHashError => InternalPakeError::SlowHashError, + Self::SealError => InternalPakeError::SealError, + Self::SealOpenError => InternalPakeError::SealOpenError, + Self::SealOpenHmacError => InternalPakeError::SealOpenHmacError, + Self::InvalidEnvelopeStructureError => InternalPakeError::InvalidEnvelopeStructureError, + Self::IncompatibleEnvelopeModeError => InternalPakeError::IncompatibleEnvelopeModeError, + Self::UnexpectedEnvelopeContentsError => { + InternalPakeError::UnexpectedEnvelopeContentsError + } + } + } +} + /// Represents an error in password checking -#[derive(Clone, Debug, Display, Error, Eq, Hash, PartialEq)] -pub enum PakeError { +#[derive(Clone, Display, Eq, Hash, PartialEq)] +pub enum PakeError { /// This error results from an internal error during PRF construction /// /// Internal error during PRF verification: {0} - CryptoError(InternalPakeError), + CryptoError(InternalPakeError), /// This error occurs when the server object that is being called finish() on is malformed /// Incomplete set of keys passed into finish() function IncompleteKeysError, @@ -77,21 +155,62 @@ pub enum PakeError { IdentityGroupElementError, } +impl Debug for PakeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::CryptoError(internal_pake_error) => f + .debug_tuple("CryptoError") + .field(internal_pake_error) + .finish(), + Self::IncompleteKeysError => f.debug_tuple("IncompleteKeysError").finish(), + Self::IncompatibleServerStaticPublicKeyError => f + .debug_tuple("IncompatibleServerStaticPublicKeyError") + .finish(), + Self::KeyExchangeMacValidationError => { + f.debug_tuple("KeyExchangeMacValidationError").finish() + } + Self::InvalidLoginError => f.debug_tuple("InvalidLoginError").finish(), + Self::SerializationError => f.debug_tuple("SerializationError").finish(), + Self::IdentityGroupElementError => f.debug_tuple("IdentityGroupElementError").finish(), + } + } +} + +impl Error for PakeError {} + // This is meant to express future(ly) non-trivial ways of converting the // internal error into a PakeError -impl From for PakeError { - fn from(e: InternalPakeError) -> PakeError { +impl From> for PakeError { + fn from(e: InternalPakeError) -> PakeError { PakeError::CryptoError(e) } } +impl PakeError { + pub fn into_custom(self) -> PakeError { + match self { + Self::CryptoError(internal_pake_error) => { + PakeError::CryptoError(internal_pake_error.into_custom()) + } + Self::IncompleteKeysError => PakeError::IncompleteKeysError, + Self::IncompatibleServerStaticPublicKeyError => { + PakeError::IncompatibleServerStaticPublicKeyError + } + Self::KeyExchangeMacValidationError => PakeError::KeyExchangeMacValidationError, + Self::InvalidLoginError => PakeError::InvalidLoginError, + Self::SerializationError => PakeError::SerializationError, + Self::IdentityGroupElementError => PakeError::IdentityGroupElementError, + } + } +} + /// Represents an error in protocol handling -#[derive(Clone, Debug, Display, Error, Eq, Hash, PartialEq)] -pub enum ProtocolError { +#[derive(Clone, Display, Eq, Hash, PartialEq)] +pub enum ProtocolError { /// This error results from an error during password verification /// /// Internal error during password verification: {0} - VerificationError(PakeError), + VerificationError(PakeError), /// This error occurs when the inner envelope is malformed InvalidInnerEnvelopeError, /// This error occurs when the server answer cannot be handled @@ -108,18 +227,38 @@ pub enum ProtocolError { ReflectedValueError, } +impl Debug for ProtocolError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::VerificationError(pake_error) => f + .debug_tuple("VerificationError") + .field(pake_error) + .finish(), + Self::InvalidInnerEnvelopeError => f.debug_tuple("InvalidInnerEnvelopeError").finish(), + Self::ServerError => f.debug_tuple("ServerError").finish(), + Self::ServerInvalidEnvelopeCredentialsFormatError => f + .debug_tuple("ServerInvalidEnvelopeCredentialsFormatError") + .finish(), + Self::ClientError => f.debug_tuple("ClientError").finish(), + Self::ReflectedValueError => f.debug_tuple("ReflectedValueError").finish(), + } + } +} + +impl Error for ProtocolError {} + // This is meant to express future(ly) non-trivial ways of converting the // Pake error into a ProtocolError -impl From for ProtocolError { - fn from(e: PakeError) -> ProtocolError { +impl From> for ProtocolError { + fn from(e: PakeError) -> ProtocolError { ProtocolError::VerificationError(e) } } // This is meant to express future(ly) non-trivial ways of converting the // internal error into a ProtocolError -impl From for ProtocolError { - fn from(e: InternalPakeError) -> ProtocolError { +impl From> for ProtocolError { + fn from(e: InternalPakeError) -> ProtocolError { ProtocolError::VerificationError(e.into()) } } @@ -127,25 +266,42 @@ impl From for ProtocolError { // See https://github.com/rust-lang/rust/issues/64715 and remove this when // merged, and https://github.com/dtolnay/thiserror/issues/62 for why this // comes up in our doc tests. -impl From<::std::convert::Infallible> for ProtocolError { +impl From<::std::convert::Infallible> for ProtocolError { fn from(_: ::std::convert::Infallible) -> Self { unreachable!() } } -impl From for InternalPakeError { +impl ProtocolError { + pub fn into_custom(self) -> ProtocolError { + match self { + Self::VerificationError(pake_error) => { + ProtocolError::VerificationError(pake_error.into_custom()) + } + Self::InvalidInnerEnvelopeError => ProtocolError::InvalidInnerEnvelopeError, + Self::ServerError => ProtocolError::ServerError, + Self::ServerInvalidEnvelopeCredentialsFormatError => { + ProtocolError::ServerInvalidEnvelopeCredentialsFormatError + } + Self::ClientError => ProtocolError::ClientError, + Self::ReflectedValueError => ProtocolError::ReflectedValueError, + } + } +} + +impl From for InternalPakeError { fn from(_: generic_bytes::TryFromSizedBytesError) -> Self { InternalPakeError::InvalidByteSequence } } -impl From for PakeError { +impl From for PakeError { fn from(e: generic_bytes::TryFromSizedBytesError) -> Self { PakeError::CryptoError(e.into()) } } -impl From for ProtocolError { +impl From for ProtocolError { fn from(e: generic_bytes::TryFromSizedBytesError) -> Self { PakeError::CryptoError(e.into()).into() } @@ -154,11 +310,11 @@ impl From for ProtocolError { pub(crate) mod utils { use super::*; - pub fn check_slice_size<'a>( + pub fn check_slice_size<'a, T>( slice: &'a [u8], expected_len: usize, arg_name: &'static str, - ) -> Result<&'a [u8], InternalPakeError> { + ) -> Result<&'a [u8], InternalPakeError> { if slice.len() != expected_len { return Err(InternalPakeError::SizeError { name: arg_name, diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs index 4b32707..33add31 100644 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -35,7 +35,7 @@ pub trait KeyExchange { id_u: Vec, id_s: Vec, context: Vec, - ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>; + ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>; #[allow(clippy::too_many_arguments, clippy::type_complexity)] fn generate_ke3( diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index 486aff3..b5caeb9 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -80,13 +80,13 @@ impl KeyExchange for TripleDH { id_u: Vec, id_s: Vec, context: Vec, - ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> { + ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> { let server_e_kp = KeyPair::::generate_random(rng); let server_nonce = generate_nonce::(rng); let mut transcript_hasher = D::new() .chain(STR_RFC) - .chain(&serialize(&context, 2)?) + .chain(&serialize(&context, 2).map_err(PakeError::into_custom)?) .chain(&id_u) .chain(&serialized_credential_request[..]) .chain(&id_s) @@ -456,11 +456,15 @@ impl> FromBytes for Ke3Message { fn derive_3dh_keys>( dh: TripleDHComponents, hashed_derivation_transcript: &[u8], -) -> Result, ProtocolError> { +) -> Result, ProtocolError> { let ikm: Vec = [ - &dh.sk1.diffie_hellman(dh.pk1)?[..], + &dh.sk1 + .diffie_hellman(dh.pk1) + .map_err(InternalPakeError::into_custom)?[..], &dh.sk2.diffie_hellman(dh.pk2)?[..], - &dh.sk3.diffie_hellman(dh.pk3)?[..], + &dh.sk3 + .diffie_hellman(dh.pk3) + .map_err(InternalPakeError::into_custom)?[..], ] .concat(); @@ -469,25 +473,29 @@ fn derive_3dh_keys>( &extracted_ikm, STR_HANDSHAKE_SECRET, hashed_derivation_transcript, - )?; + ) + .map_err(ProtocolError::into_custom)?; let session_key = derive_secrets::( &extracted_ikm, STR_SESSION_KEY, hashed_derivation_transcript, - )?; + ) + .map_err(ProtocolError::into_custom)?; let km2 = hkdf_expand_label::( &handshake_secret, STR_SERVER_MAC, b"", ::OutputSize::to_usize(), - )?; + ) + .map_err(ProtocolError::into_custom)?; let km3 = hkdf_expand_label::( &handshake_secret, STR_CLIENT_MAC, b"", ::OutputSize::to_usize(), - )?; + ) + .map_err(ProtocolError::into_custom)?; Ok(( GenericArray::clone_from_slice(&session_key), diff --git a/src/keypair.rs b/src/keypair.rs index e989af8..f86ff0f 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -114,7 +114,7 @@ impl> KeyPair { } /// Obtains a KeyPair from a slice representing the private key - pub fn from_private_key_slice(input: &[u8]) -> Result { + pub fn from_private_key_slice(input: &[u8]) -> Result> { let sk = S::deserialize(input)?; let pk = sk.public_key()?; Ok(Self { pk, sk }) @@ -276,20 +276,24 @@ impl SizedBytes for PrivateKey { /// A trait specifying the requirements for a private key container pub trait SecretKey: Clone + Sized + Zeroize { + type Error; + /// Diffie-Hellman key exchange implementation - fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalPakeError>; + fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalPakeError>; /// Returns public key from private key - fn public_key(&self) -> Result, InternalPakeError>; + fn public_key(&self) -> Result, InternalPakeError>; /// Serialization into bytes fn serialize(&self) -> Vec; /// Deserialization from bytes - fn deserialize(input: &[u8]) -> Result; + fn deserialize(input: &[u8]) -> Result>; } impl SecretKey for PrivateKey { + type Error = std::convert::Infallible; + fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalPakeError> { let pk_data = GenericArray::::from_slice(&pk.0[..]); let point = G::from_element_slice(pk_data)?; diff --git a/src/messages.rs b/src/messages.rs index 7652e0a..9e67209 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -14,7 +14,7 @@ use crate::{ }, group::Group, key_exchange::traits::{FromBytes, KeyExchange, ToBytes}, - keypair::{KeyPair, PublicKey, SizedBytesExt}, + keypair::{KeyPair, PublicKey, SecretKey, SizedBytesExt}, opaque::ServerSetup, }; use digest::Digest; @@ -193,9 +193,9 @@ impl RegistrationUpload { } // Creates a dummy instance used for faking a [CredentialResponse] - pub(crate) fn dummy( + pub(crate) fn dummy>( rng: &mut R, - server_setup: &ServerSetup, + server_setup: &ServerSetup, ) -> Self { let mut masking_key = vec![0u8; ::OutputSize::to_usize()]; rng.fill_bytes(&mut masking_key); diff --git a/src/opaque.rs b/src/opaque.rs index fa573f4..ebd559b 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -80,7 +80,7 @@ impl> ServerSetup { } /// Deserialization from bytes - pub fn deserialize(input: &[u8]) -> Result { + pub fn deserialize(input: &[u8]) -> Result> { let seed_len = ::OutputSize::to_usize(); let key_len = as SizedBytes>::Len::to_usize(); let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?; @@ -88,7 +88,8 @@ impl> ServerSetup { Ok(Self { oprf_seed: GenericArray::clone_from_slice(&checked_slice[..seed_len]), keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len..seed_len + key_len])?, - fake_keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len + key_len..])?, + fake_keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len + key_len..]) + .map_err(ProtocolError::into_custom)?, }) } @@ -402,9 +403,9 @@ impl ServerRegistration { } // Creates a dummy instance used for faking a [CredentialResponse] - pub(crate) fn dummy( + pub(crate) fn dummy>( rng: &mut R, - server_setup: &ServerSetup, + server_setup: &ServerSetup, ) -> Self { Self(RegistrationUpload::dummy(rng, server_setup)) } @@ -763,14 +764,14 @@ impl ServerLogin { /// From the client's "blinded" password, returns a challenge to be /// sent back to the client, as well as a ServerLogin - pub fn start( + pub fn start>( rng: &mut R, - server_setup: &ServerSetup, + server_setup: &ServerSetup, password_file: Option>, l1: CredentialRequest, credential_identifier: &[u8], params: ServerLoginStartParameters, - ) -> Result, ProtocolError> { + ) -> Result, ProtocolError> { let record = match password_file { Some(x) => x, None => ServerRegistration::dummy(rng, server_setup), @@ -797,20 +798,23 @@ impl ServerLogin { &masking_nonce, &server_s_pk, &record.0.envelope, - )?; + ) + .map_err(ProtocolError::into_custom)?; let (id_u, id_s) = bytestrings_from_identifiers( &optional_ids, &client_s_pk.to_arr(), &server_s_pk.to_arr(), - )?; + ) + .map_err(ProtocolError::into_custom)?; let l1_bytes = &l1.serialize(); let oprf_key = oprf_key_from_seed::( &server_setup.oprf_seed, credential_identifier, - )?; + ) + .map_err(ProtocolError::into_custom)?; let beta = oprf::evaluate(l1.alpha, &oprf_key); let credential_response_component =