chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/argon2/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"1e48f28552f9d12886e475f04efc87f37cccde08941ade92ea39ef05000d7516","CHANGELOG.md":"31d741c25e3a80d8b20332a01aee54a543c6b983a714bd1b89beb0a5f527708b","Cargo.toml":"a5425d64a793e1a2e5a5c102364f26b3a601b6e6f753edd0c3872318ab832bc2","Cargo.toml.orig":"22cc7ce2d11c81ef97e4c273415a5145c5e5d0084e76325db204f150fc8da458","LICENSE-APACHE":"a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5","LICENSE-MIT":"33f702959c0ea91c08b21b65cf1f08b6c122ec9e6db0b5db784a7b367d942330","README.md":"b9ef80190d233da5761284bded0e5ebbe79794ac230b08b08cb24ac1454d5724","src/algorithm.rs":"147013d8476ef71da944636de336db3006471f7b70b04a3061d3cf1e631235ef","src/blake2b_long.rs":"bc4ed31907946ff5d99ddc8641944d794a91cd108e6f2bd356ba29a2f9620983","src/block.rs":"08b0c81b9d273f08047a27175520513eab08015a1b7ea887272a533237bae8f7","src/error.rs":"be431ea75ef7161ad919737c3d129e2d2fdc26c16100d7edd2af5154f8603b73","src/lib.rs":"c1ed703c605d55d4b82f807a891bb3eb9d79e061bf129ac5637ab41325817a9a","src/params.rs":"54e4c29dd876be643aa0d0d6466133346cd4ffeae75fe3f316a5196b39cf4fb5","src/version.rs":"f6d0cadcfc094d4050b2075384795da1415d2d1490f90d9dd52c4c395245f6e9","tests/kat.rs":"5d37362a512d8563c4523f9bde563ff043e88291857bc7dc97bba997404275c5","tests/phc_strings.rs":"75ffd36929bd1103565e08da36979e67cfd067ef2654a70854a6d3e838326c21"},"package":"3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"}

6
vendor/argon2/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "5b6a74e0799955b21c9d3854de1674a78a472648"
},
"path_in_vcs": "argon2"
}

