    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

    // #![no_std]
    // #![doc = include_str!("../README.md")]
    // #![doc(
    //     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
    //     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
    // )]
    // #![warn(
    //     clippy::checked_conversions,
    //     clippy::integer_arithmetic,
    //     clippy::panic,
    //     clippy::panic_in_result_fn,
    //     clippy::unwrap_used,
    //     missing_docs,
    //     rust_2018_idioms,
    //     unused_lifetimes,
    //     unused_qualifications
    // )]

    extern crate alloc;
    #[cfg(feature = "std")]
    extern crate std;

    use alloc::string::{String, ToString};
    use core::fmt;
    use password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString};
    use rand_core::OsRng;

    // #[cfg(not(any(feature = "argon2", feature = "pbkdf2", feature = "scrypt")))]
    // compile_error!(
    //     "please enable at least one password hash crate feature, e.g. argon2, pbkdf2, scrypt"
    // );

    use argon2::Argon2;

    /// Opaque error type.
    #[derive(Clone, Copy, Debug)]
    pub struct VerifyError;

    impl fmt::Display for VerifyError {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            f.write_str("password verification error")
        }
    }

    // #[cfg(feature = "std")]
    // #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
    // impl std::error::Error for VerifyError {}

    /// Generate a password hash for the given password.
    pub fn generate_hash(password: impl AsRef<[u8]>) -> String {
        let salt = SaltString::generate(OsRng);
        generate_phc_hash(password.as_ref(), &salt)
            .map(|hash| hash.to_string())
            .expect("password hashing error")
    }

    /// Generate a PHC hash using the preferred algorithm.
    #[allow(unreachable_code)]
    fn generate_phc_hash<'a>(
        password: &[u8],
        salt: &'a SaltString,
    ) -> password_hash::Result<PasswordHash<'a>> {
        return Argon2::default().hash_password(password, salt);
    }

    /// Verify the provided password against the provided password hash.
    pub fn verify_password(password: impl AsRef<[u8]>, hash: &str) -> Result<(), VerifyError> {
        let hash = PasswordHash::new(hash).map_err(|_| VerifyError)?;
        let algs: &[&dyn PasswordVerifier] = &[
            &Argon2::default(),
        ];
        hash.verify_password(algs, password)
            .map_err(|_| VerifyError)
    }