更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

@@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"cba0482b4328c05f545e94d6fea5d068b8c2e8c27abec3851b8fb567c6a0f562","Cargo.toml":"be0df25f7235deb18a52323de163e63bd5aefe4ad91ed276022d4757ccddeece","LICENSE-APACHE":"a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5","LICENSE-MIT":"9e0dfd2dd4173a530e238cb6adb37aa78c34c6bc7444e0e10c1ab5d8881f63ba","README.md":"edf9f16c57466b06d201b8646182b7332324c7aba28f832dde7f57d03249637d","src/core_api.rs":"b52728aba8a84f980f3f9cc8a94a64d3a97f1eb5f4db144904822c2f8eefb1f8","src/core_api/ct_variable.rs":"703bd62fb693a437e319d1192988bd674f9127a6b76f73b4c58c71afc79bc013","src/core_api/rt_variable.rs":"b57f89bf3991a313e2ddde09c701375e23539e7df74d685a161707ba1fbc99e4","src/core_api/wrapper.rs":"033777bed7d140b158e15d50fda8a6e06557ce89bd0738fcca692be2c39e8b8a","src/core_api/xof_reader.rs":"f33ca7b2c17eb99d84ea460d5567af68690e4fa6c2d94069a5d6748f8c8620eb","src/dev.rs":"cbaeab07489efcadec917d7b7bcf2fdade79e78a4839ab3c3d8ad442f8f82833","src/dev/fixed.rs":"1cbabc651645c1e781d31825791132b4e3741f426e99d7e40988e2a5ee49bddd","src/dev/mac.rs":"e8837d3b99dc8b6ddb398e7fad5731c2ed36931f851ed625d3ae59fb31244165","src/dev/rng.rs":"156f42e9eb8fb2083cd12dc4a9bff9d57a321d33367efe6cd42cdc02c17ed2dc","src/dev/variable.rs":"51939602b43f5a813fc725bc603a34246bbf76facaa7930cb7bf78c283ec94a7","src/dev/xof.rs":"b3971175e50f615247e4158cba87d77c369461eda22751d888725cec45b61985","src/digest.rs":"8beab74640774c9f6811daa6dac9b5a8867f5beeb0b552a9b5ddbc5cfc196ed0","src/lib.rs":"5128199102bf0f7638fba0bbcf42b23822e31065841fb0c4304b64f681fde961","src/mac.rs":"6303caa2c5b76513346c082dd600e007354179ad440fc83dad3d7f4240281803"},"package":"8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"}

View File

