pwhash-1.0.0/.cargo_vcs_info.json0000644000000001121377440505500124260ustar00{ "git": { "sha1": "8f9ec460060a38bda718d725d9de9ddc8c716ba0" } } pwhash-1.0.0/.github/workflows/build.yml010064400017500000144000000016501377436777700164740ustar0000000000000000name: continuous integration on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-2019, macOS-latest] rust: [stable, nightly] steps: - name: Checkout code uses: actions/checkout@v1 - name: Install rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - name: Build uses: actions-rs/cargo@v1 # Force to build without warnings env: RUSTFLAGS: '-D warnings' with: command: build args: --verbose - name: Run tests uses: actions-rs/cargo@v1 # force to build tests without warnings env: RUSTFLAGS: '-D warnings' with: command: test args: --verbose pwhash-1.0.0/.gitignore010064400017500000144000000000321327455443500132160ustar0000000000000000target Cargo.lock scratch pwhash-1.0.0/CHANGELOG.md010064400017500000144000000022661377440243600130500ustar0000000000000000## v1.0.0, 2021-01-03 * Increase the default bcrypt cost to 10, in line with the recent OpenBSD value. Technically, this is a breaking change, so the crate version should go up. * [internal] Update for 2018 Edition. * [internal] Bump outdated dependencies and clean up Clippy warnings ([#15](https://github.com/inejge/pwhash/pull/15)). * [internal] Move CI to GitHub. ## v0.3.1, 2020-07-08 * [internal] Update dependencies ([#13](https://github.com/inejge/pwhash/pull/13)). ## v0.3.0, 2019-01-13 * [internal] Update dependencies, replace outdated crates ([#11](https://github.com/inejge/pwhash/pull/11)). ## v0.2.0, 2018-05-09 * Widen password input type to [u8] ([#8](https://github.com/inejge/pwhash/issues/8)). * Warn users who try to generate new insecure hashes by deprecating the corresponding hashing funcions ([#8](https://github.com/inejge/pwhash/issues/9)). Verification for insecure hashes is not deprecated. * [internal] Use ? for error handling instead of try! ## v0.1.2, 2017-04-01 * Better compatibility with Unix crypt through the removal of length check from `unix::crypt()` ([#3](https://github.com/inejge/pwhash/pull/3)). ## v0.1.1, 2016-02-09 Initial version. pwhash-1.0.0/Cargo.toml0000644000000023751377440505500104410ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "pwhash" version = "1.0.0" authors = ["Ivan Nejgebauer "] description = "A collection of password hashing routines in pure Rust." homepage = "https://github.com/inejge/pwhash" documentation = "https://inejge.github.io/pwhash" readme = "README.md" keywords = ["password", "hash", "hashing", "crypt"] categories = ["cryptography", "authentication"] license = "MIT" repository = "https://github.com/inejge/pwhash" [dependencies.blowfish] version = "0.7" features = ["bcrypt"] [dependencies.byteorder] version = "1" [dependencies.hmac] version = "0.10" [dependencies.md-5] version = "0.9" [dependencies.rand] version = "0.8" [dependencies.sha-1] version = "0.9" [dependencies.sha2] version = "0.9" pwhash-1.0.0/Cargo.toml.orig010064400017500000144000000011501377440265600141210ustar0000000000000000[package] name = "pwhash" version = "1.0.0" authors = ["Ivan Nejgebauer "] description = "A collection of password hashing routines in pure Rust." documentation = "https://inejge.github.io/pwhash" homepage = "https://github.com/inejge/pwhash" repository = "https://github.com/inejge/pwhash" readme = "README.md" license = "MIT" keywords = ["password", "hash", "hashing", "crypt"] categories = ["cryptography", "authentication"] edition = "2018" [dependencies] md-5 = "0.9" sha-1 = "0.9" sha2 = "0.9" blowfish = { version = "0.7", features = ["bcrypt"] } hmac = "0.10" byteorder = "1" rand = "0.8" pwhash-1.0.0/LICENSE-MIT010064400017500000144000000021151346365221200126560ustar0000000000000000The MIT License (MIT) Copyright (c) 2016 Ivan Nejgebauer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pwhash-1.0.0/README.md010064400017500000144000000022221377440263000125020ustar0000000000000000# pwhash A collection of password hashing and verification routines. See the [documentation](https://docs.rs/pwhash/1.0.0/pwhash/) for API reference. ## Getting Started Add the following to the `[dependencies]` section of your `Cargo.toml`: ```toml pwhash = "1" ``` ## Example ```rust use pwhash::bcrypt; // Hash a password with default parameters. let h_new = bcrypt::hash("password").unwrap(); // Verify a password against an existing hash. let h = "$2y$05$bvIG6Nmid91Mu9RcmmWZfO\ 5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe"; assert!(bcrypt::verify("password", h)); ``` ## Summary The following algorithms are currently implemented (in alphabetical order): * bcrypt * bsdi_crypt * md5_crypt * sha1_crypt * sha256_crypt * sha512_crypt * unix_crypt Each algorithm resides in its eponymous module, and provides the following interface: * `verify()`: verify a password against a hash. * `hash()`: hash a password with default algorithm-spacific parameters. * `hash_with()`: hash a password with customized parameters. There is also a convenience module `unix` which provides the functions `unix::crypt`, a __crypt__(3) work-alike, and `unix::verify`. pwhash-1.0.0/src/bcrypt.rs010064400017500000144000000241251377440153700136760ustar0000000000000000//! Standard *BSD hash. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! Bcrypt is a hashing algorithm based on the Blowfish stream cipher, //! originally developed for OpenBSD and since adopted on other BSD //! variants and other systems. It has a large salt, variable number //! of rounds, and no known weaknesses. //! //! # Examples //! //! To hash a password with a randomly generated salt, default cost, //! and default output variant (__2b__): //! //! ``` //! use pwhash::bcrypt; //! //! let hash = bcrypt::hash("password").unwrap(); //! ``` //! //! To use a different variant (__2y__), while letting the program //! pick the salt and use the default cost: //! //! ``` //! use pwhash::bcrypt::{self, BcryptSetup, BcryptVariant}; //! //! let hash = bcrypt::hash_with(BcryptSetup { //! variant: Some(BcryptVariant::V2y), //! ..Default::default() }, //! "password").unwrap(); //! ``` //! //! # Parameters //! //! * __Password length__: up to 72 characters. Longer passwords are //! truncated to the maximum length. //! //! * __Salt length__: 16 random bytes, encoded as 22 Base64 characters. //! //! * __Cost__: logarithmic value between 4 and 31, inclusive. Increasing //! the value by 1 doubles the amount of work. The default is 8. //! //! # Hash Format //! //! The format of the hash is //! **`$`**_`{variant}`_**`$`**_`{cost}`_**`$`**_`{salt}{checksum}`_, where: //! //! * _`{variant}`_ is one of **2a**, **2b**, or **2y**. The default is **2b**. //! The actual computation is the same for all three variants; the choice //! exists in order to retain compatibility with other software. See //! [`BcryptVariant`](enum.BcryptVariant.html) for details. //! //! * _`{cost}`_ is a two-digit decimal cost value between 4 and 31. Values //! below 10 have a leading zero. //! //! * _`{salt}`_ is a 22-character Base64 encoding of the 16 bytes of salt. The //! salt must be exactly this long. //! //! * _`{checksum}`_ is a 31-character Base64 encoding of the computed hash. use super::{Result, HashSetup, consteq}; use crate::enc_dec::{bcrypt_hash64_encode,bcrypt_hash64_decode}; use crate::error::Error; use crate::random; use crate::parse::{self, HashIterator}; use std::{iter, fmt}; use std::cmp::min; use std::default::Default; use blowfish::Blowfish; use byteorder::{BE, ByteOrder}; const MAX_PASS_LEN: usize = 72; const DEFAULT_VARIANT: BcryptVariant = BcryptVariant::V2b; const ENC_SALT_LEN: usize = 22; /// Minimum cost. pub const MIN_COST: u32 = 4; /// Maximum cost. pub const MAX_COST: u32 = 31; /// Default cost. pub const DEFAULT_COST: u32 = 10; /// Identifiers of algorithm variants which can be produced. /// /// Bcrypt has a long history of use, during which a number bugs were found /// and fixed in the widely-used implementations. Some bugs were serious /// enough to warrant a change in the minor version number of the algorithm /// identifier. /// /// There are two major bcrypt implementations: OpenBSD (the original, used in /// all *BSDs) and Openwall. A short history of variants is as follows: /// /// * **2** is the original OpenBSD version, which was very quickly replaced by /// /// * **2a**, which fixed a bug that caused passwords with repeated strings to /// produce the same hash as those with a single string ("abab" hashed the same /// as "ab".) This was the most widely used version, until /// /// * **2y**, produced by Openwall, which fixed a sign-extension bug that /// caused certain passwords with high-bit-set characters to produce weak keys. /// OpenBSD didn't have this bug, and their logic can transparently handle the /// **2y** hashes. The Openwall fix also introduced /// /// * **2x**, meant for unambiguously identifying pre-fix **2a** hashes as /// those produced by the buggy algorithm. OpenBSD doesn't treat **2x** hashes /// specially, which means that it won't be able to verify buggy hashes. Some /// time later, a wraparound bug was found in OpenBSD, leading to /// /// * **2b**, which fixed the bug. As the problem involved unrealistically long /// passwords, the bug was, fortunately, mostly theoretical. This variant is the /// current default in most implementations. /// /// This crate has a single bcrypt algorithm implementation which is equivalent /// to the **2b** variant. It accepts **2a** and **2y** on input, and can /// generate both on output, but doesn't treat them specially in any way. pub enum BcryptVariant { /// Second OpenBSD variant, fixed repeated string hashing. V2a, /// Third OpenBSD variant, fixed a wraparound bug. V2b, /// Openwall variant, fixed a sign extension bug. V2y, } impl fmt::Display for BcryptVariant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", match *self { BcryptVariant::V2a => "2a", BcryptVariant::V2b => "2b", BcryptVariant::V2y => "2y", }) } } /// Setup struct for bcrypt. /// /// In addition to custom salt and cost values, a bcrypt hash can use different /// algorithm variant identifiers. pub struct BcryptSetup<'a> { /// Custom salt. pub salt: Option<&'a str>, /// Custom cost. pub cost: Option, /// Algorithm variant. pub variant: Option, } /// A trait for converting a type into a `BcryptSetup` struct. pub trait IntoBcryptSetup<'a> { /// The conversion function. fn into_bcrypt_setup(self) -> Result>; } const MAGIC_LEN: usize = 4; impl<'a> IntoBcryptSetup<'a> for &'a str { fn into_bcrypt_setup(self) -> Result> { let mut hs = parse::HashSlice::new(self); let variant = match hs.take(MAGIC_LEN).unwrap_or("X") { "$2a$" => BcryptVariant::V2a, "$2b$" => BcryptVariant::V2b, "$2y$" => BcryptVariant::V2y, _ => return Err(Error::InvalidHashString), }; let cost = if let Some(cost_str) = hs.take_until(b'$') { if cost_str.len() != 2 { return Err(Error::InvalidHashString); } let cost = cost_str.parse::().map_err(|_e| Error::InvalidRounds)?; if cost < 10 && !cost_str.starts_with('0') { return Err(Error::InvalidHashString); } cost } else { return Err(Error::InvalidHashString); }; let salt = if let Some(salt) = hs.take(ENC_SALT_LEN) { salt } else { return Err(Error::InvalidHashString); }; Ok(BcryptSetup { salt: Some(salt), cost: Some(cost), variant: Some(variant) }) } } impl<'a> IntoBcryptSetup<'a> for HashSetup<'a> { fn into_bcrypt_setup(self) -> Result> { Ok(BcryptSetup { salt: self.salt, cost: self.rounds, variant: Some(DEFAULT_VARIANT) }) } } impl<'a> IntoBcryptSetup<'a> for BcryptSetup<'a> { fn into_bcrypt_setup(self) -> Result> { Ok(self) } } impl<'a> Default for BcryptSetup<'a> { fn default() -> Self { BcryptSetup { salt: None, cost: Some(DEFAULT_COST), variant: Some(DEFAULT_VARIANT) } } } fn bcrypt(cost: u32, salt: &[u8], password: &[u8], output: &mut [u8]) { assert!(cost < 32); assert!(salt.len() == 16); assert!(password.len() <= 72 && !password.is_empty()); assert!(output.len() == 24); let mut state = Blowfish::bc_init_state(); state.salted_expand_key(salt, password); for _ in 0..1u32 << cost { state.bc_expand_key(password); state.bc_expand_key(salt); } let mut ctext = [0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274]; for i in (0..6).step_by(2) { for _ in 0..64 { let (l, r) = state.bc_encrypt(ctext[i], ctext[i+1]); ctext[i] = l; ctext[i+1] = r; } BE::write_u32(&mut output[i*4..(i+1)*4], ctext[i]); BE::write_u32(&mut output[(i+1)*4..(i+2)*4], ctext[i+1]); } } fn do_bcrypt(pass: &[u8], salt: &[u8], cost: u32, variant: BcryptVariant) -> Result { let mut upd_pass = pass.iter().copied().chain(iter::repeat(0u8)).take(min(pass.len() + 1, MAX_PASS_LEN)).collect::>(); let mut output = [0u8; 24]; bcrypt(cost, &salt, &upd_pass[..], &mut output); for b in &mut upd_pass { *b = 0u8; } Ok(format!("${}${:02}${}{}", variant, cost, bcrypt_hash64_encode(&salt), bcrypt_hash64_encode(&output[..23]))) } /// Hash a password with a randomly generated salt, default cost, /// and default variant. /// /// An error is returned if the system random number generator cannot /// be opened. pub fn hash>(pass: B) -> Result { let mut salt_buf = [0u8; 16]; random::gen_salt_bytes(&mut salt_buf); do_bcrypt(pass.as_ref(), &salt_buf, DEFAULT_COST, DEFAULT_VARIANT) } /// Hash a password with user-provided parameters. /// /// Bcrypt has its own setup struct because of the additional variant /// field. An ordinary `HashSetup` can be converted into `BcryptSetup`, which /// will set the variant to default. The `Default` trait is implemented for /// `BcryptSetup`, which makes it easier to initialize just the desired /// fields (see the module-level example.) pub fn hash_with<'a, IBS, B>(param: IBS, pass: B) -> Result where IBS: IntoBcryptSetup<'a>, B: AsRef<[u8]> { let bs = param.into_bcrypt_setup()?; let cost = if let Some(c) = bs.cost { if c < MIN_COST || c > MAX_COST { return Err(Error::InvalidRounds); } c } else { DEFAULT_COST }; let variant = if let Some(v) = bs.variant { v } else { DEFAULT_VARIANT }; let mut salt_buf = [0u8; 16]; if bs.salt.is_some() { bcrypt_hash64_decode(bs.salt.unwrap(), &mut salt_buf)?; } else { random::gen_salt_bytes(&mut salt_buf); } do_bcrypt(pass.as_ref(), &salt_buf, cost, variant) } /// Verify that the hash corresponds to a password. pub fn verify>(pass: B, hash: &str) -> bool { consteq(hash, hash_with(hash, pass)) } #[cfg(test)] mod tests { use super::{BcryptSetup, BcryptVariant}; #[test] fn variant() { assert_eq!("$2y$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe", super::hash_with(BcryptSetup { salt: Some("bvIG6Nmid91Mu9RcmmWZfO"), cost: Some(5), variant: Some(BcryptVariant::V2y) }, "password").unwrap()); } } pwhash-1.0.0/src/bsdi_crypt.rs010064400017500000144000000105701377437446300145430ustar0000000000000000//! Enhanced DES-based hash. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! This algorithm was developed by BSDi for BSD/OS as an extension of //! the traditional Unix __crypt__(3). It supports longer passwords, larger //! salt, and a variable number of rounds. Despite that, the algorithm //! is considered weak and is not recommended for new passwords. //! //! # Example //! //! ``` //! use pwhash::{bsdi_crypt, HashSetup}; //! //! assert_eq!(bsdi_crypt::hash_with( //! HashSetup { salt: Some("K0Ay"), rounds: Some(7250) }, //! "password").unwrap(), //! "_Gl/.K0Ay.aosctsbJ1k"); //! ``` //! //! # Parameters //! //! * __Password length__: unlimited. //! //! * __Salt length__: 4 characters (24 bits). //! //! * __Rounds__: 1 to 224-1. Default is 7250. //! //! # Hash Format //! //! The format of the hash is __`_`__*`{rounds}{salt}{checksum}`*, where: //! //! * *`{rounds}`* is a 4-character Base64 encoding of the number of rounds. //! //! * *`{salt}`* is a 4-character Base64 encoding of the salt. //! //! * *`{checksum}`* is a 11-character Base64 encoding of the checksum. use super::{Result, HashSetup, IntoHashSetup, consteq}; use crate::des_crypt::bsdi_crypt; use crate::enc_dec::decode_val; use crate::error::Error; use crate::random; use crate::parse::{self, HashIterator}; const MIN_ROUNDS: u32 = 1; const MAX_ROUNDS: u32 = (1 << 24) - 1; /// Default number of rounds. /// /// The value is aligned with the default used on NetBSD. pub const DEFAULT_ROUNDS: u32 = 7250; /// Salt length. pub const SALT_LEN: usize = 4; const ROUNDS_LEN: usize = 4; /// Hash a password with a randomly generated salt and the default /// number of rounds. /// /// An error is returned if the system random number generator cannot /// be opened. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash>(pass: B) -> Result { let saltstr = random::gen_salt_str(SALT_LEN); bsdi_crypt(pass.as_ref(), &saltstr, DEFAULT_ROUNDS) } fn parse_bsdi_hash(hash: &str) -> Result { let mut hs = parse::HashSlice::new(hash); if hs.take(1).unwrap_or("X") != "_" { return Err(Error::InvalidHashString); } let rounds = if let Some(rounds_enc) = hs.take(ROUNDS_LEN) { decode_val(rounds_enc, SALT_LEN)? } else { return Err(Error::InvalidHashString); }; let salt = if let Some(salt) = hs.take(SALT_LEN) { salt } else { return Err(Error::InvalidHashString); }; Ok(HashSetup { salt: Some(salt), rounds: Some(rounds) }) } /// Hash a password with user-provided parameters. /// /// If the `param` argument is a `&str`, it must be in the final hash /// format. The number of rounds and the salt are parsed out of that value. /// An error is returned if the salt is too short or contains an invalid /// character. An out-of-range rounds value will also result in an error. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash_with<'a, IHS, B>(param: IHS, pass: B) -> Result where IHS: IntoHashSetup<'a>, B: AsRef<[u8]> { let hs = IHS::into_hash_setup(param, parse_bsdi_hash)?; let rounds = if let Some(r) = hs.rounds { if r < MIN_ROUNDS || r > MAX_ROUNDS { return Err(Error::InvalidRounds); } r } else { DEFAULT_ROUNDS }; if hs.salt.is_some() { bsdi_crypt(pass.as_ref(), hs.salt.unwrap(), rounds) } else { let saltstr = random::gen_salt_str(SALT_LEN); bsdi_crypt(pass.as_ref(), &saltstr, rounds) } } /// Verify that the hash corresponds to a password. pub fn verify>(pass: B, hash: &str) -> bool { #[allow(deprecated)] consteq(hash, hash_with(hash, pass)) } #[cfg(test)] mod tests { use super::HashSetup; #[test] #[allow(deprecated)] fn custom() { assert_eq!(super::hash_with(HashSetup { salt: Some("K0Ay"), rounds: None }, "password").unwrap(), "_Gl/.K0Ay.aosctsbJ1k"); assert_eq!(super::hash_with("_Gl/.K0Ay.aosctsbJ1k", "password").unwrap(), "_Gl/.K0Ay.aosctsbJ1k"); } #[test] #[allow(deprecated)] #[should_panic(expected="value: InvalidRounds")] fn bad_rounds() { let _ = super::hash_with(HashSetup { salt: Some("K0Ay"), rounds: Some(0) }, "password").unwrap(); } } pwhash-1.0.0/src/des_crypt.rs010064400017500000144000001212271377437446300143770ustar0000000000000000// Historic Unix crypt(3) password hashing routines. // // Rust version Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. // // Original copyright/license notices follow: // // @(#)UnixCrypt.java 0.9 96/11/25 // // Copyright (c) 1996 Aki Yoshida. All rights reserved. // // Permission to use, copy, modify and distribute this software // for non-commercial or commercial purposes and without fee is // hereby granted provided that this copyright notice appears in // all copies. // --- // Unix crypt(3C) utility // @version 0.9, 11/25/96 // @author Aki Yoshida // --- // modified April 2001 // by Iris Van den Broeke, Daniel Deville // --- // Unix Crypt. // Implements the one way cryptography used by Unix systems for // simple password protection. // @version $Id: UnixCrypt2.txt,v 1.1.1.1 2005/09/13 22:20:13 christos Exp $ // @author Greg Wilkins (gregw) const PC1ROT: [[u64; 16]; 16] = [ [ 0x0000000000000000, 0x0000000000000000, 0x0000010000000000, 0x0000010000000000, 0x0000000100000000, 0x0000000100000000, 0x0000010100000000, 0x0000010100000000, 0x0000000000100000, 0x0000000000100000, 0x0000010000100000, 0x0000010000100000, 0x0000000100100000, 0x0000000100100000, 0x0000010100100000, 0x0000010100100000, ], [ 0x0000000000000000, 0x0000000080000000, 0x0000040000000000, 0x0000040080000000, 0x0010000000000000, 0x0010000080000000, 0x0010040000000000, 0x0010040080000000, 0x0000000800000000, 0x0000000880000000, 0x0000040800000000, 0x0000040880000000, 0x0010000800000000, 0x0010000880000000, 0x0010040800000000, 0x0010040880000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000004000, 0x0000000000004000, 0x0000000000000008, 0x0000000000000008, 0x0000000000004008, 0x0000000000004008, 0x0000000000000010, 0x0000000000000010, 0x0000000000004010, 0x0000000000004010, 0x0000000000000018, 0x0000000000000018, 0x0000000000004018, 0x0000000000004018, ], [ 0x0000000000000000, 0x0000000200000000, 0x0001000000000000, 0x0001000200000000, 0x0400000000000000, 0x0400000200000000, 0x0401000000000000, 0x0401000200000000, 0x0020000000000000, 0x0020000200000000, 0x0021000000000000, 0x0021000200000000, 0x0420000000000000, 0x0420000200000000, 0x0421000000000000, 0x0421000200000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000400000, 0x0000000000400000, 0x0000000004000000, 0x0000000004000000, 0x0000000004400000, 0x0000000004400000, 0x0000000000000800, 0x0000000000000800, 0x0000000000400800, 0x0000000000400800, 0x0000000004000800, 0x0000000004000800, 0x0000000004400800, 0x0000000004400800, ], [ 0x0000000000000000, 0x0000000000008000, 0x0040000000000000, 0x0040000000008000, 0x0000004000000000, 0x0000004000008000, 0x0040004000000000, 0x0040004000008000, 0x8000000000000000, 0x8000000000008000, 0x8040000000000000, 0x8040000000008000, 0x8000004000000000, 0x8000004000008000, 0x8040004000000000, 0x8040004000008000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000080, 0x0000000000000080, 0x0000000000080000, 0x0000000000080000, 0x0000000000080080, 0x0000000000080080, 0x0000000000800000, 0x0000000000800000, 0x0000000000800080, 0x0000000000800080, 0x0000000000880000, 0x0000000000880000, 0x0000000000880080, 0x0000000000880080, ], [ 0x0000000000000000, 0x0000000008000000, 0x0000002000000000, 0x0000002008000000, 0x0000100000000000, 0x0000100008000000, 0x0000102000000000, 0x0000102008000000, 0x0000200000000000, 0x0000200008000000, 0x0000202000000000, 0x0000202008000000, 0x0000300000000000, 0x0000300008000000, 0x0000302000000000, 0x0000302008000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000010000000, 0x0000000010000000, 0x0000000000001000, 0x0000000000001000, 0x0000000010001000, 0x0000000010001000, 0x0000000040000000, 0x0000000040000000, 0x0000000050000000, 0x0000000050000000, 0x0000000040001000, 0x0000000040001000, 0x0000000050001000, 0x0000000050001000, ], [ 0x0000000000000000, 0x0000001000000000, 0x0000080000000000, 0x0000081000000000, 0x1000000000000000, 0x1000001000000000, 0x1000080000000000, 0x1000081000000000, 0x0004000000000000, 0x0004001000000000, 0x0004080000000000, 0x0004081000000000, 0x1004000000000000, 0x1004001000000000, 0x1004080000000000, 0x1004081000000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000040000, 0x0000000000040000, 0x0000020000000000, 0x0000020000000000, 0x0000020000040000, 0x0000020000040000, 0x0000000000000004, 0x0000000000000004, 0x0000000000040004, 0x0000000000040004, 0x0000020000000004, 0x0000020000000004, 0x0000020000040004, 0x0000020000040004, ], [ 0x0000000000000000, 0x0000400000000000, 0x0200000000000000, 0x0200400000000000, 0x0080000000000000, 0x0080400000000000, 0x0280000000000000, 0x0280400000000000, 0x0000008000000000, 0x0000408000000000, 0x0200008000000000, 0x0200408000000000, 0x0080008000000000, 0x0080408000000000, 0x0280008000000000, 0x0280408000000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000040, 0x0000000000000040, 0x0000000020000000, 0x0000000020000000, 0x0000000020000040, 0x0000000020000040, 0x0000000000200000, 0x0000000000200000, 0x0000000000200040, 0x0000000000200040, 0x0000000020200000, 0x0000000020200000, 0x0000000020200040, 0x0000000020200040, ], [ 0x0000000000000000, 0x0002000000000000, 0x0800000000000000, 0x0802000000000000, 0x0100000000000000, 0x0102000000000000, 0x0900000000000000, 0x0902000000000000, 0x4000000000000000, 0x4002000000000000, 0x4800000000000000, 0x4802000000000000, 0x4100000000000000, 0x4102000000000000, 0x4900000000000000, 0x4902000000000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000002000, 0x0000000000002000, 0x0000000000000020, 0x0000000000000020, 0x0000000000002020, 0x0000000000002020, 0x0000000000000400, 0x0000000000000400, 0x0000000000002400, 0x0000000000002400, 0x0000000000000420, 0x0000000000000420, 0x0000000000002420, 0x0000000000002420, ], [ 0x0000000000000000, 0x2000000000000000, 0x0000000400000000, 0x2000000400000000, 0x0000800000000000, 0x2000800000000000, 0x0000800400000000, 0x2000800400000000, 0x0008000000000000, 0x2008000000000000, 0x0008000400000000, 0x2008000400000000, 0x0008800000000000, 0x2008800000000000, 0x0008800400000000, 0x2008800400000000, ], ]; const PC2ROT: [[[u64; 16]; 16]; 2] = [ [ [ 0x0000000000000000, 0x0000800000000000, 0x0800000000000000, 0x0800800000000000, 0x0000004000000000, 0x0000804000000000, 0x0800004000000000, 0x0800804000000000, 0x0000000400000000, 0x0000800400000000, 0x0800000400000000, 0x0800800400000000, 0x0000004400000000, 0x0000804400000000, 0x0800004400000000, 0x0800804400000000, ], [ 0x0000000000000000, 0x0080000000000000, 0x0000040000000000, 0x0080040000000000, 0x0008000000000000, 0x0088000000000000, 0x0008040000000000, 0x0088040000000000, 0x0000200000000000, 0x0080200000000000, 0x0000240000000000, 0x0080240000000000, 0x0008200000000000, 0x0088200000000000, 0x0008240000000000, 0x0088240000000000, ], [ 0x0000000000000000, 0x0040000000000000, 0x2000000000000000, 0x2040000000000000, 0x0000008000000000, 0x0040008000000000, 0x2000008000000000, 0x2040008000000000, 0x0000001000000000, 0x0040001000000000, 0x2000001000000000, 0x2040001000000000, 0x0000009000000000, 0x0040009000000000, 0x2000009000000000, 0x2040009000000000, ], [ 0x0000000000000000, 0x0400000000000000, 0x8000000000000000, 0x8400000000000000, 0x0000002000000000, 0x0400002000000000, 0x8000002000000000, 0x8400002000000000, 0x0100000000000000, 0x0500000000000000, 0x8100000000000000, 0x8500000000000000, 0x0100002000000000, 0x0500002000000000, 0x8100002000000000, 0x8500002000000000, ], [ 0x0000000000000000, 0x0000000000004000, 0x0000000020000000, 0x0000000020004000, 0x0001000000000000, 0x0001000000004000, 0x0001000020000000, 0x0001000020004000, 0x0200000000000000, 0x0200000000004000, 0x0200000020000000, 0x0200000020004000, 0x0201000000000000, 0x0201000000004000, 0x0201000020000000, 0x0201000020004000, ], [ 0x0000000000000000, 0x1000000000000000, 0x0004000000000000, 0x1004000000000000, 0x0002000000000000, 0x1002000000000000, 0x0006000000000000, 0x1006000000000000, 0x0000000800000000, 0x1000000800000000, 0x0004000800000000, 0x1004000800000000, 0x0002000800000000, 0x1002000800000000, 0x0006000800000000, 0x1006000800000000, ], [ 0x0000000000000000, 0x0000000000000008, 0x0000000000008000, 0x0000000000008008, 0x0010000000000000, 0x0010000000000008, 0x0010000000008000, 0x0010000000008008, 0x0020000000000000, 0x0020000000000008, 0x0020000000008000, 0x0020000000008008, 0x0030000000000000, 0x0030000000000008, 0x0030000000008000, 0x0030000000008008, ], [ 0x0000000000000000, 0x0000400000000000, 0x0000080000000000, 0x0000480000000000, 0x0000100000000000, 0x0000500000000000, 0x0000180000000000, 0x0000580000000000, 0x4000000000000000, 0x4000400000000000, 0x4000080000000000, 0x4000480000000000, 0x4000100000000000, 0x4000500000000000, 0x4000180000000000, 0x4000580000000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000100000, 0x0000000000100000, 0x0000000000100000, 0x0000000000100000, 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, ], [ 0x0000000000000000, 0x0000000000040000, 0x0000000000000020, 0x0000000000040020, 0x0000000000000004, 0x0000000000040004, 0x0000000000000024, 0x0000000000040024, 0x0000000200000000, 0x0000000200040000, 0x0000000200000020, 0x0000000200040020, 0x0000000200000004, 0x0000000200040004, 0x0000000200000024, 0x0000000200040024, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000040, 0x0000000000000040, 0x0000000000000040, 0x0000000000000040, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, ], [ 0x0000000000000000, 0x0000000000000010, 0x0000000000000400, 0x0000000000000410, 0x0000000000000080, 0x0000000000000090, 0x0000000000000480, 0x0000000000000490, 0x0000000040000000, 0x0000000040000010, 0x0000000040000400, 0x0000000040000410, 0x0000000040000080, 0x0000000040000090, 0x0000000040000480, 0x0000000040000490, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000100000000, 0x0000000100000000, 0x0000000100000000, 0x0000000100000000, 0x0000000000800000, 0x0000000000800000, 0x0000000000800000, 0x0000000000800000, 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, ], [ 0x0000000000000000, 0x0000020000000000, 0x0000000080000000, 0x0000020080000000, 0x0000000000400000, 0x0000020000400000, 0x0000000080400000, 0x0000020080400000, 0x0000000008000000, 0x0000020008000000, 0x0000000088000000, 0x0000020088000000, 0x0000000008400000, 0x0000020008400000, 0x0000000088400000, 0x0000020088400000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000200000, 0x0000000000200000, 0x0000000000200000, 0x0000000000200000, 0x0000000004000000, 0x0000000004000000, 0x0000000004000000, 0x0000000004000000, 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, ], [ 0x0000000000000000, 0x0000000000000800, 0x0000010000000000, 0x0000010000000800, 0x0000000000002000, 0x0000000000002800, 0x0000010000002000, 0x0000010000002800, 0x0000000010000000, 0x0000000010000800, 0x0000010010000000, 0x0000010010000800, 0x0000000010002000, 0x0000000010002800, 0x0000010010002000, 0x0000010010002800, ], ], [ [ 0x0000000000000000, 0x0000000800000000, 0x0000000400000000, 0x0000000c00000000, 0x0000100000000000, 0x0000100800000000, 0x0000100400000000, 0x0000100c00000000, 0x0010000000000000, 0x0010000800000000, 0x0010000400000000, 0x0010000c00000000, 0x0010100000000000, 0x0010100800000000, 0x0010100400000000, 0x0010100c00000000, ], [ 0x0000000000000000, 0x0100000000000000, 0x0001000000000000, 0x0101000000000000, 0x0000001000000000, 0x0100001000000000, 0x0001001000000000, 0x0101001000000000, 0x0004000000000000, 0x0104000000000000, 0x0005000000000000, 0x0105000000000000, 0x0004001000000000, 0x0104001000000000, 0x0005001000000000, 0x0105001000000000, ], [ 0x0000000000000000, 0x0000002000000000, 0x0000040000000000, 0x0000042000000000, 0x4000000000000000, 0x4000002000000000, 0x4000040000000000, 0x4000042000000000, 0x0000400000000000, 0x0000402000000000, 0x0000440000000000, 0x0000442000000000, 0x4000400000000000, 0x4000402000000000, 0x4000440000000000, 0x4000442000000000, ], [ 0x0000000000000000, 0x0000004000000000, 0x0000200000000000, 0x0000204000000000, 0x0000080000000000, 0x0000084000000000, 0x0000280000000000, 0x0000284000000000, 0x0000800000000000, 0x0000804000000000, 0x0000a00000000000, 0x0000a04000000000, 0x0000880000000000, 0x0000884000000000, 0x0000a80000000000, 0x0000a84000000000, ], [ 0x0000000000000000, 0x0000000000400000, 0x0000000000000020, 0x0000000000400020, 0x0040000000000000, 0x0040000000400000, 0x0040000000000020, 0x0040000000400020, 0x0800000000000000, 0x0800000000400000, 0x0800000000000020, 0x0800000000400020, 0x0840000000000000, 0x0840000000400000, 0x0840000000000020, 0x0840000000400020, ], [ 0x0000000000000000, 0x0080000000000000, 0x0000008000000000, 0x0080008000000000, 0x2000000000000000, 0x2080000000000000, 0x2000008000000000, 0x2080008000000000, 0x0020000000000000, 0x00a0000000000000, 0x0020008000000000, 0x00a0008000000000, 0x2020000000000000, 0x20a0000000000000, 0x2020008000000000, 0x20a0008000000000, ], [ 0x0000000000000000, 0x0000000004000000, 0x0000000008000000, 0x000000000c000000, 0x0400000000000000, 0x0400000004000000, 0x0400000008000000, 0x040000000c000000, 0x8000000000000000, 0x8000000004000000, 0x8000000008000000, 0x800000000c000000, 0x8400000000000000, 0x8400000004000000, 0x8400000008000000, 0x840000000c000000, ], [ 0x0000000000000000, 0x0002000000000000, 0x0200000000000000, 0x0202000000000000, 0x1000000000000000, 0x1002000000000000, 0x1200000000000000, 0x1202000000000000, 0x0008000000000000, 0x000a000000000000, 0x0208000000000000, 0x020a000000000000, 0x1008000000000000, 0x100a000000000000, 0x1208000000000000, 0x120a000000000000, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000000010, 0x0000000000000010, 0x0000000000000010, 0x0000000000000010, 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, ], [ 0x0000000000000000, 0x0000000000000040, 0x0000010000000000, 0x0000010000000040, 0x0000000000200000, 0x0000000000200040, 0x0000010000200000, 0x0000010000200040, 0x0000000000008000, 0x0000000000008040, 0x0000010000008000, 0x0000010000008040, 0x0000000000208000, 0x0000000000208040, 0x0000010000208000, 0x0000010000208040, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000002000, 0x0000000000002000, 0x0000000000002000, 0x0000000000002000, 0x0000020000000000, 0x0000020000000000, 0x0000020000000000, 0x0000020000000000, 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, ], [ 0x0000000000000000, 0x0000000000000800, 0x0000000100000000, 0x0000000100000800, 0x0000000010000000, 0x0000000010000800, 0x0000000110000000, 0x0000000110000800, 0x0000000000000004, 0x0000000000000804, 0x0000000100000004, 0x0000000100000804, 0x0000000010000004, 0x0000000010000804, 0x0000000110000004, 0x0000000110000804, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000008, 0x0000000000000008, 0x0000000000000008, 0x0000000000000008, 0x0000000040000000, 0x0000000040000000, 0x0000000040000000, 0x0000000040000000, 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, ], [ 0x0000000000000000, 0x0000000020000000, 0x0000000200000000, 0x0000000220000000, 0x0000000000000080, 0x0000000020000080, 0x0000000200000080, 0x0000000220000080, 0x0000000000100000, 0x0000000020100000, 0x0000000200100000, 0x0000000220100000, 0x0000000000100080, 0x0000000020100080, 0x0000000200100080, 0x0000000220100080, ], [ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000400, 0x0000000000000400, 0x0000000000000400, 0x0000000000000400, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, ], [ 0x0000000000000000, 0x0000000000800000, 0x0000000000004000, 0x0000000000804000, 0x0000000080000000, 0x0000000080800000, 0x0000000080004000, 0x0000000080804000, 0x0000000000040000, 0x0000000000840000, 0x0000000000044000, 0x0000000000844000, 0x0000000080040000, 0x0000000080840000, 0x0000000080044000, 0x0000000080844000, ], ] ]; #[allow(non_upper_case_globals)] const Rotates: [usize; 16] = [ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 ]; const IE3264: [[u64; 16]; 8] = [ [ 0x0000000000000000, 0x0000000000004004, 0x0400000000000040, 0x0400000000004044, 0x0000000040040000, 0x0000000040044004, 0x0400000040040040, 0x0400000040044044, 0x0000000000400400, 0x0000000000404404, 0x0400000000400440, 0x0400000000404444, 0x0000000040440400, 0x0000000040444404, 0x0400000040440440, 0x0400000040444444, ], [ 0x0000000000000000, 0x0000400400000000, 0x0000004004000000, 0x0000404404000000, 0x4004000000000000, 0x4004400400000000, 0x4004004004000000, 0x4004404404000000, 0x0040040000000000, 0x0040440400000000, 0x0040044004000000, 0x0040444404000000, 0x4044040000000000, 0x4044440400000000, 0x4044044004000000, 0x4044444404000000, ], [ 0x0000000000000000, 0x0000000000002000, 0x0000000000000020, 0x0000000000002020, 0x0000000020000000, 0x0000000020002000, 0x0000000020000020, 0x0000000020002020, 0x0000000000200000, 0x0000000000202000, 0x0000000000200020, 0x0000000000202020, 0x0000000020200000, 0x0000000020202000, 0x0000000020200020, 0x0000000020202020, ], [ 0x0000000000000000, 0x0000200000000000, 0x0000002000000000, 0x0000202000000000, 0x2000000000000000, 0x2000200000000000, 0x2000002000000000, 0x2000202000000000, 0x0020000000000000, 0x0020200000000000, 0x0020002000000000, 0x0020202000000000, 0x2020000000000000, 0x2020200000000000, 0x2020002000000000, 0x2020202000000000, ], [ 0x0000000000000000, 0x0000000000001000, 0x0000000000000010, 0x0000000000001010, 0x0000000010000000, 0x0000000010001000, 0x0000000010000010, 0x0000000010001010, 0x0000000000100000, 0x0000000000101000, 0x0000000000100010, 0x0000000000101010, 0x0000000010100000, 0x0000000010101000, 0x0000000010100010, 0x0000000010101010, ], [ 0x0000000000000000, 0x0000100000000000, 0x0000001000000000, 0x0000101000000000, 0x1000000000000000, 0x1000100000000000, 0x1000001000000000, 0x1000101000000000, 0x0010000000000000, 0x0010100000000000, 0x0010001000000000, 0x0010101000000000, 0x1010000000000000, 0x1010100000000000, 0x1010001000000000, 0x1010101000000000, ], [ 0x0000000000000000, 0x0000000000800800, 0x0000000000008008, 0x0000000000808808, 0x0000008008000000, 0x0000008008800800, 0x0000008008008008, 0x0000008008808808, 0x0000000080080000, 0x0000000080880800, 0x0000000080088008, 0x0000000080888808, 0x0000008088080000, 0x0000008088880800, 0x0000008088088008, 0x0000008088888808, ], [ 0x0000000000000000, 0x0080080000000000, 0x0000800800000000, 0x0080880800000000, 0x0800000000000080, 0x0880080000000080, 0x0800800800000080, 0x0880880800000080, 0x8008000000000000, 0x8088080000000000, 0x8008800800000000, 0x8088880800000000, 0x8808000000000080, 0x8888080000000080, 0x8808800800000080, 0x8888880800000080, ], ]; const CF6464: [[u64; 16]; 16] = [ [ 0x0000000000000000, 0x0000000000000040, 0x0000000000004000, 0x0000000000004040, 0x0000000000400000, 0x0000000000400040, 0x0000000000404000, 0x0000000000404040, 0x0000000040000000, 0x0000000040000040, 0x0000000040004000, 0x0000000040004040, 0x0000000040400000, 0x0000000040400040, 0x0000000040404000, 0x0000000040404040, ], [ 0x0000000000000000, 0x0000000000000004, 0x0000000000000400, 0x0000000000000404, 0x0000000000040000, 0x0000000000040004, 0x0000000000040400, 0x0000000000040404, 0x0000000004000000, 0x0000000004000004, 0x0000000004000400, 0x0000000004000404, 0x0000000004040000, 0x0000000004040004, 0x0000000004040400, 0x0000000004040404, ], [ 0x0000000000000000, 0x0000004000000000, 0x0000400000000000, 0x0000404000000000, 0x0040000000000000, 0x0040004000000000, 0x0040400000000000, 0x0040404000000000, 0x4000000000000000, 0x4000004000000000, 0x4000400000000000, 0x4000404000000000, 0x4040000000000000, 0x4040004000000000, 0x4040400000000000, 0x4040404000000000, ], [ 0x0000000000000000, 0x0000000400000000, 0x0000040000000000, 0x0000040400000000, 0x0004000000000000, 0x0004000400000000, 0x0004040000000000, 0x0004040400000000, 0x0400000000000000, 0x0400000400000000, 0x0400040000000000, 0x0400040400000000, 0x0404000000000000, 0x0404000400000000, 0x0404040000000000, 0x0404040400000000, ], [ 0x0000000000000000, 0x0000000000000010, 0x0000000000001000, 0x0000000000001010, 0x0000000000100000, 0x0000000000100010, 0x0000000000101000, 0x0000000000101010, 0x0000000010000000, 0x0000000010000010, 0x0000000010001000, 0x0000000010001010, 0x0000000010100000, 0x0000000010100010, 0x0000000010101000, 0x0000000010101010, ], [ 0x0000000000000000, 0x0000000000000001, 0x0000000000000100, 0x0000000000000101, 0x0000000000010000, 0x0000000000010001, 0x0000000000010100, 0x0000000000010101, 0x0000000001000000, 0x0000000001000001, 0x0000000001000100, 0x0000000001000101, 0x0000000001010000, 0x0000000001010001, 0x0000000001010100, 0x0000000001010101, ], [ 0x0000000000000000, 0x0000001000000000, 0x0000100000000000, 0x0000101000000000, 0x0010000000000000, 0x0010001000000000, 0x0010100000000000, 0x0010101000000000, 0x1000000000000000, 0x1000001000000000, 0x1000100000000000, 0x1000101000000000, 0x1010000000000000, 0x1010001000000000, 0x1010100000000000, 0x1010101000000000, ], [ 0x0000000000000000, 0x0000000100000000, 0x0000010000000000, 0x0000010100000000, 0x0001000000000000, 0x0001000100000000, 0x0001010000000000, 0x0001010100000000, 0x0100000000000000, 0x0100000100000000, 0x0100010000000000, 0x0100010100000000, 0x0101000000000000, 0x0101000100000000, 0x0101010000000000, 0x0101010100000000, ], [ 0x0000000000000000, 0x0000000000000080, 0x0000000000008000, 0x0000000000008080, 0x0000000000800000, 0x0000000000800080, 0x0000000000808000, 0x0000000000808080, 0x0000000080000000, 0x0000000080000080, 0x0000000080008000, 0x0000000080008080, 0x0000000080800000, 0x0000000080800080, 0x0000000080808000, 0x0000000080808080, ], [ 0x0000000000000000, 0x0000000000000008, 0x0000000000000800, 0x0000000000000808, 0x0000000000080000, 0x0000000000080008, 0x0000000000080800, 0x0000000000080808, 0x0000000008000000, 0x0000000008000008, 0x0000000008000800, 0x0000000008000808, 0x0000000008080000, 0x0000000008080008, 0x0000000008080800, 0x0000000008080808, ], [ 0x0000000000000000, 0x0000008000000000, 0x0000800000000000, 0x0000808000000000, 0x0080000000000000, 0x0080008000000000, 0x0080800000000000, 0x0080808000000000, 0x8000000000000000, 0x8000008000000000, 0x8000800000000000, 0x8000808000000000, 0x8080000000000000, 0x8080008000000000, 0x8080800000000000, 0x8080808000000000, ], [ 0x0000000000000000, 0x0000000800000000, 0x0000080000000000, 0x0000080800000000, 0x0008000000000000, 0x0008000800000000, 0x0008080000000000, 0x0008080800000000, 0x0800000000000000, 0x0800000800000000, 0x0800080000000000, 0x0800080800000000, 0x0808000000000000, 0x0808000800000000, 0x0808080000000000, 0x0808080800000000, ], [ 0x0000000000000000, 0x0000000000000020, 0x0000000000002000, 0x0000000000002020, 0x0000000000200000, 0x0000000000200020, 0x0000000000202000, 0x0000000000202020, 0x0000000020000000, 0x0000000020000020, 0x0000000020002000, 0x0000000020002020, 0x0000000020200000, 0x0000000020200020, 0x0000000020202000, 0x0000000020202020, ], [ 0x0000000000000000, 0x0000000000000002, 0x0000000000000200, 0x0000000000000202, 0x0000000000020000, 0x0000000000020002, 0x0000000000020200, 0x0000000000020202, 0x0000000002000000, 0x0000000002000002, 0x0000000002000200, 0x0000000002000202, 0x0000000002020000, 0x0000000002020002, 0x0000000002020200, 0x0000000002020202, ], [ 0x0000000000000000, 0x0000002000000000, 0x0000200000000000, 0x0000202000000000, 0x0020000000000000, 0x0020002000000000, 0x0020200000000000, 0x0020202000000000, 0x2000000000000000, 0x2000002000000000, 0x2000200000000000, 0x2000202000000000, 0x2020000000000000, 0x2020002000000000, 0x2020200000000000, 0x2020202000000000, ], [ 0x0000000000000000, 0x0000000200000000, 0x0000020000000000, 0x0000020200000000, 0x0002000000000000, 0x0002000200000000, 0x0002020000000000, 0x0002020200000000, 0x0200000000000000, 0x0200000200000000, 0x0200020000000000, 0x0200020200000000, 0x0202000000000000, 0x0202000200000000, 0x0202020000000000, 0x0202020200000000, ], ]; const SPE: [[u64; 64]; 8] = [ [ 0x0080088008200000, 0x0000008008000000, 0x0000000000200020, 0x0080088008200020, 0x0000000000200000, 0x0080088008000020, 0x0000008008000020, 0x0000000000200020, 0x0080088008000020, 0x0080088008200000, 0x0000008008200000, 0x0080080000000020, 0x0080080000200020, 0x0000000000200000, 0x0000000000000000, 0x0000008008000020, 0x0000008008000000, 0x0000000000000020, 0x0080080000200000, 0x0080088008000000, 0x0080088008200020, 0x0000008008200000, 0x0080080000000020, 0x0080080000200000, 0x0000000000000020, 0x0080080000000000, 0x0080088008000000, 0x0000008008200020, 0x0080080000000000, 0x0080080000200020, 0x0000008008200020, 0x0000000000000000, 0x0000000000000000, 0x0080088008200020, 0x0080080000200000, 0x0000008008000020, 0x0080088008200000, 0x0000008008000000, 0x0080080000000020, 0x0080080000200000, 0x0000008008200020, 0x0080080000000000, 0x0080088008000000, 0x0000000000200020, 0x0080088008000020, 0x0000000000000020, 0x0000000000200020, 0x0000008008200000, 0x0080088008200020, 0x0080088008000000, 0x0000008008200000, 0x0080080000200020, 0x0000000000200000, 0x0080080000000020, 0x0000008008000020, 0x0000000000000000, 0x0000008008000000, 0x0000000000200000, 0x0080080000200020, 0x0080088008200000, 0x0000000000000020, 0x0000008008200020, 0x0080080000000000, 0x0080088008000020, ], [ 0x1000800810004004, 0x0000000000000000, 0x0000800810000000, 0x0000000010004004, 0x1000000000004004, 0x1000800800000000, 0x0000800800004004, 0x0000800810000000, 0x0000800800000000, 0x1000000010004004, 0x1000000000000000, 0x0000800800004004, 0x1000000010000000, 0x0000800810004004, 0x0000000010004004, 0x1000000000000000, 0x0000000010000000, 0x1000800800004004, 0x1000000010004004, 0x0000800800000000, 0x1000800810000000, 0x0000000000004004, 0x0000000000000000, 0x1000000010000000, 0x1000800800004004, 0x1000800810000000, 0x0000800810004004, 0x1000000000004004, 0x0000000000004004, 0x0000000010000000, 0x1000800800000000, 0x1000800810004004, 0x1000000010000000, 0x0000800810004004, 0x0000800800004004, 0x1000800810000000, 0x1000800810004004, 0x1000000010000000, 0x1000000000004004, 0x0000000000000000, 0x0000000000004004, 0x1000800800000000, 0x0000000010000000, 0x1000000010004004, 0x0000800800000000, 0x0000000000004004, 0x1000800810000000, 0x1000800800004004, 0x0000800810004004, 0x0000800800000000, 0x0000000000000000, 0x1000000000004004, 0x1000000000000000, 0x1000800810004004, 0x0000800810000000, 0x0000000010004004, 0x1000000010004004, 0x0000000010000000, 0x1000800800000000, 0x0000800800004004, 0x1000800800004004, 0x1000000000000000, 0x0000000010004004, 0x0000800810000000, ], [ 0x0000000000400410, 0x0010004004400400, 0x0010000000000000, 0x0010000000400410, 0x0000004004000010, 0x0000000000400400, 0x0010000000400410, 0x0010004004000000, 0x0010000000400400, 0x0000004004000000, 0x0000004004400400, 0x0000000000000010, 0x0010004004400410, 0x0010000000000010, 0x0000000000000010, 0x0000004004400410, 0x0000000000000000, 0x0000004004000010, 0x0010004004400400, 0x0010000000000000, 0x0010000000000010, 0x0010004004400410, 0x0000004004000000, 0x0000000000400410, 0x0000004004400410, 0x0010000000400400, 0x0010004004000010, 0x0000004004400400, 0x0010004004000000, 0x0000000000000000, 0x0000000000400400, 0x0010004004000010, 0x0010004004400400, 0x0010000000000000, 0x0000000000000010, 0x0000004004000000, 0x0010000000000010, 0x0000004004000010, 0x0000004004400400, 0x0010000000400410, 0x0000000000000000, 0x0010004004400400, 0x0010004004000000, 0x0000004004400410, 0x0000004004000010, 0x0000000000400400, 0x0010004004400410, 0x0000000000000010, 0x0010004004000010, 0x0000000000400410, 0x0000000000400400, 0x0010004004400410, 0x0000004004000000, 0x0010000000400400, 0x0010000000400410, 0x0010004004000000, 0x0010000000400400, 0x0000000000000000, 0x0000004004400410, 0x0010000000000010, 0x0000000000400410, 0x0010004004000010, 0x0010000000000000, 0x0000004004400400, ], [ 0x0800100040040080, 0x0000100000001000, 0x0800000000000080, 0x0800100040041080, 0x0000000000000000, 0x0000000040041000, 0x0800100000001080, 0x0800000040040080, 0x0000100040041000, 0x0800000000001080, 0x0000000000001000, 0x0800100000000080, 0x0800000000001080, 0x0800100040040080, 0x0000000040040000, 0x0000000000001000, 0x0800000040041080, 0x0000100040040000, 0x0000100000000000, 0x0800000000000080, 0x0000100040040000, 0x0800100000001080, 0x0000000040041000, 0x0000100000000000, 0x0800100000000080, 0x0000000000000000, 0x0800000040040080, 0x0000100040041000, 0x0000100000001000, 0x0800000040041080, 0x0800100040041080, 0x0000000040040000, 0x0800000040041080, 0x0800100000000080, 0x0000000040040000, 0x0800000000001080, 0x0000100040040000, 0x0000100000001000, 0x0800000000000080, 0x0000000040041000, 0x0800100000001080, 0x0000000000000000, 0x0000100000000000, 0x0800000040040080, 0x0000000000000000, 0x0800000040041080, 0x0000100040041000, 0x0000100000000000, 0x0000000000001000, 0x0800100040041080, 0x0800100040040080, 0x0000000040040000, 0x0800100040041080, 0x0800000000000080, 0x0000100000001000, 0x0800100040040080, 0x0800000040040080, 0x0000100040040000, 0x0000000040041000, 0x0800100000001080, 0x0800100000000080, 0x0000000000001000, 0x0800000000001080, 0x0000100040041000, ], [ 0x0000000000800800, 0x0000001000000000, 0x0040040000000000, 0x2040041000800800, 0x2000001000800800, 0x0040040000800800, 0x2040041000000000, 0x0000001000800800, 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040041000000000, 0x2040040000800800, 0x2000001000800800, 0x0040041000800800, 0x0000000000000000, 0x0040041000000000, 0x0000000000800800, 0x2000001000000000, 0x2040040000000000, 0x0040040000800800, 0x2040041000000000, 0x0000000000000000, 0x2000000000800800, 0x2000000000000000, 0x2040040000800800, 0x2040041000800800, 0x2000001000000000, 0x0000001000800800, 0x0040040000000000, 0x2040040000000000, 0x0040041000800800, 0x0040041000800800, 0x2040040000800800, 0x2000001000000000, 0x0000001000800800, 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040040000800800, 0x0000000000800800, 0x0040041000000000, 0x2040041000800800, 0x0000000000000000, 0x2040041000000000, 0x0000000000800800, 0x0040040000000000, 0x2000001000000000, 0x2040040000800800, 0x0040040000000000, 0x0000000000000000, 0x2040041000800800, 0x2000001000800800, 0x0040041000800800, 0x2040040000000000, 0x0000001000000000, 0x0040041000000000, 0x2000001000800800, 0x0040040000800800, 0x2040040000000000, 0x2000000000000000, 0x2040041000000000, 0x0000001000800800, 0x2000000000800800, ], [ 0x4004000000008008, 0x4004000020000000, 0x0000000000000000, 0x0000200020008008, 0x4004000020000000, 0x0000200000000000, 0x4004200000008008, 0x0000000020000000, 0x4004200000000000, 0x4004200020008008, 0x0000200020000000, 0x0000000000008008, 0x0000200000008008, 0x4004000000008008, 0x0000000020008008, 0x4004200020000000, 0x0000000020000000, 0x4004200000008008, 0x4004000020008008, 0x0000000000000000, 0x0000200000000000, 0x4004000000000000, 0x0000200020008008, 0x4004000020008008, 0x4004200020008008, 0x0000000020008008, 0x0000000000008008, 0x4004200000000000, 0x4004000000000000, 0x0000200020000000, 0x4004200020000000, 0x0000200000008008, 0x4004200000000000, 0x0000000000008008, 0x0000200000008008, 0x4004200020000000, 0x0000200020008008, 0x4004000020000000, 0x0000000000000000, 0x0000200000008008, 0x0000000000008008, 0x0000200000000000, 0x4004000020008008, 0x0000000020000000, 0x4004000020000000, 0x4004200020008008, 0x0000200020000000, 0x4004000000000000, 0x4004200020008008, 0x0000200020000000, 0x0000000020000000, 0x4004200000008008, 0x4004000000008008, 0x0000000020008008, 0x4004200020000000, 0x0000000000000000, 0x0000200000000000, 0x4004000000008008, 0x4004200000008008, 0x0000200020008008, 0x0000000020008008, 0x4004200000000000, 0x4004000000000000, 0x4004000020008008, ], [ 0x0000400400000000, 0x0020000000000000, 0x0020000000100000, 0x0400000000100040, 0x0420400400100040, 0x0400400400000040, 0x0020400400000000, 0x0000000000000000, 0x0000000000100000, 0x0420000000100040, 0x0420000000000040, 0x0000400400100000, 0x0400000000000040, 0x0020400400100000, 0x0000400400100000, 0x0420000000000040, 0x0420000000100040, 0x0000400400000000, 0x0400400400000040, 0x0420400400100040, 0x0000000000000000, 0x0020000000100000, 0x0400000000100040, 0x0020400400000000, 0x0400400400100040, 0x0420400400000040, 0x0020400400100000, 0x0400000000000040, 0x0420400400000040, 0x0400400400100040, 0x0020000000000000, 0x0000000000100000, 0x0420400400000040, 0x0000400400100000, 0x0400400400100040, 0x0420000000000040, 0x0000400400000000, 0x0020000000000000, 0x0000000000100000, 0x0400400400100040, 0x0420000000100040, 0x0420400400000040, 0x0020400400000000, 0x0000000000000000, 0x0020000000000000, 0x0400000000100040, 0x0400000000000040, 0x0020000000100000, 0x0000000000000000, 0x0420000000100040, 0x0020000000100000, 0x0020400400000000, 0x0420000000000040, 0x0000400400000000, 0x0420400400100040, 0x0000000000100000, 0x0020400400100000, 0x0400000000000040, 0x0400400400000040, 0x0420400400100040, 0x0400000000100040, 0x0020400400100000, 0x0000400400100000, 0x0400400400000040, ], [ 0x8008000080082000, 0x0000002080082000, 0x8008002000000000, 0x0000000000000000, 0x0000002000002000, 0x8008000080080000, 0x0000000080082000, 0x8008002080082000, 0x8008000000000000, 0x0000000000002000, 0x0000002080080000, 0x8008002000000000, 0x8008002080080000, 0x8008002000002000, 0x8008000000002000, 0x0000000080082000, 0x0000002000000000, 0x8008002080080000, 0x8008000080080000, 0x0000002000002000, 0x8008002080082000, 0x8008000000002000, 0x0000000000000000, 0x0000002080080000, 0x0000000000002000, 0x0000000080080000, 0x8008002000002000, 0x8008000080082000, 0x0000000080080000, 0x0000002000000000, 0x0000002080082000, 0x8008000000000000, 0x0000000080080000, 0x0000002000000000, 0x8008000000002000, 0x8008002080082000, 0x8008002000000000, 0x0000000000002000, 0x0000000000000000, 0x0000002080080000, 0x8008000080082000, 0x8008002000002000, 0x0000002000002000, 0x8008000080080000, 0x0000002080082000, 0x8008000000000000, 0x8008000080080000, 0x0000002000002000, 0x8008002080082000, 0x0000000080080000, 0x0000000080082000, 0x8008000000002000, 0x0000002080080000, 0x8008002000000000, 0x8008002000002000, 0x0000000080082000, 0x8008000000000000, 0x0000002080082000, 0x8008002080080000, 0x0000000000000000, 0x0000000000002000, 0x8008000080082000, 0x0000002000000000, 0x8008002080080000, ], ]; #[allow(non_snake_case)] pub fn des_cipher(input: u64, keyword: u64, salt: u32, mut num_iter: u32) -> u64 { let salt = ((salt << 26) & 0xFC000000) | ((salt << 12) & 0xFC0000) | ((salt >> 2) & 0xFC00) | ((salt >> 16) & 0xFC); let mut L = input; let mut R = L; L &= 0x5555555555555555; R = (R & 0xAAAAAAAA00000000) | ((R >> 1) & 0x0000000055555555); L = (((L << 1) | (L << 32)) & 0xFFFFFFFF00000000) | ((R | (R >> 32)) & 0x00000000FFFFFFFF); fn perm3264(mut c: u32, p: &[[u64; 16]; 8]) -> u64 { let mut out = 0u64; let mut i = 3; while i >= 0 { let t = (c & 0xFF) as u32; c >>= 8; let tp = p[(i << 1) as usize][(t & 0xF) as usize]; out |= tp; let tp = p[(i << 1) as usize + 1][(t >> 4) as usize]; out |= tp; i -= 1; } out } R = perm3264((L & 0xFFFFFFFF) as u32, &IE3264); L = perm3264((L >> 32) as u32, &IE3264); fn des_setkey(keyword: u64) -> [u64; 16] { let mut KS = [0u64; 16]; let mut K = perm6464(keyword, &PC1ROT); KS[0] = K & !0x0303030300000000; for i in 1..16 { K = perm6464(K, &PC2ROT[Rotates[i] - 1]); KS[i] = K & !0x0303030300000000; } KS } let KS = des_setkey(keyword); fn perm6464(mut c: u64, p: &[[u64; 16]; 16]) -> u64 { let mut out = 0u64; let mut i = 7; while i >= 0 { let t = (c & 0xFF) as u32; c >>= 8; let tp = p[(i << 1) as usize][(t & 0xF) as usize]; out |= tp; let tp = p[(i << 1) as usize + 1][(t >> 4) as usize]; out |= tp; i -= 1; } out } while num_iter > 0 { num_iter -= 1; for loop_count in 0..8 { let kp = KS[loop_count << 1]; let mut k = ((R >> 32) ^ R) & salt as u64 & 0xFFFFFFFF; k |= k << 32; let B = k ^ R ^ kp; L ^= SPE[0][((B >> 58) & 0x3F) as usize] ^ SPE[1][((B >> 50) & 0x3F) as usize] ^ SPE[2][((B >> 42) & 0x3F) as usize] ^ SPE[3][((B >> 34) & 0x3F) as usize] ^ SPE[4][((B >> 26) & 0x3F) as usize] ^ SPE[5][((B >> 18) & 0x3F) as usize] ^ SPE[6][((B >> 10) & 0x3F) as usize] ^ SPE[7][((B >> 2) & 0x3F) as usize]; let kp = KS[(loop_count << 1) + 1]; k = ((L >> 32) ^ L) & salt as u64 & 0xFFFFFFFF; k |= k << 32; let B = k ^ L ^ kp; R ^= SPE[0][((B >> 58) & 0x3F) as usize] ^ SPE[1][((B >> 50) & 0x3F) as usize] ^ SPE[2][((B >> 42) & 0x3F) as usize] ^ SPE[3][((B >> 34) & 0x3F) as usize] ^ SPE[4][((B >> 26) & 0x3F) as usize] ^ SPE[5][((B >> 18) & 0x3F) as usize] ^ SPE[6][((B >> 10) & 0x3F) as usize] ^ SPE[7][((B >> 2) & 0x3F) as usize]; } L ^= R; R ^= L; L ^= R; } L = (((L >> 35) & 0x0F0F0F0F) | (((L & 0xFFFFFFFF) << 1) & 0xF0F0F0F0)) << 32 | (((R >> 35) & 0x0F0F0F0F) | (((R & 0xFFFFFFFF) << 1) & 0xF0F0F0F0)); perm6464(L, &CF6464) } use std::iter; use crate::enc_dec::{crypt_hash64_encode,decode_val,encode_val}; fn secret_to_key(key: &[u8]) -> u64 { key.iter().chain(iter::repeat(&0u8)).take(8).fold(0u64, |kw, b| (kw << 8) | (b << 1) as u64) } fn do_0_crypt(keyword: u64, salt: u32, rounds: u32) -> String { let mut result_block = des_cipher(0, keyword, salt, rounds); let mut result_array = [0u8; 8]; for i in 0..8 { result_array[7-i] = (result_block & 0xFF) as u8; result_block >>= 8; } crypt_hash64_encode(&result_array) } use super::unix_crypt; use super::Result; const DES_ROUNDS: u32 = 25; pub fn unix_crypt(key: &[u8], salt: &str) -> Result { let keyword = secret_to_key(key); let salt_val = decode_val(salt, unix_crypt::SALT_LEN)?; Ok(format!("{}{}", encode_val(salt_val, unix_crypt::SALT_LEN), do_0_crypt(keyword, salt_val, DES_ROUNDS))) } use super::bsdi_crypt; use std::cmp::min; pub fn bsdi_crypt(key: &[u8], salt: &str, rounds: u32) -> Result { let keylen = key.len(); let mut keyword = secret_to_key(&key[..min(keylen, 8)]); let mut idx = 8; while idx < keylen { let next_keyword = secret_to_key(&key[idx..min(keylen, idx + 8)]); keyword = des_cipher(keyword, keyword, 0, 1) ^ next_keyword; idx += 8; } let salt_val = decode_val(salt, bsdi_crypt::SALT_LEN)?; Ok(format!("_{}{}{}", encode_val(rounds as u32, bsdi_crypt::SALT_LEN), encode_val(salt_val, bsdi_crypt::SALT_LEN), do_0_crypt(keyword, salt_val, rounds))) } pwhash-1.0.0/src/enc_dec.rs010064400017500000144000000116341377437446300137630ustar0000000000000000// Encoding and decoding routines. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. use std::char; use std::str::from_utf8; use crate::error::Error; use super::Result; const CRYPT_HASH64: &[u8] = b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const CRYPT_HASH64_ENC_MAP: &[u8] = b"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x00\x01\ \x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x40\x40\x40\x40\x40\x40\ \x40\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\ \x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x40\x40\x40\x40\x40\ \x40\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\ \x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x40\x40\x40\x40"; const BCRYPT_HASH64: &[u8] = b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const BCRYPT_HASH64_ENC_MAP: &[u8] = b"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x00\x01\ \x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x40\x40\x40\x40\x40\ \x40\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\ \x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x40\x40\x40\x40\x40\ \x40\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\ \x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x40\x40\x40\x40\x40"; pub fn bcrypt_hash64_decode(enc: &str, decbuf: &mut [u8]) -> Result<()> { let mut cbuild = 0u8; let mut cpos = 0; let mut dec_idx = 0; for b in enc.chars() { let b = b as u32 - 0x20; if b > 0x60 { return Err(Error::EncodingError); } let dec = BCRYPT_HASH64_ENC_MAP[b as usize]; if dec == 64 { return Err(Error::EncodingError); } if cpos == 0 { cbuild = dec; } else { cbuild <<= cpos; cbuild |= dec >> (6 - cpos); decbuf[dec_idx] = cbuild; dec_idx += 1; if dec_idx == decbuf.len() { break; } cbuild = dec & (0x3F >> cpos); } cpos += 2; if cpos > 6 { cpos = 0; } } Ok(()) } pub fn bcrypt_hash64_encode(bs: &[u8]) -> String { b_c_hash64_encode(bs, &BCRYPT_HASH64) } pub fn crypt_hash64_encode(bs: &[u8]) -> String { b_c_hash64_encode(bs, &CRYPT_HASH64) } fn b_c_hash64_encode(bs: &[u8], hs: &[u8]) -> String { let ngroups = (bs.len() + 2) / 3; let mut out = String::with_capacity(ngroups * 4); for g in 0..ngroups { let mut g_idx = g * 3; let mut enc = 0u32; for _ in 0..3 { let b = (if g_idx < bs.len() { bs[g_idx] } else { 0 }) as u32; enc <<= 8; enc |= b; g_idx += 1; } for _ in 0..4 { out.push(char::from_u32(hs[((enc >> 18) & 0x3F) as usize] as u32).unwrap()); enc <<= 6; } } match bs.len() % 3 { 1 => { out.pop(); out.pop(); }, 2 => { out.pop(); }, _ => (), } out } const SHA1_HASH_LEN: usize = 20; pub fn sha1crypt_hash64_encode(bs: &[u8]) -> String { assert!(bs.len() >= SHA1_HASH_LEN); let ngroups = (SHA1_HASH_LEN + 2) / 3; let mut out = String::with_capacity(ngroups * 4); for g in 0..ngroups { let mut g_idx = g * 3; let mut enc: u32 = 0; for _ in 0..3 { let b = (if g_idx < SHA1_HASH_LEN { bs[g_idx] } else { bs[0] }) as u32; enc <<= 8; enc |= b; g_idx += 1; } for _ in 0..4 { out.push(char::from_u32(CRYPT_HASH64[(enc & 0x3F) as usize] as u32).unwrap()); enc >>= 6; } } out } pub fn md5_sha2_hash64_encode(bs: &[u8]) -> String { let ngroups = (bs.len() + 2) / 3; let mut out = String::with_capacity(ngroups * 4); for g in 0..ngroups { let mut g_idx = g * 3; let mut enc = 0u32; for _ in 0..3 { let b = (if g_idx < bs.len() { bs[g_idx] } else { 0 }) as u32; enc >>= 8; enc |= b << 16; g_idx += 1; } for _ in 0..4 { out.push(char::from_u32(CRYPT_HASH64[(enc & 0x3F) as usize] as u32).unwrap()); enc >>= 6; } } match bs.len() % 3 { 1 => { out.pop(); out.pop(); }, 2 => { out.pop(); }, _ => (), } out } pub fn decode_val(val: &str, len: usize) -> Result { let mut processed = 0; let mut s = 0u32; for b in val.chars() { let b = b as u32 - 0x20; if b > 0x60 { return Err(Error::EncodingError); } let dec = CRYPT_HASH64_ENC_MAP[b as usize]; if dec == 64 { return Err(Error::EncodingError); } s >>= 6; s |= (dec as u32) << 26; processed += 1; if processed == len { break; } } if processed < len { return Err(Error::InsufficientLength); } Ok(s >> (32 - 6 * len)) } pub fn encode_val(mut val: u32, mut nhex: usize) -> String { let mut val_arr = [0u8; 4]; if nhex > 4 { nhex = 4; } let vlen = nhex; let mut i = 0; while nhex > 0 { nhex -= 1; val_arr[i] = CRYPT_HASH64[(val & 0x3F) as usize]; val >>= 6; i += 1; } from_utf8(&val_arr[..vlen]).unwrap().to_owned() } pwhash-1.0.0/src/error.rs010064400017500000144000000026041370127216000135060ustar0000000000000000//! Error values. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! For simplicity, there's no provision for recording the cause of any //! errors except I/O errors when opening the system entropy source. use std::fmt; use std::error::Error as StdError; /// Possible errors. #[derive(Debug)] pub enum Error { /// Random value cannot be generated. RandomError(String), /// Some component of the hash string contains an invalid character. EncodingError, /// An encoded value is too short. InsufficientLength, /// The number of rounds is out of range. InvalidRounds, /// The hash string is not in the expected format. InvalidHashString, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::RandomError(ref err) => write!(f, "{}", err), Error::EncodingError => write!(f, "Invalid encoding"), Error::InsufficientLength => write!(f, "Encoded value is too short"), Error::InvalidRounds => write!(f, "Invalid rounds value"), Error::InvalidHashString => write!(f, "Invalid hash string"), } } } impl StdError for Error {} pwhash-1.0.0/src/lib.rs010064400017500000144000000270511377440250200131330ustar0000000000000000//! A collection of password hashing and verification routines. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! For the summary of supported algorithms and recommendations, see //! [Summary](#summary). Every algorithm has its own module; alphabetical list //! is in the [Modules](#modules) section. //! //! # Getting Started //! //! Add the following to the `[dependencies]` section of your `Cargo.toml`: //! //! ```toml //! pwhash = "1" //! ``` //! //! # Examples //! //! To verify a password hashed with a known algorithm: //! //! ``` //! use pwhash::bcrypt; //! //! let h = "$2y$05$bvIG6Nmid91Mu9RcmmWZfO\ //! 5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe"; //! assert_eq!(bcrypt::verify("password", h), true); //! ``` //! //! To hash a password using default parameters: //! //! ``` //! use pwhash::bcrypt; //! //! let h = bcrypt::hash("password").unwrap(); //! ``` //! //! To verify a password known to be in one of Unix modular hash formats: //! //! ``` //! use pwhash::unix; //! //! let h = "$2y$05$bvIG6Nmid91Mu9RcmmWZfO\ //! 5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe"; //! assert_eq!(unix::verify("password", h), true); //! ``` //! //! # Summary //! //! Currently, there are implementations of seven algorithms, which should //! cover anything one might find as a system-wide hash on a free Unix-like //! OS: [bcrypt](bcrypt), [SHA-512](sha512_crypt), [SHA-256](sha256_crypt), //! [HMAC-SHA1](sha1_crypt), [MD5](md5_crypt), [BSDi crypt](bsdi_crypt), and //! [DES crypt](unix_crypt). The list is ordered roughly by security, with the //! most secure algorithms first. The first two are recommended for new //! passwords. //! //! Each algorithm is implemented in its own module, and offers three ways of //! using it: //! //! * The `verify` function checks whether the provided hash corresponds to a //! password. //! //! * The `hash` function hashes a password using the default parameters for the //! algorithm. //! //! * The `hash_with` function allows the caller to customize the hashing //! parameters. //! //! Customization can always be accomplished by passing a `&str` with encoded //! parameters (in the appropriate hash format) to `hash_with`. All algorithms //! except DES crypt accept a `HashSetup` struct as a means of customization, //! while bcrypt also has its own setup structure (see the module documenation.) //! //! The [unix](unix) module provides a __crypt__(3)-compatible function and a //! `verify` which uses it to automatically recognize the algorithm of the //! provided hash. #![warn(missing_docs)] mod enc_dec; pub mod error; pub mod unix_crypt; pub mod bsdi_crypt; mod des_crypt; pub mod bcrypt; pub mod sha1_crypt; pub mod md5_crypt; mod sha2_crypt; pub mod sha256_crypt; pub mod sha512_crypt; /// Type alias for the Result type. pub type Result = std::result::Result; /// Setup struct for basic hashing customization. /// /// All implemented hash functions accept a custom salt value. If set to `None`, /// a random salt will be generated. The usage of `rounds` varies with the /// algorithm; visit the algorithm's module-level documentation for details. /// It's always safe to initialize `rounds` to `None`, in which case the suitable /// default value will be used. pub struct HashSetup<'a> { /// Custom salt. pub salt: Option<&'a str>, /// Number of rounds. pub rounds: Option, } /// A trait for converting a type into a `HashSetup` struct. pub trait IntoHashSetup<'a> { /// The conversion function. fn into_hash_setup(self, f: fn(&'a str) -> Result>) -> Result>; } impl<'a> IntoHashSetup<'a> for &'a str { fn into_hash_setup(self, f: fn(&'a str) -> Result>) -> Result> { f(self) } } impl<'a> IntoHashSetup<'a> for HashSetup<'a> { fn into_hash_setup(self, _f: fn(&'a str) -> Result>) -> Result> { Ok(self) } } /// A trait for extracting a NUL-terminated subslice from a slice. /// /// The original Unix hashing functions expect passwords to be NUL-terminated C strings. This /// allows values which can't be represented by Rust strings, which are constrained to be UTF-8. /// On the other hand, Rust strings can contain NUL bytes, and C strings can't. /// /// For maximum flexibility, hashing functions in this crate accept both strings and raw byte /// vectors as password input. This trait can be used to ensure that any input value will be /// truncated at the first NUL byte. pub trait FindNul { /// Subslice extraction function. /// /// Given a slice, find and return the subslice before the first NUL byte, or the original /// slice if no NUL byte is found. Before searching, the slice is converted into a byte /// slice, if necessary. The returned slice also consists of raw bytes. fn nul_terminated_subslice(&self) -> &[u8]; } impl FindNul for str { fn nul_terminated_subslice(&self) -> &[u8] { let nul_pos = self.as_bytes().windows(1).position(|window| window == [0u8]).unwrap_or_else(|| self.len()); self[..nul_pos].as_bytes() } } impl FindNul for [u8] { fn nul_terminated_subslice(&self) -> &[u8] { let nul_pos = self.windows(1).position(|window| window == [0u8]).unwrap_or_else(|| self.len()); self[..nul_pos].as_ref() } } fn consteq(hash: &str, calchash: Result) -> bool { if calchash.is_err() { return false; } let hstr = calchash.unwrap(); if hash.len() != hstr.len() { return false; } 0 == hash.bytes().zip(hstr.bytes()).fold(0, |xs, (h1, h2)| xs | h1 ^ h2) } mod random { use rand::{Rng, random}; use rand::rngs::OsRng; use rand::distributions::Standard; use crate::enc_dec::bcrypt_hash64_encode; pub fn gen_salt_str(chars: usize) -> String { let bytes = ((chars + 3) / 4) * 3; let rv = OsRng.sample_iter(&Standard).take(bytes).collect::>(); let mut sstr = bcrypt_hash64_encode(&rv); while sstr.len() > chars { sstr.pop(); } sstr } pub fn gen_salt_bytes(bytes: &mut [u8]) { OsRng.fill(bytes); } pub fn vary_rounds(ceil: u32) -> u32 { ceil - (random::() % (ceil / 4)) } } mod parse { use std::str; /// A trait for traversing a hash string. /// /// Hash strings have internal structure: they consist of a concatenation /// of a number of substrings. This trait enables extracting references to /// those substrings with the necessary semantics. pub trait HashIterator { /// The substring that is returned by methods. type Elem; /// Extract a fixed-size substring. /// /// There must be at least `n` ASCII characters remaining in the /// string. If there are less, `None` is returned. If called with a non-zero /// `n`, this method drains the string: if there are exactly `n` characters /// remaining, subsequent calls will return `None`. /// /// Calling `take` with `n` set to zero returns an empty string if the main /// string is not drained. fn take(&mut self, n: usize) -> Option; /// Extract a substring delimited by a byte. /// /// Return a substring from the current position to the next occurrence of the /// ASCII delimiter `ac` or the end of the string. If the delimiter is found, /// advance the position one byte after it. Drains the string. fn take_until(&mut self, ac: u8) -> Option; /// Returns `true` if the string is not drained. fn at_end(&mut self) -> bool; } pub struct HashSlice<'a> { bp: &'a [u8], len: usize, pos: usize, } impl<'a> HashSlice<'a> { pub fn new(hash: &'a str) -> HashSlice<'a> { HashSlice { bp: hash.as_bytes(), len: hash.len(), pos: 0 } } } impl<'a> HashIterator for HashSlice<'a> { type Elem = &'a str; fn take(&mut self, n: usize) -> Option { if self.pos > self.len { return None; } let sp = self.pos; if sp + n > self.len { self.pos = self.len + 1; None } else { let endp = self.pos + n; self.pos = endp + if endp == self.len { 1 } else { 0 }; if let Ok(s) = str::from_utf8(&self.bp[sp..endp]) { Some(s) } else { None } } } fn take_until(&mut self, ac: u8) -> Option { if self.pos > self.len { return None; } let mut sp = self.pos; while sp < self.len { if self.bp[sp] == ac { break; } sp += 1; } let oldp = self.pos; self.pos = sp + 1; if let Ok(s) = str::from_utf8(&self.bp[oldp..sp]) { Some(s) } else { None } } fn at_end(&mut self) -> bool { self.take(0).unwrap_or("X") == "X" } } #[cfg(test)] mod tests { use super::{HashSlice, HashIterator}; #[test] fn drain_string() { let mut hs = HashSlice::new("$2y$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe"); assert_eq!(hs.take_until(b'$').unwrap(), ""); assert_eq!(hs.take_until(b'$').unwrap(), "2y"); assert_eq!(hs.take_until(b'$').unwrap(), "05"); assert_eq!(hs.take(22).unwrap(), "bvIG6Nmid91Mu9RcmmWZfO"); let mut hs1 = HashSlice { bp: hs.bp, pos: hs.pos, len: hs.len }; assert_eq!(hs.take_until(b'$').unwrap(), "5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe"); assert_eq!(hs.at_end(), true); assert_eq!(hs1.take(31).unwrap(), "5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe"); assert_eq!(hs1.at_end(), true); } #[test] fn empty_string() { let mut hs = HashSlice::new(""); assert_eq!(hs.take_until(b'$').unwrap(), ""); assert_eq!(hs.at_end(), true); let mut hs = HashSlice::new(""); assert_eq!(hs.at_end(), false); } #[test] fn empty_elements() { let mut hs = HashSlice::new("$"); assert_eq!(hs.take_until(b'$').unwrap(), ""); assert_eq!(hs.take_until(b'$').unwrap(), ""); assert_eq!(hs.at_end(), true); } #[test] fn combined_take() { let mut hs = HashSlice::new("$"); let _ = hs.take_until(b'$').unwrap(); assert_eq!(hs.take_until(b'$').unwrap(), ""); assert_eq!(hs.at_end(), true); } } } pub mod unix { //! Convenience functions for Unix modular hashes. //! //! If it's known that a hash is in one of the supported modular hash formats, //! the functions in this module can be used to verify or re-calculate the //! hash. use super::{Result, consteq}; use crate::parse::{self, HashIterator}; use crate::error::Error; use crate::{bsdi_crypt, md5_crypt, bcrypt, sha1_crypt, sha256_crypt, sha512_crypt, unix_crypt}; /// A Unix __crypt__(3) work-alike. pub fn crypt>(pass: B, hash: &str) -> Result { let mut hs = parse::HashSlice::new(hash); #[allow(deprecated)] match hs.take(1).unwrap_or("X") { "_" => bsdi_crypt::hash_with(hash, pass), "$" => match hs.take_until(b'$').unwrap_or("X") { "1" => md5_crypt::hash_with(hash, pass), "2a" | "2b" | "2y" => bcrypt::hash_with(hash, pass), "sha1" => sha1_crypt::hash_with(hash, pass), "5" => sha256_crypt::hash_with(hash, pass), "6" => sha512_crypt::hash_with(hash, pass), _ => Err(Error::InvalidHashString), }, _ => unix_crypt::hash_with(hash, pass) } } /// Verify that the hash corresponds to a password, using hash format recognition. pub fn verify>(pass: B, hash: &str) -> bool { consteq(hash, crypt(pass, hash)) } #[cfg(test)] mod tests { #[test] fn crypt_recognized() { assert_eq!(super::crypt("password", "$1$5pZSV9va$azfrPr6af3Fc7dLblQXVa0").unwrap(), "$1$5pZSV9va$azfrPr6af3Fc7dLblQXVa0"); assert_eq!(super::crypt("test", "aZGJuE6EXrjEE").unwrap(), "aZGJuE6EXrjEE"); } } } pwhash-1.0.0/src/md5_crypt.rs010064400017500000144000000121521377437446300143050ustar0000000000000000//! MD5 based hash. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! This algorithm was developed for FreeBSD to replace the //! aging DES crypt. It was adopted in various Linux distributions //! and saw wide use. Presently, it's considered insecure and //! shouldn't be used for new passwords. //! //! # Example //! //! ``` //! use pwhash::md5_crypt; //! //! assert_eq!(md5_crypt::hash_with( //! "$1$5pZSV9va$azfrPr6af3Fc7dLblQXVa0", //! "password").unwrap(), //! "$1$5pZSV9va$azfrPr6af3Fc7dLblQXVa0"); //! ``` //! //! # Parameters //! //! * __Password length__: unlimited. //! //! * __Salt length__: 0 to 8 characters. Default is 8. //! //! * __Rounds__: 1000 (fixed.) //! //! # Hash Format //! //! The format of the hash is //! __`$1$`__*`{salt}`*__$__*`{checksum}`*, where: //! //! * *`{salt}`* is the salt string. //! //! * *`{checksum}`* is a 22-character Base64 encoding of the checksum. use md5::{Md5, Digest}; use super::{Result, HashSetup, IntoHashSetup, consteq}; use crate::error::Error; use crate::random; use crate::parse::{self, HashIterator}; use crate::enc_dec::{md5_sha2_hash64_encode, bcrypt_hash64_decode}; use std::cmp::min; /// Maximium salt length. pub const MAX_SALT_LEN: usize = 8; const MD5_MAGIC: &str = "$1$"; const MD5_TRANSPOSE: &[u8] = b"\x0c\x06\x00\x0d\x07\x01\x0e\x08\x02\x0f\x09\x03\x05\x0a\x04\x0b"; fn do_md5_crypt(pass: &[u8], salt: &str) -> Result { let mut dummy_buf = [0u8; 6]; bcrypt_hash64_decode(salt, &mut dummy_buf)?; let mut dgst_b = Md5::new(); dgst_b.update(pass); dgst_b.update(salt.as_bytes()); dgst_b.update(pass); let mut hash_b = dgst_b.finalize(); let mut dgst_a = Md5::new(); dgst_a.update(pass); dgst_a.update(MD5_MAGIC.as_bytes()); dgst_a.update(salt.as_bytes()); let mut plen = pass.len(); while plen > 0 { dgst_a.update(&hash_b[..min(plen, 16)]); if plen < 16 { break; } plen -= 16; } plen = pass.len(); while plen > 0 { match plen & 1 { 0 => dgst_a.update(&pass[..1]), 1 => dgst_a.update(&[0u8]), _ => unreachable!() } plen >>= 1; } let mut hash_a = dgst_a.finalize(); for r in 0..1000 { let mut dgst_a = Md5::new(); if r % 2 == 1 { dgst_a.update(pass); } else { dgst_a.update(&hash_a); } if r % 3 > 0 { dgst_a.update(salt.as_bytes()); } if r % 7 > 0 { dgst_a.update(pass); } if r % 2 == 0 { dgst_a.update(pass); } else { dgst_a.update(&hash_a); } hash_a = dgst_a.finalize(); } for (i, &ti) in MD5_TRANSPOSE.iter().enumerate() { hash_b[i] = hash_a[ti as usize]; } Ok(format!("{}{}${}", MD5_MAGIC, salt, md5_sha2_hash64_encode(&hash_b))) } /// Hash a password with a randomly generated salt. /// /// An error is returned if the system random number generator cannot /// be opened. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash>(pass: B) -> Result { let saltstr = random::gen_salt_str(MAX_SALT_LEN); do_md5_crypt(pass.as_ref(), &saltstr) } const MAGIC_LEN: usize = 3; fn parse_md5_hash(hash: &str) -> Result { let mut hs = parse::HashSlice::new(hash); if hs.take(MAGIC_LEN).unwrap_or("X") != MD5_MAGIC { return Err(Error::InvalidHashString); } let salt = if let Some(salt) = hs.take_until(b'$') { salt } else { return Err(Error::InvalidHashString); }; Ok(HashSetup { salt: Some(salt), rounds: None }) } /// Hash a password with user-provided parameters. /// /// If the `param` argument is a `&str`, it must be in the final hash /// format. The salt is parsed out of that value. /// If the salt is too long, it is truncated to maximum length. If it contains /// an invalid character, an error is returned. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash_with<'a, IHS, B>(param: IHS, pass: B) -> Result where IHS: IntoHashSetup<'a>, B: AsRef<[u8]> { let hs = IHS::into_hash_setup(param, parse_md5_hash)?; if let Some(salt) = hs.salt { let salt = if salt.len() <= MAX_SALT_LEN { salt } else if let Some(truncated_salt) = parse::HashSlice::new(salt).take(MAX_SALT_LEN) { truncated_salt } else { return Err(Error::InvalidHashString); }; do_md5_crypt(pass.as_ref(), salt) } else { let salt = random::gen_salt_str(MAX_SALT_LEN); do_md5_crypt(pass.as_ref(), &salt) } } /// Verify that the hash corresponds to a password. pub fn verify>(pass: B, hash: &str) -> bool { #[allow(deprecated)] consteq(hash, hash_with(hash, pass)) } #[cfg(test)] mod tests { use super::HashSetup; #[test] #[allow(deprecated)] fn custom() { assert_eq!(super::hash_with("$1$5pZSV9va$azfrPr6af3Fc7dLblQXVa0", "password").unwrap(), "$1$5pZSV9va$azfrPr6af3Fc7dLblQXVa0"); assert_eq!(super::hash_with(HashSetup { salt: Some("5pZSV9va"), rounds: None }, "password").unwrap(), "$1$5pZSV9va$azfrPr6af3Fc7dLblQXVa0"); } } pwhash-1.0.0/src/sha1_crypt.rs010064400017500000144000000127061377437446300144610ustar0000000000000000//! HMAC-SHA1 based hash. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! This algorithm was developed for NetBSD. It's a modern //! algorithm with a large salt and a variable number of rounds. //! Although the SHA-1 hash, on which it's based, is considered //! insecure and is being phased out in the PKI environment, its //! use in a HMAC setup, as is the case here, is still acceptable. //! //! # Example //! //! ``` //! use pwhash::sha1_crypt; //! //! assert_eq!(sha1_crypt::hash_with( //! "$sha1$19703$iVdJqfSE$v4qYKl1zqYThwpjJAoKX6UvlHq/a", //! "password").unwrap(), //! "$sha1$19703$iVdJqfSE$v4qYKl1zqYThwpjJAoKX6UvlHq/a"); //! ``` //! //! # Parameters //! //! * __Password length__: unlimited. //! //! * __Salt length__: 0 to 64 characters. Default is 8. //! //! * __Rounds__: 1 to 232-1. Default is 24680, which //! is slightly varied if chosen. //! //! # Hash Format //! //! The format of the hash is //! __`$sha1$`__*`{rounds}`*__$__*`{salt}`*__$__*`{checksum}`*, where: //! //! * *`{rounds}`* is the number of rounds, encoded as a decimal number //! without leading zeroes. //! //! * *`{salt}`* is the salt string. //! //! * *`{checksum}`* is a 28-character Base64 encoding of the checksum. use hmac::{Hmac, Mac, NewMac}; use sha1::Sha1; use super::{Result, HashSetup, IntoHashSetup, consteq}; use crate::enc_dec::{sha1crypt_hash64_encode, bcrypt_hash64_decode}; use crate::error::Error; use crate::random; use crate::parse::{self, HashIterator}; const MIN_ROUNDS: u32 = 1; /// Default number of rounds. /// /// The value is aligned with the default used on NetBSD. pub const DEFAULT_ROUNDS: u32 = 24680; const MAX_SALT_LEN: usize = 64; /// Default salt length. pub const DEFAULT_SALT_LEN: usize = 8; fn do_sha1_crypt(pass: &[u8], salt: &str, rounds: u32) -> Result { let mut dummy_buf = [0u8; 48]; bcrypt_hash64_decode(salt, &mut dummy_buf)?; let mut hmac = Hmac::::new_varkey(pass).map_err(|_| Error::InsufficientLength)?; hmac.update(format!("{}$sha1${}", salt, rounds).as_bytes()); let mut result = hmac.finalize(); for _ in 1..rounds { let mut hmac = Hmac::::new_varkey(pass).map_err(|_| Error::InsufficientLength)?; hmac.update(&result.into_bytes()); result = hmac.finalize(); } Ok(format!("$sha1${}${}${}", rounds, salt, sha1crypt_hash64_encode(&result.into_bytes()))) } /// Hash a password with a randomly generated salt and the default /// number of rounds (varied by a small amount, like on NetBSD). /// /// An error is returned if the system random number generator cannot /// be opened. pub fn hash>(pass: B) -> Result { let saltstr = random::gen_salt_str(DEFAULT_SALT_LEN); do_sha1_crypt(pass.as_ref(), &saltstr, random::vary_rounds(DEFAULT_ROUNDS)) } const MAGIC_LEN: usize = 6; fn parse_sha1_hash(hash: &str) -> Result { let mut hs = parse::HashSlice::new(hash); if hs.take(MAGIC_LEN).unwrap_or("X") != "$sha1$" { return Err(Error::InvalidHashString); } let rounds = if let Some(rounds_str) = hs.take_until(b'$') { rounds_str.parse::().map_err(|_e| Error::InvalidRounds)? } else { return Err(Error::InvalidHashString); }; let salt = if let Some(salt) = hs.take_until(b'$') { salt } else { return Err(Error::InvalidHashString); }; Ok(HashSetup { salt: Some(salt), rounds: Some(rounds) }) } /// Hash a password with user-provided parameters. /// /// If the `param` argument is a `&str`, it must be in the final hash /// format. The number of iterations (rounds) and the salt are parsed out /// of that value. /// If the salt is too long, it is truncated to maximum length. If it contains /// an invalid character, an error is returned. An out-of-range rounds value /// will also result in an error. pub fn hash_with<'a, IHS, B>(param: IHS, pass: B) -> Result where IHS: IntoHashSetup<'a>, B: AsRef<[u8]> { let hs = IHS::into_hash_setup(param, parse_sha1_hash)?; let rounds = if let Some(r) = hs.rounds { if r < MIN_ROUNDS { return Err(Error::InvalidRounds); } r } else { random::vary_rounds(DEFAULT_ROUNDS) }; if let Some(salt) = hs.salt { let salt = if salt.len() <= MAX_SALT_LEN { salt } else if let Some(truncated_salt) = parse::HashSlice::new(salt).take(MAX_SALT_LEN) { truncated_salt } else { return Err(Error::InvalidHashString); }; do_sha1_crypt(pass.as_ref(), salt, rounds) } else { let salt = random::gen_salt_str(DEFAULT_SALT_LEN); do_sha1_crypt(pass.as_ref(), &salt, rounds) } } /// Verify that the hash corresponds to a password. pub fn verify>(pass: B, hash: &str) -> bool { consteq(hash, hash_with(hash, pass)) } #[cfg(test)] mod tests { use super::HashSetup; #[test] fn custom() { assert_eq!(super::hash_with("$sha1$19703$iVdJqfSE$v4qYKl1zqYThwpjJAoKX6UvlHq/a", "password").unwrap(), "$sha1$19703$iVdJqfSE$v4qYKl1zqYThwpjJAoKX6UvlHq/a"); assert_eq!(super::hash_with(HashSetup { salt: Some("iVdJqfSE"), rounds: Some(19703) }, "password").unwrap(), "$sha1$19703$iVdJqfSE$v4qYKl1zqYThwpjJAoKX6UvlHq/a"); } #[test] #[should_panic(expected="value: InvalidRounds")] fn bad_rounds() { let _ = super::hash_with(HashSetup { salt: Some("K0Ay"), rounds: Some(0) }, "password").unwrap(); } } pwhash-1.0.0/src/sha256_crypt.rs010064400017500000144000000104421377437446300146300ustar0000000000000000//! SHA-256 based hash. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! This algorithm was developed as an alternative to bcrypt //! with NIST-approved hashing functions. It is similar to //! MD5-crypt, but has a variable number of rounds and a larger //! salt. //! //! # Example //! //! ``` //! use pwhash::sha256_crypt; //! //! let h = "$5$rounds=11858$WH1ABM5sKhxbkgCK$\ //! aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1"; //! assert_eq!(sha256_crypt::hash_with(h, "test").unwrap(), h); //! ``` //! //! # Parameters //! //! * __Password length__: unlimited. //! //! * __Salt length__: 0 to 16 characters. Default is 16. //! //! * __Rounds__: 1000 to 999999999. Default is 5000. If a number //! outside of the range is chosen, it is coerced to the nearest //! limit. //! //! # Hash Format //! //! The format of the hash is //! __`$5$rounds=`__*`{rounds}`*__$__*`{salt}`*__$__*`{checksum}`*, where: //! //! * *`{rounds}`* is the number of rounds, encoded as a decimal number //! without leading zeroes. //! //! * *`{salt}`* is the salt string. //! //! * *`{checksum}`* is a 43-character Base64 encoding of the checksum. //! //! The format __`$5$`__*`{salt}`*__$__*`{checksum}`* can be used if //! the default number of rounds is chosen. use sha2::Sha256; use super::{Result, HashSetup, IntoHashSetup, consteq}; use crate::random; use crate::sha2_crypt::{sha2_crypt, parse_sha2_hash, sha2_hash_with}; pub use crate::sha2_crypt::MIN_ROUNDS; pub use crate::sha2_crypt::MAX_ROUNDS; pub use crate::sha2_crypt::DEFAULT_ROUNDS; pub use crate::sha2_crypt::MAX_SALT_LEN; const SHA256_MAGIC: &str = "$5$"; const SHA256_TRANSPOSE: &[u8] = b"\x14\x0a\x00\x0b\x01\x15\x02\x16\x0c\x17\x0d\x03\x0e\x04\x18\x05\ \x19\x0f\x1a\x10\x06\x11\x07\x1b\x08\x1c\x12\x1d\x13\x09\x1e\x1f"; fn do_sha256_crypt(pass: &[u8], salt: &str, rounds: Option) -> Result { sha2_crypt(pass, salt, rounds, Sha256::default, SHA256_TRANSPOSE, SHA256_MAGIC) } /// Hash a password with a randomly generated salt and the default /// number of rounds. /// /// An error is returned if the system random number generator cannot /// be opened. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash>(pass: B) -> Result { let saltstr = random::gen_salt_str(MAX_SALT_LEN); do_sha256_crypt(pass.as_ref(), &saltstr, None) } fn parse_sha256_hash(hash: &str) -> Result { parse_sha2_hash(hash, SHA256_MAGIC) } /// Hash a password with user-provided parameters. /// /// If the `param` argument is a `&str`, it must be in the final hash /// format. The number of rounds and the salt are parsed out of that value. /// If the salt is too long, it is truncated to maximum length. If it contains /// an invalid character, an error is returned. An out-of-range rounds value /// will be coerced into the allowed range. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash_with<'a, IHS, B>(param: IHS, pass: B) -> Result where IHS: IntoHashSetup<'a>, B: AsRef<[u8]> { sha2_hash_with(IHS::into_hash_setup(param, parse_sha256_hash)?, pass.as_ref(), do_sha256_crypt) } /// Verify that the hash corresponds to a password. pub fn verify>(pass: B, hash: &str) -> bool { #[allow(deprecated)] consteq(hash, hash_with(hash, pass)) } #[cfg(test)] mod tests { use super::HashSetup; #[test] #[allow(deprecated)] fn custom() { assert_eq!(super::hash_with( "$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1", "test").unwrap(), "$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1"); assert_eq!(super::hash_with(HashSetup { salt: Some("WH1ABM5sKhxbkgCK"), rounds: Some(11858) }, "test").unwrap(), "$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1"); } #[test] #[allow(deprecated)] fn implicit_dflt_rounds() { assert_eq!(super::hash_with( "$5$WH1ABM5sKhxbkgCK$sOnTVjQn1Y3EWibd8gWqqJqjH.KaFrxJE5rijqxcPp7", "test").unwrap(), "$5$WH1ABM5sKhxbkgCK$sOnTVjQn1Y3EWibd8gWqqJqjH.KaFrxJE5rijqxcPp7"); } } pwhash-1.0.0/src/sha2_crypt.rs010064400017500000144000000107201377437446300144540ustar0000000000000000// Common routines for SHA-2 hashing. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. use std::cmp::min; use sha2::Digest; use crate::enc_dec::{md5_sha2_hash64_encode, bcrypt_hash64_decode}; use super::{Result, HashSetup}; use crate::error::Error; use crate::parse::{self, HashIterator}; use crate::random; /// Minimum rounds. pub const MIN_ROUNDS: u32 = 1000; /// Maximum rounds. pub const MAX_ROUNDS: u32 = 999999999; /// Default number of rounds. pub const DEFAULT_ROUNDS: u32 = 5000; /// Maximum (and default) salt length. pub const MAX_SALT_LEN: usize = 16; pub fn sha2_crypt(pass: &[u8], salt: &str, rounds: Option, new_digest: fn() -> D, trn_table: &[u8], magic: &str) -> Result { let mut dummy_buf = [0u8; 12]; bcrypt_hash64_decode(salt, &mut dummy_buf)?; let mut dgst_b = new_digest(); let dsize = D::output_size(); dgst_b.update(pass); dgst_b.update(salt.as_bytes()); dgst_b.update(pass); let mut hash_b = dgst_b.finalize(); let mut dgst_a = new_digest(); dgst_a.update(pass); dgst_a.update(salt.as_bytes()); let plen = pass.len(); let mut p = plen; while p > 0 { dgst_a.update(&hash_b[..min(p, dsize)]); if p < dsize { break; } p -= dsize; } p = plen; while p > 0 { match p & 1 { 0 => dgst_a.update(pass), 1 => dgst_a.update(&hash_b[..dsize]), _ => unreachable!() } p >>= 1; } let mut hash_a = dgst_a.finalize(); let mut dgst_b = new_digest(); for _ in 0..plen { dgst_b.update(pass); } hash_b = dgst_b.finalize(); let mut dgst_b = new_digest(); let mut seq_p = Vec::::with_capacity(((plen + dsize - 1) / dsize) * dsize); p = plen; while p > 0 { seq_p.extend(&hash_b[..min(p, dsize)]); if p < dsize { break; } p -= dsize; } for _ in 0..MAX_SALT_LEN+(hash_a[0] as usize) { dgst_b.update(salt.as_bytes()); } hash_b = dgst_b.finalize(); let mut seq_s = Vec::::with_capacity(MAX_SALT_LEN); seq_s.extend(&hash_b[..salt.len()]); for r in 0..rounds.unwrap_or(DEFAULT_ROUNDS) { let mut dgst_a = new_digest(); if r % 2 == 1 { dgst_a.update(&seq_p[..]); } else { dgst_a.update(&hash_a[..dsize]); } if r % 3 > 0 { dgst_a.update(&seq_s[..]); } if r % 7 > 0 { dgst_a.update(&seq_p[..]); } if r % 2 == 1 { dgst_a.update(&hash_a[..dsize]); } else { dgst_a.update(&seq_p[..]); } hash_a = dgst_a.finalize(); } for (i, &ti) in trn_table.iter().enumerate() { hash_b[i] = hash_a[ti as usize]; } match rounds { Some(rounds) => Ok(format!("{}rounds={}${}${}", magic, rounds, salt, md5_sha2_hash64_encode(&hash_b[..dsize]))), None => Ok(format!("{}{}${}", magic, salt, md5_sha2_hash64_encode(&hash_b[..dsize]))) } } const MAGIC_LEN: usize = 3; pub fn parse_sha2_hash<'a>(hash: &'a str, magic: &str) -> Result> { let mut hs = parse::HashSlice::new(hash); if hs.take(MAGIC_LEN).unwrap_or("X") != magic { return Err(Error::InvalidHashString); } let maybe_rounds = if let Some(elem) = hs.take_until(b'$') { elem } else { return Err(Error::InvalidHashString); }; let rounds = if maybe_rounds.starts_with("rounds=") { let mut rhs = parse::HashSlice::new(maybe_rounds); rhs.take_until(b'='); Some(rhs.take_until(b'$').unwrap().parse::().map_err(|_e| Error::InvalidRounds)?) } else { None }; let salt = if rounds.is_none() { maybe_rounds } else if let Some(salt) = hs.take_until(b'$') { salt } else { return Err(Error::InvalidHashString); }; Ok(HashSetup { salt: Some(salt), rounds }) } pub fn sha2_hash_with(param: HashSetup, pass: &[u8], hf: fn(&[u8], &str, Option) -> Result) -> Result { let rounds = if let Some(r) = param.rounds { if r < MIN_ROUNDS { Some(MIN_ROUNDS) } else if r > MAX_ROUNDS { Some(MAX_ROUNDS) } else { Some(r) } } else { None }; if let Some(salt) = param.salt { let salt = if salt.len() <= MAX_SALT_LEN { salt } else if let Some(truncated_salt) = parse::HashSlice::new(salt).take(MAX_SALT_LEN) { truncated_salt } else { return Err(Error::InvalidHashString); }; hf(pass, salt, rounds) } else { let salt = random::gen_salt_str(MAX_SALT_LEN); hf(pass, &salt, rounds) } } pwhash-1.0.0/src/sha512_crypt.rs010064400017500000144000000107471377437446300146330ustar0000000000000000//! SHA-512 based hash. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! This algorithm was developed as an alternative to bcrypt //! with NIST-approved hashing functions. It is similar to //! MD5-crypt, but has a variable number of rounds and a larger //! salt. //! //! # Example //! //! ``` //! use pwhash::sha512_crypt; //! //! let h = //! "$6$G/gkPn17kHYo0gTF$xhDFU0QYExdMH2ghOWKrrVtu1BuTpNMSJ\ //! URCXk43.EYekmK8iwV6RNqftUUC8mqDel1J7m3JEbUkbu4YyqSyv/"; //! assert_eq!(sha512_crypt::hash_with(h, "test").unwrap(), h); //! ``` //! //! # Parameters //! //! * __Password length__: unlimited. //! //! * __Salt length__: 0 to 16 characters. Default is 16. //! //! * __Rounds__: 1000 to 999999999. Default is 5000. If a number //! outside of the range is chosen, it is coerced to the nearest //! limit. //! //! # Hash Format //! //! The format of the hash is //! __`$6$rounds=`__*`{rounds}`*__$__*`{salt}`*__$__*`{checksum}`*, where: //! //! * *`{rounds}`* is the number of rounds, encoded as a decimal number //! without leading zeroes. //! //! * *`{salt}`* is the salt string. //! //! * *`{checksum}`* is a 86-character Base64 encoding of the checksum. //! //! The format __`$6$`__*`{salt}`*__$__*`{checksum}`* can be used if //! the default number of rounds is chosen. use sha2::Sha512; use super::{Result, HashSetup, IntoHashSetup, consteq}; use crate::random; use crate::sha2_crypt::{sha2_crypt, parse_sha2_hash, sha2_hash_with}; pub use crate::sha2_crypt::MIN_ROUNDS; pub use crate::sha2_crypt::MAX_ROUNDS; pub use crate::sha2_crypt::DEFAULT_ROUNDS; pub use crate::sha2_crypt::MAX_SALT_LEN; const SHA512_MAGIC: &str = "$6$"; const SHA512_TRANSPOSE: &[u8] = b"\x2a\x15\x00\x01\x2b\x16\x17\x02\x2c\x2d\x18\x03\x04\x2e\x19\x1a\ \x05\x2f\x30\x1b\x06\x07\x31\x1c\x1d\x08\x32\x33\x1e\x09\x0a\x34\ \x1f\x20\x0b\x35\x36\x21\x0c\x0d\x37\x22\x23\x0e\x38\x39\x24\x0f\ \x10\x3a\x25\x26\x11\x3b\x3c\x27\x12\x13\x3d\x28\x29\x14\x3e\x3f"; fn do_sha512_crypt(pass: &[u8], salt: &str, rounds: Option) -> Result { sha2_crypt(pass, salt, rounds, Sha512::default, SHA512_TRANSPOSE, SHA512_MAGIC) } /// Hash a password with a randomly generated salt and the default /// number of rounds. /// /// An error is returned if the system random number generator cannot /// be opened. pub fn hash>(pass: B) -> Result { let saltstr = random::gen_salt_str(MAX_SALT_LEN); do_sha512_crypt(pass.as_ref(), &saltstr, None) } fn parse_sha512_hash(hash: &str) -> Result { parse_sha2_hash(hash, SHA512_MAGIC) } /// Hash a password with user-provided parameters. /// /// If the `param` argument is a `&str`, it must be in the final hash /// format. The number of rounds and the salt are parsed out of that value. /// If the salt is too long, it is truncated to maximum length. If it contains /// an invalid character, an error is returned. An out-of-range rounds value /// will be coerced into the allowed range. pub fn hash_with<'a, IHS, B>(param: IHS, pass: B) -> Result where IHS: IntoHashSetup<'a>, B: AsRef<[u8]> { sha2_hash_with(IHS::into_hash_setup(param, parse_sha512_hash)?, pass.as_ref(), do_sha512_crypt) } /// Verify that the hash corresponds to a password. pub fn verify>(pass: B, hash: &str) -> bool { consteq(hash, hash_with(hash, pass)) } #[cfg(test)] mod tests { use super::HashSetup; #[test] fn custom() { assert_eq!(super::hash_with( "$6$rounds=11531$G/gkPn17kHYo0gTF$Kq.uZBHlSBXyzsOJXtxJruOOH4yc0Is13\ uY7yK0PvAvXxbvc1w8DO1RzREMhKsc82K/Jh8OquV8FZUlreYPJk1", "test").unwrap(), "$6$rounds=11531$G/gkPn17kHYo0gTF$Kq.uZBHlSBXyzsOJXtxJruOOH4yc0Is13\ uY7yK0PvAvXxbvc1w8DO1RzREMhKsc82K/Jh8OquV8FZUlreYPJk1"); assert_eq!(super::hash_with(HashSetup { salt: Some("G/gkPn17kHYo0gTF"), rounds: Some(11531) }, "test").unwrap(), "$6$rounds=11531$G/gkPn17kHYo0gTF$Kq.uZBHlSBXyzsOJXtxJruOOH4yc0Is13\ uY7yK0PvAvXxbvc1w8DO1RzREMhKsc82K/Jh8OquV8FZUlreYPJk1"); } #[test] fn implicit_dflt_rounds() { assert_eq!(super::hash_with( "$6$G/gkPn17kHYo0gTF$xhDFU0QYExdMH2ghOWKrrVtu1BuTpNMSJURCXk43.\ EYekmK8iwV6RNqftUUC8mqDel1J7m3JEbUkbu4YyqSyv/", "test").unwrap(), "$6$G/gkPn17kHYo0gTF$xhDFU0QYExdMH2ghOWKrrVtu1BuTpNMSJURCXk43.\ EYekmK8iwV6RNqftUUC8mqDel1J7m3JEbUkbu4YyqSyv/"); } } pwhash-1.0.0/src/unix_crypt.rs010064400017500000144000000053711377437446300146100ustar0000000000000000//! Seventh Edition Unix DES-based hash. // // Copyright (c) 2016 Ivan Nejgebauer // // Licensed under the MIT license . This file may not be copied, // modified, or distributed except according to the terms of this // license. //! //! The original Unix password-hashing algorithm, extremely weak by //! today's standards. It should be used for backward compatibility only. //! //! # Example //! //! ``` //! use pwhash::unix_crypt; //! //! assert_eq!(unix_crypt::hash_with("xO", //! "password").unwrap(), "xOAFZqRz5RduI"); //! assert_eq!(unix_crypt::verify("password","xOAFZqRz5RduI"), //! true); //! //! ``` //! //! # Parameters //! //! * __Password length__: effectively eight 7-bit characters; anything //! longer is ignored. //! //! * __Salt length__: 2 characters (12 bits). //! //! * __Rounds__: 25 (fixed). //! //! # Hash Format //! //! The format of the hash is *`{salt}`*_`{checksum}`_, where: //! //! * *`{salt}`* is a 2-character Base64 encoding of the salt. //! //! * *`{checksum}`* is a 11-character Base64 encoding of the checksum. use super::{Result, consteq}; use crate::des_crypt::unix_crypt; use crate::random; /// Salt length. pub const SALT_LEN: usize = 2; /// Hash a password with a randomly generated salt. /// /// An error is returned if the system random number generator cannot /// be opened. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash>(pass: B) -> Result { let saltstr = random::gen_salt_str(SALT_LEN); unix_crypt(pass.as_ref(), &saltstr) } /// Hash a password with a user-provided salt. /// /// An error is returned if the salt is too short or contains an invalid /// character. #[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")] pub fn hash_with>(salt: &str, pass: B) -> Result { unix_crypt(pass.as_ref(), salt) } /// Verify that the hash corresponds to a password. pub fn verify>(pass: B, hash: &str) -> bool { consteq(hash, unix_crypt(pass.as_ref(), hash)) } #[cfg(test)] mod tests { #[test] #[allow(deprecated)] fn custom() { assert_eq!("aZGJuE6EXrjEE", super::hash_with("aZ", "test").unwrap()); assert_eq!(super::verify("test", "aZGJuE6EXrjEE"), true); assert_eq!(super::verify("test", "aZFJuE6EXrjEE"), false); assert_eq!(super::verify("test", "!!"), false); } #[test] #[allow(deprecated)] #[should_panic(expected="value: EncodingError")] fn bad_salt_chars() { let _ = super::hash_with("!!", "test").unwrap(); } #[test] #[allow(deprecated)] #[should_panic(expected="value: InsufficientLength")] fn short_salt() { let _ = super::hash_with("Z", "test").unwrap(); } }