mirror of
https://github.com/RustCrypto/password-hashes.git
synced 2026-01-25 04:06:23 +00:00
Argon2 (#87)
Translation of the CC0-licensed reference implementation as implemented in C, as of this commit: commit 440ceb9612d5a20997e3e12728542df2de713ca4 Merge: 3df7b84 6d86209 Author: Dmitry Khovratovich <khovratovich@gmail.com> Date: Thu Jul 9 14:56:35 2020 +0200
This commit is contained in:
54
.github/workflows/argon2.yml
vendored
Normal file
54
.github/workflows/argon2.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: argon2
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "argon2/**"
|
||||
- "Cargo.*"
|
||||
push:
|
||||
branches: master
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: argon2
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- 1.47.0 # MSRV
|
||||
- stable
|
||||
target:
|
||||
- thumbv7em-none-eabi
|
||||
- wasm32-unknown-unknown
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- run: cargo build --target ${{ matrix.target }} --release
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- 1.47.0 # MSRV
|
||||
- stable
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
- run: cargo test --release
|
||||
36
Cargo.lock
generated
36
Cargo.lock
generated
@@ -1,5 +1,14 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"hex-literal",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
@@ -23,12 +32,23 @@ name = "bcrypt-pbkdf"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"blowfish",
|
||||
"crypto-mac",
|
||||
"crypto-mac 0.10.0",
|
||||
"pbkdf2",
|
||||
"sha2",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4"
|
||||
dependencies = [
|
||||
"crypto-mac 0.8.0",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
@@ -128,6 +148,16 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.10.0"
|
||||
@@ -195,7 +225,7 @@ version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
"crypto-mac 0.10.0",
|
||||
"digest",
|
||||
]
|
||||
|
||||
@@ -249,7 +279,7 @@ name = "pbkdf2"
|
||||
version = "0.7.0-pre"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"crypto-mac",
|
||||
"crypto-mac 0.10.0",
|
||||
"hex-literal",
|
||||
"hmac",
|
||||
"password-hash",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"argon2",
|
||||
"bcrypt-pbkdf",
|
||||
"pbkdf2",
|
||||
"scrypt",
|
||||
|
||||
@@ -7,10 +7,11 @@ derivation functions, written in pure Rust.
|
||||
|
||||
| Name | Crates.io | Documentation |
|
||||
| --------- |:----------:| :-----:|
|
||||
| [Argon2](https://en.wikipedia.org/wiki/Argon2) | [](https://crates.io/crates/argon2) | [](https://docs.rs/argon2) |
|
||||
| [bcrypt-pbkdf](https://flak.tedunangst.com/post/bcrypt-pbkdf) | [](https://crates.io/crates/bcrypt-pbkdf) | [](https://docs.rs/bcrypt-pbkdf) |
|
||||
| [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2) | [](https://crates.io/crates/pbkdf2) | [](https://docs.rs/pbkdf2) |
|
||||
| [scrypt](https://en.wikipedia.org/wiki/Scrypt) | [](https://crates.io/crates/scrypt) | [](https://docs.rs/scrypt) |
|
||||
| [SHA-crypt](https://www.akkadia.org/drepper/SHA-crypt.txt) | [](https://crates.io/crates/sha-crypt) | [](https://docs.rs/sha-crypt) |
|
||||
| [bcrypt-pbkdf](https://flak.tedunangst.com/post/bcrypt-pbkdf) | [](https://crates.io/crates/bcrypt-pbkdf) | [](https://docs.rs/bcrypt-pbkdf) |
|
||||
|
||||
## License
|
||||
|
||||
|
||||
6
argon2/CHANGELOG.md
Normal file
6
argon2/CHANGELOG.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
22
argon2/Cargo.toml
Normal file
22
argon2/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "argon2"
|
||||
description = """
|
||||
Pure Rust implementation of the Argon2 password hashing function with support
|
||||
for the Argon2d, Argon2i, and Argon2id algorithmic variants
|
||||
"""
|
||||
version = "0.0.0"
|
||||
authors = ["RustCrypto Developers"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
documentation = "https://docs.rs/argon2"
|
||||
repository = "https://github.com/RustCrypto/password-hashes/tree/master/argon2"
|
||||
keywords = ["crypto", "password", "hashing"]
|
||||
categories = ["cryptography", "no-std"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
blake2 = { version = "0.9", default-features = false }
|
||||
zeroize = { version = "1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.3"
|
||||
201
argon2/LICENSE-APACHE
Normal file
201
argon2/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
argon2/LICENSE-MIT
Normal file
25
argon2/LICENSE-MIT
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2021 The RustCrypto Project Developers
|
||||
|
||||
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.
|
||||
56
argon2/README.md
Normal file
56
argon2/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# RustCrypto: Argon2
|
||||
|
||||
[![crate][crate-image]][crate-link]
|
||||
[![Docs][docs-image]][docs-link]
|
||||
![Apache2/MIT licensed][license-image]
|
||||
![Rust Version][rustc-image]
|
||||
[![Project Chat][chat-image]][chat-link]
|
||||
[![Build Status][build-image]][build-link]
|
||||
|
||||
Pure Rust implementation of the [Argon2] password hashing function.
|
||||
|
||||
[Documentation][docs-link]
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
Rust **1.47** or higher.
|
||||
|
||||
Minimum supported Rust version can be changed in the future, but it will be
|
||||
done with a minor version bump.
|
||||
|
||||
## SemVer Policy
|
||||
|
||||
- All on-by-default features of this library are covered by SemVer
|
||||
- MSRV is considered exempt from SemVer as noted above
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* [MIT license](http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
[//]: # (badges)
|
||||
|
||||
[crate-image]: https://img.shields.io/crates/v/argon2.svg
|
||||
[crate-link]: https://crates.io/crates/argon2
|
||||
[docs-image]: https://docs.rs/argon2/badge.svg
|
||||
[docs-link]: https://docs.rs/argon2/
|
||||
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
|
||||
[rustc-image]: https://img.shields.io/badge/rustc-1.47+-blue.svg
|
||||
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
|
||||
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260046-password-hashes
|
||||
[build-image]: https://github.com/RustCrypto/password-hashes/workflows/argon2/badge.svg?branch=master&event=push
|
||||
[build-link]: https://github.com/RustCrypto/password-hashes/actions?query=workflow%3Aargon2
|
||||
|
||||
[//]: # (general links)
|
||||
|
||||
[Argon2]: https://en.wikipedia.org/wiki/Argon2
|
||||
186
argon2/src/block.rs
Normal file
186
argon2/src/block.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
//! Argon2 memory block functions
|
||||
|
||||
use crate::BLOCK_SIZE;
|
||||
use core::{
|
||||
convert::TryInto,
|
||||
num::Wrapping,
|
||||
ops::{BitXor, BitXorAssign, Index, IndexMut},
|
||||
slice,
|
||||
};
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// Quadwords in block
|
||||
const QWORDS_IN_BLOCK: usize = BLOCK_SIZE / 8;
|
||||
|
||||
/// Structure for the (1KB) memory block implemented as 128 64-bit words.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct Block([u64; QWORDS_IN_BLOCK]);
|
||||
|
||||
impl Default for Block {
|
||||
fn default() -> Self {
|
||||
Self([0u64; QWORDS_IN_BLOCK])
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor for Block {
|
||||
type Output = Self;
|
||||
|
||||
fn bitxor(self, rhs: Self) -> Self::Output {
|
||||
let mut res = self;
|
||||
res ^= rhs;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXorAssign for Block {
|
||||
fn bitxor_assign(&mut self, rhs: Self) {
|
||||
for (a, b) in self.iter_mut().zip(rhs.iter()) {
|
||||
*a ^= *b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Block {
|
||||
type Output = u64;
|
||||
|
||||
fn index(&self, index: usize) -> &u64 {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Block {
|
||||
fn index_mut(&mut self, index: usize) -> &mut u64 {
|
||||
&mut self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for Block {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl Block {
|
||||
/// Load a block from a block-sized byte slice
|
||||
pub fn load(&mut self, input: &[u8]) {
|
||||
debug_assert_eq!(input.len(), BLOCK_SIZE);
|
||||
|
||||
for (i, chunk) in input.chunks(8).enumerate() {
|
||||
self[i] = u64::from_le_bytes(chunk.try_into().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over the `u64` values contained in this block
|
||||
pub fn iter(&self) -> slice::Iter<'_, u64> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Iterate mutably over the `u64` values contained in this block
|
||||
pub fn iter_mut(&mut self) -> slice::IterMut<'_, u64> {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
|
||||
/// Function fills a new memory block and optionally XORs the old block over the new one.
|
||||
// TODO(tarcieri): optimized implementation (i.e. from opt.c instead of ref.c)
|
||||
pub fn fill_block(&mut self, prev_block: Block, ref_block: Block, with_xor: bool) {
|
||||
let mut block_r = ref_block ^ prev_block;
|
||||
let mut block_tmp = block_r;
|
||||
|
||||
// Now block_r = ref_block + prev_block and block_tmp = ref_block + prev_block
|
||||
if with_xor {
|
||||
// Saving the next block contents for XOR over
|
||||
block_tmp ^= *self;
|
||||
// Now block_r = ref_block + prev_block and
|
||||
// block_tmp = ref_block + prev_block + next_block
|
||||
}
|
||||
|
||||
/// Blake2 round function
|
||||
// TODO(tarcieri): use the `blake2` crate
|
||||
macro_rules! blake2_round {
|
||||
(
|
||||
$v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr, $v5:expr, $v6:expr, $v7:expr,
|
||||
$v8:expr, $v9:expr, $v10:expr, $v11:expr, $v12:expr, $v13:expr, $v14:expr, $v15:expr
|
||||
) => {
|
||||
blake2_inner!($v0, $v4, $v8, $v12);
|
||||
blake2_inner!($v1, $v5, $v9, $v13);
|
||||
blake2_inner!($v2, $v6, $v10, $v14);
|
||||
blake2_inner!($v3, $v7, $v11, $v15);
|
||||
blake2_inner!($v0, $v5, $v10, $v15);
|
||||
blake2_inner!($v1, $v6, $v11, $v12);
|
||||
blake2_inner!($v2, $v7, $v8, $v13);
|
||||
blake2_inner!($v3, $v4, $v9, $v14);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! blake2_inner {
|
||||
($a:expr, $b:expr, $c:expr, $d:expr) => {
|
||||
$a = blake2_mult($a, $b);
|
||||
$d = ($d ^ $a).rotate_right(32);
|
||||
$c = blake2_mult($c, $d);
|
||||
$b = ($b ^ $c).rotate_right(24);
|
||||
$a = blake2_mult($a, $b);
|
||||
$d = ($d ^ $a).rotate_right(16);
|
||||
$c = blake2_mult($c, $d);
|
||||
$b = ($b ^ $c).rotate_right(63);
|
||||
};
|
||||
}
|
||||
|
||||
// Apply Blake2 on columns of 64-bit words: (0, 1, ..., 15), then
|
||||
// (16, 17, ..31)... finally (112, 113, ...127)
|
||||
for i in 0..8 {
|
||||
blake2_round!(
|
||||
block_r[16 * i],
|
||||
block_r[16 * i + 1],
|
||||
block_r[16 * i + 2],
|
||||
block_r[16 * i + 3],
|
||||
block_r[16 * i + 4],
|
||||
block_r[16 * i + 5],
|
||||
block_r[16 * i + 6],
|
||||
block_r[16 * i + 7],
|
||||
block_r[16 * i + 8],
|
||||
block_r[16 * i + 9],
|
||||
block_r[16 * i + 10],
|
||||
block_r[16 * i + 11],
|
||||
block_r[16 * i + 12],
|
||||
block_r[16 * i + 13],
|
||||
block_r[16 * i + 14],
|
||||
block_r[16 * i + 15]
|
||||
);
|
||||
}
|
||||
|
||||
// Apply Blake2 on rows of 64-bit words: (0, 1, 16, 17, ...112, 113), then
|
||||
// (2, 3, 18, 19, ..., 114, 115).. finally (14, 15, 30, 31, ..., 126, 127)
|
||||
for i in 0..8 {
|
||||
blake2_round!(
|
||||
block_r[2 * i],
|
||||
block_r[2 * i + 1],
|
||||
block_r[2 * i + 16],
|
||||
block_r[2 * i + 17],
|
||||
block_r[2 * i + 32],
|
||||
block_r[2 * i + 33],
|
||||
block_r[2 * i + 48],
|
||||
block_r[2 * i + 49],
|
||||
block_r[2 * i + 64],
|
||||
block_r[2 * i + 65],
|
||||
block_r[2 * i + 80],
|
||||
block_r[2 * i + 81],
|
||||
block_r[2 * i + 96],
|
||||
block_r[2 * i + 97],
|
||||
block_r[2 * i + 112],
|
||||
block_r[2 * i + 113]
|
||||
);
|
||||
}
|
||||
|
||||
*self = block_tmp ^ block_r;
|
||||
}
|
||||
}
|
||||
|
||||
/// Designed by the Lyra PHC team
|
||||
fn blake2_mult(x: u64, y: u64) -> u64 {
|
||||
let m = 0xFFFFFFFF;
|
||||
let xy = Wrapping((x & m) * (y & m)) * Wrapping(2);
|
||||
(Wrapping(x) + Wrapping(y) + xy).0
|
||||
}
|
||||
73
argon2/src/error.rs
Normal file
73
argon2/src/error.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
//! Error type
|
||||
|
||||
use core::fmt;
|
||||
|
||||
/// Error type
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
/// Associated data is too long
|
||||
AdTooLong,
|
||||
|
||||
/// Too few lanes
|
||||
LanesTooFew,
|
||||
|
||||
/// Too many lanes
|
||||
LanesTooMany,
|
||||
|
||||
/// Memory cost is too small
|
||||
MemoryTooLittle,
|
||||
|
||||
/// Memory cost is too large
|
||||
MemoryTooMuch,
|
||||
|
||||
/// Output is too short
|
||||
OutputTooShort,
|
||||
|
||||
/// Output is too long
|
||||
OutputTooLong,
|
||||
|
||||
/// Password is too long
|
||||
PwdTooLong,
|
||||
|
||||
/// Salt is too short
|
||||
SaltTooShort,
|
||||
|
||||
/// Salt is too long
|
||||
SaltTooLong,
|
||||
|
||||
/// Secret is too long
|
||||
SecretTooLong,
|
||||
|
||||
/// Not enough threads
|
||||
ThreadsTooFew,
|
||||
|
||||
/// Too many threads
|
||||
ThreadsTooMany,
|
||||
|
||||
/// Time cost is too small
|
||||
TimeTooSmall,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
Error::AdTooLong => "associated data is too long",
|
||||
Error::LanesTooFew => "too few lanes",
|
||||
Error::LanesTooMany => "too many lanes",
|
||||
Error::MemoryTooLittle => "memory cost is too small",
|
||||
Error::MemoryTooMuch => "memory cost is too large",
|
||||
Error::OutputTooShort => "output is too short",
|
||||
Error::OutputTooLong => "output is too long",
|
||||
Error::PwdTooLong => "password is too long",
|
||||
Error::SaltTooShort => "salt is too short",
|
||||
Error::SaltTooLong => "salt is too long",
|
||||
Error::SecretTooLong => "secret is too long",
|
||||
Error::ThreadsTooFew => "not enough threads",
|
||||
Error::ThreadsTooMany => "too many threads",
|
||||
Error::TimeTooSmall => "time cost is too small",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Result type
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
389
argon2/src/instance.rs
Normal file
389
argon2/src/instance.rs
Normal file
@@ -0,0 +1,389 @@
|
||||
//! Argon2 instance (i.e. state)
|
||||
|
||||
use crate::{Algorithm, Argon2, Block, Error, Result, Version, BLOCK_SIZE, SYNC_POINTS};
|
||||
use blake2::{
|
||||
digest::{self, VariableOutput},
|
||||
Blake2b, Digest, VarBlake2b,
|
||||
};
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// Number of pseudo-random values generated by one call to Blake in Argon2i
|
||||
/// to generate reference block positions
|
||||
const ADDRESSES_IN_BLOCK: u32 = 128;
|
||||
|
||||
/// Output size of BLAKE2b in bytes
|
||||
const BLAKE2B_OUTBYTES: usize = 64;
|
||||
|
||||
/// Argon2 position: where we construct the block right now.
|
||||
///
|
||||
/// Used to distribute work between threads.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
struct Position {
|
||||
pass: u32,
|
||||
lane: u32,
|
||||
slice: u32,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
/// Argon2 instance: memory pointer, number of passes, amount of memory, type,
|
||||
/// and derived values.
|
||||
///
|
||||
/// Used to evaluate the number and location of blocks to construct in each
|
||||
/// thread.
|
||||
pub(crate) struct Instance<'a> {
|
||||
/// Memory blocks
|
||||
memory: &'a mut [Block],
|
||||
|
||||
/// Version
|
||||
version: Version,
|
||||
|
||||
/// Number of passes
|
||||
passes: u32,
|
||||
|
||||
/// Segment length
|
||||
segment_length: u32,
|
||||
|
||||
/// Lane length
|
||||
lane_length: u32,
|
||||
|
||||
/// Number of lanes
|
||||
lanes: u32,
|
||||
|
||||
/// Number of threads
|
||||
threads: u32,
|
||||
|
||||
/// Argon2 type
|
||||
alg: Algorithm,
|
||||
}
|
||||
|
||||
impl<'a> Instance<'a> {
|
||||
/// Hash the given inputs with Argon2, writing the output into the
|
||||
/// provided buffer.
|
||||
pub fn hash(
|
||||
context: &Argon2<'_>,
|
||||
alg: Algorithm,
|
||||
initial_hash: digest::Output<Blake2b>,
|
||||
memory: &'a mut [Block],
|
||||
out: &mut [u8],
|
||||
) -> Result<()> {
|
||||
let mut instance = Self::new(context, alg, initial_hash, memory)?;
|
||||
|
||||
// Filling memory
|
||||
instance.fill_memory_blocks();
|
||||
|
||||
// Finalization
|
||||
instance.finalize(out)
|
||||
}
|
||||
|
||||
/// Hashes the inputs with BLAKE2b and creates first two blocks.
|
||||
///
|
||||
/// Returns struct containing main memory with 2 blocks per lane initialized.
|
||||
#[allow(unused_mut)]
|
||||
fn new(
|
||||
context: &Argon2<'_>,
|
||||
alg: Algorithm,
|
||||
mut initial_hash: digest::Output<Blake2b>,
|
||||
memory: &'a mut [Block],
|
||||
) -> Result<Self> {
|
||||
let mut instance = Instance {
|
||||
version: context.version,
|
||||
memory,
|
||||
passes: context.t_cost,
|
||||
segment_length: context.segment_length,
|
||||
lane_length: context.segment_length * SYNC_POINTS,
|
||||
lanes: context.lanes,
|
||||
threads: context.threads,
|
||||
alg,
|
||||
};
|
||||
|
||||
if instance.threads > instance.lanes {
|
||||
instance.threads = instance.lanes;
|
||||
}
|
||||
|
||||
// GENKAT note: this is where `initial_kat` would be called
|
||||
|
||||
// Creating first blocks, we always have at least two blocks in a slice
|
||||
instance.fill_first_blocks(&initial_hash)?;
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
initial_hash.zeroize();
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
/// Function that fills the entire memory t_cost times based on the first two
|
||||
/// blocks in each lane
|
||||
fn fill_memory_blocks(&mut self) {
|
||||
// TODO(tarcieri): multithread support
|
||||
// Single-threaded version for p=1 case
|
||||
for r in 0..self.passes {
|
||||
for s in 0..SYNC_POINTS {
|
||||
for l in 0..self.lanes {
|
||||
self.fill_segment(Position {
|
||||
pass: r,
|
||||
lane: l,
|
||||
slice: s,
|
||||
index: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// GENKAT note: this is where `internal_kat` would be called
|
||||
}
|
||||
}
|
||||
|
||||
/// XORing the last block of each lane, hashing it, making the tag.
|
||||
fn finalize(&mut self, out: &mut [u8]) -> Result<()> {
|
||||
let mut blockhash = self.memory[(self.lane_length - 1) as usize];
|
||||
|
||||
// XOR the last blocks
|
||||
for l in 1..self.lanes {
|
||||
let last_block_in_lane = l * self.lane_length + (self.lane_length - 1);
|
||||
blockhash ^= self.memory[last_block_in_lane as usize];
|
||||
}
|
||||
|
||||
// Hash the result
|
||||
let mut blockhash_bytes = [0u8; BLOCK_SIZE];
|
||||
|
||||
for (chunk, v) in blockhash_bytes.chunks_mut(8).zip(blockhash.iter()) {
|
||||
chunk.copy_from_slice(&v.to_le_bytes())
|
||||
}
|
||||
|
||||
blake2b_long(&[&blockhash_bytes], out)?;
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
blockhash.zeroize();
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
blockhash_bytes.zeroize();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Function creates first 2 blocks per lane
|
||||
fn fill_first_blocks(&mut self, blockhash: &[u8]) -> Result<()> {
|
||||
let mut hash = [0u8; BLOCK_SIZE];
|
||||
|
||||
for l in 0..self.lanes {
|
||||
// Make the first and second block in each lane as G(H0||0||i) or
|
||||
// G(H0||1||i)
|
||||
for i in 0u32..2u32 {
|
||||
blake2b_long(&[blockhash, &i.to_le_bytes(), &l.to_le_bytes()], &mut hash)?;
|
||||
self.memory[(l * self.lane_length + i) as usize].load(&hash);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Function that fills the segment using previous segments
|
||||
// TODO(tarcieri): optimized implementation (i.e. from opt.c instead of ref.c)
|
||||
fn fill_segment(&mut self, mut position: Position) {
|
||||
let mut address_block = Block::default();
|
||||
let mut input_block = Block::default();
|
||||
let zero_block = Block::default();
|
||||
|
||||
let data_independent_addressing = (self.alg == Algorithm::Argon2i)
|
||||
|| (self.alg == Algorithm::Argon2id
|
||||
&& (position.pass == 0)
|
||||
&& (position.slice < SYNC_POINTS / 2));
|
||||
|
||||
if data_independent_addressing {
|
||||
input_block[0] = position.pass as u64;
|
||||
input_block[1] = position.lane as u64;
|
||||
input_block[2] = position.slice as u64;
|
||||
input_block[3] = self.memory.len() as u64;
|
||||
input_block[4] = self.passes as u64;
|
||||
input_block[5] = self.alg as u64;
|
||||
}
|
||||
|
||||
let mut starting_index = 0;
|
||||
|
||||
if position.pass == 0 && position.slice == 0 {
|
||||
starting_index = 2; // we have already generated the first two blocks
|
||||
|
||||
// Don't forget to generate the first block of addresses
|
||||
if data_independent_addressing {
|
||||
next_addresses(&mut address_block, &mut input_block, &zero_block);
|
||||
}
|
||||
}
|
||||
|
||||
// Offset of the current block
|
||||
let mut curr_offset = position.lane * self.lane_length
|
||||
+ position.slice * self.segment_length
|
||||
+ starting_index;
|
||||
|
||||
let mut prev_offset = if 0 == curr_offset % self.lane_length {
|
||||
// Last block in this lane
|
||||
curr_offset + self.lane_length - 1
|
||||
} else {
|
||||
// Previous block
|
||||
curr_offset - 1
|
||||
};
|
||||
|
||||
for i in starting_index..self.segment_length {
|
||||
// 1.1 Rotating prev_offset if needed
|
||||
if curr_offset % self.lane_length == 1 {
|
||||
prev_offset = curr_offset - 1;
|
||||
}
|
||||
|
||||
// 1.2 Computing the index of the reference block
|
||||
// 1.2.1 Taking pseudo-random value from the previous block
|
||||
let pseudo_rand = if data_independent_addressing {
|
||||
if i % ADDRESSES_IN_BLOCK == 0 {
|
||||
next_addresses(&mut address_block, &mut input_block, &zero_block);
|
||||
}
|
||||
address_block[(i % ADDRESSES_IN_BLOCK) as usize]
|
||||
} else {
|
||||
self.memory[prev_offset as usize][0]
|
||||
};
|
||||
|
||||
// 1.2.2 Computing the lane of the reference block
|
||||
let mut ref_lane = (pseudo_rand >> 32) as u32 % self.lanes;
|
||||
|
||||
if position.pass == 0 && position.slice == 0 {
|
||||
// Can not reference other lanes yet
|
||||
ref_lane = position.lane;
|
||||
}
|
||||
|
||||
// 1.2.3 Computing the number of possible reference block within the lane.
|
||||
position.index = i;
|
||||
|
||||
let ref_index = self.index_alpha(
|
||||
position,
|
||||
(pseudo_rand & 0xFFFFFFFF) as u32,
|
||||
ref_lane == position.lane,
|
||||
);
|
||||
|
||||
// 2 Creating a new block
|
||||
let ref_block = self.memory[(self.lane_length * ref_lane + ref_index) as usize];
|
||||
let prev_block = self.memory[prev_offset as usize];
|
||||
|
||||
// version 1.2.1 and earlier: overwrite, not XOR
|
||||
let without_xor = self.version == Version::V0x10 || position.pass == 0;
|
||||
self.memory[curr_offset as usize].fill_block(prev_block, ref_block, !without_xor);
|
||||
|
||||
curr_offset += 1;
|
||||
prev_offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes absolute position of reference block in the lane following a skewed
|
||||
/// distribution and using a pseudo-random value as input.
|
||||
///
|
||||
/// # Params
|
||||
/// - `position`: Pointer to the current position
|
||||
/// - `pseudo_rand`: 32-bit pseudo-random value used to determine the position
|
||||
/// - `same_lane`: Indicates if the block will be taken from the current lane.
|
||||
/// If so we can reference the current segment.
|
||||
fn index_alpha(&self, position: Position, pseudo_rand: u32, same_lane: bool) -> u32 {
|
||||
// Pass 0:
|
||||
// - This lane: all already finished segments plus already constructed
|
||||
// blocks in this segment
|
||||
// - Other lanes: all already finished segments
|
||||
//
|
||||
// Pass 1+:
|
||||
// - This lane: (SYNC_POINTS - 1) last segments plus already constructed
|
||||
// blocks in this segment
|
||||
// - Other lanes : (SYNC_POINTS - 1) last segments
|
||||
let reference_area_size = if 0 == position.pass {
|
||||
// First pass
|
||||
if position.slice == 0 {
|
||||
// First slice
|
||||
position.index - 1 // all but the previous
|
||||
} else if same_lane {
|
||||
// The same lane => add current segment
|
||||
position.slice * self.segment_length + position.index - 1
|
||||
} else {
|
||||
position.slice * self.segment_length - if position.index == 0 { 1 } else { 0 }
|
||||
}
|
||||
} else {
|
||||
// Second pass
|
||||
if same_lane {
|
||||
self.lane_length - self.segment_length + position.index - 1
|
||||
} else {
|
||||
self.lane_length - self.segment_length - if position.index == 0 { 1 } else { 0 }
|
||||
}
|
||||
};
|
||||
|
||||
// 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1> and produce
|
||||
// relative position
|
||||
let mut relative_position = pseudo_rand as u64;
|
||||
relative_position = (relative_position * relative_position) >> 32;
|
||||
let relative_position = reference_area_size
|
||||
- 1
|
||||
- (((reference_area_size as u64 * relative_position) >> 32) as u32);
|
||||
|
||||
// 1.2.5 Computing starting position
|
||||
let mut start_position = 0;
|
||||
|
||||
if position.pass != 0 {
|
||||
start_position = if position.slice == SYNC_POINTS - 1 {
|
||||
0
|
||||
} else {
|
||||
(position.slice + 1) * self.segment_length
|
||||
}
|
||||
}
|
||||
|
||||
// 1.2.6. Computing absolute position
|
||||
(start_position + relative_position as u32) % self.lane_length
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute next addresses
|
||||
fn next_addresses(address_block: &mut Block, input_block: &mut Block, zero_block: &Block) {
|
||||
input_block[6] += 1;
|
||||
address_block.fill_block(*zero_block, *input_block, false);
|
||||
address_block.fill_block(*zero_block, *address_block, false);
|
||||
}
|
||||
|
||||
/// BLAKE2b with an extended output
|
||||
fn blake2b_long(inputs: &[&[u8]], mut out: &mut [u8]) -> Result<()> {
|
||||
if out.len() > u32::MAX as usize {
|
||||
return Err(Error::OutputTooLong);
|
||||
}
|
||||
|
||||
let outlen_bytes = (out.len() as u32).to_le_bytes();
|
||||
|
||||
if out.len() <= BLAKE2B_OUTBYTES {
|
||||
let mut digest = VarBlake2b::new(out.len()).unwrap();
|
||||
digest::Update::update(&mut digest, &outlen_bytes);
|
||||
|
||||
for input in inputs {
|
||||
digest::Update::update(&mut digest, input);
|
||||
}
|
||||
|
||||
digest.finalize_variable(|hash| out.copy_from_slice(hash));
|
||||
} else {
|
||||
let mut digest = Blake2b::new();
|
||||
digest.update(&outlen_bytes);
|
||||
|
||||
for input in inputs {
|
||||
digest.update(input);
|
||||
}
|
||||
|
||||
let mut out_buffer = [0u8; BLAKE2B_OUTBYTES];
|
||||
out_buffer.copy_from_slice(&digest.finalize());
|
||||
|
||||
out[..(BLAKE2B_OUTBYTES / 2)].copy_from_slice(&out_buffer[..(BLAKE2B_OUTBYTES / 2)]);
|
||||
out = &mut out[(BLAKE2B_OUTBYTES / 2)..];
|
||||
|
||||
let mut in_buffer = [0u8; BLAKE2B_OUTBYTES];
|
||||
|
||||
while out.len() > BLAKE2B_OUTBYTES {
|
||||
in_buffer.copy_from_slice(&out_buffer);
|
||||
out_buffer.copy_from_slice(&Blake2b::digest(&in_buffer));
|
||||
|
||||
out[..(BLAKE2B_OUTBYTES / 2)].copy_from_slice(&out_buffer[..(BLAKE2B_OUTBYTES / 2)]);
|
||||
out = &mut out[(BLAKE2B_OUTBYTES / 2)..];
|
||||
}
|
||||
|
||||
let mut digest = VarBlake2b::new(out.len()).unwrap();
|
||||
digest::Update::update(&mut digest, &out_buffer);
|
||||
digest.finalize_variable(|hash| out.copy_from_slice(hash));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
370
argon2/src/lib.rs
Normal file
370
argon2/src/lib.rs
Normal file
@@ -0,0 +1,370 @@
|
||||
//! Pure Rust implementation of the [Argon2] password hashing function.
|
||||
//!
|
||||
//! # About
|
||||
//!
|
||||
//! Argon2 is a memory-hard [key derivation function] chosen as the winner of
|
||||
//! the 2015 [Password Hashing Competition] in July 2015.
|
||||
//!
|
||||
//! It provides three algorithmic variants (chosen via the [`Algorithm`] enum):
|
||||
//!
|
||||
//! - **Argon2d**: maximizes resistance to GPU cracking attacks
|
||||
//! - **Argon2i**: optimized to resist side-channel attacks
|
||||
//! - **Argon2id**: (default) hybrid version
|
||||
//!
|
||||
//! # Notes
|
||||
//!
|
||||
//! Multithreading has not yet been implemented.
|
||||
//!
|
||||
//! Will still compute the correct results for higher parallelism factors, but
|
||||
//! there will be no associated performance improvement.
|
||||
//!
|
||||
//! [Argon2]: https://en.wikipedia.org/wiki/Argon2
|
||||
//! [key derivation function]: https://en.wikipedia.org/wiki/Key_derivation_function
|
||||
//! [Password Hashing Competition]: https://www.password-hashing.net/
|
||||
|
||||
#![no_std]
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
|
||||
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(rust_2018_idioms, missing_docs)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
mod block;
|
||||
mod error;
|
||||
mod instance;
|
||||
|
||||
pub use crate::error::{Error, Result};
|
||||
|
||||
use crate::{block::Block, instance::Instance};
|
||||
use blake2::{digest, Blake2b, Digest};
|
||||
|
||||
/// Minimum and maximum number of lanes (degree of parallelism)
|
||||
pub const MIN_LANES: u32 = 1;
|
||||
|
||||
/// Minimum and maximum number of lanes (degree of parallelism)
|
||||
pub const MAX_LANES: u32 = 0xFFFFFF;
|
||||
|
||||
/// Minimum and maximum number of threads
|
||||
pub const MIN_THREADS: u32 = 1;
|
||||
|
||||
/// Minimum and maximum number of threads
|
||||
pub const MAX_THREADS: u32 = 0xFFFFFF;
|
||||
|
||||
/// Minimum digest size in bytes
|
||||
pub const MIN_OUTLEN: usize = 4;
|
||||
|
||||
/// Maximum digest size in bytes
|
||||
pub const MAX_OUTLEN: usize = 0xFFFFFFFF;
|
||||
|
||||
/// Minimum number of memory blocks (each of [`BLOCK_SIZE`] bytes)
|
||||
pub const MIN_MEMORY: u32 = 2 * SYNC_POINTS; // 2 blocks per slice
|
||||
|
||||
/// Maximum number of memory blocks (each of [`BLOCK_SIZE`] bytes)
|
||||
pub const MAX_MEMORY: u32 = 0x0FFFFFFF;
|
||||
|
||||
/// Minimum number of passes
|
||||
pub const MIN_TIME: u32 = 1;
|
||||
|
||||
/// Maximum number of passes
|
||||
pub const MAX_TIME: u32 = 0xFFFFFFFF;
|
||||
|
||||
/// Maximum password length in bytes
|
||||
pub const MAX_PWD_LENGTH: usize = 0xFFFFFFFF;
|
||||
|
||||
/// Minimum and maximum associated data length in bytes
|
||||
pub const MAX_AD_LENGTH: usize = 0xFFFFFFFF;
|
||||
|
||||
/// Minimum and maximum salt length in bytes
|
||||
pub const MIN_SALT_LENGTH: usize = 8;
|
||||
|
||||
/// Maximum salt length in bytes
|
||||
pub const MAX_SALT_LENGTH: usize = 0xFFFFFFFF;
|
||||
|
||||
/// Maximum key length in bytes
|
||||
pub const MAX_SECRET: usize = 0xFFFFFFFF;
|
||||
|
||||
/// Memory block size in bytes
|
||||
pub const BLOCK_SIZE: usize = 1024;
|
||||
|
||||
/// Number of synchronization points between lanes per pass
|
||||
const SYNC_POINTS: u32 = 4;
|
||||
|
||||
/// Argon2 primitive type: variants of the algorithm
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Algorithm {
|
||||
/// Optimizes against GPU cracking attacks but vulnerable to side-channels.
|
||||
///
|
||||
/// Accesses the memory array in a password dependent order, reducing the
|
||||
/// possibility of time–memory tradeoff (TMTO) attacks.
|
||||
Argon2d = 0,
|
||||
|
||||
/// Optimized to resist side-channel attacks.
|
||||
///
|
||||
/// Accesses the memory array in a password independent order, increasing the
|
||||
/// possibility of time-memory tradeoff (TMTO) attacks.
|
||||
Argon2i = 1,
|
||||
|
||||
/// Hybrid that mixes Argon2i and Argon2d passes (*default*).
|
||||
///
|
||||
/// Uses the Argon2i approach for the first half pass over memory and
|
||||
/// Argon2d approach for subsequent passes.
|
||||
Argon2id = 2,
|
||||
}
|
||||
|
||||
impl Default for Algorithm {
|
||||
fn default() -> Algorithm {
|
||||
Algorithm::Argon2id
|
||||
}
|
||||
}
|
||||
|
||||
impl Algorithm {
|
||||
/// Serialize primitive type as little endian bytes
|
||||
fn to_le_bytes(self) -> [u8; 4] {
|
||||
(self as u32).to_le_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// Version of the algorithm
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
#[repr(u32)]
|
||||
pub enum Version {
|
||||
/// Version 16 (0x10 in hex)
|
||||
///
|
||||
/// Performs overwrite internally
|
||||
V0x10 = 0x10,
|
||||
|
||||
/// Version 19 (0x13 in hex, default)
|
||||
///
|
||||
/// Performs XOR internally
|
||||
V0x13 = 0x13,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
/// Serialize version as little endian bytes
|
||||
fn to_le_bytes(self) -> [u8; 4] {
|
||||
(self as u32).to_le_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Version {
|
||||
fn default() -> Self {
|
||||
Self::V0x13
|
||||
}
|
||||
}
|
||||
|
||||
/// Argon2 context
|
||||
///
|
||||
/// Holds the following Argon2 inputs:
|
||||
///
|
||||
/// - output array and its length,
|
||||
/// - password and its length,
|
||||
/// - salt and its length,
|
||||
/// - secret and its length,
|
||||
/// - associated data and its length,
|
||||
/// - number of passes, amount of used memory (in KBytes, can be rounded up a bit)
|
||||
/// - number of parallel threads that will be run.
|
||||
///
|
||||
/// All the parameters above affect the output hash value.
|
||||
/// Additionally, two function pointers can be provided to allocate and
|
||||
/// deallocate the memory (if NULL, memory will be allocated internally).
|
||||
/// Also, three flags indicate whether to erase password, secret as soon as they
|
||||
/// are pre-hashed (and thus not needed anymore), and the entire memory
|
||||
///
|
||||
/// Simplest situation: you have output array `out[8]`, password is stored in
|
||||
/// `pwd[32]`, salt is stored in `salt[16]`, you do not have keys nor associated
|
||||
/// data.
|
||||
///
|
||||
/// You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
|
||||
/// 4 parallel lanes.
|
||||
///
|
||||
/// You want to erase the password, but you're OK with last pass not being
|
||||
/// erased.
|
||||
pub struct Argon2<'key> {
|
||||
/// Key array
|
||||
secret: Option<&'key [u8]>,
|
||||
|
||||
/// Number of passes
|
||||
t_cost: u32,
|
||||
|
||||
/// Amount of memory requested (kB)
|
||||
m_cost: u32,
|
||||
|
||||
/// Number of lanes
|
||||
lanes: u32,
|
||||
|
||||
/// Maximum number of threads
|
||||
threads: u32,
|
||||
|
||||
/// Segment length
|
||||
pub(crate) segment_length: u32,
|
||||
|
||||
/// Version number
|
||||
version: Version,
|
||||
}
|
||||
|
||||
impl Default for Argon2<'_> {
|
||||
fn default() -> Self {
|
||||
Self::new(None, 3, 4096, 1, Version::default()).expect("invalid default Argon2 params")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'key> Argon2<'key> {
|
||||
/// Create a new Argon2 context
|
||||
pub fn new(
|
||||
secret: Option<&'key [u8]>,
|
||||
t_cost: u32,
|
||||
m_cost: u32,
|
||||
parallelism: u32,
|
||||
version: Version,
|
||||
) -> Result<Self> {
|
||||
let lanes = parallelism;
|
||||
|
||||
if let Some(secret) = &secret {
|
||||
if MAX_SECRET < secret.len() {
|
||||
return Err(Error::SecretTooLong);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate memory cost
|
||||
if MIN_MEMORY > m_cost {
|
||||
return Err(Error::MemoryTooLittle);
|
||||
}
|
||||
|
||||
if MAX_MEMORY < m_cost {
|
||||
return Err(Error::MemoryTooMuch);
|
||||
}
|
||||
|
||||
if m_cost < 8 * lanes {
|
||||
return Err(Error::MemoryTooLittle);
|
||||
}
|
||||
|
||||
// Validate time cost
|
||||
if t_cost < MIN_TIME {
|
||||
return Err(Error::TimeTooSmall);
|
||||
}
|
||||
|
||||
// Validate lanes
|
||||
if MIN_LANES > lanes {
|
||||
return Err(Error::LanesTooFew);
|
||||
}
|
||||
|
||||
if MAX_LANES < parallelism {
|
||||
return Err(Error::LanesTooMany);
|
||||
}
|
||||
|
||||
// Validate threads
|
||||
if MIN_THREADS > lanes {
|
||||
return Err(Error::ThreadsTooFew);
|
||||
}
|
||||
|
||||
if MAX_THREADS < parallelism {
|
||||
return Err(Error::ThreadsTooMany);
|
||||
}
|
||||
|
||||
// Align memory size
|
||||
// Minimum memory_blocks = 8L blocks, where L is the number of lanes
|
||||
let memory_blocks = if m_cost < 2 * SYNC_POINTS * lanes {
|
||||
2 * SYNC_POINTS * lanes
|
||||
} else {
|
||||
m_cost
|
||||
};
|
||||
|
||||
let segment_length = memory_blocks / (lanes * SYNC_POINTS);
|
||||
|
||||
Ok(Self {
|
||||
secret,
|
||||
t_cost,
|
||||
m_cost,
|
||||
lanes,
|
||||
threads: parallelism,
|
||||
segment_length,
|
||||
version,
|
||||
})
|
||||
}
|
||||
|
||||
/// Function that performs memory-hard hashing with certain degree of parallelism.
|
||||
pub fn hash_password(
|
||||
&self,
|
||||
alg: Algorithm,
|
||||
pwd: &[u8],
|
||||
salt: &[u8],
|
||||
ad: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> Result<()> {
|
||||
// Validate output length
|
||||
if MIN_OUTLEN > out.len() {
|
||||
return Err(Error::OutputTooShort);
|
||||
}
|
||||
|
||||
if MAX_OUTLEN < out.len() {
|
||||
return Err(Error::OutputTooLong);
|
||||
}
|
||||
|
||||
if MAX_PWD_LENGTH < pwd.len() {
|
||||
return Err(Error::PwdTooLong);
|
||||
}
|
||||
|
||||
// Validate salt (required param)
|
||||
if MIN_SALT_LENGTH > salt.len() {
|
||||
return Err(Error::SaltTooShort);
|
||||
}
|
||||
|
||||
if MAX_SALT_LENGTH < salt.len() {
|
||||
return Err(Error::SaltTooLong);
|
||||
}
|
||||
|
||||
// Validate associated data (optional param)
|
||||
if MAX_AD_LENGTH < ad.len() {
|
||||
return Err(Error::AdTooLong);
|
||||
}
|
||||
|
||||
let memory_blocks = (self.segment_length * self.lanes * SYNC_POINTS) as usize;
|
||||
|
||||
// Hashing all inputs
|
||||
#[allow(unused_mut)]
|
||||
let mut initial_hash = self.initial_hash(alg, pwd, salt, ad, out);
|
||||
|
||||
// TODO(tarcieri): support for stack-allocated memory blocks (i.e. no alloc)
|
||||
let mut memory = vec![Block::default(); memory_blocks];
|
||||
|
||||
Instance::hash(self, alg, initial_hash, &mut memory, out)
|
||||
}
|
||||
|
||||
/// Hashes all the inputs into `blockhash[PREHASH_DIGEST_LENGTH]`.
|
||||
fn initial_hash(
|
||||
&self,
|
||||
alg: Algorithm,
|
||||
pwd: &[u8],
|
||||
salt: &[u8],
|
||||
ad: &[u8],
|
||||
out: &[u8],
|
||||
) -> digest::Output<Blake2b> {
|
||||
let mut digest = Blake2b::new();
|
||||
digest.update(&self.lanes.to_le_bytes());
|
||||
digest.update(&(out.len() as u32).to_le_bytes());
|
||||
digest.update(&self.m_cost.to_le_bytes());
|
||||
digest.update(&self.t_cost.to_le_bytes());
|
||||
digest.update(&self.version.to_le_bytes());
|
||||
digest.update(&alg.to_le_bytes());
|
||||
digest.update(&(pwd.len() as u32).to_le_bytes());
|
||||
digest.update(pwd);
|
||||
digest.update(&(salt.len() as u32).to_le_bytes());
|
||||
digest.update(salt);
|
||||
|
||||
if let Some(secret) = &self.secret {
|
||||
digest.update(&(secret.len() as u32).to_le_bytes());
|
||||
digest.update(secret);
|
||||
} else {
|
||||
digest.update(0u32.to_le_bytes());
|
||||
}
|
||||
|
||||
digest.update(&(ad.len() as u32).to_le_bytes());
|
||||
digest.update(ad);
|
||||
|
||||
digest.finalize()
|
||||
}
|
||||
}
|
||||
299
argon2/tests/test_vectors.rs
Normal file
299
argon2/tests/test_vectors.rs
Normal file
@@ -0,0 +1,299 @@
|
||||
//! Argon2 test vectors.
|
||||
//!
|
||||
//! Taken from the Argon2 reference implementation as well as
|
||||
//! `draft-irtf-cfrg-argon2-12` Section 5:
|
||||
//! <https://datatracker.ietf.org/doc/draft-irtf-cfrg-argon2/>
|
||||
|
||||
use argon2::{Algorithm, Argon2, Version};
|
||||
use hex_literal::hex;
|
||||
|
||||
/// =======================================
|
||||
/// Argon2d version number 16
|
||||
/// =======================================
|
||||
/// Memory: 32 KiB, Iterations: 3, Parallelism: 4 lanes, Tag length: 32 bytes
|
||||
/// Password[32]:
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// Salt[16]: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
|
||||
/// Secret[8]: 03 03 03 03 03 03 03 03
|
||||
/// Associated data[12]: 04 04 04 04 04 04 04 04 04 04 04 04
|
||||
/// Pre-hashing digest:
|
||||
/// ec dc 26 dc 6b dd 21 56 19 68 97 aa 8c c9 a0 4c
|
||||
/// 03 ed 07 cd 12 92 67 c5 3c a6 ae f7 76 a4 30 89
|
||||
/// 6a 09 80 54 e4 de c3 e0 2e cd 82 c4 7f 56 2c a2
|
||||
/// 73 d2 f6 97 8a 5c 05 41 1a 0c d0 9d 47 7b 7b 06
|
||||
/// Tag[32]:
|
||||
/// 96 a9 d4 e5 a1 73 40 92 c8 5e 29 f4 10 a4 59 14
|
||||
/// a5 dd 1f 5c bf 08 b2 67 0d a6 8a 02 85 ab f3 2b
|
||||
#[test]
|
||||
fn argon2d_v0x10() {
|
||||
let version = Version::V0x10;
|
||||
let m_cost = 32;
|
||||
let t_cost = 3;
|
||||
let parallelism = 4;
|
||||
let password = [0x01; 32];
|
||||
let salt = [0x02; 16];
|
||||
let secret = [0x03; 8];
|
||||
let ad = [0x04; 12];
|
||||
let expected_tag = hex!(
|
||||
"
|
||||
96 a9 d4 e5 a1 73 40 92 c8 5e 29 f4 10 a4 59 14
|
||||
a5 dd 1f 5c bf 08 b2 67 0d a6 8a 02 85 ab f3 2b
|
||||
"
|
||||
);
|
||||
|
||||
let ctx = Argon2::new(Some(&secret), t_cost, m_cost, parallelism, version).unwrap();
|
||||
|
||||
let mut out = [0u8; 32];
|
||||
ctx.hash_password(Algorithm::Argon2d, &password, &salt, &ad, &mut out)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(out, expected_tag);
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Argon2i version number 16
|
||||
/// =======================================
|
||||
/// Memory: 32 KiB, Iterations: 3, Parallelism: 4 lanes, Tag length: 32 bytes
|
||||
/// Password[32]:
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// Salt[16]: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
|
||||
/// Secret[8]: 03 03 03 03 03 03 03 03
|
||||
/// Associated data[12]: 04 04 04 04 04 04 04 04 04 04 04 04
|
||||
/// Pre-hashing digest:
|
||||
/// 1c dc ec c8 58 ca 1b 6d 45 c7 3c 78 d0 00 76 c5
|
||||
/// ec fc 5e df 14 45 b4 43 73 97 b1 b8 20 83 ff bf
|
||||
/// e3 c9 1a a8 f5 06 67 ad 8f b9 d4 e7 52 df b3 85
|
||||
/// 34 71 9f ba d2 22 61 33 7b 2b 55 29 81 44 09 af
|
||||
/// Tag[32]:
|
||||
/// 87 ae ed d6 51 7a b8 30 cd 97 65 cd 82 31 ab b2
|
||||
/// e6 47 a5 de e0 8f 7c 05 e0 2f cb 76 33 35 d0 fd
|
||||
#[test]
|
||||
fn argon2i_v0x10() {
|
||||
let version = Version::V0x10;
|
||||
let m_cost = 32;
|
||||
let t_cost = 3;
|
||||
let parallelism = 4;
|
||||
let password = [0x01; 32];
|
||||
let salt = [0x02; 16];
|
||||
let secret = [0x03; 8];
|
||||
let ad = [0x04; 12];
|
||||
let expected_tag = hex!(
|
||||
"
|
||||
87 ae ed d6 51 7a b8 30 cd 97 65 cd 82 31 ab b2
|
||||
e6 47 a5 de e0 8f 7c 05 e0 2f cb 76 33 35 d0 fd
|
||||
"
|
||||
);
|
||||
|
||||
let ctx = Argon2::new(Some(&secret), t_cost, m_cost, parallelism, version).unwrap();
|
||||
|
||||
let mut out = [0u8; 32];
|
||||
ctx.hash_password(Algorithm::Argon2i, &password, &salt, &ad, &mut out)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(out, expected_tag);
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Argon2id version number 16
|
||||
/// =======================================
|
||||
/// Memory: 32 KiB, Iterations: 3, Parallelism: 4 lanes, Tag length: 32 bytes
|
||||
/// Password[32]:
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// Salt[16]: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
|
||||
/// Secret[8]: 03 03 03 03 03 03 03 03
|
||||
/// Associated data[12]: 04 04 04 04 04 04 04 04 04 04 04 04
|
||||
/// Pre-hashing digest:
|
||||
/// 70 65 ab 9c 82 b5 f0 e8 71 28 c7 84 7a 02 1d 1e
|
||||
/// 59 aa 16 66 6f c8 b4 ef ac a3 86 3f bf d6 5e 0e
|
||||
/// 8b a6 f6 09 eb bc 9b 60 e2 78 22 c8 24 b7 50 6f
|
||||
/// b9 f9 5b e9 0e e5 84 2a ac 6e d6 b7 da 67 30 44
|
||||
/// Tag[32]:
|
||||
/// b6 46 15 f0 77 89 b6 6b 64 5b 67 ee 9e d3 b3 77
|
||||
/// ae 35 0b 6b fc bb 0f c9 51 41 ea 8f 32 26 13 c0
|
||||
#[test]
|
||||
fn argon2id_v0x10() {
|
||||
let version = Version::V0x10;
|
||||
let m_cost = 32;
|
||||
let t_cost = 3;
|
||||
let parallelism = 4;
|
||||
let password = [0x01; 32];
|
||||
let salt = [0x02; 16];
|
||||
let secret = [0x03; 8];
|
||||
let ad = [0x04; 12];
|
||||
let expected_tag = hex!(
|
||||
"
|
||||
b6 46 15 f0 77 89 b6 6b 64 5b 67 ee 9e d3 b3 77
|
||||
ae 35 0b 6b fc bb 0f c9 51 41 ea 8f 32 26 13 c0
|
||||
"
|
||||
);
|
||||
|
||||
let ctx = Argon2::new(Some(&secret), t_cost, m_cost, parallelism, version).unwrap();
|
||||
|
||||
let mut out = [0u8; 32];
|
||||
ctx.hash_password(Algorithm::Argon2id, &password, &salt, &ad, &mut out)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(out, expected_tag);
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Argon2d version number 19
|
||||
/// =======================================
|
||||
/// Memory: 32 KiB
|
||||
/// Passes: 3
|
||||
/// Parallelism: 4 lanes
|
||||
/// Tag length: 32 bytes
|
||||
/// Password[32]:
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// Salt[16]: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
|
||||
/// Secret[8]: 03 03 03 03 03 03 03 03
|
||||
/// Associated data[12]: 04 04 04 04 04 04 04 04 04 04 04 04
|
||||
/// Pre-hashing digest:
|
||||
/// b8 81 97 91 a0 35 96 60
|
||||
/// bb 77 09 c8 5f a4 8f 04
|
||||
/// d5 d8 2c 05 c5 f2 15 cc
|
||||
/// db 88 54 91 71 7c f7 57
|
||||
/// 08 2c 28 b9 51 be 38 14
|
||||
/// 10 b5 fc 2e b7 27 40 33
|
||||
/// b9 fd c7 ae 67 2b ca ac
|
||||
/// 5d 17 90 97 a4 af 31 09
|
||||
/// Tag[32]:
|
||||
/// 51 2b 39 1b 6f 11 62 97
|
||||
/// 53 71 d3 09 19 73 42 94
|
||||
/// f8 68 e3 be 39 84 f3 c1
|
||||
/// a1 3a 4d b9 fa be 4a cb
|
||||
#[test]
|
||||
fn argon2d_v0x13() {
|
||||
let version = Version::V0x13;
|
||||
let m_cost = 32;
|
||||
let t_cost = 3;
|
||||
let parallelism = 4;
|
||||
let password = [0x01; 32];
|
||||
let salt = [0x02; 16];
|
||||
let secret = [0x03; 8];
|
||||
let ad = [0x04; 12];
|
||||
let expected_tag = hex!(
|
||||
"
|
||||
51 2b 39 1b 6f 11 62 97
|
||||
53 71 d3 09 19 73 42 94
|
||||
f8 68 e3 be 39 84 f3 c1
|
||||
a1 3a 4d b9 fa be 4a cb
|
||||
"
|
||||
);
|
||||
|
||||
let ctx = Argon2::new(Some(&secret), t_cost, m_cost, parallelism, version).unwrap();
|
||||
|
||||
let mut out = [0u8; 32];
|
||||
ctx.hash_password(Algorithm::Argon2d, &password, &salt, &ad, &mut out)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(out, expected_tag);
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Argon2i version number 19
|
||||
/// =======================================
|
||||
/// Memory: 32 KiB
|
||||
/// Passes: 3
|
||||
/// Parallelism: 4 lanes
|
||||
/// Tag length: 32 bytes
|
||||
/// Password[32]:
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01
|
||||
/// Salt[16]: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
|
||||
/// Secret[8]: 03 03 03 03 03 03 03 03
|
||||
/// Associated data[12]: 04 04 04 04 04 04 04 04 04 04 04 04
|
||||
/// Pre-hashing digest:
|
||||
/// c4 60 65 81 52 76 a0 b3
|
||||
/// e7 31 73 1c 90 2f 1f d8
|
||||
/// 0c f7 76 90 7f bb 7b 6a
|
||||
/// 5c a7 2e 7b 56 01 1f ee
|
||||
/// ca 44 6c 86 dd 75 b9 46
|
||||
/// 9a 5e 68 79 de c4 b7 2d
|
||||
/// 08 63 fb 93 9b 98 2e 5f
|
||||
/// 39 7c c7 d1 64 fd da a9
|
||||
/// Tag[32]:
|
||||
/// c8 14 d9 d1 dc 7f 37 aa
|
||||
/// 13 f0 d7 7f 24 94 bd a1
|
||||
/// c8 de 6b 01 6d d3 88 d2
|
||||
/// 99 52 a4 c4 67 2b 6c e8
|
||||
#[test]
|
||||
fn argon2i_v0x13() {
|
||||
let version = Version::V0x13;
|
||||
let m_cost = 32;
|
||||
let t_cost = 3;
|
||||
let parallelism = 4;
|
||||
let password = [0x01; 32];
|
||||
let salt = [0x02; 16];
|
||||
let secret = [0x03; 8];
|
||||
let ad = [0x04; 12];
|
||||
let expected_tag = hex!(
|
||||
"
|
||||
c8 14 d9 d1 dc 7f 37 aa
|
||||
13 f0 d7 7f 24 94 bd a1
|
||||
c8 de 6b 01 6d d3 88 d2
|
||||
99 52 a4 c4 67 2b 6c e8
|
||||
"
|
||||
);
|
||||
|
||||
let ctx = Argon2::new(Some(&secret), t_cost, m_cost, parallelism, version).unwrap();
|
||||
|
||||
let mut out = [0u8; 32];
|
||||
ctx.hash_password(Algorithm::Argon2i, &password, &salt, &ad, &mut out)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(out, expected_tag);
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Argon2id version number 19
|
||||
/// =======================================
|
||||
/// Memory: 32 KiB, Passes: 3,
|
||||
/// Parallelism: 4 lanes, Tag length: 32 bytes
|
||||
/// Password[32]:
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
/// Salt[16]: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
|
||||
/// Secret[8]: 03 03 03 03 03 03 03 03
|
||||
/// Associated data[12]: 04 04 04 04 04 04 04 04 04 04 04 04
|
||||
/// Pre-hashing digest:
|
||||
/// 28 89 de 48 7e b4 2a e5 00 c0 00 7e d9 25 2f 10
|
||||
/// 69 ea de c4 0d 57 65 b4 85 de 6d c2 43 7a 67 b8
|
||||
/// 54 6a 2f 0a cc 1a 08 82 db 8f cf 74 71 4b 47 2e
|
||||
/// 94 df 42 1a 5d a1 11 2f fa 11 43 43 70 a1 e9 97
|
||||
/// Tag[32]:
|
||||
/// 0d 64 0d f5 8d 78 76 6c 08 c0 37 a3 4a 8b 53 c9
|
||||
/// d0 1e f0 45 2d 75 b6 5e b5 25 20 e9 6b 01 e6 59
|
||||
#[test]
|
||||
fn argon2id_v0x13() {
|
||||
let version = Version::V0x13;
|
||||
let m_cost = 32;
|
||||
let t_cost = 3;
|
||||
let parallelism = 4;
|
||||
let password = [0x01; 32];
|
||||
let salt = [0x02; 16];
|
||||
let secret = [0x03; 8];
|
||||
let ad = [0x04; 12];
|
||||
let expected_tag = hex!(
|
||||
"
|
||||
0d 64 0d f5 8d 78 76 6c 08 c0 37 a3 4a 8b 53 c9
|
||||
d0 1e f0 45 2d 75 b6 5e b5 25 20 e9 6b 01 e6 59
|
||||
"
|
||||
);
|
||||
|
||||
let ctx = Argon2::new(Some(&secret), t_cost, m_cost, parallelism, version).unwrap();
|
||||
|
||||
let mut out = [0u8; 32];
|
||||
ctx.hash_password(Algorithm::Argon2id, &password, &salt, &ad, &mut out)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(out, expected_tag);
|
||||
}
|
||||
Reference in New Issue
Block a user