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
use log::*;

use pretty_hex::PrettyHex;

use std::error;
use std::fmt;
use std::io;
use std::string;

#[derive(Debug)]
pub enum Error {
    /// Unknown database cipher UUID.
    /// Only `ChaCha20` and `AES256` are supported.
    UnsupportedCipher(Vec<u8>),

    /// Unknown key derivation function UUID.
    /// Only `Argon2` and `AES` are supported.
    UnsupportedKdf(Vec<u8>),

    /// Unknown cipher for the inner stream (i.e. data encrypted within XML).
    /// Only `ChaCha20` and `Salsa20` are supported.
    UnsupportedStreamCipher(Vec<u8>),

    /// Error during file decryption (see log output for more info).
    Decryption,

    /// Error during parsing XML.
    XmlParse,

    /// Malformed KDBX4 file or unsupported features (see log output for more info).
    BadFormat,

    /// Wrong password, key file or corrupted file.
    CorruptedFile,

    Io(io::Error),
    Other(String),
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::Io(err)
    }
}

impl From<xmlparser::Error> for Error {
    fn from(err: xmlparser::Error) -> Self {
        error!("unable to parse xml\n{}", err);
        Error::XmlParse
    }
}

impl From<string::FromUtf8Error> for Error {
    fn from(err: string::FromUtf8Error) -> Self {
        error!(
            "unable to convert to a string\n{}",
            err.as_bytes().hex_dump()
        );
        Error::Decryption
    }
}

impl From<String> for Error {
    fn from(err: String) -> Error {
        Error::Other(err)
    }
}

impl<'a> From<&'a str> for Error {
    fn from(err: &'a str) -> Error {
        Error::Other(err.to_owned())
    }
}

impl error::Error for Error {
    fn cause(&self) -> Option<&dyn error::Error> {
        if let Error::Io(ref e) = self {
            Some(e)
        } else {
            None
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnsupportedCipher(ref u) => {
                write!(f, "Unsupported cipher (UUID: {})", u.hex_dump())
            }
            Error::UnsupportedKdf(ref u) => write!(f, "Unsupported KDF (UUID: {})", u.hex_dump()),
            Error::UnsupportedStreamCipher(ref u) => {
                write!(f, "Unsupported stream cipher (UUID: {})", u.hex_dump())
            }
            Error::Decryption => write!(f, "Unable to decrypt database"),
            Error::XmlParse => write!(f, "Unable to parse XML"),
            Error::BadFormat => write!(f, "Unsupported database file format"),
            Error::CorruptedFile => write!(f, "Database file corrupted or wrong key"),
            Error::Io(ref e) => e.fmt(f),
            Error::Other(ref s) => f.write_str(&**s),
        }
    }
}