1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use base64::DecodeError as base64Error;
use std::error::Error as StdError;
use std::fmt;
use std::io::Error as ioError;
use std::sync::mpsc::RecvError as channelError;

#[derive(Debug)]
pub enum YubicoError {
    Network(reqwest::Error),
    HTTPStatusCode(reqwest::StatusCode),
    IOError(ioError),
    ChannelError(channelError),
    DecodeError(base64Error),
    #[cfg(feature = "online-tokio")]
    MultipleErrors(Vec<YubicoError>),
    BadOTP,
    ReplayedOTP,
    BadSignature,
    MissingParameter,
    NoSuchClient,
    OperationNotAllowed,
    BackendError,
    NotEnoughAnswers,
    ReplayedRequest,
    UnknownStatus,
    OTPMismatch,
    NonceMismatch,
    SignatureMismatch,
    InvalidKeyLength,
}

impl fmt::Display for YubicoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            YubicoError::Network(ref err) => write!(f, "Connectivity error: {}", err),
            YubicoError::HTTPStatusCode(code) => write!(f, "Error found: {}", code),
            YubicoError::IOError(ref err) => write!(f, "IO error: {}", err),
            YubicoError::ChannelError(ref err) => write!(f, "Channel error: {}", err),
            YubicoError::DecodeError(ref err) => write!(f, "Decode  error: {}", err),
            #[cfg(feature = "online-tokio")]
            YubicoError::MultipleErrors(ref errs) => {
                write!(f, "Multiple errors. ")?;

                for err in errs {
                    write!(f, "{} ", err)?;
                }

                Ok(())
            }
            YubicoError::BadOTP => write!(f, "The OTP has invalid format."),
            YubicoError::ReplayedOTP => write!(f, "The OTP has already been seen by the service."),
            YubicoError::BadSignature => write!(f, "The HMAC signature verification failed."),
            YubicoError::MissingParameter => write!(f, "The request lacks a parameter."),
            YubicoError::NoSuchClient => write!(f, "The request id does not exist."),
            YubicoError::OperationNotAllowed => {
                write!(f, "The request id is not allowed to verify OTPs.")
            }
            YubicoError::BackendError => write!(
                f,
                "Unexpected error in our server. Please contact us if you see this error."
            ),
            YubicoError::NotEnoughAnswers => write!(
                f,
                "Server could not get requested number of syncs during before timeout"
            ),
            YubicoError::ReplayedRequest => {
                write!(f, "Server has seen the OTP/Nonce combination before")
            }
            YubicoError::UnknownStatus => {
                write!(f, "Unknown status sent by the OTP validation server")
            }
            YubicoError::OTPMismatch => write!(f, "OTP mismatch, It may be an attack attempt"),
            YubicoError::NonceMismatch => write!(f, "Nonce mismatch, It may be an attack attempt"),
            YubicoError::SignatureMismatch => {
                write!(f, "Signature mismatch, It may be an attack attempt")
            }
            YubicoError::InvalidKeyLength => {
                write!(f, "Invalid key length encountered while building signature")
            }
        }
    }
}

impl StdError for YubicoError {
    fn description(&self) -> &str {
        match *self {
            YubicoError::Network(ref err) => err.description(),
            YubicoError::HTTPStatusCode(_) => "200 not received",
            YubicoError::IOError(ref err) => err.description(),
            YubicoError::ChannelError(ref err) => err.description(),
            YubicoError::DecodeError(ref err) => err.description(),
            #[cfg(feature = "online-tokio")]
            YubicoError::MultipleErrors(ref _errs) => {
                "Multiple errors. "
            }
            YubicoError::BadOTP => "The OTP has invalid format.",
            YubicoError::ReplayedOTP => "The OTP has already been seen by the service.",
            YubicoError::BadSignature => "The HMAC signature verification failed.",
            YubicoError::MissingParameter => "The request lacks a parameter.",
            YubicoError::NoSuchClient => "The request id does not exist.",
            YubicoError::OperationNotAllowed => "The request id is not allowed to verify OTPs.",
            YubicoError::BackendError => "Unexpected error in our server. Please contact us if you see this error.",
            YubicoError::NotEnoughAnswers => "Server could not get requested number of syncs during before timeout",
            YubicoError::ReplayedRequest => "Server has seen the OTP/Nonce combination before",
            YubicoError::UnknownStatus => "Unknown status sent by the OTP validation server",
            YubicoError::OTPMismatch => "OTP in the response is the same as the supplied in the request. It may be an attack attempt",
            YubicoError::NonceMismatch => "Nonce in the response is the same as the supplied in the request. It may be an attack attempt",
            YubicoError::SignatureMismatch => "Signature in the response is the same as the supplied in the request. It may be an attack attempt",
            YubicoError::InvalidKeyLength => "Invalid key length",
        }
    }

    fn cause(&self) -> Option<&dyn StdError> {
        match *self {
            YubicoError::Network(ref err) => Some(err),
            YubicoError::HTTPStatusCode(_) => None,
            YubicoError::IOError(ref err) => Some(err),
            YubicoError::ChannelError(ref err) => Some(err),
            YubicoError::DecodeError(ref err) => Some(err),
            #[cfg(feature = "online-tokio")]
            YubicoError::MultipleErrors(ref errs) => match errs.first() {
                Some(err) => Some(err),
                None => None,
            },
            _ => None,
        }
    }
}

impl From<reqwest::Error> for YubicoError {
    fn from(err: reqwest::Error) -> YubicoError {
        YubicoError::Network(err)
    }
}

impl From<reqwest::StatusCode> for YubicoError {
    fn from(err: reqwest::StatusCode) -> YubicoError {
        YubicoError::HTTPStatusCode(err)
    }
}

impl From<ioError> for YubicoError {
    fn from(err: ioError) -> YubicoError {
        YubicoError::IOError(err)
    }
}

impl From<channelError> for YubicoError {
    fn from(err: channelError) -> YubicoError {
        YubicoError::ChannelError(err)
    }
}

impl From<base64Error> for YubicoError {
    fn from(err: base64Error) -> YubicoError {
        YubicoError::DecodeError(err)
    }
}