215
vendor/argon2/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,215 @@
# 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).
## 0.5.3 (2024-01-20)
### Fixed
- Documentation ([#458], [#459])
- Big endian support ([#482])
[#458]: https://github.com/RustCrypto/password-hashes/pull/458
[#459]: https://github.com/RustCrypto/password-hashes/pull/459
[#482]: https://github.com/RustCrypto/password-hashes/pull/482
## 0.5.2 (2023-09-03)
### Changed
- Improved `const fn` support ([#450])
### Fixed
- Max params ([#452])
[#450]: https://github.com/RustCrypto/password-hashes/pull/450
[#452]: https://github.com/RustCrypto/password-hashes/pull/452
## 0.5.1 (2023-07-13)
### Added
- Provide `std::error::Error::source` for `argon2::Error` ([#379])
- `ParamsBuilder::context` ([#400])
- Enable `password-hash/alloc` when `alloc` feature is enabled ([#422])
- Impl `Debug` for `Argon2` ([#423])
- `Block::new()` const initializer ([#427])
- `Params::DEFAULT` constant ([#439])
- Initial AVX2 SIMD optimizations ([#440])
[#379]: https://github.com/RustCrypto/password-hashes/pull/379
[#400]: https://github.com/RustCrypto/password-hashes/pull/400
[#422]: https://github.com/RustCrypto/password-hashes/pull/422
[#423]: https://github.com/RustCrypto/password-hashes/pull/423
[#427]: https://github.com/RustCrypto/password-hashes/pull/427
[#439]: https://github.com/RustCrypto/password-hashes/pull/439
[#440]: https://github.com/RustCrypto/password-hashes/pull/440
## 0.5.0 (2023-03-04)
### Added
- Key derivation usage example ([#366])
- Inherent constants for `Params` recommendations ([#387])
### Changed
- Merge `Argon2` and `Instance` structs ([#247])
- Refactor `ParamsBuilder` to make it more ergonomic ([#247])
- Bump `password-hash` dependency to v0.5 ([#383])
- Adopt OWASP recommended default `Params` ([#386])
- MSRV 1.65 ([#391])
### Fixed
- Erroneous docs for `m_cost` and `Block` ([#247])
- Allow `zeroize` in heapless environments (i.e. without `alloc`) ([#374])
### Removed
- `Memory` struct ([#247])
- Unsound `parallel` feature - see [#380] ([#247])
[#247]: https://github.com/RustCrypto/password-hashes/pull/247
[#366]: https://github.com/RustCrypto/password-hashes/pull/366
[#374]: https://github.com/RustCrypto/password-hashes/pull/374
[#380]: https://github.com/RustCrypto/password-hashes/pull/380
[#383]: https://github.com/RustCrypto/password-hashes/pull/383
[#386]: https://github.com/RustCrypto/password-hashes/pull/386
[#387]: https://github.com/RustCrypto/password-hashes/pull/387
[#391]: https://github.com/RustCrypto/password-hashes/pull/391
## 0.4.1 (2022-06-27)
### Added
- `argon2::RECOMMENDED_SALT_LEN` ([#307])
[#307]: https://github.com/RustCrypto/password-hashes/pull/307
## 0.4.0 (2022-03-18)
### Changed
- Bump `password-hash` dependency to v0.4; MSRV 1.57 ([#283])
- 2021 edition upgrade ([#284])
[#283]: https://github.com/RustCrypto/password-hashes/pull/283
[#284]: https://github.com/RustCrypto/password-hashes/pull/284
## 0.3.4 (2022-02-17)
### Fixed
- Minimal versions build ([#273])
[#273]: https://github.com/RustCrypto/password-hashes/pull/273
## 0.3.3 (2022-01-30)
### Changed
- Make `Params::block_count()` public ([#258])
[#258]: https://github.com/RustCrypto/password-hashes/pull/258
## 0.3.2 (2021-12-07)
### Changed
- Bump `blake2` dependency to v0.10 ([#254])
[#254]: https://github.com/RustCrypto/password-hashes/pull/254
## 0.3.1 (2021-09-11)
### Fixed
- Handling of `p_cost` parameter ([#235])
[#235]: https://github.com/RustCrypto/password-hashes/pull/235
## 0.3.0 (2021-08-27) [YANKED]
### Added
- `alloc` feature ([#215])
- `Params` now supports `keyid` and `data` fields ([#216])
### Changed
- `Argon2::new` now has explicit `version` and `params` ([#213])
- Factored apart `Argon2::new` and `Argon2::new_with_secret`, making the
former infallible ([#213])
- `Params` struct is now opaque with field accessors, and ensured to
always represent a valid set of parameters ([#213])
- Removed `version` parameter from `hash_password_into`, using the one
supplied to `Argon2::new` ([#213])
- Bump `password-hash` to v0.3 ([#217], [RustCrypto/traits#724])
- Use `resolver = "2"`; MSRV 1.51+ ([#220])
### Removed
- `Params` no longer has a `version` field ([#211])
[#211]: https://github.com/RustCrypto/password-hashes/pull/211
[#213]: https://github.com/RustCrypto/password-hashes/pull/213
[#215]: https://github.com/RustCrypto/password-hashes/pull/215
[#216]: https://github.com/RustCrypto/password-hashes/pull/216
[#217]: https://github.com/RustCrypto/password-hashes/pull/217
[#220]: https://github.com/RustCrypto/password-hashes/pull/220
[RustCrypto/traits#724]: https://github.com/RustCrypto/traits/pull/724
## 0.2.4 (2021-08-21)
### Added
- Impl `std::error::Error` for `argon2::Error` ([#200])
- Impl `TryFrom<Params>` for `Argon2` ([#202])
- `Result` type alias ([#203])
- `ParamsBuilder` ([#204])
[#200]: https://github.com/RustCrypto/password-hashes/pull/200
[#202]: https://github.com/RustCrypto/password-hashes/pull/202
[#203]: https://github.com/RustCrypto/password-hashes/pull/203
[#204]: https://github.com/RustCrypto/password-hashes/pull/204
## 0.2.3 (2021-08-15)
### Changed
- Relax `zeroize` requirements to `>=1, <1.4` ([#195])
[#195]: https://github.com/RustCrypto/password-hashes/pull/195
## 0.2.2 (2021-07-20)
### Changed
- Pin `zeroize` dependency to v1.3 ([#190])
[#190]: https://github.com/RustCrypto/password-hashes/pull/190
## 0.2.1 (2021-05-28)
### Changed
- `Params` always available; no longer feature-gated on `password-hash` ([#182])
### Fixed
- Configured params are used with `hash_password_simple` ([#182])
[#182]: https://github.com/RustCrypto/password-hashes/pull/182
## 0.2.0 (2021-04-29)
### Changed
- Forbid unsafe code outside parallel implementation ([#157])
- Bump `password-hash` crate dependency to v0.2 ([#164])
### Removed
- `argon2::BLOCK_SIZE` constant ([#161])
[#157]: https://github.com/RustCrypto/password-hashes/pull/157
[#161]: https://github.com/RustCrypto/password-hashes/pull/161
[#164]: https://github.com/RustCrypto/password-hashes/pull/164
## 0.1.5 (2021-04-18)
### Added
- Parallel lane processing using `rayon` ([#149])
[#149]: https://github.com/RustCrypto/password-hashes/pull/149
## 0.1.4 (2021-02-28)
### Added
- `std` feature ([#141])
[#141]: https://github.com/RustCrypto/password-hashes/pull/141
## 0.1.3 (2021-02-12)
### Fixed
- Salt-length related panic ([#135])
[#135]: https://github.com/RustCrypto/password-hashes/pull/135
## 0.1.2 (2021-02-07)
### Fixed
- rustdoc typo ([#128])
[#128]: https://github.com/RustCrypto/password-hashes/pull/128
## 0.1.1 (2021-02-07)
### Added
- `rand` feature; enabled-by-default ([#126])
[#126]: https://github.com/RustCrypto/password-hashes/pull/126
## 0.1.0 (2021-01-29)
- Initial release

83
vendor/argon2/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,83 @@
# 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 are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.65"
name = "argon2"
version = "0.5.3"
authors = ["RustCrypto Developers"]
description = """
Pure Rust implementation of the Argon2 password hashing function with support
for the Argon2d, Argon2i, and Argon2id algorithmic variants
"""
documentation = "https://docs.rs/argon2"
readme = "README.md"
keywords = [
"crypto",
"hashing",
"password",
"phf",
]
categories = [
"authentication",
"cryptography",
"no-std",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/RustCrypto/password-hashes/tree/master/argon2"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
[dependencies.base64ct]
version = "1"
[dependencies.blake2]
version = "0.10.6"
default-features = false
[dependencies.password-hash]
version = "0.5"
optional = true
[dependencies.zeroize]
version = "1"
optional = true
default-features = false
[dev-dependencies.hex-literal]
version = "0.4"
[dev-dependencies.password-hash]
version = "0.5"
features = ["rand_core"]
[features]
alloc = ["password-hash/alloc"]
default = [
"alloc",
"password-hash",
"rand",
]
rand = ["password-hash/rand_core"]
simple = ["password-hash"]
std = [
"alloc",
"password-hash/std",
]
[target."cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))".dependencies.cpufeatures]
version = "0.2.12"

201
vendor/argon2/LICENSE-APACHE vendored Normal file
View 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
vendor/argon2/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2021-2024 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.

72
vendor/argon2/README.md vendored Normal file
View File

@@ -0,0 +1,72 @@
# RustCrypto: Argon2
[![crate][crate-image]][crate-link]
[![Docs][docs-image]][docs-link]
[![Build Status][build-image]][build-link]
![Apache2/MIT licensed][license-image]
![Rust Version][rustc-image]
[![Project Chat][chat-image]][chat-link]
Pure Rust implementation of the [Argon2] password hashing function.
[Documentation][docs-link]
# About
Argon2 is a memory-hard [key derivation function] chosen as the winner of
the [Password Hashing Competition] in July 2015.
It implements the following three algorithmic variants:
- **Argon2d**: maximizes resistance to GPU cracking attacks
- **Argon2i**: optimized to resist side-channel attacks
- **Argon2id**: (default) hybrid version combining both Argon2i and Argon2d
Support is provided for embedded (i.e. `no_std`) environments, including
ones without `alloc` support.
## Minimum Supported Rust Version
Rust **1.65** 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://buildstats.info/crate/argon2
[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.65+-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
[key derivation function]: https://en.wikipedia.org/wiki/Key_derivation_function
[Password Hashing Competition]: https://www.password-hashing.net/

132
vendor/argon2/src/algorithm.rs vendored Normal file
View File

@@ -0,0 +1,132 @@
//! Argon2 algorithms (e.g. Argon2d, Argon2i, Argon2id).
use crate::{Error, Result};
use core::{
fmt::{self, Display},
str::FromStr,
};
#[cfg(feature = "password-hash")]
use password_hash::Ident;
/// Argon2d algorithm identifier
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const ARGON2D_IDENT: Ident<'_> = Ident::new_unwrap("argon2d");
/// Argon2i algorithm identifier
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const ARGON2I_IDENT: Ident<'_> = Ident::new_unwrap("argon2i");
/// Argon2id algorithm identifier
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const ARGON2ID_IDENT: Ident<'_> = Ident::new_unwrap("argon2id");
/// Argon2 primitive type: variants of the algorithm.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Default, 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 timememory 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. This effectively places it in
/// the "middle" between the other two: it doesn't provide as good
/// TMTO/GPU cracking resistance as Argon2d, nor as good of side-channel
/// resistance as Argon2i, but overall provides the most well-rounded
/// approach to both classes of attacks.
#[default]
Argon2id = 2,
}
impl Algorithm {
/// Parse an [`Algorithm`] from the provided string.
pub fn new(id: impl AsRef<str>) -> Result<Self> {
id.as_ref().parse()
}
/// Get the identifier string for this PBKDF2 [`Algorithm`].
pub const fn as_str(&self) -> &'static str {
match self {
Algorithm::Argon2d => "argon2d",
Algorithm::Argon2i => "argon2i",
Algorithm::Argon2id => "argon2id",
}
}
/// Get the [`Ident`] that corresponds to this Argon2 [`Algorithm`].
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub const fn ident(&self) -> Ident<'static> {
match self {
Algorithm::Argon2d => ARGON2D_IDENT,
Algorithm::Argon2i => ARGON2I_IDENT,
Algorithm::Argon2id => ARGON2ID_IDENT,
}
}
/// Serialize primitive type as little endian bytes
pub(crate) const fn to_le_bytes(self) -> [u8; 4] {
(self as u32).to_le_bytes()
}
}
impl AsRef<str> for Algorithm {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Algorithm {
type Err = Error;
fn from_str(s: &str) -> Result<Algorithm> {
match s {
"argon2d" => Ok(Algorithm::Argon2d),
"argon2i" => Ok(Algorithm::Argon2i),
"argon2id" => Ok(Algorithm::Argon2id),
_ => Err(Error::AlgorithmInvalid),
}
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl From<Algorithm> for Ident<'static> {
fn from(alg: Algorithm) -> Ident<'static> {
alg.ident()
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl<'a> TryFrom<Ident<'a>> for Algorithm {
type Error = password_hash::Error;
fn try_from(ident: Ident<'a>) -> password_hash::Result<Algorithm> {
match ident {
ARGON2D_IDENT => Ok(Algorithm::Argon2d),
ARGON2I_IDENT => Ok(Algorithm::Argon2i),
ARGON2ID_IDENT => Ok(Algorithm::Argon2id),
_ => Err(password_hash::Error::Algorithm),
}
}
}

79
vendor/argon2/src/blake2b_long.rs vendored Normal file
View File

@@ -0,0 +1,79 @@
//! The variable length hash function used in the Argon2 algorithm.
use crate::{Error, Result};
use blake2::{
digest::{self, Digest, VariableOutput},
Blake2b512, Blake2bVar,
};
use core::convert::TryFrom;
pub fn blake2b_long(inputs: &[&[u8]], out: &mut [u8]) -> Result<()> {
if out.is_empty() {
return Err(Error::OutputTooShort);
}
let len_bytes = u32::try_from(out.len())
.map(|v| v.to_le_bytes())
.map_err(|_| Error::OutputTooLong)?;
// Use blake2b directly if the output is small enough.
if out.len() <= Blake2b512::output_size() {
let mut digest = Blake2bVar::new(out.len()).map_err(|_| Error::OutputTooLong)?;
// Conflicting method name from `Digest` and `Update` traits
digest::Update::update(&mut digest, &len_bytes);
for input in inputs {
digest::Update::update(&mut digest, input);
}
digest
.finalize_variable(out)
.map_err(|_| Error::OutputTooLong)?;
return Ok(());
}
// Calculate longer hashes by first calculating a full 64 byte hash
let half_hash_len = Blake2b512::output_size() / 2;
let mut digest = Blake2b512::new();
digest.update(len_bytes);
for input in inputs {
digest.update(input);
}
let mut last_output = digest.finalize();
// Then we write the first 32 bytes of this hash to the output
out[..half_hash_len].copy_from_slice(&last_output[..half_hash_len]);
// Next, we write a number of 32 byte blocks to the output.
// Each block is the first 32 bytes of the hash of the last block.
// The very last block of the output is excluded, and has a variable
// length in range [1, 32].
let mut counter = 0;
let out_len = out.len();
for chunk in out[half_hash_len..]
.chunks_exact_mut(half_hash_len)
.take_while(|_| {
counter += half_hash_len;
out_len - counter > 64
})
{
last_output = Blake2b512::digest(last_output);
chunk.copy_from_slice(&last_output[..half_hash_len]);
}
// Calculate the last block with VarBlake2b.
let last_block_size = out.len() - counter;
let mut digest = Blake2bVar::new(last_block_size).map_err(|_| Error::OutputTooLong)?;
digest::Update::update(&mut digest, &last_output);
digest
.finalize_variable(&mut out[counter..])
.expect("invalid Blake2bVar out length");
Ok(())
}

156
vendor/argon2/src/block.rs vendored Normal file
View File

@@ -0,0 +1,156 @@
//! Argon2 memory block functions
use core::{
convert::{AsMut, AsRef},
num::Wrapping,
ops::{BitXor, BitXorAssign},
slice,
};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
const TRUNC: u64 = u32::MAX as u64;
#[rustfmt::skip]
macro_rules! permute_step {
($a:expr, $b:expr, $c:expr, $d:expr) => {
$a = (Wrapping($a) + Wrapping($b) + (Wrapping(2) * Wrapping(($a & TRUNC) * ($b & TRUNC)))).0;
$d = ($d ^ $a).rotate_right(32);
$c = (Wrapping($c) + Wrapping($d) + (Wrapping(2) * Wrapping(($c & TRUNC) * ($d & TRUNC)))).0;
$b = ($b ^ $c).rotate_right(24);
$a = (Wrapping($a) + Wrapping($b) + (Wrapping(2) * Wrapping(($a & TRUNC) * ($b & TRUNC)))).0;
$d = ($d ^ $a).rotate_right(16);
$c = (Wrapping($c) + Wrapping($d) + (Wrapping(2) * Wrapping(($c & TRUNC) * ($d & TRUNC)))).0;
$b = ($b ^ $c).rotate_right(63);
};
}
macro_rules! permute {
(
$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,
) => {
permute_step!($v0, $v4, $v8, $v12);
permute_step!($v1, $v5, $v9, $v13);
permute_step!($v2, $v6, $v10, $v14);
permute_step!($v3, $v7, $v11, $v15);
permute_step!($v0, $v5, $v10, $v15);
permute_step!($v1, $v6, $v11, $v12);
permute_step!($v2, $v7, $v8, $v13);
permute_step!($v3, $v4, $v9, $v14);
};
}
/// Structure for the (1 KiB) memory block implemented as 128 64-bit words.
#[derive(Copy, Clone, Debug)]
#[repr(align(64))]
pub struct Block([u64; Self::SIZE / 8]);
impl Block {
/// Memory block size in bytes
pub const SIZE: usize = 1024;
/// Returns a Block initialized with zeros.
pub const fn new() -> Self {
Self([0u64; Self::SIZE / 8])
}
/// Load a block from a block-sized byte slice
#[inline(always)]
pub(crate) fn load(&mut self, input: &[u8; Block::SIZE]) {
for (i, chunk) in input.chunks(8).enumerate() {
self.0[i] = u64::from_le_bytes(chunk.try_into().expect("should be 8 bytes"));
}
}
/// Iterate over the `u64` values contained in this block
#[inline(always)]
pub(crate) fn iter(&self) -> slice::Iter<'_, u64> {
self.0.iter()
}
/// NOTE: do not call this directly. It should only be called via
/// `Argon2::compress`.
#[inline(always)]
pub(crate) fn compress(rhs: &Self, lhs: &Self) -> Self {
let r = *rhs ^ lhs;
// Apply permutations rowwise
let mut q = r;
for chunk in q.0.chunks_exact_mut(16) {
#[rustfmt::skip]
permute!(
chunk[0], chunk[1], chunk[2], chunk[3],
chunk[4], chunk[5], chunk[6], chunk[7],
chunk[8], chunk[9], chunk[10], chunk[11],
chunk[12], chunk[13], chunk[14], chunk[15],
);
}
// Apply permutations columnwise
for i in 0..8 {
let b = i * 2;
#[rustfmt::skip]
permute!(
q.0[b], q.0[b + 1],
q.0[b + 16], q.0[b + 17],
q.0[b + 32], q.0[b + 33],
q.0[b + 48], q.0[b + 49],
q.0[b + 64], q.0[b + 65],
q.0[b + 80], q.0[b + 81],
q.0[b + 96], q.0[b + 97],
q.0[b + 112], q.0[b + 113],
);
}
q ^= &r;
q
}
}
impl Default for Block {
fn default() -> Self {
Self([0u64; Self::SIZE / 8])
}
}
impl AsRef<[u64]> for Block {
fn as_ref(&self) -> &[u64] {
&self.0
}
}
impl AsMut<[u64]> for Block {
fn as_mut(&mut self) -> &mut [u64] {
&mut self.0
}
}
impl BitXor<&Block> for Block {
type Output = Block;
fn bitxor(mut self, rhs: &Block) -> Self::Output {
self ^= rhs;
self
}
}
impl BitXorAssign<&Block> for Block {
fn bitxor_assign(&mut self, rhs: &Block) {
for (dst, src) in self.0.iter_mut().zip(rhs.0.iter()) {
*dst ^= src;
}
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for Block {
fn zeroize(&mut self) {
self.0.zeroize();
}
}

131
vendor/argon2/src/error.rs vendored Normal file
View File

@@ -0,0 +1,131 @@
//! Error type
use core::fmt;
#[cfg(feature = "password-hash")]
use {crate::Params, core::cmp::Ordering, password_hash::errors::InvalidValue};
/// Result with argon2's [`Error`] type.
pub type Result<T> = core::result::Result<T, Error>;
/// Error type.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
/// Associated data is too long.
AdTooLong,
/// Algorithm identifier invalid.
AlgorithmInvalid,
/// "B64" encoding is invalid.
B64Encoding(base64ct::Error),
/// Key ID is too long.
KeyIdTooLong,
/// 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,
/// Invalid version
VersionInvalid,
}
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::AlgorithmInvalid => "algorithm identifier invalid",
Error::B64Encoding(inner) => return write!(f, "B64 encoding invalid: {inner}"),
Error::KeyIdTooLong => "key ID is too long",
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",
Error::VersionInvalid => "invalid version",
})
}
}
impl From<base64ct::Error> for Error {
fn from(err: base64ct::Error) -> Error {
Error::B64Encoding(err)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl From<Error> for password_hash::Error {
fn from(err: Error) -> password_hash::Error {
match err {
Error::AdTooLong => InvalidValue::TooLong.param_error(),
Error::AlgorithmInvalid => password_hash::Error::Algorithm,
Error::B64Encoding(inner) => password_hash::Error::B64Encoding(inner),
Error::KeyIdTooLong => InvalidValue::TooLong.param_error(),
Error::MemoryTooLittle => InvalidValue::TooShort.param_error(),
Error::MemoryTooMuch => InvalidValue::TooLong.param_error(),
Error::PwdTooLong => password_hash::Error::Password,
Error::OutputTooShort => password_hash::Error::OutputSize {
provided: Ordering::Less,
expected: Params::MIN_OUTPUT_LEN,
},
Error::OutputTooLong => password_hash::Error::OutputSize {
provided: Ordering::Greater,
expected: Params::MAX_OUTPUT_LEN,
},
Error::SaltTooShort => InvalidValue::TooShort.salt_error(),
Error::SaltTooLong => InvalidValue::TooLong.salt_error(),
Error::SecretTooLong => InvalidValue::TooLong.param_error(),
Error::ThreadsTooFew => InvalidValue::TooShort.param_error(),
Error::ThreadsTooMany => InvalidValue::TooLong.param_error(),
Error::TimeTooSmall => InvalidValue::TooShort.param_error(),
Error::VersionInvalid => password_hash::Error::Version,
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::B64Encoding(err) => Some(err),
_ => None,
}
}
}

696
vendor/argon2/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,696 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
#![warn(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::checked_conversions,
clippy::implicit_saturating_sub,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_lifetimes,
unused_qualifications
)]
//! ## Usage
//!
//! ### Password Hashing
//!
//! This API hashes a password to a "PHC string" suitable for the purposes of
//! password-based authentication. Do not use this API to derive cryptographic
//! keys: see the "key derivation" usage example below.
//!
#![cfg_attr(feature = "std", doc = "```")]
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use argon2::{
//! password_hash::{
//! rand_core::OsRng,
//! PasswordHash, PasswordHasher, PasswordVerifier, SaltString
//! },
//! Argon2
//! };
//!
//! let password = b"hunter42"; // Bad password; don't actually use!
//! let salt = SaltString::generate(&mut OsRng);
//!
//! // Argon2 with default params (Argon2id v19)
//! let argon2 = Argon2::default();
//!
//! // Hash password to PHC string ($argon2id$v=19$...)
//! let password_hash = argon2.hash_password(password, &salt)?.to_string();
//!
//! // Verify password against PHC string.
//! //
//! // NOTE: hash params from `parsed_hash` are used instead of what is configured in the
//! // `Argon2` instance.
//! let parsed_hash = PasswordHash::new(&password_hash)?;
//! assert!(Argon2::default().verify_password(password, &parsed_hash).is_ok());
//! # Ok(())
//! # }
//! ```
//!
//! ### Key Derivation
//!
//! This API is useful for transforming a password into cryptographic keys for
//! e.g. password-based encryption.
//!
#![cfg_attr(feature = "std", doc = "```")]
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use argon2::Argon2;
//!
//! let password = b"hunter42"; // Bad password; don't actually use!
//! let salt = b"example salt"; // Salt should be unique per password
//!
//! let mut output_key_material = [0u8; 32]; // Can be any desired size
//! Argon2::default().hash_password_into(password, salt, &mut output_key_material)?;
//! # Ok(())
//! # }
//! ```
// Call sites which cast `u32` to `usize` and are annotated with
// allow(clippy::cast_possible_truncation) need this check to avoid truncation.
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
compile_error!("this crate builds on 32-bit and 64-bit platforms only");
#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod algorithm;
mod blake2b_long;
mod block;
mod error;
mod params;
mod version;
pub use crate::{
algorithm::Algorithm,
block::Block,
error::{Error, Result},
params::{AssociatedData, KeyId, Params, ParamsBuilder},
version::Version,
};
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub use {
crate::algorithm::{ARGON2D_IDENT, ARGON2ID_IDENT, ARGON2I_IDENT},
password_hash::{self, PasswordHash, PasswordHasher, PasswordVerifier},
};
use crate::blake2b_long::blake2b_long;
use blake2::{digest, Blake2b512, Digest};
use core::fmt;
#[cfg(all(feature = "alloc", feature = "password-hash"))]
use password_hash::{Decimal, Ident, ParamsString, Salt};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// Maximum password length in bytes.
pub const MAX_PWD_LEN: usize = 0xFFFFFFFF;
/// Minimum salt length in bytes.
pub const MIN_SALT_LEN: usize = 8;
/// Maximum salt length in bytes.
pub const MAX_SALT_LEN: usize = 0xFFFFFFFF;
/// Recommended salt length for password hashing in bytes.
pub const RECOMMENDED_SALT_LEN: usize = 16;
/// Maximum secret key length in bytes.
pub const MAX_SECRET_LEN: usize = 0xFFFFFFFF;
/// Number of synchronization points between lanes per pass
pub(crate) const SYNC_POINTS: usize = 4;
/// To generate reference block positions
const ADDRESSES_IN_BLOCK: usize = 128;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpufeatures::new!(avx2_cpuid, "avx2");
/// Argon2 context.
///
/// This is the primary type of this crate's API, and contains the following:
///
/// - Argon2 [`Algorithm`] variant to be used
/// - Argon2 [`Version`] to be used
/// - Default set of [`Params`] to be used
/// - (Optional) Secret key a.k.a. "pepper" to be used
#[derive(Clone)]
pub struct Argon2<'key> {
/// Algorithm to use
algorithm: Algorithm,
/// Version number
version: Version,
/// Algorithm parameters
params: Params,
/// Key array
secret: Option<&'key [u8]>,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: avx2_cpuid::InitToken,
}
impl Default for Argon2<'_> {
fn default() -> Self {
Self::new(Algorithm::default(), Version::default(), Params::default())
}
}
impl fmt::Debug for Argon2<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Argon2")
.field("algorithm", &self.algorithm)
.field("version", &self.version)
.field("params", &self.params)
.finish_non_exhaustive()
}
}
impl<'key> Argon2<'key> {
/// Create a new Argon2 context.
pub fn new(algorithm: Algorithm, version: Version, params: Params) -> Self {
Self {
algorithm,
version,
params,
secret: None,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: avx2_cpuid::init(),
}
}
/// Create a new Argon2 context.
pub fn new_with_secret(
secret: &'key [u8],
algorithm: Algorithm,
version: Version,
params: Params,
) -> Result<Self> {
if MAX_SECRET_LEN < secret.len() {
return Err(Error::SecretTooLong);
}
Ok(Self {
algorithm,
version,
params,
secret: Some(secret),
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: avx2_cpuid::init(),
})
}
/// Hash a password and associated parameters into the provided output buffer.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn hash_password_into(&self, pwd: &[u8], salt: &[u8], out: &mut [u8]) -> Result<()> {
let mut blocks = vec![Block::default(); self.params.block_count()];
self.hash_password_into_with_memory(pwd, salt, out, &mut blocks)
}
/// Hash a password and associated parameters into the provided output buffer.
///
/// This method takes an explicit `memory_blocks` parameter which allows
/// the caller to provide the backing storage for the algorithm's state:
///
/// - Users with the `alloc` feature enabled can use [`Argon2::hash_password_into`]
/// to have it allocated for them.
/// - `no_std` users on "heapless" targets can use an array of the [`Block`] type
/// to stack allocate this buffer.
pub fn hash_password_into_with_memory(
&self,
pwd: &[u8],
salt: &[u8],
out: &mut [u8],
mut memory_blocks: impl AsMut<[Block]>,
) -> Result<()> {
// Validate output length
if out.len() < self.params.output_len().unwrap_or(Params::MIN_OUTPUT_LEN) {
return Err(Error::OutputTooShort);
}
if out.len() > self.params.output_len().unwrap_or(Params::MAX_OUTPUT_LEN) {
return Err(Error::OutputTooLong);
}
Self::verify_inputs(pwd, salt)?;
// Hashing all inputs
let initial_hash = self.initial_hash(pwd, salt, out);
self.fill_blocks(memory_blocks.as_mut(), initial_hash)?;
self.finalize(memory_blocks.as_mut(), out)
}
/// Use a password and associated parameters only to fill the given memory blocks.
///
/// This method omits the calculation of a hash and can be used when only the
/// filled memory is required. It is not necessary to call this method
/// before calling any of the hashing functions.
pub fn fill_memory(
&self,
pwd: &[u8],
salt: &[u8],
mut memory_blocks: impl AsMut<[Block]>,
) -> Result<()> {
Self::verify_inputs(pwd, salt)?;
let initial_hash = self.initial_hash(pwd, salt, &[]);
self.fill_blocks(memory_blocks.as_mut(), initial_hash)
}
#[allow(clippy::cast_possible_truncation, unused_mut)]
fn fill_blocks(
&self,
memory_blocks: &mut [Block],
mut initial_hash: digest::Output<Blake2b512>,
) -> Result<()> {
let block_count = self.params.block_count();
let memory_blocks = memory_blocks
.get_mut(..block_count)
.ok_or(Error::MemoryTooLittle)?;
let segment_length = self.params.segment_length();
let iterations = self.params.t_cost() as usize;
let lane_length = self.params.lane_length();
let lanes = self.params.lanes();
// Initialize the first two blocks in each lane
for (l, lane) in memory_blocks.chunks_exact_mut(lane_length).enumerate() {
for (i, block) in lane[..2].iter_mut().enumerate() {
let i = i as u32;
let l = l as u32;
// Make the first and second block in each lane as G(H0||0||i) or
// G(H0||1||i)
let inputs = &[
initial_hash.as_ref(),
&i.to_le_bytes()[..],
&l.to_le_bytes()[..],
];
let mut hash = [0u8; Block::SIZE];
blake2b_long(inputs, &mut hash)?;
block.load(&hash);
}
}
#[cfg(feature = "zeroize")]
initial_hash.zeroize();
// Run passes on blocks
for pass in 0..iterations {
for slice in 0..SYNC_POINTS {
let data_independent_addressing = self.algorithm == Algorithm::Argon2i
|| (self.algorithm == Algorithm::Argon2id
&& pass == 0
&& slice < SYNC_POINTS / 2);
for lane in 0..lanes {
let mut address_block = Block::default();
let mut input_block = Block::default();
let zero_block = Block::default();
if data_independent_addressing {
input_block.as_mut()[..6].copy_from_slice(&[
pass as u64,
lane as u64,
slice as u64,
memory_blocks.len() as u64,
iterations as u64,
self.algorithm as u64,
]);
}
let first_block = if pass == 0 && slice == 0 {
if data_independent_addressing {
// Generate first set of addresses
self.update_address_block(
&mut address_block,
&mut input_block,
&zero_block,
);
}
// The first two blocks of each lane are already initialized
2
} else {
0
};
let mut cur_index = lane * lane_length + slice * segment_length + first_block;
let mut prev_index = if slice == 0 && first_block == 0 {
// Last block in current lane
cur_index + lane_length - 1
} else {
// Previous block
cur_index - 1
};
// Fill blocks in the segment
for block in first_block..segment_length {
// Extract entropy
let rand = if data_independent_addressing {
let addres_index = block % ADDRESSES_IN_BLOCK;
if addres_index == 0 {
self.update_address_block(
&mut address_block,
&mut input_block,
&zero_block,
);
}
address_block.as_ref()[addres_index]
} else {
memory_blocks[prev_index].as_ref()[0]
};
// Calculate source block index for compress function
let ref_lane = if pass == 0 && slice == 0 {
// Cannot reference other lanes yet
lane
} else {
(rand >> 32) as usize % lanes
};
let reference_area_size = if pass == 0 {
// First pass
if slice == 0 {
// First slice
block - 1 // all but the previous
} else if ref_lane == lane {
// The same lane => add current segment
slice * segment_length + block - 1
} else {
slice * segment_length - if block == 0 { 1 } else { 0 }
}
} else {
// Second pass
if ref_lane == lane {
lane_length - segment_length + block - 1
} else {
lane_length - segment_length - if block == 0 { 1 } else { 0 }
}
};
// 1.2.4. Mapping rand to 0..<reference_area_size-1> and produce
// relative position
let mut map = rand & 0xFFFFFFFF;
map = (map * map) >> 32;
let relative_position = reference_area_size
- 1
- ((reference_area_size as u64 * map) >> 32) as usize;
// 1.2.5 Computing starting position
let start_position = if pass != 0 && slice != SYNC_POINTS - 1 {
(slice + 1) * segment_length
} else {
0
};
let lane_index = (start_position + relative_position) % lane_length;
let ref_index = ref_lane * lane_length + lane_index;
// Calculate new block
let result =
self.compress(&memory_blocks[prev_index], &memory_blocks[ref_index]);
if self.version == Version::V0x10 || pass == 0 {
memory_blocks[cur_index] = result;
} else {
memory_blocks[cur_index] ^= &result;
};
prev_index = cur_index;
cur_index += 1;
}
}
}
}
Ok(())
}
fn compress(&self, rhs: &Block, lhs: &Block) -> Block {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
/// Enable AVX2 optimizations.
#[target_feature(enable = "avx2")]
unsafe fn compress_avx2(rhs: &Block, lhs: &Block) -> Block {
Block::compress(rhs, lhs)
}
if self.cpu_feat_avx2.get() {
return unsafe { compress_avx2(rhs, lhs) };
}
}
Block::compress(rhs, lhs)
}
/// Get default configured [`Params`].
pub const fn params(&self) -> &Params {
&self.params
}
fn finalize(&self, memory_blocks: &[Block], out: &mut [u8]) -> Result<()> {
let lane_length = self.params.lane_length();
let mut blockhash = memory_blocks[lane_length - 1];
// XOR the last blocks
for l in 1..self.params.lanes() {
let last_block_in_lane = l * lane_length + (lane_length - 1);
blockhash ^= &memory_blocks[last_block_in_lane];
}
// 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();
blockhash_bytes.zeroize();
}
Ok(())
}
fn update_address_block(
&self,
address_block: &mut Block,
input_block: &mut Block,
zero_block: &Block,
) {
input_block.as_mut()[6] += 1;
*address_block = self.compress(zero_block, input_block);
*address_block = self.compress(zero_block, address_block);
}
/// Hashes all the inputs into `blockhash[PREHASH_DIGEST_LEN]`.
#[allow(clippy::cast_possible_truncation)]
fn initial_hash(&self, pwd: &[u8], salt: &[u8], out: &[u8]) -> digest::Output<Blake2b512> {
let mut digest = Blake2b512::new();
digest.update(self.params.p_cost().to_le_bytes());
digest.update((out.len() as u32).to_le_bytes());
digest.update(self.params.m_cost().to_le_bytes());
digest.update(self.params.t_cost().to_le_bytes());
digest.update(self.version.to_le_bytes());
digest.update(self.algorithm.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((self.params.data().len() as u32).to_le_bytes());
digest.update(self.params.data());
digest.finalize()
}
const fn verify_inputs(pwd: &[u8], salt: &[u8]) -> Result<()> {
if pwd.len() > MAX_PWD_LEN {
return Err(Error::PwdTooLong);
}
// Validate salt (required param)
if salt.len() < MIN_SALT_LEN {
return Err(Error::SaltTooShort);
}
if salt.len() > MAX_SALT_LEN {
return Err(Error::SaltTooLong);
}
Ok(())
}
}
#[cfg(all(feature = "alloc", feature = "password-hash"))]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl PasswordHasher for Argon2<'_> {
type Params = Params;
fn hash_password<'a>(
&self,
password: &[u8],
salt: impl Into<Salt<'a>>,
) -> password_hash::Result<PasswordHash<'a>> {
let salt = salt.into();
let mut salt_arr = [0u8; 64];
let salt_bytes = salt.decode_b64(&mut salt_arr)?;
let output_len = self
.params
.output_len()
.unwrap_or(Params::DEFAULT_OUTPUT_LEN);
let output = password_hash::Output::init_with(output_len, |out| {
Ok(self.hash_password_into(password, salt_bytes, out)?)
})?;
Ok(PasswordHash {
algorithm: self.algorithm.ident(),
version: Some(self.version.into()),
params: ParamsString::try_from(&self.params)?,
salt: Some(salt),
hash: Some(output),
})
}
fn hash_password_customized<'a>(
&self,
password: &[u8],
alg_id: Option<Ident<'a>>,
version: Option<Decimal>,
params: Params,
salt: impl Into<Salt<'a>>,
) -> password_hash::Result<PasswordHash<'a>> {
let algorithm = alg_id
.map(Algorithm::try_from)
.transpose()?
.unwrap_or_default();
let version = version
.map(Version::try_from)
.transpose()?
.unwrap_or_default();
let salt = salt.into();
Self {
secret: self.secret,
algorithm,
version,
params,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
cpu_feat_avx2: self.cpu_feat_avx2,
}
.hash_password(password, salt)
}
}
impl<'key> From<Params> for Argon2<'key> {
fn from(params: Params) -> Self {
Self::new(Algorithm::default(), Version::default(), params)
}
}
impl<'key> From<&Params> for Argon2<'key> {
fn from(params: &Params) -> Self {
Self::from(params.clone())
}
}
#[cfg(all(test, feature = "alloc", feature = "password-hash"))]
#[allow(clippy::unwrap_used)]
mod tests {
use crate::{Algorithm, Argon2, Params, PasswordHasher, Salt, Version};
/// Example password only: don't use this as a real password!!!
const EXAMPLE_PASSWORD: &[u8] = b"hunter42";
/// Example salt value. Don't use a static salt value!!!
const EXAMPLE_SALT: &str = "examplesaltvalue";
#[test]
fn decoded_salt_too_short() {
let argon2 = Argon2::default();
// Too short after decoding
let salt = Salt::from_b64("somesalt").unwrap();
let res =
argon2.hash_password_customized(EXAMPLE_PASSWORD, None, None, Params::default(), salt);
assert_eq!(
res,
Err(password_hash::Error::SaltInvalid(
password_hash::errors::InvalidValue::TooShort
))
);
}
#[test]
fn hash_simple_retains_configured_params() {
// Non-default but valid parameters
let t_cost = 4;
let m_cost = 2048;
let p_cost = 2;
let version = Version::V0x10;
let params = Params::new(m_cost, t_cost, p_cost, None).unwrap();
let hasher = Argon2::new(Algorithm::default(), version, params);
let salt = Salt::from_b64(EXAMPLE_SALT).unwrap();
let hash = hasher.hash_password(EXAMPLE_PASSWORD, salt).unwrap();
assert_eq!(hash.version.unwrap(), version.into());
for &(param, value) in &[("t", t_cost), ("m", m_cost), ("p", p_cost)] {
assert_eq!(
hash.params
.get(param)
.and_then(|p| p.decimal().ok())
.unwrap(),
value,
);
}
}
}

569
vendor/argon2/src/params.rs vendored Normal file
View File

@@ -0,0 +1,569 @@
//! Argon2 password hash parameters.
use crate::{Algorithm, Argon2, Error, Result, Version, SYNC_POINTS};
use base64ct::{Base64Unpadded as B64, Encoding};
use core::str::FromStr;
#[cfg(feature = "password-hash")]
use password_hash::{ParamsString, PasswordHash};
/// Argon2 password hash parameters.
///
/// These are parameters which can be encoded into a PHC hash string.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Params {
/// Memory size, expressed in kibibytes, between 8\*`p_cost` and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
m_cost: u32,
/// Number of iterations, between 1 and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
t_cost: u32,
/// Degree of parallelism, between 1 and (2^24)-1.
///
/// Value is an integer in decimal (1 to 8 digits).
p_cost: u32,
/// Key identifier.
keyid: KeyId,
/// Associated data.
data: AssociatedData,
/// Size of the output (in bytes).
output_len: Option<usize>,
}
impl Params {
/// Default memory cost.
pub const DEFAULT_M_COST: u32 = 19 * 1024;
/// Minimum number of 1 KiB memory blocks.
#[allow(clippy::cast_possible_truncation)]
pub const MIN_M_COST: u32 = 2 * SYNC_POINTS as u32; // 2 blocks per slice
/// Maximum number of 1 KiB memory blocks.
pub const MAX_M_COST: u32 = u32::MAX;
/// Default number of iterations (i.e. "time").
pub const DEFAULT_T_COST: u32 = 2;
/// Minimum number of passes.
pub const MIN_T_COST: u32 = 1;
/// Maximum number of passes.
pub const MAX_T_COST: u32 = u32::MAX;
/// Default degree of parallelism.
pub const DEFAULT_P_COST: u32 = 1;
/// Minimum and maximum number of threads (i.e. parallelism).
pub const MIN_P_COST: u32 = 1;
/// Minimum and maximum number of threads (i.e. parallelism).
pub const MAX_P_COST: u32 = 0xFFFFFF;
/// Maximum length of a key ID in bytes.
pub const MAX_KEYID_LEN: usize = 8;
/// Maximum length of associated data in bytes.
pub const MAX_DATA_LEN: usize = 32;
/// Default output length.
pub const DEFAULT_OUTPUT_LEN: usize = 32;
/// Minimum digest size in bytes.
pub const MIN_OUTPUT_LEN: usize = 4;
/// Maximum digest size in bytes.
pub const MAX_OUTPUT_LEN: usize = 0xFFFFFFFF;
/// Default parameters (recommended).
pub const DEFAULT: Self = Params {
m_cost: Self::DEFAULT_M_COST,
t_cost: Self::DEFAULT_T_COST,
p_cost: Self::DEFAULT_P_COST,
keyid: KeyId {
bytes: [0u8; Self::MAX_KEYID_LEN],
len: 0,
},
data: AssociatedData {
bytes: [0u8; Self::MAX_DATA_LEN],
len: 0,
},
output_len: None,
};
/// Create new parameters.
///
/// # Arguments
/// - `m_cost`: memory size in 1 KiB blocks. Between 8\*`p_cost` and (2^32)-1.
/// - `t_cost`: number of iterations. Between 1 and (2^32)-1.
/// - `p_cost`: degree of parallelism. Between 1 and (2^24)-1.
/// - `output_len`: size of the KDF output in bytes. Default 32.
pub const fn new(
m_cost: u32,
t_cost: u32,
p_cost: u32,
output_len: Option<usize>,
) -> Result<Self> {
if m_cost < Params::MIN_M_COST {
return Err(Error::MemoryTooLittle);
}
// Note: we don't need to check `MAX_M_COST`, since it's `u32::MAX`
if m_cost < p_cost * 8 {
return Err(Error::MemoryTooLittle);
}
if t_cost < Params::MIN_T_COST {
return Err(Error::TimeTooSmall);
}
// Note: we don't need to check `MAX_T_COST`, since it's `u32::MAX`
if p_cost < Params::MIN_P_COST {
return Err(Error::ThreadsTooFew);
}
if p_cost > Params::MAX_P_COST {
return Err(Error::ThreadsTooMany);
}
if let Some(len) = output_len {
if len < Params::MIN_OUTPUT_LEN {
return Err(Error::OutputTooShort);
}
if len > Params::MAX_OUTPUT_LEN {
return Err(Error::OutputTooLong);
}
}
Ok(Params {
m_cost,
t_cost,
p_cost,
keyid: KeyId::EMPTY,
data: AssociatedData::EMPTY,
output_len,
})
}
/// Memory size, expressed in kibibytes. Between 8\*`p_cost` and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
pub const fn m_cost(&self) -> u32 {
self.m_cost
}
/// Number of iterations. Between 1 and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
pub const fn t_cost(&self) -> u32 {
self.t_cost
}
/// Degree of parallelism. Between 1 and (2^24)-1.
///
/// Value is an integer in decimal (1 to 3 digits).
pub const fn p_cost(&self) -> u32 {
self.p_cost
}
/// Key identifier: byte slice between 0 and 8 bytes in length.
///
/// Defaults to an empty byte slice.
///
/// Note this field is only present as a helper for reading/storing in
/// the PHC hash string format (i.e. it is totally ignored from a
/// cryptographical standpoint).
///
/// On top of that, this field is not longer part of the Argon2 standard
/// (see: <https://github.com/P-H-C/phc-winner-argon2/pull/173>), and should
/// not be used for any non-legacy work.
pub fn keyid(&self) -> &[u8] {
self.keyid.as_bytes()
}
/// Associated data: byte slice between 0 and 32 bytes in length.
///
/// Defaults to an empty byte slice.
///
/// This field is not longer part of the argon2 standard
/// (see: <https://github.com/P-H-C/phc-winner-argon2/pull/173>), and should
/// not be used for any non-legacy work.
pub fn data(&self) -> &[u8] {
self.data.as_bytes()
}
/// Length of the output (in bytes).
pub const fn output_len(&self) -> Option<usize> {
self.output_len
}
/// Get the number of lanes.
#[allow(clippy::cast_possible_truncation)]
pub(crate) const fn lanes(&self) -> usize {
self.p_cost as usize
}
/// Get the number of blocks in a lane.
pub(crate) const fn lane_length(&self) -> usize {
self.segment_length() * SYNC_POINTS
}
/// Get the segment length given the configured `m_cost` and `p_cost`.
///
/// Minimum memory_blocks = 8*`L` blocks, where `L` is the number of lanes.
pub(crate) const fn segment_length(&self) -> usize {
let m_cost = self.m_cost as usize;
let memory_blocks = if m_cost < 2 * SYNC_POINTS * self.lanes() {
2 * SYNC_POINTS * self.lanes()
} else {
m_cost
};
memory_blocks / (self.lanes() * SYNC_POINTS)
}
/// Get the number of blocks required given the configured `m_cost` and `p_cost`.
pub const fn block_count(&self) -> usize {
self.segment_length() * self.lanes() * SYNC_POINTS
}
}
impl Default for Params {
fn default() -> Params {
Params::DEFAULT
}
}
macro_rules! param_buf {
($ty:ident, $name:expr, $max_len:expr, $error:expr, $doc:expr) => {
#[doc = $doc]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct $ty {
/// Byte array
bytes: [u8; Self::MAX_LEN],
/// Length of byte array
len: usize,
}
impl $ty {
/// Maximum length in bytes
pub const MAX_LEN: usize = $max_len;
#[doc = "Create a new"]
#[doc = $name]
#[doc = "from a slice."]
pub fn new(slice: &[u8]) -> Result<Self> {
let mut bytes = [0u8; Self::MAX_LEN];
let len = slice.len();
bytes.get_mut(..len).ok_or($error)?.copy_from_slice(slice);
Ok(Self { bytes, len })
}
/// Empty value.
pub const EMPTY: Self = Self {
bytes: [0u8; Self::MAX_LEN],
len: 0,
};
#[doc = "Decode"]
#[doc = $name]
#[doc = " from a B64 string"]
pub fn from_b64(s: &str) -> Result<Self> {
let mut bytes = [0u8; Self::MAX_LEN];
let len = B64::decode(s, &mut bytes)?.len();
Ok(Self { bytes, len })
}
/// Borrow the inner value as a byte slice.
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
/// Get the length in bytes.
pub const fn len(&self) -> usize {
self.len
}
/// Is this value empty?
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl AsRef<[u8]> for $ty {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl FromStr for $ty {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::from_b64(s)
}
}
impl TryFrom<&[u8]> for $ty {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
Self::new(bytes)
}
}
};
}
// KeyId
param_buf!(
KeyId,
"KeyId",
Params::MAX_KEYID_LEN,
Error::KeyIdTooLong,
"Key identifier"
);
// AssociatedData
param_buf!(
AssociatedData,
"AssociatedData",
Params::MAX_DATA_LEN,
Error::AdTooLong,
"Associated data"
);
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl<'a> TryFrom<&'a PasswordHash<'a>> for Params {
type Error = password_hash::Error;
fn try_from(hash: &'a PasswordHash<'a>) -> password_hash::Result<Self> {
let mut builder = ParamsBuilder::new();
for (ident, value) in hash.params.iter() {
match ident.as_str() {
"m" => {
builder.m_cost(value.decimal()?);
}
"t" => {
builder.t_cost(value.decimal()?);
}
"p" => {
builder.p_cost(value.decimal()?);
}
"keyid" => {
builder.keyid(value.as_str().parse()?);
}
"data" => {
builder.data(value.as_str().parse()?);
}
_ => return Err(password_hash::Error::ParamNameInvalid),
}
}
if let Some(output) = &hash.hash {
builder.output_len(output.len());
}
Ok(builder.build()?)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl TryFrom<Params> for ParamsString {
type Error = password_hash::Error;
fn try_from(params: Params) -> password_hash::Result<ParamsString> {
ParamsString::try_from(&params)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl TryFrom<&Params> for ParamsString {
type Error = password_hash::Error;
fn try_from(params: &Params) -> password_hash::Result<ParamsString> {
let mut output = ParamsString::new();
output.add_decimal("m", params.m_cost)?;
output.add_decimal("t", params.t_cost)?;
output.add_decimal("p", params.p_cost)?;
if !params.keyid.is_empty() {
output.add_b64_bytes("keyid", params.keyid.as_bytes())?;
}
if !params.data.is_empty() {
output.add_b64_bytes("data", params.data.as_bytes())?;
}
Ok(output)
}
}
/// Builder for Argon2 [`Params`].
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParamsBuilder {
m_cost: u32,
t_cost: u32,
p_cost: u32,
keyid: Option<KeyId>,
data: Option<AssociatedData>,
output_len: Option<usize>,
}
impl ParamsBuilder {
/// Create a new builder with the default parameters.
pub const fn new() -> Self {
Self::DEFAULT
}
/// Set memory size, expressed in kibibytes, between 8\*`p_cost` and (2^32)-1.
pub fn m_cost(&mut self, m_cost: u32) -> &mut Self {
self.m_cost = m_cost;
self
}
/// Set number of iterations, between 1 and (2^32)-1.
pub fn t_cost(&mut self, t_cost: u32) -> &mut Self {
self.t_cost = t_cost;
self
}
/// Set degree of parallelism, between 1 and (2^24)-1.
pub fn p_cost(&mut self, p_cost: u32) -> &mut Self {
self.p_cost = p_cost;
self
}
/// Set key identifier.
pub fn keyid(&mut self, keyid: KeyId) -> &mut Self {
self.keyid = Some(keyid);
self
}
/// Set associated data.
pub fn data(&mut self, data: AssociatedData) -> &mut Self {
self.data = Some(data);
self
}
/// Set length of the output (in bytes).
pub fn output_len(&mut self, len: usize) -> &mut Self {
self.output_len = Some(len);
self
}
/// Get the finished [`Params`].
///
/// This performs validations to ensure that the given parameters are valid
/// and compatible with each other, and will return an error if they are not.
pub const fn build(&self) -> Result<Params> {
let mut params = match Params::new(self.m_cost, self.t_cost, self.p_cost, self.output_len) {
Ok(params) => params,
Err(err) => return Err(err),
};
if let Some(keyid) = self.keyid {
params.keyid = keyid;
}
if let Some(data) = self.data {
params.data = data;
};
Ok(params)
}
/// Create a new [`Argon2`] context using the provided algorithm/version.
pub fn context(&self, algorithm: Algorithm, version: Version) -> Result<Argon2<'_>> {
Ok(Argon2::new(algorithm, version, self.build()?))
}
/// Default parameters (recommended).
pub const DEFAULT: ParamsBuilder = {
let params = Params::DEFAULT;
Self {
m_cost: params.m_cost,
t_cost: params.t_cost,
p_cost: params.p_cost,
keyid: None,
data: None,
output_len: params.output_len,
}
};
}
impl Default for ParamsBuilder {
fn default() -> Self {
Self::DEFAULT
}
}
impl TryFrom<ParamsBuilder> for Params {
type Error = Error;
fn try_from(builder: ParamsBuilder) -> Result<Params> {
builder.build()
}
}
#[cfg(all(test, feature = "alloc", feature = "password-hash"))]
mod tests {
use super::*;
#[test]
fn params_builder_bad_values() {
assert_eq!(
ParamsBuilder::new().m_cost(Params::MIN_M_COST - 1).build(),
Err(Error::MemoryTooLittle)
);
assert_eq!(
ParamsBuilder::new().t_cost(Params::MIN_T_COST - 1).build(),
Err(Error::TimeTooSmall)
);
assert_eq!(
ParamsBuilder::new().p_cost(Params::MIN_P_COST - 1).build(),
Err(Error::ThreadsTooFew)
);
assert_eq!(
ParamsBuilder::new()
.m_cost(Params::DEFAULT_P_COST * 8 - 1)
.build(),
Err(Error::MemoryTooLittle)
);
assert_eq!(
ParamsBuilder::new()
.m_cost((Params::MAX_P_COST + 1) * 8)
.p_cost(Params::MAX_P_COST + 1)
.build(),
Err(Error::ThreadsTooMany)
);
}
#[test]
fn associated_data_too_long() {
let ret = AssociatedData::new(&[0u8; Params::MAX_DATA_LEN + 1]);
assert_eq!(ret, Err(Error::AdTooLong));
}
#[test]
fn keyid_too_long() {
let ret = KeyId::new(&[0u8; Params::MAX_KEYID_LEN + 1]);
assert_eq!(ret, Err(Error::KeyIdTooLong));
}
}

44
vendor/argon2/src/version.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
//! Version of the algorithm.
use crate::{Error, Result};
/// Version of the algorithm.
#[derive(Copy, Clone, Debug, Default, 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
#[default]
V0x13 = 0x13,
}
impl Version {
/// Serialize version as little endian bytes
pub(crate) const fn to_le_bytes(self) -> [u8; 4] {
(self as u32).to_le_bytes()
}
}
impl From<Version> for u32 {
fn from(version: Version) -> u32 {
version as u32
}
}
impl TryFrom<u32> for Version {
type Error = Error;
fn try_from(version_id: u32) -> Result<Version> {
match version_id {
0x10 => Ok(Version::V0x10),
0x13 => Ok(Version::V0x13),
_ => Err(Error::VersionInvalid),
}
}
}

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1 @@
{"name":"argon2","vers":"0.5.3","deps":[{"name":"base64ct","req":"^1","features":[],"optional":false,"default_features":true,"target":null,"kind":"normal","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false},{"name":"blake2","req":"^0.10.6","features":[],"optional":false,"default_features":false,"target":null,"kind":"normal","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false},{"name":"password-hash","req":"^0.5","features":[],"optional":true,"default_features":true,"target":null,"kind":"normal","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false},{"name":"zeroize","req":"^1","features":[],"optional":true,"default_features":false,"target":null,"kind":"normal","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false},{"name":"hex-literal","req":"^0.4","features":[],"optional":false,"default_features":true,"target":null,"kind":"dev","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false},{"name":"password-hash","req":"^0.5","features":["rand_core"],"optional":false,"default_features":true,"target":null,"kind":"dev","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false},{"name":"cpufeatures","req":"^0.2.12","features":[],"optional":false,"default_features":true,"target":"cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))","kind":"normal","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false}],"features":{"alloc":["password-hash/alloc"],"default":["alloc","password-hash","rand"],"rand":["password-hash/rand_core"],"simple":["password-hash"],"std":["alloc","password-hash/std"]},"features2":null,"cksum":"18fd414b1b0eea065241a124486f12c3eb0b4fd099a6dce1c5cea559f6048cce","yanked":null,"links":null,"rust_version":null,"v":2}

829
vendor/argon2/tests/kat.rs vendored Normal file
View File

@@ -0,0 +1,829 @@
//! Argon2 Known Answer Tests (KAT).
//!
//! 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/>
#![cfg(all(feature = "alloc", feature = "password-hash"))]
// TODO(tarcieri): test full set of vectors from the reference implementation:
// https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c
use argon2::{
Algorithm, Argon2, AssociatedData, Error, Params, ParamsBuilder, PasswordHash, PasswordHasher,
PasswordVerifier, Version,
};
use hex_literal::hex;
use password_hash::SaltString;
/// Params used by the KATs.
fn example_params() -> Params {
ParamsBuilder::new()
.m_cost(32)
.t_cost(3)
.p_cost(4)
.data(AssociatedData::new(&[0x04; 12]).unwrap())
.build()
.unwrap()
}
/// =======================================
/// 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 algorithm = Algorithm::Argon2d;
let version = Version::V0x10;
let params = example_params();
let password = [0x01; 32];
let salt = [0x02; 16];
let secret = [0x03; 8];
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_with_secret(&secret, algorithm, version, params).unwrap();
let mut out = [0u8; 32];
ctx.hash_password_into(&password, &salt, &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 algorithm = Algorithm::Argon2i;
let version = Version::V0x10;
let params = example_params();
let password = [0x01; 32];
let salt = [0x02; 16];
let secret = [0x03; 8];
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_with_secret(&secret, algorithm, version, params).unwrap();
let mut out = [0u8; 32];
ctx.hash_password_into(&password, &salt, &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 algorithm = Algorithm::Argon2id;
let version = Version::V0x10;
let params = example_params();
let password = [0x01; 32];
let salt = [0x02; 16];
let secret = [0x03; 8];
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_with_secret(&secret, algorithm, version, params).unwrap();
let mut out = [0u8; 32];
ctx.hash_password_into(&password, &salt, &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 algorithm = Algorithm::Argon2d;
let version = Version::V0x13;
let params = example_params();
let password = [0x01; 32];
let salt = [0x02; 16];
let secret = [0x03; 8];
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_with_secret(&secret, algorithm, version, params).unwrap();
let mut out = [0u8; 32];
ctx.hash_password_into(&password, &salt, &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 algorithm = Algorithm::Argon2i;
let version = Version::V0x13;
let params = example_params();
let password = [0x01; 32];
let salt = [0x02; 16];
let secret = [0x03; 8];
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_with_secret(&secret, algorithm, version, params).unwrap();
let mut out = [0u8; 32];
ctx.hash_password_into(&password, &salt, &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 algorithm = Algorithm::Argon2id;
let version = Version::V0x13;
let params = example_params();
let password = [0x01; 32];
let salt = [0x02; 16];
let secret = [0x03; 8];
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_with_secret(&secret, algorithm, version, params).unwrap();
let mut out = [0u8; 32];
ctx.hash_password_into(&password, &salt, &mut out).unwrap();
assert_eq!(out, expected_tag);
}
// =======================================
// Basic error checks
// =======================================
#[test]
fn salt_bad_length() {
let ctx = Argon2::new(Algorithm::Argon2id, Version::V0x13, example_params());
let mut out = [0u8; 32];
let too_short_salt = [0u8; argon2::MIN_SALT_LEN - 1];
let ret = ctx.hash_password_into(b"password", &too_short_salt, &mut out);
assert_eq!(ret, Err(Error::SaltTooShort));
#[cfg(target_pointer_width = "64")] // MAX_SALT_LEN + 1 is too big for 32-bit targets
{
// 4 GiB of RAM seems big, but as long as we ask for a zero-initialized vector
// optimizations kicks in an nothing is really allocated
let too_long_salt = vec![0u8; argon2::MAX_SALT_LEN + 1];
let ret = ctx.hash_password_into(b"password", &too_long_salt, &mut out);
assert_eq!(ret, Err(Error::SaltTooLong));
}
}
#[test]
fn output_bad_length() {
let ctx = Argon2::new(Algorithm::Argon2id, Version::V0x13, example_params());
let mut out = [0u8; Params::MIN_OUTPUT_LEN - 1];
let ret = ctx.hash_password_into(b"password", b"diffsalt", &mut out);
assert_eq!(ret, Err(Error::OutputTooShort));
#[cfg(target_pointer_width = "64")] // MAX_SALT_LEN + 1 is too big for 32-bit targets
{
// 4 GiB of RAM seems big, but as long as we ask for a zero-initialized vector
// optimizations kicks in an nothing is really allocated
let mut out = vec![0u8; Params::MAX_OUTPUT_LEN + 1];
let ret = ctx.hash_password_into(b"password", b"diffsalt", &mut out);
assert_eq!(ret, Err(Error::OutputTooLong));
}
}
// =======================================
// Reference implementation's test suite
// =======================================
// Taken from https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c
#[allow(clippy::too_many_arguments)]
fn hashtest(
algorithm: Algorithm,
version: Version,
t: u32,
m: u32,
p: u32,
pwd: &[u8],
salt: &[u8],
expected_raw_hash: [u8; 32],
expected_phc_hash: &str,
alternative_phc_hash: &str,
) {
let params = ParamsBuilder::new()
.t_cost(t)
.m_cost(1 << m)
.p_cost(p)
.build()
.unwrap();
let ctx = Argon2::new(algorithm, version, params);
// Test raw hash
let mut out = [0u8; 32];
ctx.hash_password_into(pwd, salt, &mut out).unwrap();
assert_eq!(out, expected_raw_hash);
// Test hash encoding
let salt_string = SaltString::encode_b64(salt).unwrap();
let phc_hash = ctx.hash_password(pwd, &salt_string).unwrap().to_string();
assert_eq!(phc_hash, expected_phc_hash);
let hash = PasswordHash::new(alternative_phc_hash).unwrap();
assert!(Argon2::default().verify_password(pwd, &hash).is_ok());
}
macro_rules! testcase_good {
($name: ident, $algorithm: expr, $version: expr, $t: expr, $m: expr, $p: expr, $pwd: expr, $salt: expr, $expected_raw: expr, $expected_phc: expr) => {
#[test]
fn $name() {
hashtest(
$algorithm,
$version,
$t,
$m,
$p,
$pwd,
$salt,
$expected_raw,
$expected_phc,
$expected_phc,
)
}
};
($name: ident, $algorithm: expr, $version: expr, $t: expr, $m: expr, $p: expr, $pwd: expr, $salt: expr, $expected_raw: expr, $expected_phc: expr, $alternative_phc: expr) => {
#[test]
fn $name() {
hashtest(
$algorithm,
$version,
$t,
$m,
$p,
$pwd,
$salt,
$expected_raw,
$expected_phc,
$alternative_phc,
)
}
};
}
macro_rules! ignored_testcase_good {
($name: ident, $algorithm: expr, $version: expr, $t: expr, $m: expr, $p: expr, $pwd: expr, $salt: expr, $expected_raw: expr, $expected_phc: expr, $alternative_phc: expr) => {
#[test]
#[ignore]
fn $name() {
hashtest(
$algorithm,
$version,
$t,
$m,
$p,
$pwd,
$salt,
$expected_raw,
$expected_phc,
$alternative_phc,
)
}
};
}
/* Argon2i V0x10: Multiple test cases for various input values */
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_2_16_1,
Algorithm::Argon2i,
Version::V0x10,
2,
16,
1,
b"password",
b"somesalt",
hex!("f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694"),
"$argon2i$v=16$m=65536,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ",
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
#[ignore]
#[cfg(feature = "test_large_ram")]
testcase_good!(
reference_argon2i_v0x10_2_20_1_large_ram,
Algorithm::Argon2i,
Version::V0x10,
2,
20,
1,
b"password",
b"somesalt",
hex!("9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9"),
"$argon2i$v=16$m=1048576,t=2,p=1$c29tZXNhbHQ$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk",
"$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_2_18_1,
Algorithm::Argon2i,
Version::V0x10,
2,
18,
1,
b"password",
b"somesalt",
hex!("3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467"),
"$argon2i$v=16$m=262144,t=2,p=1$c29tZXNhbHQ$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc",
"$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_2_8_1,
Algorithm::Argon2i,
Version::V0x10,
2,
8,
1,
b"password",
b"somesalt",
hex!("fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06"),
"$argon2i$v=16$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY",
"$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_2_8_2,
Algorithm::Argon2i,
Version::V0x10,
2,
8,
2,
b"password",
b"somesalt",
hex!("b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb"),
"$argon2i$v=16$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs",
"$argon2i$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_1_16_1,
Algorithm::Argon2i,
Version::V0x10,
1,
16,
1,
b"password",
b"somesalt",
hex!("81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2"),
"$argon2i$v=16$m=65536,t=1,p=1$c29tZXNhbHQ$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI",
"$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_4_16_1,
Algorithm::Argon2i,
Version::V0x10,
4,
16,
1,
b"password",
b"somesalt",
hex!("f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b"),
"$argon2i$v=16$m=65536,t=4,p=1$c29tZXNhbHQ$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs",
"$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_2_16_1_differentpassword,
Algorithm::Argon2i,
Version::V0x10,
2,
16,
1,
b"differentpassword",
b"somesalt",
hex!("e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3"),
"$argon2i$v=16$m=65536,t=2,p=1$c29tZXNhbHQ$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM",
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM"
);
// TODO: If version is not provided, verifier incorrectly uses version 0x13
ignored_testcase_good!(
reference_argon2i_v0x10_2_16_1_diffsalt,
Algorithm::Argon2i,
Version::V0x10,
2,
16,
1,
b"password",
b"diffsalt",
hex!("79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497"),
"$argon2i$v=16$m=65536,t=2,p=1$ZGlmZnNhbHQ$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc",
"$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc"
);
/* Argon2i V0x10: Error state tests */
// TODO: If version is not provided, verifier incorrectly uses version 0x13
#[ignore]
#[test]
fn reference_argon2i_v0x10_mismatching_hash() {
/* Handle an mismatching hash (the encoded password is "passwore") */
let hash = PasswordHash::new(
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$b2G3seW+uPzerwQQC+/E1K50CLLO7YXy0JRcaTuswRo",
)
.unwrap();
assert_eq!(
Argon2::default().verify_password(b"password", &hash),
Err(password_hash::errors::Error::Password)
);
}
/* Argon2i V0x13: Multiple test cases for various input values */
testcase_good!(
reference_argon2i_v0x13_2_16_1,
Algorithm::Argon2i,
Version::V0x13,
2,
16,
1,
b"password",
b"somesalt",
hex!("c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0"),
"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"
);
#[cfg(feature = "test_large_ram")]
testcase_good!(
reference_argon2i_v0x13_2_20_1,
Algorithm::Argon2i,
Version::V0x13,
2,
20,
1,
b"password",
b"somesalt",
hex!("d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41"),
"$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ$0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E"
);
testcase_good!(
reference_argon2i_v0x13_2_18_1,
Algorithm::Argon2i,
Version::V0x13,
2,
18,
1,
b"password",
b"somesalt",
hex!("296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb"),
"$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s"
);
testcase_good!(
reference_argon2i_v0x13_2_8_1,
Algorithm::Argon2i,
Version::V0x13,
2,
8,
1,
b"password",
b"somesalt",
hex!("89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f"),
"$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"
);
testcase_good!(
reference_argon2i_v0x13_2_8_2,
Algorithm::Argon2i,
Version::V0x13,
2,
8,
2,
b"password",
b"somesalt",
hex!("4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61"),
"$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E"
);
testcase_good!(
reference_argon2i_v0x13_1_16_1,
Algorithm::Argon2i,
Version::V0x13,
1,
16,
1,
b"password",
b"somesalt",
hex!("d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf"),
"$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8"
);
testcase_good!(
reference_argon2i_v0x13_4_16_1,
Algorithm::Argon2i,
Version::V0x13,
4,
16,
1,
b"password",
b"somesalt",
hex!("aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b"),
"$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ$qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls"
);
testcase_good!(
reference_argon2i_v0x13_2_16_1_differentpassword,
Algorithm::Argon2i,
Version::V0x13,
2,
16,
1,
b"differentpassword",
b"somesalt",
hex!("14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee"),
"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4"
);
testcase_good!(
reference_argon2i_v0x13_2_16_1_diffsalt,
Algorithm::Argon2i,
Version::V0x13,
2,
16,
1,
b"password",
b"diffsalt",
hex!("b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271"),
"$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ$sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE"
);
#[test]
fn reference_argon2i_v0x13_mismatching_hash() {
/* Handle an mismatching hash (the encoded password is "passwore") */
let hash = PasswordHash::new(
"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$8iIuixkI73Js3G1uMbezQXD0b8LG4SXGsOwoQkdAQIM",
)
.unwrap();
assert_eq!(
Argon2::default().verify_password(b"password", &hash),
Err(password_hash::errors::Error::Password)
);
}
/* Argon2id V0x13: Multiple test cases for various input values */
testcase_good!(
reference_argon2id_v0x13_2_16_1,
Algorithm::Argon2id,
Version::V0x13,
2,
16,
1,
b"password",
b"somesalt",
hex!("09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7"),
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"
);
testcase_good!(
reference_argon2id_v0x13_2_18_1,
Algorithm::Argon2id,
Version::V0x13,
2,
18,
1,
b"password",
b"somesalt",
hex!("78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c"),
"$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow"
);
testcase_good!(
reference_argon2id_v0x13_2_8_1,
Algorithm::Argon2id,
Version::V0x13,
2,
8,
1,
b"password",
b"somesalt",
hex!("9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe"),
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"
);
testcase_good!(
reference_argon2id_v0x13_2_8_2,
Algorithm::Argon2id,
Version::V0x13,
2,
8,
2,
b"password",
b"somesalt",
hex!("6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037"),
"$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc"
);
testcase_good!(
reference_argon2id_v0x13_1_16_1,
Algorithm::Argon2id,
Version::V0x13,
1,
16,
1,
b"password",
b"somesalt",
hex!("f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98"),
"$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg"
);
testcase_good!(
reference_argon2id_v0x13_4_16_1,
Algorithm::Argon2id,
Version::V0x13,
4,
16,
1,
b"password",
b"somesalt",
hex!("9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c"),
"$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw"
);
testcase_good!(
reference_argon2id_v0x13_2_16_1_differentpassword,
Algorithm::Argon2id,
Version::V0x13,
2,
16,
1,
b"differentpassword",
b"somesalt",
hex!("0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde"),
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94"
);
testcase_good!(
reference_argon2id_v0x13_2_16_1_diffsalt,
Algorithm::Argon2id,
Version::V0x13,
2,
16,
1,
b"password",
b"diffsalt",
hex!("bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c"),
"$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ$vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw"
);

220
vendor/argon2/tests/phc_strings.rs vendored Normal file
View File

@@ -0,0 +1,220 @@
//! Test vectors for Argon2 password hashes in the PHC string format
//!
//! Adapted from: <https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c>
#![cfg(all(feature = "alloc", feature = "password-hash"))]
use argon2::{
Algorithm, Argon2, AssociatedData, KeyId, ParamsBuilder, PasswordHash, PasswordHasher,
PasswordVerifier, Version,
};
use password_hash::{
errors::{Error, InvalidValue},
SaltString,
};
/// Valid password
pub const VALID_PASSWORD: &[u8] = b"password";
/// Invalid password
pub const INVALID_PASSWORD: &[u8] = b"sassword";
/// Password hashes for "password"
pub const VALID_PASSWORD_HASHES: &[&str] = &[
"$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQAAAAAAAAAAA$+r0d29hqEB0yasKr55ZgICsQGSkl0v0kgwhd+U3wyRo",
"$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow",
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc",
"$argon2d$v=19$m=65536,t=2,p=1$YzI5dFpYTmhiSFFBQUFBQUFBQUFBQQ$Jxy74cswY2mq9y+u+iJcJy8EqOp4t/C7DWDzGwGB3IM",
// Password with optional keyid
"$argon2d$v=19$m=65536,t=2,p=1,keyid=8PDw8A$YzI5dFpYTmhiSFFBQUFBQUFBQUFBQQ$Jxy74cswY2mq9y+u+iJcJy8EqOp4t/C7DWDzGwGB3IM",
// Password with optional data
"$argon2d$v=16$m=32,t=2,p=3,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o",
// Password with optional keyid&data
"$argon2d$v=16$m=32,t=2,p=3,keyid=8PDw8A,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o",
];
#[test]
fn verifies_correct_password() {
for hash_string in VALID_PASSWORD_HASHES {
let hash = PasswordHash::new(hash_string).unwrap();
assert_eq!(
Argon2::default().verify_password(VALID_PASSWORD, &hash),
Ok(())
);
}
}
#[test]
fn rejects_incorrect_password() {
for hash_string in VALID_PASSWORD_HASHES {
let hash = PasswordHash::new(hash_string).unwrap();
assert!(Argon2::default()
.verify_password(INVALID_PASSWORD, &hash)
.is_err());
}
}
// Test PHC string format according to spec
// see: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#argon2-encoding
macro_rules! testcase_bad_encoding {
($name: ident, $err: expr, $hash: expr) => {
#[test]
fn $name() {
let hash = PasswordHash::new($hash).unwrap();
assert_eq!(
Argon2::default().verify_password(b"password", &hash),
Err($err)
);
}
};
}
macro_rules! ignored_testcase_bad_encoding {
($name: ident, $err: expr, $hash: expr) => {
#[test]
#[ignore]
fn $name() {
let hash = PasswordHash::new($hash).unwrap();
assert_eq!(
Argon2::default().verify_password(b"password", &hash),
Err($err)
);
}
};
}
testcase_bad_encoding!(
argon2i_invalid_encoding_invalid_version,
Error::Version,
"$argon2i$v=42$m=65536,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_m_not_a_number,
Error::ParamValueInvalid(InvalidValue::InvalidChar('d')),
"$argon2i$m=dummy,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_m_too_small,
Error::ParamValueInvalid(InvalidValue::TooShort),
"$argon2i$m=0,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_m_too_big,
Error::ParamValueInvalid(InvalidValue::InvalidFormat),
"$argon2i$m=4294967296,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_t_not_a_number,
Error::ParamValueInvalid(InvalidValue::InvalidChar('d')),
"$argon2i$m=65536,t=dummy,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_t_too_small,
Error::ParamValueInvalid(InvalidValue::TooShort),
"$argon2i$m=65536,t=0,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_t_too_big,
Error::ParamValueInvalid(InvalidValue::InvalidFormat),
"$argon2i$m=65536,t=4294967296,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_p_not_a_number,
Error::ParamValueInvalid(InvalidValue::InvalidChar('d')),
"$argon2i$m=65536,t=2,p=dummy$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_p_too_small,
Error::ParamValueInvalid(InvalidValue::TooShort),
"$argon2i$m=65536,t=2,p=0$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
// TODO: Wrong error is returned, this should be changed in the `password-hash` crate
ignored_testcase_bad_encoding!(
argon2i_invalid_encoding_p_too_big,
Error::ParamValueInvalid(InvalidValue::InvalidFormat),
"$argon2i$m=65536,t=2,p=256$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_keyid_not_b64,
Error::B64Encoding(base64ct::Error::InvalidEncoding),
"$argon2i$m=65536,t=2,p=1,keyid=dummy$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
testcase_bad_encoding!(
argon2i_invalid_encoding_data_not_b64,
Error::B64Encoding(base64ct::Error::InvalidEncoding),
"$argon2i$m=65536,t=2,p=1,data=dummy$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"
);
#[test]
fn check_decoding_supports_out_of_order_parameters() {
// parameters order is not mandatory
let hash = "$argon2d$v=16$m=32,t=2,p=3,keyid=8PDw8A,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o";
let hash = PasswordHash::new(hash).unwrap();
assert!(Argon2::default()
.verify_password(b"password", &hash)
.is_ok());
}
// TODO: Fix default parameters for decoding !
#[test]
#[ignore]
fn check_decoding_supports_default_parameters() {
// parameters order is not mandatory
let hash = "$argon2i$p=2,m=256,t=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs";
let hash = PasswordHash::new(hash).unwrap();
assert!(Argon2::default()
.verify_password(b"password", &hash)
.is_ok());
}
// m/t/p parameters are NOT optional according to spec
// TODO: Wrong error is returned, this should be changed in the `password-hash` crate
ignored_testcase_bad_encoding!(
argon2i_invalid_encoding_missing_m,
Error::ParamValueInvalid(InvalidValue::InvalidFormat),
"$argon2d$v=16$t=2,p=3$8PDw8PDw8PA$Xv5daH0zPuKO3c9tMBG/WOIUsDrPqq815/xyQTukNxY"
);
// TODO: Wrong error is returned, this should be changed in the `password-hash` crate
ignored_testcase_bad_encoding!(
argon2i_invalid_encoding_missing_t,
Error::ParamValueInvalid(InvalidValue::InvalidFormat),
"$argon2d$v=16$m=32,p=3$8PDw8PDw8PA$Xv5daH0zPuKO3c9tMBG/WOIUsDrPqq815/xyQTukNxY"
);
// TODO: Wrong error is returned, this should be changed in the `password-hash` crate
ignored_testcase_bad_encoding!(
argon2i_invalid_encoding_missing_p,
Error::ParamValueInvalid(InvalidValue::InvalidFormat),
"$argon2d$v=16$m=32,t=2$8PDw8PDw8PA$Xv5daH0zPuKO3c9tMBG/WOIUsDrPqq815/xyQTukNxY"
);
// Missing&invalid id/salt/hash fields is handled by `PasswordHash` so no need to test that here
#[test]
fn check_hash_encoding_parameters_order() {
let params = ParamsBuilder::new()
.m_cost(32)
.t_cost(2)
.p_cost(3)
.data(AssociatedData::new(&[0x0f; 6]).unwrap())
.keyid(KeyId::new(&[0xf0; 4]).unwrap())
.build()
.unwrap();
let ctx = Argon2::new(Algorithm::Argon2d, Version::V0x10, params);
let salt = vec![0; 8];
let password = b"password";
let salt_string = SaltString::encode_b64(&salt).unwrap();
let password_hash = ctx
.hash_password(password, &salt_string)
.unwrap()
.to_string();
// The parameters shall appear in the m,t,p,keyid,data order
assert_eq!(password_hash, "$argon2d$v=16$m=32,t=2,p=3,keyid=8PDw8A,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o");
}