diff --git a/src/amortized_tokens/request.rs b/src/amortized_tokens/request.rs index 56489c1..e43b594 100644 --- a/src/amortized_tokens/request.rs +++ b/src/amortized_tokens/request.rs @@ -101,7 +101,7 @@ impl AmortizedBatchTokenRequest { ) -> Result<(AmortizedBatchTokenRequest, TokenState), IssueTokenRequestError> { let challenge_digest = challenge .digest() - .map_err(|_| IssueTokenRequestError::InvalidTokenChallenge)?; + .map_err(|source| IssueTokenRequestError::InvalidTokenChallenge { source })?; let token_key_id = public_key_to_token_key_id::(&public_key); @@ -125,8 +125,11 @@ impl AmortizedBatchTokenRequest { token_key_id, ); - let blind = VoprfClient::::blind(&token_input.serialize(), &mut OsRng) - .map_err(|_| IssueTokenRequestError::BlindingError)?; + let blind = VoprfClient::::blind(&token_input.serialize(), &mut OsRng).map_err( + |source| IssueTokenRequestError::BlindingError { + source: source.into(), + }, + )?; #[cfg(feature = "kat")] let blind = if _blinds.is_some() { @@ -134,7 +137,9 @@ impl AmortizedBatchTokenRequest { &token_input.serialize(), *blinds_iter.next().unwrap(), ) - .map_err(|_| IssueTokenRequestError::BlindingError)? + .map_err(|source| IssueTokenRequestError::BlindingError { + source: source.into(), + })? } else { blind }; diff --git a/src/amortized_tokens/response.rs b/src/amortized_tokens/response.rs index 9e046de..f2d383e 100644 --- a/src/amortized_tokens/response.rs +++ b/src/amortized_tokens/response.rs @@ -52,7 +52,8 @@ impl AmortizedBatchTokenResponse { /// valid `TokenResponse`. pub fn try_from_bytes(bytes: &[u8]) -> Result { let mut bytes = bytes; - Self::tls_deserialize(&mut bytes).map_err(|_| SerializationError::InvalidData) + Self::tls_deserialize(&mut bytes) + .map_err(|source| SerializationError::InvalidData { source }) } /// Issue a token. @@ -64,15 +65,30 @@ impl AmortizedBatchTokenResponse { token_state: &TokenState, ) -> Result>, IssueTokenError> { let mut evaluated_elements = Vec::new(); - for element in self.evaluated_elements.iter() { - let evaluated_element = - EvaluationElement::::deserialize(&element.evaluated_element) - .map_err(|_| IssueTokenError::InvalidTokenResponse)?; + let default_token_type = token_state + .token_inputs + .first() + .map(|token_input| token_input.token_type) + .unwrap_or_else(CS::token_type); + for (index, element) in self.evaluated_elements.iter().enumerate() { + let token_type = token_state + .token_inputs + .get(index) + .map(|token_input| token_input.token_type) + .unwrap_or(default_token_type); + let evaluated_element = EvaluationElement::::deserialize( + &element.evaluated_element, + ) + .map_err(|source| IssueTokenError::InvalidEvaluationElement { token_type, source })?; evaluated_elements.push(evaluated_element); } - let proof = Proof::deserialize(&self.evaluated_proof) - .map_err(|_| IssueTokenError::InvalidTokenResponse)?; + let proof = Proof::deserialize(&self.evaluated_proof).map_err(|source| { + IssueTokenError::InvalidProof { + token_type: default_token_type, + source, + } + })?; let client_batch_finalize_result = VoprfClient::batch_finalize( &token_state @@ -85,9 +101,15 @@ impl AmortizedBatchTokenResponse { &proof, token_state.public_key, ) - .map_err(|_| IssueTokenError::InvalidTokenResponse)? + .map_err(|source| IssueTokenError::BatchFinalizationFailed { + token_type: default_token_type, + source, + })? .collect::>>() - .map_err(|_| IssueTokenError::InvalidTokenResponse)?; + .map_err(|source| IssueTokenError::BatchFinalizationFailed { + token_type: default_token_type, + source, + })?; let mut tokens = Vec::new(); diff --git a/src/amortized_tokens/server.rs b/src/amortized_tokens/server.rs index 81e23b1..8e57829 100644 --- a/src/amortized_tokens/server.rs +++ b/src/amortized_tokens/server.rs @@ -30,7 +30,8 @@ impl Server { ::Scalar: Send + Sync, ::Elem: Send + Sync, { - VoprfServer::::new_from_seed(seed, info).map_err(|_| CreateKeypairError::SeedError) + VoprfServer::::new_from_seed(seed, info) + .map_err(|source| CreateKeypairError::SeedError { source }) } /// Create a new server. The new server does not contain any key material. @@ -103,7 +104,10 @@ impl Server { token_request: AmortizedBatchTokenRequest, ) -> Result, IssueTokenResponseError> { if token_request.token_type != CS::token_type() { - return Err(IssueTokenResponseError::InvalidTokenType); + return Err(IssueTokenResponseError::InvalidTokenType { + expected: CS::token_type(), + found: token_request.token_type, + }); } let server = key_store .get(&token_request.truncated_token_key_id) @@ -113,7 +117,7 @@ impl Server { let mut blinded_elements = Vec::new(); for element in token_request.blinded_elements.iter() { let blinded_element = BlindedElement::::deserialize(&element.blinded_element) - .map_err(|_| IssueTokenResponseError::InvalidTokenRequest)?; + .map_err(|source| IssueTokenResponseError::InvalidBlindedMessage { source })?; blinded_elements.push(blinded_element); } @@ -122,7 +126,7 @@ impl Server { .collect::>(); let VoprfServerBatchEvaluateFinishResult { messages, proof } = server .batch_blind_evaluate_finish(&mut OsRng, blinded_elements.iter(), &prepared_elements) - .map_err(|_| IssueTokenResponseError::InvalidTokenRequest)?; + .map_err(|source| IssueTokenResponseError::BlindEvaluationFailed { source })?; let evaluated_elements = messages .map(|m| super::EvaluatedElement { @@ -149,18 +153,26 @@ impl Server { nonce_store: &NS, token: AmortizedToken, ) -> Result<(), RedeemTokenError> { - if token.token_type() != CS::token_type() { - return Err(RedeemTokenError::InvalidToken); + let token_type = token.token_type(); + if token_type != CS::token_type() { + return Err(RedeemTokenError::TokenTypeMismatch { + expected: CS::token_type(), + found: token_type, + }); } let auth_len = <::OutputSize as Unsigned>::USIZE; - if token.authenticator().len() != auth_len { - return Err(RedeemTokenError::InvalidToken); + let authenticator_len = token.authenticator().len(); + if authenticator_len != auth_len { + return Err(RedeemTokenError::InvalidAuthenticatorLength { + expected: auth_len, + found: authenticator_len, + }); } if nonce_store.exists(&token.nonce()).await { return Err(RedeemTokenError::DoubleSpending); } let token_input = TokenInput { - token_type: token.token_type(), + token_type, nonce: token.nonce(), challenge_digest: *token.challenge_digest(), token_key_id: *token.token_key_id(), @@ -171,13 +183,16 @@ impl Server { .ok_or(RedeemTokenError::KeyIdNotFound)?; let token_authenticator = server .evaluate(&token_input.serialize()) - .map_err(|_| RedeemTokenError::InvalidToken)? + .map_err(|source| RedeemTokenError::AuthenticatorDerivationFailed { + token_type, + source, + })? .to_vec(); if token.authenticator() == token_authenticator { nonce_store.insert(token.nonce()).await; Ok(()) } else { - Err(RedeemTokenError::InvalidToken) + Err(RedeemTokenError::AuthenticatorMismatch { token_type }) } } @@ -193,7 +208,7 @@ impl Server { ::Elem: Send + Sync, { let server = VoprfServer::::new_with_key(private_key) - .map_err(|_| CreateKeypairError::SeedError)?; + .map_err(|source| CreateKeypairError::SeedError { source })?; let public_key = server.get_public_key(); let token_key_id = public_key_to_token_key_id::(&server.get_public_key()); key_store diff --git a/src/auth/authenticate.rs b/src/auth/authenticate.rs index 945c721..71a50cc 100644 --- a/src/auth/authenticate.rs +++ b/src/auth/authenticate.rs @@ -136,7 +136,7 @@ impl TokenChallenge { } /// An error that occurred during serialization or deserialization. -#[derive(Error, Debug)] +#[derive(PartialEq, Eq, Error, Debug)] pub enum SerializationError { #[error("Invalid TokenChallenge")] /// Invalid TokenChallenge @@ -174,7 +174,7 @@ pub fn build_www_authenticate_header( } /// Building error for the `Authorization` header values -#[derive(Error, Debug)] +#[derive(PartialEq, Eq, Error, Debug)] pub enum BuildError { #[error("Invalid TokenChallenge")] /// Invalid TokenChallenge diff --git a/src/auth/authorize.rs b/src/auth/authorize.rs index bc25ace..78ab70e 100644 --- a/src/auth/authorize.rs +++ b/src/auth/authorize.rs @@ -149,7 +149,7 @@ pub fn build_authorization_header( } /// Building error for the `Authorization` header values -#[derive(Error, Debug)] +#[derive(PartialEq, Eq, Error, Debug)] pub enum BuildError { #[error("Invalid token")] /// Invalid token @@ -172,7 +172,7 @@ pub fn parse_authorization_header( } /// Parsing error for the `WWW-Authenticate` header values -#[derive(Error, Debug)] +#[derive(PartialEq, Eq, Error, Debug)] pub enum ParseError { #[error("Invalid token")] /// Invalid token diff --git a/src/common/errors.rs b/src/common/errors.rs index 8a849e5..609d013 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -1,35 +1,76 @@ //! Common error types +use blind_rsa_signatures::Error as BlindRsaError; use thiserror::Error; +use crate::{ + TokenType, auth::authenticate::SerializationError as TokenChallengeSerializationError, +}; +use tls_codec::Error as TlsCodecError; +use voprf::Error as VoprfError; + /// Serialization error -#[derive(Error, Debug)] +#[derive(PartialEq, Eq, Error, Debug)] pub enum SerializationError { #[error("Invalid serialized data")] /// Invalid serialized data - InvalidData, + InvalidData { + /// Underlying TLS codec error that triggered the failure. + #[source] + source: TlsCodecError, + }, } /// Errors that can occur when creating a keypair. -#[derive(Error, Debug, PartialEq, Eq)] +#[derive(PartialEq, Eq, Error, Debug)] pub enum CreateKeypairError { - #[error("Seed does not have the right length")] - /// Error when the seed does not have the right length - SeedError, + #[error("Seed is too long")] + /// Error when the seed is too long. + SeedError { + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Key generation failed")] + /// Error when generating an RSA keypair fails. + KeyGenerationFailed { + /// Underlying RSA error that triggered the failure. + #[source] + source: BlindRsaError, + }, #[error("Collision exhausted")] /// Error when collision attempts are exhausted CollisionExhausted, } /// Errors that can occur when issuing token requests. -#[derive(Error, Debug, PartialEq, Eq)] +#[derive(PartialEq, Eq, Error, Debug)] pub enum IssueTokenRequestError { #[error("Token blinding error")] /// Error when blinding the token. - BlindingError, + BlindingError { + /// Underlying blinding error that triggered the failure. + #[source] + source: BlindingErrorSource, + }, #[error("Invalid TokenChallenge")] /// Error when the token challenge is invalid. - InvalidTokenChallenge, + InvalidTokenChallenge { + /// Underlying token challenge serialization error that triggered the failure. + #[source] + source: TokenChallengeSerializationError, + }, +} + +/// Source errors for blinding failures. +#[derive(Error, Debug, PartialEq, Eq)] +pub enum BlindingErrorSource { + /// VOPRF-specific blinding error. + #[error(transparent)] + Voprf(#[from] VoprfError), + /// RSA-specific blinding error. + #[error(transparent)] + Rsa(#[from] BlindRsaError), } /// Errors that can occur when issuing the token response. @@ -38,20 +79,93 @@ pub enum IssueTokenResponseError { #[error("Key ID not found")] /// Error when the key ID is not found. KeyIdNotFound, - #[error("Invalid TokenRequest")] - /// Error when the token request is invalid. - InvalidTokenRequest, - #[error("Invalid toke type")] - /// Error when the token type is invalid. - InvalidTokenType, + #[error("Invalid blinded message")] + /// Error when deserializing the blinded message fails. + InvalidBlindedMessage { + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Blind evaluation failed")] + /// Error when the server fails to evaluate the blinded elements. + BlindEvaluationFailed { + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Blind signature failed")] + /// Error when the server fails to compute a blind signature. + BlindSignatureFailed { + /// Underlying RSA error that triggered the failure. + #[source] + source: BlindRsaError, + }, + #[error("Invalid token type: expected {expected:?}, found {found:?}")] + /// Error when the token type does not match the expected type. + InvalidTokenType { + /// Expected token type for the operation. + expected: TokenType, + /// Actual token type found in the request. + found: TokenType, + }, } /// Errors that can occur when issuing tokens. #[derive(Error, Debug, PartialEq, Eq)] pub enum IssueTokenError { - #[error("Invalid TokenResponse")] - /// Error when the token response is invalid. - InvalidTokenResponse, + #[error("Invalid evaluation element for {token_type:?} token")] + /// Error when the evaluation element cannot be deserialized. + InvalidEvaluationElement { + /// Token type for which deserialization failed. + token_type: TokenType, + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Invalid proof for {token_type:?} token")] + /// Error when the proof cannot be deserialized. + InvalidProof { + /// Token type for which proof deserialization failed. + token_type: TokenType, + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Finalization failed for {token_type:?} token")] + /// Error when finalizing a single token fails. + FinalizationFailed { + /// Token type for which finalization failed. + token_type: TokenType, + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Batch finalization failed for {token_type:?} token")] + /// Error when finalizing a batch of tokens fails. + BatchFinalizationFailed { + /// Token type for which batch finalization failed. + token_type: TokenType, + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Unexpected token response type: expected {expected:?}, found {found:?}")] + /// Error when a generic token response does not match the expected type. + UnexpectedTokenResponseType { + /// Expected token type inferred from the token state. + expected: TokenType, + /// Token type present in the response. + found: TokenType, + }, + #[error("Signature finalization failed for {token_type:?} token")] + /// Error when finalizing a public token signature fails. + SignatureFinalizationFailed { + /// Token type for which signature finalization failed. + token_type: TokenType, + /// Underlying RSA error that triggered the failure. + #[source] + source: BlindRsaError, + }, } /// Errors that can occur when redeeming the token. @@ -63,7 +177,41 @@ pub enum RedeemTokenError { #[error("The token has already been redeemed")] /// Error when the token has already been redeemed. DoubleSpending, - #[error("The token is invalid")] - /// Error when the token is invalid. - InvalidToken, + #[error("Token type mismatch: expected {expected:?}, found {found:?}")] + /// Error when the token type does not match the expected type. + TokenTypeMismatch { + /// Expected token type. + expected: TokenType, + /// Token type found in the token. + found: TokenType, + }, + #[error("Invalid authenticator length: expected {expected}, found {found}")] + /// Error when the authenticator length does not match the expected size. + InvalidAuthenticatorLength { + /// Expected authenticator length. + expected: usize, + /// Actual authenticator length found in the token. + found: usize, + }, + #[error("Failed to derive authenticator for {token_type:?} token")] + /// Error when deriving the expected authenticator fails. + AuthenticatorDerivationFailed { + /// Token type that was being redeemed. + token_type: TokenType, + /// Underlying VOPRF error that triggered the failure. + #[source] + source: VoprfError, + }, + #[error("Authenticator mismatch for {token_type:?} token")] + /// Error when the provided authenticator does not match the expected value. + AuthenticatorMismatch { + /// Token type that was being redeemed. + token_type: TokenType, + }, + #[error("Invalid {token_type:?} token signature")] + /// Error when the public token signature verification fails. + InvalidSignature { + /// Token type that was being redeemed. + token_type: TokenType, + }, } diff --git a/src/generic_tokens/request.rs b/src/generic_tokens/request.rs index 65212ba..b5c5dda 100644 --- a/src/generic_tokens/request.rs +++ b/src/generic_tokens/request.rs @@ -139,7 +139,8 @@ impl GenericBatchTokenRequest { /// # Errors /// Returns `SerializationError::InvalidData` if the byte slice is not valid. pub fn try_from_bytes(mut bytes: &[u8]) -> Result { - Self::tls_deserialize(&mut bytes).map_err(|_| SerializationError::InvalidData) + Self::tls_deserialize(&mut bytes) + .map_err(|source| SerializationError::InvalidData { source }) } /// Create a builder for `BatchTokenRequest`. diff --git a/src/generic_tokens/response.rs b/src/generic_tokens/response.rs index 0ebdd2e..527d030 100644 --- a/src/generic_tokens/response.rs +++ b/src/generic_tokens/response.rs @@ -65,7 +65,8 @@ impl GenericBatchTokenResponse { /// # Errors /// Returns `SerializationError::InvalidData` if the byte slice is not valid. pub fn try_from_bytes(mut bytes: &[u8]) -> Result { - Self::tls_deserialize(&mut bytes).map_err(|_| SerializationError::InvalidData) + Self::tls_deserialize(&mut bytes) + .map_err(|source| SerializationError::InvalidData { source }) } } @@ -87,33 +88,40 @@ impl GenericBatchTokenResponse { .zip(token_states.token_states.iter()) { if let Some(response) = token_response { - match (response, token_state) { + let expected = match token_state { + GenericTokenState::PrivateP384(_) => TokenType::PrivateP384, + GenericTokenState::Public(_) => TokenType::Public, + GenericTokenState::PrivateRistretto255(_) => TokenType::PrivateRistretto255, + }; + let found = match &response { + GenericTokenResponse::PrivateP384(_) => TokenType::PrivateP384, + GenericTokenResponse::Public(_) => TokenType::Public, + GenericTokenResponse::PrivateRistretto255(_) => TokenType::PrivateRistretto255, + }; + if expected != found { + return Err(IssueTokenError::UnexpectedTokenResponseType { expected, found }); + } + + let token = match (response, token_state) { ( GenericTokenResponse::PrivateP384(response), GenericTokenState::PrivateP384(state), - ) => { - let token = response - .issue_token(state) - .map(GenericToken::from_private_p384)?; - tokens.push(token); - } + ) => response + .issue_token(state) + .map(GenericToken::from_private_p384)?, (GenericTokenResponse::Public(response), GenericTokenState::Public(state)) => { - let token = response.issue_token(state).map(GenericToken::from_public)?; - tokens.push(token); + response.issue_token(state).map(GenericToken::from_public)? } ( GenericTokenResponse::PrivateRistretto255(response), GenericTokenState::PrivateRistretto255(state), - ) => { - let token = response - .issue_token(state) - .map(GenericToken::from_private_ristretto)?; - tokens.push(token); - } - _ => { - return Err(IssueTokenError::InvalidTokenResponse); - } - } + ) => response + .issue_token(state) + .map(GenericToken::from_private_ristretto)?, + // The mismatch case is handled above. + _ => unreachable!("token type mismatch checked earlier"), + }; + tokens.push(token); } } diff --git a/src/private_tokens/request.rs b/src/private_tokens/request.rs index 2bc70b0..ca7fb68 100644 --- a/src/private_tokens/request.rs +++ b/src/private_tokens/request.rs @@ -74,7 +74,7 @@ impl TokenRequest { ) -> Result<(TokenRequest, TokenState), IssueTokenRequestError> { let challenge_digest = challenge .digest() - .map_err(|_| IssueTokenRequestError::InvalidTokenChallenge)?; + .map_err(|source| IssueTokenRequestError::InvalidTokenChallenge { source })?; let token_key_id = public_key_to_token_key_id::(&public_key); @@ -86,12 +86,16 @@ impl TokenRequest { let token_input = TokenInput::new(CS::token_type(), nonce, challenge_digest, token_key_id); let blinded_element = VoprfClient::::blind(&token_input.serialize(), &mut OsRng) - .map_err(|_| IssueTokenRequestError::BlindingError)?; + .map_err(|source| IssueTokenRequestError::BlindingError { + source: source.into(), + })?; #[cfg(feature = "kat")] let blinded_element = if let Some(blind) = _blind { VoprfClient::::deterministic_blind_unchecked(&token_input.serialize(), blind) - .map_err(|_| IssueTokenRequestError::BlindingError)? + .map_err(|source| IssueTokenRequestError::BlindingError { + source: source.into(), + })? } else { blinded_element }; diff --git a/src/private_tokens/response.rs b/src/private_tokens/response.rs index a425e6d..b624f52 100644 --- a/src/private_tokens/response.rs +++ b/src/private_tokens/response.rs @@ -36,7 +36,8 @@ impl TokenResponse { /// # Errors /// Returns `SerializationError::InvalidData` if the byte slice is not valid. pub fn try_from_bytes(mut bytes: &[u8]) -> Result { - Self::tls_deserialize(&mut bytes).map_err(|_| SerializationError::InvalidData) + Self::tls_deserialize(&mut bytes) + .map_err(|source| SerializationError::InvalidData { source }) } #[cfg(feature = "kat")] @@ -95,10 +96,11 @@ impl TokenResponse { self, token_state: &TokenState, ) -> Result, IssueTokenError> { + let token_type = token_state.token_input.token_type; let evaluation_element = EvaluationElement::deserialize(&self.evaluate_msg) - .map_err(|_| IssueTokenError::InvalidTokenResponse)?; + .map_err(|source| IssueTokenError::InvalidEvaluationElement { token_type, source })?; let proof = Proof::deserialize(&self.evaluate_proof) - .map_err(|_| IssueTokenError::InvalidTokenResponse)?; + .map_err(|source| IssueTokenError::InvalidProof { token_type, source })?; let token_input = token_state.token_input.serialize(); // authenticator = client_context.Finalize(token_input, blind, evaluated_element, blinded_element, proof) let authenticator = token_state @@ -109,7 +111,7 @@ impl TokenResponse { &proof, token_state.public_key, ) - .map_err(|_| IssueTokenError::InvalidTokenResponse)?; + .map_err(|source| IssueTokenError::FinalizationFailed { token_type, source })?; let authenticator = GenericArray::from_slice(authenticator.as_ref()).clone(); Ok(Token::new( diff --git a/src/private_tokens/server.rs b/src/private_tokens/server.rs index fa90ab2..67753fa 100644 --- a/src/private_tokens/server.rs +++ b/src/private_tokens/server.rs @@ -27,7 +27,8 @@ pub struct Server { impl Server { fn server_from_seed(seed: &[u8], info: &[u8]) -> Result, CreateKeypairError> { - VoprfServer::::new_from_seed(seed, info).map_err(|_| CreateKeypairError::SeedError) + VoprfServer::::new_from_seed(seed, info) + .map_err(|source| CreateKeypairError::SeedError { source }) } /// Creates a new server. @@ -92,14 +93,17 @@ impl Server { token_request: TokenRequest, ) -> Result, IssueTokenResponseError> { if token_request.token_type != CS::token_type() { - return Err(IssueTokenResponseError::InvalidTokenType); + return Err(IssueTokenResponseError::InvalidTokenType { + expected: CS::token_type(), + found: token_request.token_type, + }); } let server = key_store .get(&token_request.truncated_token_key_id) .await .ok_or(IssueTokenResponseError::KeyIdNotFound)?; let blinded_element = BlindedElement::::deserialize(&token_request.blinded_msg) - .map_err(|_| IssueTokenResponseError::InvalidTokenRequest)?; + .map_err(|source| IssueTokenResponseError::InvalidBlindedMessage { source })?; let evaluated_result = server.blind_evaluate(&mut OsRng, &blinded_element); Ok(TokenResponse { @@ -119,18 +123,26 @@ impl Server { nonce_store: &NS, token: Token, ) -> Result<(), RedeemTokenError> { - if token.token_type() != CS::token_type() { - return Err(RedeemTokenError::InvalidToken); + let token_type = token.token_type(); + if token_type != CS::token_type() { + return Err(RedeemTokenError::TokenTypeMismatch { + expected: CS::token_type(), + found: token_type, + }); } let auth_len = <::OutputSize as Unsigned>::USIZE; - if token.authenticator().len() != auth_len { - return Err(RedeemTokenError::InvalidToken); + let authenticator_len = token.authenticator().len(); + if authenticator_len != auth_len { + return Err(RedeemTokenError::InvalidAuthenticatorLength { + expected: auth_len, + found: authenticator_len, + }); } if nonce_store.exists(&token.nonce()).await { return Err(RedeemTokenError::DoubleSpending); } let token_input = TokenInput::new( - token.token_type(), + token_type, token.nonce(), *token.challenge_digest(), *token.token_key_id(), @@ -142,13 +154,16 @@ impl Server { .ok_or(RedeemTokenError::KeyIdNotFound)?; let token_authenticator = server .evaluate(&token_input.serialize()) - .map_err(|_| RedeemTokenError::InvalidToken)? + .map_err(|source| RedeemTokenError::AuthenticatorDerivationFailed { + token_type, + source, + })? .to_vec(); if token.authenticator() == token_authenticator { nonce_store.insert(token.nonce()).await; Ok(()) } else { - Err(RedeemTokenError::InvalidToken) + Err(RedeemTokenError::AuthenticatorMismatch { token_type }) } } @@ -160,7 +175,7 @@ impl Server { private_key: &[u8], ) -> Result, CreateKeypairError> { let server = VoprfServer::::new_with_key(private_key) - .map_err(|_| CreateKeypairError::SeedError)?; + .map_err(|source| CreateKeypairError::SeedError { source })?; let public_key = server.get_public_key(); let truncated_token_key_id = truncate_token_key_id(&public_key_to_token_key_id::(&server.get_public_key())); diff --git a/src/public_tokens/request.rs b/src/public_tokens/request.rs index d64b165..003a370 100644 --- a/src/public_tokens/request.rs +++ b/src/public_tokens/request.rs @@ -51,7 +51,7 @@ impl TokenRequest { let challenge_digest = challenge .digest() - .map_err(|_| IssueTokenRequestError::InvalidTokenChallenge)?; + .map_err(|source| IssueTokenRequestError::InvalidTokenChallenge { source })?; let token_key_id = public_key_to_token_key_id(&public_key); @@ -65,7 +65,9 @@ impl TokenRequest { let options = Options::default(); let blinding_result = public_key .blind(rng, token_input.serialize(), false, &options) - .map_err(|_| IssueTokenRequestError::BlindingError)?; + .map_err(|source| IssueTokenRequestError::BlindingError { + source: source.into(), + })?; debug_assert!(blinding_result.blind_msg.len() == NK); let mut blinded_msg = [0u8; NK]; diff --git a/src/public_tokens/response.rs b/src/public_tokens/response.rs index 1d1ab4e..c1e22b1 100644 --- a/src/public_tokens/response.rs +++ b/src/public_tokens/response.rs @@ -29,6 +29,7 @@ impl TokenResponse { // authenticator = rsabssa_finalize(pkI, nonce, blind_sig, blind_inv) let token_input = token_state.token_input.serialize(); let options = Options::default(); + let token_type = TokenType::Public; let blind_sig = BlindSignature(self.blind_sig.to_vec()); let signature = token_state .public_key @@ -39,7 +40,10 @@ impl TokenResponse { token_input, &options, ) - .map_err(|_| IssueTokenError::InvalidTokenResponse)?; + .map_err(|source| IssueTokenError::SignatureFinalizationFailed { + token_type, + source, + })?; let authenticator: GenericArray = *GenericArray::from_slice(&signature[0..256]); Ok(Token::new( TokenType::Public, diff --git a/src/public_tokens/server.rs b/src/public_tokens/server.rs index 42b9370..6ea83ba 100644 --- a/src/public_tokens/server.rs +++ b/src/public_tokens/server.rs @@ -70,7 +70,7 @@ impl IssuerServer { ) -> Result { for _ in 0..COLLISION_AVOIDANCE_ATTEMPTS { let key_pair = KeyPair::generate(rng, KEYSIZE_IN_BITS) - .map_err(|_| CreateKeypairError::SeedError)?; + .map_err(|source| CreateKeypairError::KeyGenerationFailed { source })?; let truncated_token_key_id = truncate_token_key_id(&public_key_to_token_key_id(&key_pair.pk)); @@ -98,7 +98,10 @@ impl IssuerServer { ) -> Result { let rng = &mut OsRng; if token_request.token_type != TokenType::Public { - return Err(IssueTokenResponseError::InvalidTokenType); + return Err(IssueTokenResponseError::InvalidTokenType { + expected: TokenType::Public, + found: token_request.token_type, + }); } let key_pair = key_store .get(&token_request.truncated_token_key_id) @@ -110,7 +113,7 @@ impl IssuerServer { let blind_signature = key_pair .sk .blind_sign(rng, token_request.blinded_msg, &options) - .map_err(|_| IssueTokenResponseError::InvalidTokenRequest)?; + .map_err(|source| IssueTokenResponseError::BlindSignatureFailed { source })?; debug_assert!(blind_signature.len() == NK); let mut blind_sig = [0u8; NK]; @@ -149,17 +152,25 @@ impl OriginServer { nonce_store: &NS, token: Token, ) -> Result<(), RedeemTokenError> { - if token.token_type() != TokenType::Public { - return Err(RedeemTokenError::InvalidToken); + let token_type = token.token_type(); + if token_type != TokenType::Public { + return Err(RedeemTokenError::TokenTypeMismatch { + expected: TokenType::Public, + found: token_type, + }); } - if token.authenticator().len() != KEYSIZE_IN_BYTES { - return Err(RedeemTokenError::InvalidToken); + let authenticator_len = token.authenticator().len(); + if authenticator_len != KEYSIZE_IN_BYTES { + return Err(RedeemTokenError::InvalidAuthenticatorLength { + expected: KEYSIZE_IN_BYTES, + found: authenticator_len, + }); } if nonce_store.exists(&token.nonce()).await { return Err(RedeemTokenError::DoubleSpending); } let token_input = TokenInput::new( - token.token_type(), + token_type, token.nonce(), *token.challenge_digest(), *token.token_key_id(), @@ -182,7 +193,7 @@ impl OriginServer { }); if !verified { - return Err(RedeemTokenError::InvalidToken); + return Err(RedeemTokenError::InvalidSignature { token_type }); } nonce_store.insert(token.nonce()).await;