@@ -0,0 +1,131 @@
# 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.10.6 (2022-11-17)
### Added
- `Mac::verify_reset` and `Mac::verify_slice_reset` methods ([#1154])
[#1154]: https://github.com/RustCrypto/traits/pull/1154
## 0.10.5 (2022-09-16)
### Fixed
- MSRV build ([#1117])
[#1117]: https://github.com/RustCrypto/traits/pull/1117
## 0.10.4 (2022-09-16)
### Added
- Feature-gated implementation of the `const_oid::AssociatedOid` trait
for the core wrappers. ([#1098])
[#1098]: https://github.com/RustCrypto/traits/pull/1098
## 0.10.3 (2022-02-16)
### Fixed
- Minimal versions build ([#940])
[#940]: https://github.com/RustCrypto/traits/pull/940
## 0.10.2 (2022-02-10)
### Changed
- Relax bounds on the `Mac` trait ([#849])
[#849]: https://github.com/RustCrypto/traits/pull/849
## 0.10.1 (2021-12-14) [YANKED]
### Added
- `Update::chain` and `Digest::new_with_prefix` methods. ([#846])
- `Mac::generate_key` method. ([#847])
### Fixed
- Doc cfg attribute for `CtOutput` and `MacError`. ([#842])
- Expose `KeyInit::generate_key` method in docs. ([#847])
[#842]: https://github.com/RustCrypto/traits/pull/842
[#846]: https://github.com/RustCrypto/traits/pull/846
[#847]: https://github.com/RustCrypto/traits/pull/847
## 0.10.0 (2021-12-07) [YANKED]
### Changed
- Dirty traits are removed and instead block-level traits are introduced.
Variable output traits reworked and now support both run and compile time selection of output size. ([#380], [#819])
- The `crypto-mac` traits are reworked and merged in. ([#819])
[#819]: https://github.com/RustCrypto/traits/pull/819
[#380]: https://github.com/RustCrypto/traits/pull/380
## 0.9.0 (2020-06-09)
### Added
- `ExtendableOutputDirty` and `VariableOutputDirty` traits ([#183])
- `FixedOutputDirty` trait + `finalize_into*` ([#180])
- `XofReader::read_boxed` method ([#178], [#181], [#182])
- `alloc` feature ([#163])
- Re-export `typenum::consts` as `consts` ([#123])
- `Output` type alias ([#115])
### Changed
- Rename `*result*` methods to `finalize` ala IUF ([#161])
- Use `impl AsRef<[u8]>` instead of generic params on methods ([#112])
- Rename `Input::input` to `Update::update` ala IUF ([#111])
- Upgrade to Rust 2018 edition ([#109])
- Bump `generic-array` to v0.14 ([#95])
[#183]: https://github.com/RustCrypto/traits/pull/183
[#181]: https://github.com/RustCrypto/traits/pull/181
[#182]: https://github.com/RustCrypto/traits/pull/182
[#180]: https://github.com/RustCrypto/traits/pull/180
[#178]: https://github.com/RustCrypto/traits/pull/178
[#163]: https://github.com/RustCrypto/traits/pull/163
[#161]: https://github.com/RustCrypto/traits/pull/161
[#123]: https://github.com/RustCrypto/traits/pull/123
[#115]: https://github.com/RustCrypto/traits/pull/115
[#111]: https://github.com/RustCrypto/traits/pull/111
[#112]: https://github.com/RustCrypto/traits/pull/112
[#109]: https://github.com/RustCrypto/traits/pull/109
[#95]: https://github.com/RustCrypto/traits/pull/95
## 0.8.1 (2019-06-30)
## 0.8.0 (2018-10-01)
## 0.7.6 (2018-09-21)
## 0.7.5 (2018-07-13)
## 0.7.4 (2018-06-21)
## 0.7.3 (2018-06-20)
## 0.7.2 (2017-11-17)
## 0.7.1 (2017-11-15)
## 0.7.0 (2017-11-14)
## 0.6.2 (2017-07-24)
## 0.6.1 (2017-06-18)
## 0.6.0 (2017-06-12)
## 0.5.2 (2017-05-02)
## 0.5.1 (2017-05-02)
## 0.5.0 (2017-04-06)
## 0.4.0 (2016-12-24)
## 0.3.1 (2016-12-16)
## 0.3.0 (2016-11-17)
## 0.2.1 (2016-10-14)
## 0.2.0 (2016-10-14)
## 0.1.0 (2016-10-06)

View File

@@ -0,0 +1,70 @@
# 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 = "2018"
name = "digest"
version = "0.10.6"
authors = ["RustCrypto Developers"]
description = "Traits for cryptographic hash functions and message authentication codes"
documentation = "https://docs.rs/digest"
readme = "README.md"
keywords = [
"digest",
"crypto",
"hash",
]
categories = [
"cryptography",
"no-std",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/RustCrypto/traits"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
[dependencies.blobby]
version = "0.3"
optional = true
[dependencies.block-buffer]
version = "0.10"
optional = true
[dependencies.const-oid]
version = "0.9"
optional = true
[dependencies.crypto-common]
version = "0.1.3"
[dependencies.subtle]
version = "=2.4"
optional = true
default-features = false
[features]
alloc = []
core-api = ["block-buffer"]
default = ["core-api"]
dev = ["blobby"]
mac = ["subtle"]
oid = ["const-oid"]
rand_core = ["crypto-common/rand_core"]
std = [
"alloc",
"crypto-common/std",
]

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.

View File

@@ -0,0 +1,25 @@
Copyright (c) 2017 Artyom Pavlov
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.

View File

@@ -0,0 +1,164 @@
# RustCrypto: Digest Algorithm Traits
[![crate][crate-image]][crate-link]
[![Docs][docs-image]][docs-link]
![Apache2/MIT licensed][license-image]
![Rust Version][rustc-image]
[![Project Chat][chat-image]][chat-link]
[![Build Status][build-image]][build-link]
Traits which describe functionality of [cryptographic hash functions][0], a.k.a.
digest algorithms.
See [RustCrypto/hashes][1] for implementations which use this trait.
[Documentation][docs-link]
## Minimum Supported Rust Version
Rust **1.41** 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
## Usage
Let us demonstrate how to use crates in this repository using Sha256 as an
example.
First add the `sha2` crate to your `Cargo.toml`:
```toml
[dependencies]
sha2 = "0.10"
```
`sha2` and other crates re-export `digest` crate and `Digest` trait for
convenience, so you don't have to add `digest` crate as an explicit dependency.
Now you can write the following code:
```rust
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
let data = b"Hello world!";
hasher.update(data);
// `input` can be called repeatedly and is generic over `AsRef<[u8]>`
hasher.update("String data");
// Note that calling `finalize()` consumes hasher
let hash = hasher.finalize();
println!("Result: {:x}", hash);
```
In this example `hash` has type [`GenericArray<u8, U64>`][2], which is a generic
alternative to `[u8; 64]`.
Alternatively you can use chained approach, which is equivalent to the previous
example:
```rust
let hash = Sha256::new()
.chain_update(b"Hello world!")
.chain_update("String data")
.finalize();
println!("Result: {:x}", hash);
```
If the whole message is available you also can use convenience `digest` method:
```rust
let hash = Sha256::digest(b"my message");
println!("Result: {:x}", hash);
```
### Hashing `Read`-able objects
If you want to hash data from [`Read`][3] trait (e.g. from file) you can rely on
implementation of [`Write`][4] trait (requires enabled-by-default `std` feature):
```rust
use sha2::{Sha256, Digest};
use std::{fs, io};
let mut file = fs::File::open(&path)?;
let mut hasher = Sha256::new();
let n = io::copy(&mut file, &mut hasher)?;
let hash = hasher.finalize();
println!("Path: {}", path);
println!("Bytes processed: {}", n);
println!("Hash value: {:x}", hash);
```
### Generic code
You can write generic code over `Digest` (or other traits from `digest` crate)
trait which will work over different hash functions:
```rust
use digest::Digest;
// Toy example, do not use it in practice!
// Instead use crates from: https://github.com/RustCrypto/password-hashing
fn hash_password<D: Digest>(password: &str, salt: &str, output: &mut [u8]) {
let mut hasher = D::new();
hasher.update(password.as_bytes());
hasher.update(b"$");
hasher.update(salt.as_bytes());
output.copy_from_slice(hasher.finalize().as_slice())
}
let mut buf1 = [0u8; 32];
let mut buf2 = [0u8; 64];
hash_password::<sha2::Sha256>("my_password", "abcd", &mut buf1);
hash_password::<sha2::Sha512>("my_password", "abcd", &mut buf2);
```
If you want to use hash functions with trait objects, use `digest::DynDigest`
trait.
## License
Licensed under either of:
* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
* [MIT license](http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
[//]: # (badges)
[crate-image]: https://img.shields.io/crates/v/digest.svg
[crate-link]: https://crates.io/crates/digest
[docs-image]: https://docs.rs/digest/badge.svg
[docs-link]: https://docs.rs/digest/
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes
[build-image]: https://github.com/RustCrypto/traits/workflows/digest/badge.svg?branch=master&event=push
[build-link]: https://github.com/RustCrypto/traits/actions?query=workflow%3Adigest
[//]: # (general links)
[0]: https://en.wikipedia.org/wiki/Cryptographic_hash_function
[1]: https://github.com/RustCrypto/hashes
[2]: https://docs.rs/generic-array
[3]: https://doc.rust-lang.org/std/io/trait.Read.html
[4]: https://doc.rust-lang.org/std/io/trait.Write.html
[5]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
[6]: https://github.com/RustCrypto/MACs

View File

@@ -0,0 +1,119 @@
//! Low-level traits operating on blocks and wrappers around them.
//!
//! Usage of traits in this module in user code is discouraged. Instead use
//! core algorithm wrapped by the wrapper types, which implement the
//! higher-level traits.
use crate::InvalidOutputSize;
pub use crypto_common::{AlgorithmName, Block, BlockSizeUser, OutputSizeUser, Reset};
use block_buffer::{BlockBuffer, BufferKind};
use crypto_common::{
typenum::{IsLess, Le, NonZero, U256},
Output,
};
mod ct_variable;
mod rt_variable;
mod wrapper;
mod xof_reader;
pub use ct_variable::CtVariableCoreWrapper;
pub use rt_variable::RtVariableCoreWrapper;
pub use wrapper::{CoreProxy, CoreWrapper};
pub use xof_reader::XofReaderCoreWrapper;
/// Buffer type used by type which implements [`BufferKindUser`].
pub type Buffer<S> =
BlockBuffer<<S as BlockSizeUser>::BlockSize, <S as BufferKindUser>::BufferKind>;
/// Types which consume data in blocks.
pub trait UpdateCore: BlockSizeUser {
/// Update state using the provided data blocks.
fn update_blocks(&mut self, blocks: &[Block<Self>]);
}
/// Types which use [`BlockBuffer`] functionality.
pub trait BufferKindUser: BlockSizeUser {
/// Block buffer kind over which type operates.
type BufferKind: BufferKind;
}
/// Core trait for hash functions with fixed output size.
pub trait FixedOutputCore: UpdateCore + BufferKindUser + OutputSizeUser
where
Self::BlockSize: IsLess<U256>,
Le<Self::BlockSize, U256>: NonZero,
{
/// Finalize state using remaining data stored in the provided block buffer,
/// write result into provided array and leave `self` in a dirty state.
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>);
}
/// Core trait for hash functions with extendable (XOF) output size.
pub trait ExtendableOutputCore: UpdateCore + BufferKindUser
where
Self::BlockSize: IsLess<U256>,
Le<Self::BlockSize, U256>: NonZero,
{
/// XOF reader core state.
type ReaderCore: XofReaderCore;
/// Retrieve XOF reader using remaining data stored in the block buffer
/// and leave hasher in a dirty state.
fn finalize_xof_core(&mut self, buffer: &mut Buffer<Self>) -> Self::ReaderCore;
}
/// Core reader trait for extendable-output function (XOF) result.
pub trait XofReaderCore: BlockSizeUser {
/// Read next XOF block.
fn read_block(&mut self) -> Block<Self>;
}
/// Core trait for hash functions with variable output size.
///
/// Maximum output size is equal to [`OutputSizeUser::OutputSize`].
/// Users are expected to truncate result returned by the
/// [`finalize_variable_core`] to `output_size` passed to the [`new`] method
/// during construction. Truncation side is defined by the [`TRUNC_SIDE`]
/// associated constant.
///
/// [`finalize_variable_core`]: VariableOutputCore::finalize_variable_core
/// [`new`]: VariableOutputCore::new
/// [`TRUNC_SIDE`]: VariableOutputCore::TRUNC_SIDE
pub trait VariableOutputCore: UpdateCore + OutputSizeUser + BufferKindUser + Sized
where
Self::BlockSize: IsLess<U256>,
Le<Self::BlockSize, U256>: NonZero,
{
/// Side which should be used in a truncated result.
const TRUNC_SIDE: TruncSide;
/// Initialize hasher state for given output size.
///
/// Returns [`InvalidOutputSize`] if `output_size` is not valid for
/// the algorithm, e.g. if it's bigger than the [`OutputSize`]
/// associated type.
///
/// [`OutputSize`]: OutputSizeUser::OutputSize
fn new(output_size: usize) -> Result<Self, InvalidOutputSize>;
/// Finalize hasher and write full hashing result into the `out` buffer.
///
/// The result must be truncated to `output_size` used during hasher
/// construction. Truncation side is defined by the [`TRUNC_SIDE`]
/// associated constant.
///
/// [`TRUNC_SIDE`]: VariableOutputCore::TRUNC_SIDE
fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>);
}
/// Type which used for defining truncation side in the [`VariableOutputCore`]
/// trait.
#[derive(Copy, Clone, Debug)]
pub enum TruncSide {
/// Truncate left side, i.e. `&out[..n]`.
Left,
/// Truncate right side, i.e. `&out[m..]`.
Right,
}

View File

@@ -0,0 +1,204 @@
use super::{
AlgorithmName, Buffer, BufferKindUser, FixedOutputCore, Reset, TruncSide, UpdateCore,
VariableOutputCore,
};
use crate::HashMarker;
#[cfg(feature = "mac")]
use crate::MacMarker;
#[cfg(feature = "oid")]
use const_oid::{AssociatedOid, ObjectIdentifier};
use core::{fmt, marker::PhantomData};
use crypto_common::{
generic_array::{ArrayLength, GenericArray},
typenum::{IsLess, IsLessOrEqual, Le, LeEq, NonZero, U256},
Block, BlockSizeUser, OutputSizeUser,
};
/// Dummy type used with [`CtVariableCoreWrapper`] in cases when
/// resulting hash does not have a known OID.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct NoOid;
/// Wrapper around [`VariableOutputCore`] which selects output size
/// at compile time.
#[derive(Clone)]
pub struct CtVariableCoreWrapper<T, OutSize, O = NoOid>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
inner: T,
_out: PhantomData<(OutSize, O)>,
}
impl<T, OutSize, O> HashMarker for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore + HashMarker,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
}
#[cfg(feature = "mac")]
impl<T, OutSize, O> MacMarker for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore + MacMarker,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
}
impl<T, OutSize, O> BlockSizeUser for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
type BlockSize = T::BlockSize;
}
impl<T, OutSize, O> UpdateCore for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
self.inner.update_blocks(blocks);
}
}
impl<T, OutSize, O> OutputSizeUser for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize> + 'static,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
type OutputSize = OutSize;
}
impl<T, OutSize, O> BufferKindUser for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
type BufferKind = T::BufferKind;
}
impl<T, OutSize, O> FixedOutputCore for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize> + 'static,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn finalize_fixed_core(
&mut self,
buffer: &mut Buffer<Self>,
out: &mut GenericArray<u8, Self::OutputSize>,
) {
let mut full_res = Default::default();
self.inner.finalize_variable_core(buffer, &mut full_res);
let n = out.len();
let m = full_res.len() - n;
match T::TRUNC_SIDE {
TruncSide::Left => out.copy_from_slice(&full_res[..n]),
TruncSide::Right => out.copy_from_slice(&full_res[m..]),
}
}
}
impl<T, OutSize, O> Default for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn default() -> Self {
Self {
inner: T::new(OutSize::USIZE).unwrap(),
_out: PhantomData,
}
}
}
impl<T, OutSize, O> Reset for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn reset(&mut self) {
*self = Default::default();
}
}
impl<T, OutSize, O> AlgorithmName for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore + AlgorithmName,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::write_alg_name(f)?;
f.write_str("_")?;
write!(f, "{}", OutSize::USIZE)
}
}
#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
impl<T, OutSize, O> AssociatedOid for CtVariableCoreWrapper<T, OutSize, O>
where
T: VariableOutputCore,
O: AssociatedOid,
OutSize: ArrayLength<u8> + IsLessOrEqual<T::OutputSize>,
LeEq<OutSize, T::OutputSize>: NonZero,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
const OID: ObjectIdentifier = O::OID;
}
/// Implement dummy type with hidden docs which is used to "carry" hasher
/// OID for [`CtVariableCoreWrapper`].
#[macro_export]
macro_rules! impl_oid_carrier {
($name:ident, $oid:literal) => {
#[doc(hidden)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct $name;
#[cfg(feature = "oid")]
impl AssociatedOid for $name {
const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap($oid);
}
};
}

View File

@@ -0,0 +1,166 @@
use super::{AlgorithmName, TruncSide, UpdateCore, VariableOutputCore};
#[cfg(feature = "mac")]
use crate::MacMarker;
use crate::{HashMarker, InvalidBufferSize};
use crate::{InvalidOutputSize, Reset, Update, VariableOutput, VariableOutputReset};
use block_buffer::BlockBuffer;
use core::fmt;
use crypto_common::typenum::{IsLess, Le, NonZero, Unsigned, U256};
/// Wrapper around [`VariableOutputCore`] which selects output size
/// at run time.
#[derive(Clone)]
pub struct RtVariableCoreWrapper<T>
where
T: VariableOutputCore + UpdateCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
core: T,
buffer: BlockBuffer<T::BlockSize, T::BufferKind>,
output_size: usize,
}
impl<T> RtVariableCoreWrapper<T>
where
T: VariableOutputCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn finalize_dirty(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> {
let Self {
core,
buffer,
output_size,
} = self;
if out.len() != *output_size || out.len() > Self::MAX_OUTPUT_SIZE {
return Err(InvalidBufferSize);
}
let mut full_res = Default::default();
core.finalize_variable_core(buffer, &mut full_res);
let n = out.len();
let m = full_res.len() - n;
match T::TRUNC_SIDE {
TruncSide::Left => out.copy_from_slice(&full_res[..n]),
TruncSide::Right => out.copy_from_slice(&full_res[m..]),
}
Ok(())
}
}
impl<T> HashMarker for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + HashMarker,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
}
#[cfg(feature = "mac")]
#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
impl<T> MacMarker for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + MacMarker,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
}
impl<T> Reset for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + UpdateCore + Reset,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn reset(&mut self) {
self.buffer.reset();
self.core.reset();
}
}
impl<T> Update for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + UpdateCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn update(&mut self, input: &[u8]) {
let Self { core, buffer, .. } = self;
buffer.digest_blocks(input, |blocks| core.update_blocks(blocks));
}
}
impl<T> VariableOutput for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + UpdateCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
const MAX_OUTPUT_SIZE: usize = T::OutputSize::USIZE;
fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
let buffer = Default::default();
T::new(output_size).map(|core| Self {
core,
buffer,
output_size,
})
}
fn output_size(&self) -> usize {
self.output_size
}
fn finalize_variable(mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> {
self.finalize_dirty(out)
}
}
impl<T> VariableOutputReset for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + UpdateCore + Reset,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
fn finalize_variable_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> {
self.finalize_dirty(out)?;
self.core.reset();
self.buffer.reset();
Ok(())
}
}
impl<T> fmt::Debug for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + UpdateCore + AlgorithmName,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
T::write_alg_name(f)?;
f.write_str(" { .. }")
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<T> std::io::Write for RtVariableCoreWrapper<T>
where
T: VariableOutputCore + UpdateCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Update::update(self, buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

View File

@@ -0,0 +1,290 @@
use super::{
AlgorithmName, Buffer, BufferKindUser, ExtendableOutputCore, FixedOutputCore, OutputSizeUser,
Reset, UpdateCore, XofReaderCoreWrapper,
};
use crate::{
ExtendableOutput, ExtendableOutputReset, FixedOutput, FixedOutputReset, HashMarker, Update,
};
use block_buffer::BlockBuffer;
use core::fmt;
use crypto_common::{
typenum::{IsLess, Le, NonZero, U256},
BlockSizeUser, InvalidLength, Key, KeyInit, KeySizeUser, Output,
};
#[cfg(feature = "mac")]
use crate::MacMarker;
#[cfg(feature = "oid")]
use const_oid::{AssociatedOid, ObjectIdentifier};
/// Wrapper around [`BufferKindUser`].
///
/// It handles data buffering and implements the slice-based traits.
#[derive(Clone, Default)]
pub struct CoreWrapper<T>
where
T: BufferKindUser,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
core: T,
buffer: BlockBuffer<T::BlockSize, T::BufferKind>,
}
impl<T> HashMarker for CoreWrapper<T>
where
T: BufferKindUser + HashMarker,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
}
#[cfg(feature = "mac")]
#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
impl<T> MacMarker for CoreWrapper<T>
where
T: BufferKindUser + MacMarker,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
}
// this blanket impl is needed for HMAC
impl<T> BlockSizeUser for CoreWrapper<T>
where
T: BufferKindUser + HashMarker,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
type BlockSize = T::BlockSize;
}
impl<T> CoreWrapper<T>
where
T: BufferKindUser,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
/// Create new wrapper from `core`.
#[inline]
pub fn from_core(core: T) -> Self {
let buffer = Default::default();
Self { core, buffer }
}
/// Decompose wrapper into inner parts.
#[inline]
pub fn decompose(self) -> (T, Buffer<T>) {
let Self { core, buffer } = self;
(core, buffer)
}
}
impl<T> KeySizeUser for CoreWrapper<T>
where
T: BufferKindUser + KeySizeUser,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
type KeySize = T::KeySize;
}
impl<T> KeyInit for CoreWrapper<T>
where
T: BufferKindUser + KeyInit,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn new(key: &Key<Self>) -> Self {
Self {
core: T::new(key),
buffer: Default::default(),
}
}
#[inline]
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
Ok(Self {
core: T::new_from_slice(key)?,
buffer: Default::default(),
})
}
}
impl<T> fmt::Debug for CoreWrapper<T>
where
T: BufferKindUser + AlgorithmName,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
T::write_alg_name(f)?;
f.write_str(" { .. }")
}
}
impl<T> Reset for CoreWrapper<T>
where
T: BufferKindUser + Reset,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn reset(&mut self) {
self.core.reset();
self.buffer.reset();
}
}
impl<T> Update for CoreWrapper<T>
where
T: BufferKindUser + UpdateCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn update(&mut self, input: &[u8]) {
let Self { core, buffer } = self;
buffer.digest_blocks(input, |blocks| core.update_blocks(blocks));
}
}
impl<T> OutputSizeUser for CoreWrapper<T>
where
T: BufferKindUser + OutputSizeUser,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
type OutputSize = T::OutputSize;
}
impl<T> FixedOutput for CoreWrapper<T>
where
T: FixedOutputCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn finalize_into(mut self, out: &mut Output<Self>) {
let Self { core, buffer } = &mut self;
core.finalize_fixed_core(buffer, out);
}
}
impl<T> FixedOutputReset for CoreWrapper<T>
where
T: FixedOutputCore + Reset,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
let Self { core, buffer } = self;
core.finalize_fixed_core(buffer, out);
core.reset();
buffer.reset();
}
}
impl<T> ExtendableOutput for CoreWrapper<T>
where
T: ExtendableOutputCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
<T::ReaderCore as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<T::ReaderCore as BlockSizeUser>::BlockSize, U256>: NonZero,
{
type Reader = XofReaderCoreWrapper<T::ReaderCore>;
#[inline]
fn finalize_xof(self) -> Self::Reader {
let (mut core, mut buffer) = self.decompose();
let core = core.finalize_xof_core(&mut buffer);
let buffer = Default::default();
Self::Reader { core, buffer }
}
}
impl<T> ExtendableOutputReset for CoreWrapper<T>
where
T: ExtendableOutputCore + Reset,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
<T::ReaderCore as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<T::ReaderCore as BlockSizeUser>::BlockSize, U256>: NonZero,
{
#[inline]
fn finalize_xof_reset(&mut self) -> Self::Reader {
let Self { core, buffer } = self;
let reader_core = core.finalize_xof_core(buffer);
core.reset();
buffer.reset();
let buffer = Default::default();
Self::Reader {
core: reader_core,
buffer,
}
}
}
#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
impl<T> AssociatedOid for CoreWrapper<T>
where
T: BufferKindUser + AssociatedOid,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
const OID: ObjectIdentifier = T::OID;
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<T> std::io::Write for CoreWrapper<T>
where
T: BufferKindUser + UpdateCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Update::update(self, buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
/// A proxy trait to a core type implemented by [`CoreWrapper`]
// TODO: replace with an inherent associated type on stabilization:
// https://github.com/rust-lang/rust/issues/8995
pub trait CoreProxy: sealed::Sealed {
/// Type wrapped by [`CoreWrapper`].
type Core;
}
mod sealed {
pub trait Sealed {}
}
impl<T> sealed::Sealed for CoreWrapper<T>
where
T: BufferKindUser,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
}
impl<T> CoreProxy for CoreWrapper<T>
where
T: BufferKindUser,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
type Core = T;
}

View File

@@ -0,0 +1,63 @@
use super::{AlgorithmName, XofReaderCore};
use crate::XofReader;
use block_buffer::EagerBuffer;
use core::fmt;
use crypto_common::typenum::{IsLess, Le, NonZero, U256};
/// Wrapper around [`XofReaderCore`] implementations.
///
/// It handles data buffering and implements the mid-level traits.
#[derive(Clone, Default)]
pub struct XofReaderCoreWrapper<T>
where
T: XofReaderCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
pub(super) core: T,
pub(super) buffer: EagerBuffer<T::BlockSize>,
}
impl<T> fmt::Debug for XofReaderCoreWrapper<T>
where
T: XofReaderCore + AlgorithmName,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
T::write_alg_name(f)?;
f.write_str(" { .. }")
}
}
impl<T> XofReader for XofReaderCoreWrapper<T>
where
T: XofReaderCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn read(&mut self, buffer: &mut [u8]) {
let Self { core, buffer: buf } = self;
buf.set_data(buffer, |blocks| {
for block in blocks {
*block = core.read_block();
}
});
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<T> std::io::Read for XofReaderCoreWrapper<T>
where
T: XofReaderCore,
T::BlockSize: IsLess<U256>,
Le<T::BlockSize, U256>: NonZero,
{
#[inline]
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
XofReader::read(self, buf);
Ok(buf.len())
}
}

View File

@@ -0,0 +1,78 @@
//! Development-related functionality
pub use blobby;
mod fixed;
mod mac;
mod rng;
mod variable;
mod xof;
pub use fixed::*;
pub use mac::*;
pub use variable::*;
pub use xof::*;
/// Define hash function test
#[macro_export]
#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
macro_rules! new_test {
($name:ident, $test_name:expr, $hasher:ty, $test_func:ident $(,)?) => {
#[test]
fn $name() {
use digest::dev::blobby::Blob2Iterator;
let data = include_bytes!(concat!("data/", $test_name, ".blb"));
for (i, row) in Blob2Iterator::new(data).unwrap().enumerate() {
let [input, output] = row.unwrap();
if let Some(desc) = $test_func::<$hasher>(input, output) {
panic!(
"\n\
Failed test №{}: {}\n\
input:\t{:?}\n\
output:\t{:?}\n",
i, desc, input, output,
);
}
}
}
};
}
/// Define [`Update`][crate::Update] impl benchmark
#[macro_export]
#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
macro_rules! bench_update {
(
$init:expr;
$($name:ident $bs:expr;)*
) => {
$(
#[bench]
fn $name(b: &mut Bencher) {
let mut d = $init;
let data = [0; $bs];
b.iter(|| {
digest::Update::update(&mut d, &data[..]);
});
b.bytes = $bs;
}
)*
};
}
/// Feed ~1 MiB of pseudorandom data to an updatable state.
pub fn feed_rand_16mib<D: crate::Update>(d: &mut D) {
let buf = &mut [0u8; 1024];
let mut rng = rng::RNG;
let n = 16 * (1 << 20) / buf.len();
for _ in 0..n {
rng.fill(buf);
d.update(buf);
// additional byte, so size of fed data
// will not be multiple of block size
d.update(&[42]);
}
}

View File

@@ -0,0 +1,65 @@
use crate::{Digest, FixedOutput, FixedOutputReset, HashMarker, Update};
use core::fmt::Debug;
/// Fixed-output resettable digest test via the `Digest` trait
pub fn fixed_reset_test<D>(input: &[u8], output: &[u8]) -> Option<&'static str>
where
D: FixedOutputReset + Debug + Clone + Default + Update + HashMarker,
{
let mut hasher = D::new();
// Test that it works when accepting the message all at once
hasher.update(input);
let mut hasher2 = hasher.clone();
if hasher.finalize()[..] != output[..] {
return Some("whole message");
}
// Test if reset works correctly
hasher2.reset();
hasher2.update(input);
if hasher2.finalize_reset()[..] != output[..] {
return Some("whole message after reset");
}
// Test that it works when accepting the message in chunks
for n in 1..core::cmp::min(17, input.len()) {
let mut hasher = D::new();
for chunk in input.chunks(n) {
hasher.update(chunk);
hasher2.update(chunk);
}
if hasher.finalize()[..] != output[..] {
return Some("message in chunks");
}
if hasher2.finalize_reset()[..] != output[..] {
return Some("message in chunks");
}
}
None
}
/// Variable-output resettable digest test
pub fn fixed_test<D>(input: &[u8], output: &[u8]) -> Option<&'static str>
where
D: FixedOutput + Default + Debug + Clone,
{
let mut hasher = D::default();
// Test that it works when accepting the message all at once
hasher.update(input);
if hasher.finalize_fixed()[..] != output[..] {
return Some("whole message");
}
// Test that it works when accepting the message in chunks
for n in 1..core::cmp::min(17, input.len()) {
let mut hasher = D::default();
for chunk in input.chunks(n) {
hasher.update(chunk);
}
if hasher.finalize_fixed()[..] != output[..] {
return Some("message in chunks");
}
}
None
}

View File

@@ -0,0 +1,159 @@
/// Define MAC test
#[macro_export]
#[cfg(feature = "mac")]
#[cfg_attr(docsrs, doc(cfg(all(feature = "dev", feature = "mac"))))]
macro_rules! new_mac_test {
($name:ident, $test_name:expr, $mac:ty $(,)?) => {
digest::new_mac_test!($name, $test_name, $mac, "");
};
($name:ident, $test_name:expr, $mac:ty, trunc_left $(,)?) => {
digest::new_mac_test!($name, $test_name, $mac, "left");
};
($name:ident, $test_name:expr, $mac:ty, trunc_right $(,)?) => {
digest::new_mac_test!($name, $test_name, $mac, "right");
};
($name:ident, $test_name:expr, $mac:ty, $trunc:expr $(,)?) => {
#[test]
fn $name() {
use core::cmp::min;
use digest::dev::blobby::Blob3Iterator;
use digest::Mac;
fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> {
let mac0 = <$mac as Mac>::new_from_slice(key).unwrap();
let mut mac = mac0.clone();
mac.update(input);
let result = mac.finalize().into_bytes();
let n = tag.len();
let result_bytes = match $trunc {
"left" => &result[..n],
"right" => &result[result.len() - n..],
_ => &result[..],
};
if result_bytes != tag {
return Some("whole message");
}
// test reading different chunk sizes
for chunk_size in 1..min(64, input.len()) {
let mut mac = mac0.clone();
for chunk in input.chunks(chunk_size) {
mac.update(chunk);
}
let res = match $trunc {
"left" => mac.verify_truncated_left(tag),
"right" => mac.verify_truncated_right(tag),
_ => mac.verify_slice(tag),
};
if res.is_err() {
return Some("chunked message");
}
}
None
}
let data = include_bytes!(concat!("data/", $test_name, ".blb"));
for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() {
let [key, input, tag] = row.unwrap();
if let Some(desc) = run_test(key, input, tag) {
panic!(
"\n\
Failed test №{}: {}\n\
key:\t{:?}\n\
input:\t{:?}\n\
tag:\t{:?}\n",
i, desc, key, input, tag,
);
}
}
}
};
}
/// Define resettable MAC test
#[macro_export]
#[cfg(feature = "mac")]
#[cfg_attr(docsrs, doc(cfg(all(feature = "dev", feature = "mac"))))]
macro_rules! new_resettable_mac_test {
($name:ident, $test_name:expr, $mac:ty $(,)?) => {
digest::new_resettable_mac_test!($name, $test_name, $mac, "");
};
($name:ident, $test_name:expr, $mac:ty, trunc_left $(,)?) => {
digest::new_resettable_mac_test!($name, $test_name, $mac, "left");
};
($name:ident, $test_name:expr, $mac:ty, trunc_right $(,)?) => {
digest::new_resettable_mac_test!($name, $test_name, $mac, "right");
};
($name:ident, $test_name:expr, $mac:ty, $trunc:expr $(,)?) => {
#[test]
fn $name() {
use core::cmp::min;
use digest::dev::blobby::Blob3Iterator;
use digest::Mac;
fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> {
let mac0 = <$mac as Mac>::new_from_slice(key).unwrap();
let mut mac = mac0.clone();
mac.update(input);
let result = mac.finalize_reset().into_bytes();
let n = tag.len();
let result_bytes = match $trunc {
"left" => &result[..n],
"right" => &result[result.len() - n..],
_ => &result[..],
};
if result_bytes != tag {
return Some("whole message");
}
// test if reset worked correctly
mac.update(input);
let res = match $trunc {
"left" => mac.verify_truncated_left(tag),
"right" => mac.verify_truncated_right(tag),
_ => mac.verify_slice(tag),
};
if res.is_err() {
return Some("after reset");
}
// test reading different chunk sizes
for chunk_size in 1..min(64, input.len()) {
let mut mac = mac0.clone();
for chunk in input.chunks(chunk_size) {
mac.update(chunk);
}
let res = match $trunc {
"left" => mac.verify_truncated_left(tag),
"right" => mac.verify_truncated_right(tag),
_ => mac.verify_slice(tag),
};
if res.is_err() {
return Some("chunked message");
}
}
None
}
let data = include_bytes!(concat!("data/", $test_name, ".blb"));
for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() {
let [key, input, tag] = row.unwrap();
if let Some(desc) = run_test(key, input, tag) {
panic!(
"\n\
Failed test №{}: {}\n\
key:\t{:?}\n\
input:\t{:?}\n\
tag:\t{:?}\n",
i, desc, key, input, tag,
);
}
}
}
};
}

View File

@@ -0,0 +1,38 @@
//! Xorshift RNG used for tests. Based on the `rand_xorshift` crate.
use core::num::Wrapping;
/// Initial RNG state used in tests.
// chosen by fair dice roll. guaranteed to be random.
pub(crate) const RNG: XorShiftRng = XorShiftRng {
x: Wrapping(0x0787_3B4A),
y: Wrapping(0xFAAB_8FFE),
z: Wrapping(0x1745_980F),
w: Wrapping(0xB0AD_B4F3),
};
/// Xorshift RNG instance/
pub(crate) struct XorShiftRng {
x: Wrapping<u32>,
y: Wrapping<u32>,
z: Wrapping<u32>,
w: Wrapping<u32>,
}
impl XorShiftRng {
pub(crate) fn fill(&mut self, buf: &mut [u8; 1024]) {
for chunk in buf.chunks_exact_mut(4) {
chunk.copy_from_slice(&self.next_u32().to_le_bytes());
}
}
fn next_u32(&mut self) -> u32 {
let x = self.x;
let t = x ^ (x << 11);
self.x = self.y;
self.y = self.z;
self.z = self.w;
let w = self.w;
self.w = w ^ (w >> 19) ^ (t ^ (t >> 8));
self.w.0
}
}

View File

@@ -0,0 +1,82 @@
use crate::{VariableOutput, VariableOutputReset};
use core::fmt::Debug;
/// Variable-output resettable digest test
pub fn variable_reset_test<D>(input: &[u8], output: &[u8]) -> Option<&'static str>
where
D: VariableOutputReset + Debug + Clone,
{
let mut hasher = D::new(output.len()).unwrap();
let mut buf = [0u8; 128];
let buf = &mut buf[..output.len()];
// Test that it works when accepting the message all at once
hasher.update(input);
let mut hasher2 = hasher.clone();
hasher.finalize_variable(buf).unwrap();
if buf != output {
return Some("whole message");
}
buf.iter_mut().for_each(|b| *b = 0);
// Test if reset works correctly
hasher2.reset();
hasher2.update(input);
hasher2.finalize_variable_reset(buf).unwrap();
if buf != output {
return Some("whole message after reset");
}
buf.iter_mut().for_each(|b| *b = 0);
// Test that it works when accepting the message in chunks
for n in 1..core::cmp::min(17, input.len()) {
let mut hasher = D::new(output.len()).unwrap();
for chunk in input.chunks(n) {
hasher.update(chunk);
hasher2.update(chunk);
}
hasher.finalize_variable(buf).unwrap();
if buf != output {
return Some("message in chunks");
}
buf.iter_mut().for_each(|b| *b = 0);
hasher2.finalize_variable_reset(buf).unwrap();
if buf != output {
return Some("message in chunks");
}
buf.iter_mut().for_each(|b| *b = 0);
}
None
}
/// Variable-output resettable digest test
pub fn variable_test<D>(input: &[u8], output: &[u8]) -> Option<&'static str>
where
D: VariableOutput + Debug + Clone,
{
let mut hasher = D::new(output.len()).unwrap();
let mut buf = [0u8; 128];
let buf = &mut buf[..output.len()];
// Test that it works when accepting the message all at once
hasher.update(input);
hasher.finalize_variable(buf).unwrap();
if buf != output {
return Some("whole message");
}
buf.iter_mut().for_each(|b| *b = 0);
// Test that it works when accepting the message in chunks
for n in 1..core::cmp::min(17, input.len()) {
let mut hasher = D::new(output.len()).unwrap();
for chunk in input.chunks(n) {
hasher.update(chunk);
}
hasher.finalize_variable(buf).unwrap();
if buf != output {
return Some("message in chunks");
}
buf.iter_mut().for_each(|b| *b = 0);
}
None
}

View File

@@ -0,0 +1,51 @@
use crate::ExtendableOutputReset;
use core::fmt::Debug;
/// Resettable XOF test
pub fn xof_reset_test<D>(input: &[u8], output: &[u8]) -> Option<&'static str>
where
D: ExtendableOutputReset + Default + Debug + Clone,
{
let mut hasher = D::default();
let mut buf = [0u8; 1024];
let buf = &mut buf[..output.len()];
// Test that it works when accepting the message all at once
hasher.update(input);
let mut hasher2 = hasher.clone();
hasher.finalize_xof_into(buf);
if buf != output {
return Some("whole message");
}
buf.iter_mut().for_each(|b| *b = 0);
// Test if reset works correctly
hasher2.reset();
hasher2.update(input);
hasher2.finalize_xof_reset_into(buf);
if buf != output {
return Some("whole message after reset");
}
buf.iter_mut().for_each(|b| *b = 0);
// Test that it works when accepting the message in chunks
for n in 1..core::cmp::min(17, input.len()) {
let mut hasher = D::default();
for chunk in input.chunks(n) {
hasher.update(chunk);
hasher2.update(chunk);
}
hasher.finalize_xof_into(buf);
if buf != output {
return Some("message in chunks");
}
buf.iter_mut().for_each(|b| *b = 0);
hasher2.finalize_xof_reset_into(buf);
if buf != output {
return Some("message in chunks");
}
buf.iter_mut().for_each(|b| *b = 0);
}
None
}

View File

@@ -0,0 +1,236 @@
use super::{FixedOutput, FixedOutputReset, InvalidBufferSize, Reset, Update};
use crypto_common::{typenum::Unsigned, Output, OutputSizeUser};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
/// Marker trait for cryptographic hash functions.
pub trait HashMarker {}
/// Convenience wrapper trait covering functionality of cryptographic hash
/// functions with fixed output size.
///
/// This trait wraps [`Update`], [`FixedOutput`], [`Default`], and
/// [`HashMarker`] traits and provides additional convenience methods.
pub trait Digest: OutputSizeUser {
/// Create new hasher instance.
fn new() -> Self;
/// Create new hasher instance which has processed the provided data.
fn new_with_prefix(data: impl AsRef<[u8]>) -> Self;
/// Process data, updating the internal state.
fn update(&mut self, data: impl AsRef<[u8]>);
/// Process input data in a chained manner.
#[must_use]
fn chain_update(self, data: impl AsRef<[u8]>) -> Self;
/// Retrieve result and consume hasher instance.
fn finalize(self) -> Output<Self>;
/// Write result into provided array and consume the hasher instance.
fn finalize_into(self, out: &mut Output<Self>);
/// Retrieve result and reset hasher instance.
fn finalize_reset(&mut self) -> Output<Self>
where
Self: FixedOutputReset;
/// Write result into provided array and reset the hasher instance.
fn finalize_into_reset(&mut self, out: &mut Output<Self>)
where
Self: FixedOutputReset;
/// Reset hasher instance to its initial state.
fn reset(&mut self)
where
Self: Reset;
/// Get output size of the hasher
fn output_size() -> usize;
/// Compute hash of `data`.
fn digest(data: impl AsRef<[u8]>) -> Output<Self>;
}
impl<D: FixedOutput + Default + Update + HashMarker> Digest for D {
#[inline]
fn new() -> Self {
Self::default()
}
#[inline]
fn new_with_prefix(data: impl AsRef<[u8]>) -> Self
where
Self: Default + Sized,
{
let mut h = Self::default();
h.update(data.as_ref());
h
}
#[inline]
fn update(&mut self, data: impl AsRef<[u8]>) {
Update::update(self, data.as_ref());
}
#[inline]
fn chain_update(mut self, data: impl AsRef<[u8]>) -> Self {
Update::update(&mut self, data.as_ref());
self
}
#[inline]
fn finalize(self) -> Output<Self> {
FixedOutput::finalize_fixed(self)
}
#[inline]
fn finalize_into(self, out: &mut Output<Self>) {
FixedOutput::finalize_into(self, out);
}
#[inline]
fn finalize_reset(&mut self) -> Output<Self>
where
Self: FixedOutputReset,
{
FixedOutputReset::finalize_fixed_reset(self)
}
#[inline]
fn finalize_into_reset(&mut self, out: &mut Output<Self>)
where
Self: FixedOutputReset,
{
FixedOutputReset::finalize_into_reset(self, out);
}
#[inline]
fn reset(&mut self)
where
Self: Reset,
{
Reset::reset(self)
}
#[inline]
fn output_size() -> usize {
Self::OutputSize::to_usize()
}
#[inline]
fn digest(data: impl AsRef<[u8]>) -> Output<Self> {
let mut hasher = Self::default();
hasher.update(data.as_ref());
hasher.finalize()
}
}
/// Modification of the [`Digest`] trait suitable for trait objects.
pub trait DynDigest {
/// Digest input data.
///
/// This method can be called repeatedly for use with streaming messages.
fn update(&mut self, data: &[u8]);
/// Retrieve result and reset hasher instance
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn finalize_reset(&mut self) -> Box<[u8]> {
let mut result = vec![0; self.output_size()];
self.finalize_into_reset(&mut result).unwrap();
result.into_boxed_slice()
}
/// Retrieve result and consume boxed hasher instance
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[allow(clippy::boxed_local)]
fn finalize(mut self: Box<Self>) -> Box<[u8]> {
let mut result = vec![0; self.output_size()];
self.finalize_into_reset(&mut result).unwrap();
result.into_boxed_slice()
}
/// Write result into provided array and consume the hasher instance.
///
/// Returns error if buffer length is not equal to `output_size`.
fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize>;
/// Write result into provided array and reset the hasher instance.
///
/// Returns error if buffer length is not equal to `output_size`.
fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize>;
/// Reset hasher instance to its initial state.
fn reset(&mut self);
/// Get output size of the hasher
fn output_size(&self) -> usize;
/// Clone hasher state into a boxed trait object
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn box_clone(&self) -> Box<dyn DynDigest>;
}
impl<D: Update + FixedOutputReset + Reset + Clone + 'static> DynDigest for D {
fn update(&mut self, data: &[u8]) {
Update::update(self, data);
}
#[cfg(feature = "alloc")]
fn finalize_reset(&mut self) -> Box<[u8]> {
FixedOutputReset::finalize_fixed_reset(self)
.to_vec()
.into_boxed_slice()
}
#[cfg(feature = "alloc")]
fn finalize(self: Box<Self>) -> Box<[u8]> {
FixedOutput::finalize_fixed(*self)
.to_vec()
.into_boxed_slice()
}
fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> {
if buf.len() == self.output_size() {
FixedOutput::finalize_into(self, Output::<Self>::from_mut_slice(buf));
Ok(())
} else {
Err(InvalidBufferSize)
}
}
fn finalize_into_reset(&mut self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> {
if buf.len() == self.output_size() {
FixedOutputReset::finalize_into_reset(self, Output::<Self>::from_mut_slice(buf));
Ok(())
} else {
Err(InvalidBufferSize)
}
}
fn reset(&mut self) {
Reset::reset(self);
}
fn output_size(&self) -> usize {
<Self as OutputSizeUser>::OutputSize::to_usize()
}
#[cfg(feature = "alloc")]
fn box_clone(&self) -> Box<dyn DynDigest> {
Box::new(self.clone())
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl Clone for Box<dyn DynDigest> {
fn clone(&self) -> Self {
self.box_clone()
}
}

View File

@@ -0,0 +1,301 @@
//! This crate provides traits which describe functionality of cryptographic hash
//! functions and Message Authentication algorithms.
//!
//! Traits in this repository are organized into the following levels:
//!
//! - **High-level convenience traits**: [`Digest`], [`DynDigest`], [`Mac`].
//! Wrappers around lower-level traits for most common use-cases. Users should
//! usually prefer using these traits.
//! - **Mid-level traits**: [`Update`], [`FixedOutput`], [`FixedOutputReset`],
//! [`ExtendableOutput`], [`ExtendableOutputReset`], [`XofReader`],
//! [`VariableOutput`], [`Reset`], [`KeyInit`], and [`InnerInit`]. These
//! traits atomically describe available functionality of an algorithm.
//! - **Marker traits**: [`HashMarker`], [`MacMarker`]. Used to distinguish
//! different algorithm classes.
//! - **Low-level traits** defined in the [`core_api`] module. These traits
//! operate at a block-level and do not contain any built-in buffering.
//! They are intended to be implemented by low-level algorithm providers only.
//! Usually they should not be used in application-level code.
//!
//! Additionally hash functions implement traits from the standard library:
//! [`Default`], [`Clone`], [`Write`][std::io::Write]. The latter is
//! feature-gated behind `std` feature, which is usually enabled by default
//! by hash implementation crates.
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![warn(missing_docs, rust_2018_idioms)]
#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "rand_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
pub use crypto_common::rand_core;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "dev")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
pub mod dev;
#[cfg(feature = "core-api")]
#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))]
pub mod core_api;
mod digest;
#[cfg(feature = "mac")]
mod mac;
#[cfg(feature = "core-api")]
#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))]
pub use block_buffer;
#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
pub use const_oid;
pub use crypto_common;
pub use crate::digest::{Digest, DynDigest, HashMarker};
pub use crypto_common::{generic_array, typenum, typenum::consts, Output, OutputSizeUser, Reset};
#[cfg(feature = "mac")]
pub use crypto_common::{InnerInit, InvalidLength, Key, KeyInit};
#[cfg(feature = "mac")]
pub use mac::{CtOutput, Mac, MacError, MacMarker};
use core::fmt;
/// Types which consume data with byte granularity.
pub trait Update {
/// Update state using the provided data.
fn update(&mut self, data: &[u8]);
/// Digest input data in a chained manner.
#[must_use]
fn chain(mut self, data: impl AsRef<[u8]>) -> Self
where
Self: Sized,
{
self.update(data.as_ref());
self
}
}
/// Trait for hash functions with fixed-size output.
pub trait FixedOutput: Update + OutputSizeUser + Sized {
/// Consume value and write result into provided array.
fn finalize_into(self, out: &mut Output<Self>);
/// Retrieve result and consume the hasher instance.
#[inline]
fn finalize_fixed(self) -> Output<Self> {
let mut out = Default::default();
self.finalize_into(&mut out);
out
}
}
/// Trait for hash functions with fixed-size output able to reset themselves.
pub trait FixedOutputReset: FixedOutput + Reset {
/// Write result into provided array and reset the hasher state.
fn finalize_into_reset(&mut self, out: &mut Output<Self>);
/// Retrieve result and reset the hasher state.
#[inline]
fn finalize_fixed_reset(&mut self) -> Output<Self> {
let mut out = Default::default();
self.finalize_into_reset(&mut out);
out
}
}
/// Trait for reader types which are used to extract extendable output
/// from a XOF (extendable-output function) result.
pub trait XofReader {
/// Read output into the `buffer`. Can be called an unlimited number of times.
fn read(&mut self, buffer: &mut [u8]);
/// Read output into a boxed slice of the specified size.
///
/// Can be called an unlimited number of times in combination with `read`.
///
/// `Box<[u8]>` is used instead of `Vec<u8>` to save stack space, since
/// they have size of 2 and 3 words respectively.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn read_boxed(&mut self, n: usize) -> Box<[u8]> {
let mut buf = vec![0u8; n].into_boxed_slice();
self.read(&mut buf);
buf
}
}
/// Trait for hash functions with extendable-output (XOF).
pub trait ExtendableOutput: Sized + Update {
/// Reader
type Reader: XofReader;
/// Retrieve XOF reader and consume hasher instance.
fn finalize_xof(self) -> Self::Reader;
/// Finalize XOF and write result into `out`.
fn finalize_xof_into(self, out: &mut [u8]) {
self.finalize_xof().read(out);
}
/// Compute hash of `data` and write it into `output`.
fn digest_xof(input: impl AsRef<[u8]>, output: &mut [u8])
where
Self: Default,
{
let mut hasher = Self::default();
hasher.update(input.as_ref());
hasher.finalize_xof().read(output);
}
/// Retrieve result into a boxed slice of the specified size and consume
/// the hasher.
///
/// `Box<[u8]>` is used instead of `Vec<u8>` to save stack space, since
/// they have size of 2 and 3 words respectively.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn finalize_boxed(self, output_size: usize) -> Box<[u8]> {
let mut buf = vec![0u8; output_size].into_boxed_slice();
self.finalize_xof().read(&mut buf);
buf
}
}
/// Trait for hash functions with extendable-output (XOF) able to reset themselves.
pub trait ExtendableOutputReset: ExtendableOutput + Reset {
/// Retrieve XOF reader and reset hasher instance state.
fn finalize_xof_reset(&mut self) -> Self::Reader;
/// Finalize XOF, write result into `out`, and reset the hasher state.
fn finalize_xof_reset_into(&mut self, out: &mut [u8]) {
self.finalize_xof_reset().read(out);
}
/// Retrieve result into a boxed slice of the specified size and reset
/// the hasher state.
///
/// `Box<[u8]>` is used instead of `Vec<u8>` to save stack space, since
/// they have size of 2 and 3 words respectively.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn finalize_boxed_reset(&mut self, output_size: usize) -> Box<[u8]> {
let mut buf = vec![0u8; output_size].into_boxed_slice();
self.finalize_xof_reset().read(&mut buf);
buf
}
}
/// Trait for hash functions with variable-size output.
pub trait VariableOutput: Sized + Update {
/// Maximum size of output hash.
const MAX_OUTPUT_SIZE: usize;
/// Create new hasher instance with the given output size.
///
/// It will return `Err(InvalidOutputSize)` in case if hasher can not return
/// hash of the specified output size.
fn new(output_size: usize) -> Result<Self, InvalidOutputSize>;
/// Get output size of the hasher instance provided to the `new` method
fn output_size(&self) -> usize;
/// Write result into the output buffer.
///
/// Returns `Err(InvalidOutputSize)` if `out` size is not equal to
/// `self.output_size()`.
fn finalize_variable(self, out: &mut [u8]) -> Result<(), InvalidBufferSize>;
/// Compute hash of `data` and write it to `output`.
///
/// Length of the output hash is determined by `output`. If `output` is
/// bigger than `Self::MAX_OUTPUT_SIZE`, this method returns
/// `InvalidOutputSize`.
fn digest_variable(
input: impl AsRef<[u8]>,
output: &mut [u8],
) -> Result<(), InvalidOutputSize> {
let mut hasher = Self::new(output.len())?;
hasher.update(input.as_ref());
hasher
.finalize_variable(output)
.map_err(|_| InvalidOutputSize)
}
/// Retrieve result into a boxed slice and consume hasher.
///
/// `Box<[u8]>` is used instead of `Vec<u8>` to save stack space, since
/// they have size of 2 and 3 words respectively.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn finalize_boxed(self) -> Box<[u8]> {
let n = self.output_size();
let mut buf = vec![0u8; n].into_boxed_slice();
self.finalize_variable(&mut buf)
.expect("buf length is equal to output_size");
buf
}
}
/// Trait for hash functions with variable-size output able to reset themselves.
pub trait VariableOutputReset: VariableOutput + Reset {
/// Write result into the output buffer and reset the hasher state.
///
/// Returns `Err(InvalidOutputSize)` if `out` size is not equal to
/// `self.output_size()`.
fn finalize_variable_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize>;
/// Retrieve result into a boxed slice and reset the hasher state.
///
/// `Box<[u8]>` is used instead of `Vec<u8>` to save stack space, since
/// they have size of 2 and 3 words respectively.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn finalize_boxed_reset(&mut self) -> Box<[u8]> {
let n = self.output_size();
let mut buf = vec![0u8; n].into_boxed_slice();
self.finalize_variable_reset(&mut buf)
.expect("buf length is equal to output_size");
buf
}
}
/// The error type used in variable hash traits.
#[derive(Clone, Copy, Debug, Default)]
pub struct InvalidOutputSize;
impl fmt::Display for InvalidOutputSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid output size")
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for InvalidOutputSize {}
/// Buffer length is not equal to hash output size.
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidBufferSize;
impl fmt::Display for InvalidBufferSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid buffer length")
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidBufferSize {}

View File

@@ -0,0 +1,305 @@
use crate::{FixedOutput, FixedOutputReset, Update};
use crypto_common::{InvalidLength, Key, KeyInit, Output, OutputSizeUser, Reset};
#[cfg(feature = "rand_core")]
use crate::rand_core::{CryptoRng, RngCore};
use core::fmt;
use crypto_common::typenum::Unsigned;
use subtle::{Choice, ConstantTimeEq};
/// Marker trait for Message Authentication algorithms.
#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
pub trait MacMarker {}
/// Convenience wrapper trait covering functionality of Message Authentication algorithms.
///
/// This trait wraps [`KeyInit`], [`Update`], [`FixedOutput`], and [`MacMarker`]
/// traits and provides additional convenience methods.
#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
pub trait Mac: OutputSizeUser + Sized {
/// Create new value from fixed size key.
fn new(key: &Key<Self>) -> Self
where
Self: KeyInit;
/// Generate random key using the provided [`CryptoRng`].
#[cfg(feature = "rand_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
fn generate_key(rng: impl CryptoRng + RngCore) -> Key<Self>
where
Self: KeyInit;
/// Create new value from variable size key.
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength>
where
Self: KeyInit;
/// Update state using the provided data.
fn update(&mut self, data: &[u8]);
/// Process input data in a chained manner.
#[must_use]
fn chain_update(self, data: impl AsRef<[u8]>) -> Self;
/// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and consume
/// [`Mac`] instance.
fn finalize(self) -> CtOutput<Self>;
/// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and reset
/// [`Mac`] instance.
fn finalize_reset(&mut self) -> CtOutput<Self>
where
Self: FixedOutputReset;
/// Reset MAC instance to its initial state.
fn reset(&mut self)
where
Self: Reset;
/// Check if tag/code value is correct for the processed input.
fn verify(self, tag: &Output<Self>) -> Result<(), MacError>;
/// Check if tag/code value is correct for the processed input and reset
/// [`Mac`] instance.
fn verify_reset(&mut self, tag: &Output<Self>) -> Result<(), MacError>
where
Self: FixedOutputReset;
/// Check truncated tag correctness using all bytes
/// of calculated tag.
///
/// Returns `Error` if `tag` is not valid or not equal in length
/// to MAC's output.
fn verify_slice(self, tag: &[u8]) -> Result<(), MacError>;
/// Check truncated tag correctness using all bytes
/// of calculated tag and reset [`Mac`] instance.
///
/// Returns `Error` if `tag` is not valid or not equal in length
/// to MAC's output.
fn verify_slice_reset(&mut self, tag: &[u8]) -> Result<(), MacError>
where
Self: FixedOutputReset;
/// Check truncated tag correctness using left side bytes
/// (i.e. `tag[..n]`) of calculated tag.
///
/// Returns `Error` if `tag` is not valid or empty.
fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError>;
/// Check truncated tag correctness using right side bytes
/// (i.e. `tag[n..]`) of calculated tag.
///
/// Returns `Error` if `tag` is not valid or empty.
fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError>;
}
impl<T: Update + FixedOutput + MacMarker> Mac for T {
#[inline(always)]
fn new(key: &Key<Self>) -> Self
where
Self: KeyInit,
{
KeyInit::new(key)
}
#[inline(always)]
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength>
where
Self: KeyInit,
{
KeyInit::new_from_slice(key)
}
#[inline]
fn update(&mut self, data: &[u8]) {
Update::update(self, data);
}
#[inline]
fn chain_update(mut self, data: impl AsRef<[u8]>) -> Self {
Update::update(&mut self, data.as_ref());
self
}
#[inline]
fn finalize(self) -> CtOutput<Self> {
CtOutput::new(self.finalize_fixed())
}
#[inline(always)]
fn finalize_reset(&mut self) -> CtOutput<Self>
where
Self: FixedOutputReset,
{
CtOutput::new(self.finalize_fixed_reset())
}
#[inline]
fn reset(&mut self)
where
Self: Reset,
{
Reset::reset(self)
}
#[inline]
fn verify(self, tag: &Output<Self>) -> Result<(), MacError> {
if self.finalize() == tag.into() {
Ok(())
} else {
Err(MacError)
}
}
#[inline]
fn verify_reset(&mut self, tag: &Output<Self>) -> Result<(), MacError>
where
Self: FixedOutputReset,
{
if self.finalize_reset() == tag.into() {
Ok(())
} else {
Err(MacError)
}
}
#[inline]
fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
let n = tag.len();
if n != Self::OutputSize::USIZE {
return Err(MacError);
}
let choice = self.finalize_fixed().ct_eq(tag);
if choice.into() {
Ok(())
} else {
Err(MacError)
}
}
#[inline]
fn verify_slice_reset(&mut self, tag: &[u8]) -> Result<(), MacError>
where
Self: FixedOutputReset,
{
let n = tag.len();
if n != Self::OutputSize::USIZE {
return Err(MacError);
}
let choice = self.finalize_fixed_reset().ct_eq(tag);
if choice.into() {
Ok(())
} else {
Err(MacError)
}
}
fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
let n = tag.len();
if n == 0 || n > Self::OutputSize::USIZE {
return Err(MacError);
}
let choice = self.finalize_fixed()[..n].ct_eq(tag);
if choice.into() {
Ok(())
} else {
Err(MacError)
}
}
fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError> {
let n = tag.len();
if n == 0 || n > Self::OutputSize::USIZE {
return Err(MacError);
}
let m = Self::OutputSize::USIZE - n;
let choice = self.finalize_fixed()[m..].ct_eq(tag);
if choice.into() {
Ok(())
} else {
Err(MacError)
}
}
#[cfg(feature = "rand_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
#[inline]
fn generate_key(rng: impl CryptoRng + RngCore) -> Key<Self>
where
Self: KeyInit,
{
<T as KeyInit>::generate_key(rng)
}
}
/// Fixed size output value which provides a safe [`Eq`] implementation that
/// runs in constant time.
///
/// It is useful for implementing Message Authentication Codes (MACs).
#[derive(Clone)]
#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
pub struct CtOutput<T: OutputSizeUser> {
bytes: Output<T>,
}
impl<T: OutputSizeUser> CtOutput<T> {
/// Create a new [`CtOutput`] value.
#[inline(always)]
pub fn new(bytes: Output<T>) -> Self {
Self { bytes }
}
/// Get the inner [`Output`] array this type wraps.
#[inline(always)]
pub fn into_bytes(self) -> Output<T> {
self.bytes
}
}
impl<T: OutputSizeUser> From<Output<T>> for CtOutput<T> {
#[inline(always)]
fn from(bytes: Output<T>) -> Self {
Self { bytes }
}
}
impl<'a, T: OutputSizeUser> From<&'a Output<T>> for CtOutput<T> {
#[inline(always)]
fn from(bytes: &'a Output<T>) -> Self {
bytes.clone().into()
}
}
impl<T: OutputSizeUser> ConstantTimeEq for CtOutput<T> {
#[inline(always)]
fn ct_eq(&self, other: &Self) -> Choice {
self.bytes.ct_eq(&other.bytes)
}
}
impl<T: OutputSizeUser> PartialEq for CtOutput<T> {
#[inline(always)]
fn eq(&self, x: &CtOutput<T>) -> bool {
self.ct_eq(x).into()
}
}
impl<T: OutputSizeUser> Eq for CtOutput<T> {}
/// Error type for when the [`Output`] of a [`Mac`]
/// is not equal to the expected value.
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(docsrs, doc(cfg(feature = "mac")))]
pub struct MacError;
impl fmt::Display for MacError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("MAC tag mismatch")
}
}
#[cfg(feature = "std")]
impl std::error::Error for MacError {}