update crates to digest v0.10 (#217)

This commit is contained in:
Artyom Pavlov
2021-12-07 19:52:35 +00:00
committed by GitHub
parent d6c8c3c1b0
commit 75298d36b9
305 changed files with 7832 additions and 10014 deletions

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

View File

@@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
rust:
- 1.47.0 # MSRV
- 1.41.0 # MSRV
- stable
target:
- thumbv7em-none-eabi
@@ -35,14 +35,14 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.47.0 # MSRV
- 1.41.0 # MSRV
- stable
steps:
- uses: actions/checkout@v1
@@ -54,5 +54,4 @@ jobs:
- run: cargo check --all-features
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --features asm
- run: cargo test --all-features

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -38,7 +38,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

View File

@@ -1,16 +1,16 @@
name: ripemd320
name: ripemd
on:
pull_request:
paths:
- "ripemd320/**"
- "ripemd/**"
- "Cargo.*"
push:
branches: master
defaults:
run:
working-directory: ripemd320
working-directory: ripemd
env:
CARGO_INCREMENTAL: 0
@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

View File

@@ -1,58 +0,0 @@
name: ripemd160
on:
pull_request:
paths:
- "ripemd160/**"
- "Cargo.*"
push:
branches: master
defaults:
run:
working-directory: ripemd160
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-Dwarnings"
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.41.0 # MSRV
- stable
target:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.41.0 # MSRV
- stable
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- run: cargo check --all-features
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -1,58 +0,0 @@
name: ripemd256
on:
pull_request:
paths:
- "ripemd256/**"
- "Cargo.*"
push:
branches: master
defaults:
run:
working-directory: ripemd256
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-Dwarnings"
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.41.0 # MSRV
- stable
target:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.41.0 # MSRV
- stable
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- run: cargo check --all-features
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -36,7 +36,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
# Linux tests
linux:
@@ -69,10 +69,10 @@ jobs:
target: ${{ matrix.target }}
override: true
- run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }} --release --no-default-features
- run: cargo test --target ${{ matrix.target }} --release
- run: cargo test --target ${{ matrix.target }} --release --features asm
- run: cargo test --target ${{ matrix.target }} --release --all-features
- run: cargo test --target ${{ matrix.target }} --no-default-features
- run: cargo test --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }} --features asm
- run: cargo test --target ${{ matrix.target }} --all-features
# macOS tests
macos:
@@ -91,10 +91,10 @@ jobs:
toolchain: ${{ matrix.toolchain }}
target: x86_64-apple-darwin
override: true
- run: cargo test --release --no-default-features
- run: cargo test --release
- run: cargo test --release --features asm
- run: cargo test --release --all-features
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --features asm
- run: cargo test --all-features
# Windows tests
windows:
@@ -118,7 +118,7 @@ jobs:
target: ${{ matrix.target }}
override: true
- uses: msys2/setup-msys2@v2
- run: cargo test --target ${{ matrix.target }} --release
- run: cargo test --target ${{ matrix.target }}
# Cross-compiled tests
# *** NOTE: Currently broken with `asm` feature enabled! See:
@@ -144,5 +144,5 @@ jobs:
# target: ${{ matrix.target }}
# override: true
# - run: cargo install cross
# - run: cross test --target ${{ matrix.target }} --release
# - run: cross test --target ${{ matrix.target }} --release --features asm
# - run: cross test --target ${{ matrix.target }}
# - run: cross test --target ${{ matrix.target }} --features asm

View File

@@ -36,7 +36,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
# Linux tests
linux:
@@ -67,10 +67,10 @@ jobs:
target: ${{ matrix.target }}
override: true
- run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }} --release --no-default-features
- run: cargo test --target ${{ matrix.target }} --release
- run: cargo test --target ${{ matrix.target }} --release --features asm
- run: cargo test --target ${{ matrix.target }} --release --all-features
- run: cargo test --target ${{ matrix.target }} --no-default-features
- run: cargo test --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }} --features asm
- run: cargo test --target ${{ matrix.target }} --all-features
# macOS tests
macos:
@@ -89,9 +89,9 @@ jobs:
toolchain: ${{ matrix.toolchain }}
target: x86_64-apple-darwin
override: true
- run: cargo test --release --no-default-features
- run: cargo test --release
- run: cargo test --release --features asm
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --features asm
# Windows tests
windows:
@@ -115,7 +115,7 @@ jobs:
target: ${{ matrix.target }}
override: true
- uses: msys2/setup-msys2@v2
- run: cargo test --target ${{ matrix.target }} --release
- run: cargo test --target ${{ matrix.target }}
# Cross-compiled tests
cross:
@@ -129,6 +129,10 @@ jobs:
rust: stable
runs-on: ubuntu-latest
defaults:
run:
# Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml
working-directory: .
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
@@ -137,6 +141,11 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo install cross
- run: cross test --target ${{ matrix.target }} --release
- run: cross test --target ${{ matrix.target }} --release --features asm
- name: Install precompiled cross
run: |
export URL=$(curl -s https://api.github.com/repos/rust-embedded/cross/releases/latest | jq -r '.assets[] | select(.name | contains("x86_64-unknown-linux-gnu.tar.gz")) | .browser_download_url')
wget -O /tmp/binaries.tar.gz $URL
tar -C /tmp -xzf /tmp/binaries.tar.gz
mv /tmp/cross ~/.cargo/bin
- run: cross test --package sha2 --target ${{ matrix.target }}
- run: cross test --package sha2 --target ${{ matrix.target }} --features asm

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features

View File

@@ -38,7 +38,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

View File

@@ -35,7 +35,7 @@ jobs:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- run: cargo build --no-default-features --release --target ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}
test:
runs-on: ubuntu-latest

163
Cargo.lock generated
View File

@@ -4,50 +4,32 @@ version = 3
[[package]]
name = "blake2"
version = "0.9.2"
version = "0.10.0"
dependencies = [
"crypto-mac",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "blobby"
version = "0.1.2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe5f8c2940b65859ece4b3b2ba02d2b12c87cab455fd42dee2556a187bb2cf6"
dependencies = [
"byteorder",
]
checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec"
[[package]]
name = "block-buffer"
version = "0.9.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
dependencies = [
"block-padding",
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.69"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
@@ -65,34 +47,33 @@ dependencies = [
]
[[package]]
name = "crypto-mac"
version = "0.8.0"
name = "crypto-common"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
checksum = "567569e659735adb39ff2d4c20600f7cd78be5471f8c58ab162bce3c03fdbc5f"
dependencies = [
"generic-array",
]
[[package]]
name = "digest"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8549e6bfdecd113b7e221fe60b433087f6957387a20f8118ebca9b12af19143d"
dependencies = [
"blobby",
"block-buffer",
"crypto-common",
"generic-array",
"subtle",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"blobby",
"generic-array",
]
[[package]]
name = "fsb"
version = "0.0.2"
version = "0.1.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
"whirlpool",
]
@@ -108,22 +89,18 @@ dependencies = [
[[package]]
name = "gost94"
version = "0.9.1"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "groestl"
version = "0.9.0"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
@@ -147,7 +124,7 @@ dependencies = [
[[package]]
name = "k12"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"digest",
"hex-literal",
@@ -161,56 +138,44 @@ checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
[[package]]
name = "libc"
version = "0.2.101"
version = "0.2.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
[[package]]
name = "md-5"
version = "0.9.1"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"md5-asm",
"opaque-debug",
]
[[package]]
name = "md2"
version = "0.9.0"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "md4"
version = "0.9.0"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "md5-asm"
version = "0.4.3"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b47e5a1261ecd6ba951d1ad392534743310e513ef1e2609023368798829f33e"
checksum = "73b9a6f25ec11ea27e22d7fc8beafda909da44ece95f63e94f1eeb23d19bb5c7"
dependencies = [
"cc",
]
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
@@ -218,45 +183,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "ripemd160"
version = "0.9.1"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "ripemd256"
name = "ripemd"
version = "0.1.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "ripemd320"
version = "0.9.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "sha-1"
version = "0.9.8"
version = "0.10.0"
dependencies = [
"block-buffer",
"cfg-if",
"cpufeatures",
"digest",
"hex-literal",
"opaque-debug",
"sha1-asm",
]
@@ -271,14 +212,12 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.9.8"
version = "0.10.0"
dependencies = [
"block-buffer",
"cfg-if",
"cpufeatures",
"digest",
"hex-literal",
"opaque-debug",
"sha2-asm",
]
@@ -293,43 +232,35 @@ dependencies = [
[[package]]
name = "sha3"
version = "0.9.1"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"keccak",
"opaque-debug",
]
[[package]]
name = "shabal"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "sm3"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
name = "streebog"
version = "0.9.2"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
]
[[package]]
@@ -340,19 +271,17 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "tiger"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"block-buffer",
"byteorder",
"digest",
"hex-literal",
]
[[package]]
name = "typenum"
version = "1.13.0"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
[[package]]
name = "version_check"
@@ -362,20 +291,18 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "whirlpool"
version = "0.9.0"
version = "0.10.0"
dependencies = [
"block-buffer",
"digest",
"hex-literal",
"opaque-debug",
"whirlpool-asm",
]
[[package]]
name = "whirlpool-asm"
version = "0.5.2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbb23be8b0dec6fafef14563c42ec98bbfc33f2a97ed5bc99cd8b50ea5c68097"
checksum = "4b0930846e800a97c78fd09a494b25d1f0780be9face03b7a05151e3104a8284"
dependencies = [
"cc",
]

View File

@@ -1,16 +1,14 @@
[workspace]
members = [
"fsb",
"blake2",
"fsb",
"gost94",
"groestl",
"k12",
"md2",
"md4",
"md5",
"ripemd160",
"ripemd256",
"ripemd320",
"ripemd",
"sha1",
"sha2",
"sha3",

227
README.md
View File

@@ -1,99 +1,91 @@
# RustCrypto: hashes [![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link]
# RustCrypto: Hashes
[![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] ![Apache2/MIT licensed][license-image]
Collection of [cryptographic hash functions][1] written in pure Rust.
All algorithms reside in the separate crates and implemented using traits from
[`digest`](https://docs.rs/digest/) crate. Additionally all crates do not
require the standard library (i.e. `no_std` capable) and can be easily used for
bare-metal or WebAssembly programming.
All algorithms reside in the separate crates and implemented using traits from [`digest`] crate.
Additionally all crates do not require the standard library (i.e. `no_std` capable) and can be easily used for bare-metal or WebAssembly programming.
## Supported algorithms
**Note:** For new applications, or where compatibility with other existing
standards is not a primary concern, we strongly recommend to use either
BLAKE2, SHA-2 or SHA-3.
## Supported Algorithms
| Name | Algorithm | Crates.io | Documentation | Build Status | [Security] |
|-------------|------------|-----------|---------------|--------------|------------|
| `blake2` | [BLAKE2] | [![crates.io](https://img.shields.io/crates/v/blake2.svg)](https://crates.io/crates/blake2) | [![Documentation](https://docs.rs/blake2/badge.svg)](https://docs.rs/blake2) | [![build](https://github.com/rustcrypto/hashes/workflows/blake2/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:blake2+branch:master) | :green_heart: |
| `fsb` | [FSB] | [![crates.io](https://img.shields.io/crates/v/fsb.svg)](https://crates.io/crates/fsb) | [![Documentation](https://docs.rs/fsb/badge.svg)](https://docs.rs/fsb) | [![build](https://github.com/rustcrypto/hashes/workflows/fsb/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/fsb/actions?query=workflow:fsb+branch:master) | :green_heart: |
| `gost94` | [GOST94] (GOST R 34.11-94) | [![crates.io](https://img.shields.io/crates/v/gost94.svg)](https://crates.io/crates/gost94) | [![Documentation](https://docs.rs/gost94/badge.svg)](https://docs.rs/gost94) | [![build](https://github.com/rustcrypto/hashes/workflows/gost94/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:gost94+branch:master) | :yellow_heart: |
| `groestl` | [Grøstl] (Groestl) | [![crates.io](https://img.shields.io/crates/v/groestl.svg)](https://crates.io/crates/groestl) | [![Documentation](https://docs.rs/groestl/badge.svg)](https://docs.rs/groestl) | [![build](https://github.com/rustcrypto/hashes/workflows/groestl/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:groestl+branch:master) | :green_heart: |
| `k12` | [KangarooTwelve] | [![crates.io](https://img.shields.io/crates/v/k12.svg)](https://crates.io/crates/k12) | [![Documentation](https://docs.rs/k12/badge.svg)](https://docs.rs/k12) | [![build](https://github.com/rustcrypto/hashes/workflows/k12/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:k12+branch:master) | :green_heart: |
| `md2` | [MD2] | [![crates.io](https://img.shields.io/crates/v/md2.svg)](https://crates.io/crates/md2) | [![Documentation](https://docs.rs/md2/badge.svg)](https://docs.rs/md2) | [![build](https://github.com/rustcrypto/hashes/workflows/md2/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:md2+branch:master) | :broken_heart: |
| `md4` | [MD4] | [![crates.io](https://img.shields.io/crates/v/md4.svg)](https://crates.io/crates/md4) | [![Documentation](https://docs.rs/md4/badge.svg)](https://docs.rs/md4) | [![build](https://github.com/rustcrypto/hashes/workflows/md4/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:md4+branch:master) | :broken_heart: |
| `md-5` [:exclamation:] | [MD5] | [![crates.io](https://img.shields.io/crates/v/md-5.svg)](https://crates.io/crates/md-5) | [![Documentation](https://docs.rs/md-5/badge.svg)](https://docs.rs/md-5) | [![build](https://github.com/rustcrypto/hashes/workflows/md5/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:md5+branch:master) | :broken_heart: |
| `ripemd160` | [RIPEMD-160] | [![crates.io](https://img.shields.io/crates/v/ripemd160.svg)](https://crates.io/crates/ripemd160) | [![Documentation](https://docs.rs/ripemd160/badge.svg)](https://docs.rs/ripemd160) | [![build](https://github.com/rustcrypto/hashes/workflows/ripemd160/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:ripemd160+branch:master) | :green_heart: |
| `ripemd256` | [RIPEMD-256] | [![crates.io](https://img.shields.io/crates/v/ripemd256.svg)](https://crates.io/crates/ripemd256) | [![Documentation](https://docs.rs/ripemd256/badge.svg)](https://docs.rs/ripemd256) | [![build](https://github.com/rustcrypto/hashes/workflows/ripemd256/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:ripemd256+branch:master) | :green_heart:* |
| `ripemd320` | [RIPEMD-320] | [![crates.io](https://img.shields.io/crates/v/ripemd320.svg)](https://crates.io/crates/ripemd320) | [![Documentation](https://docs.rs/ripemd320/badge.svg)](https://docs.rs/ripemd320) | [![build](https://github.com/rustcrypto/hashes/workflows/ripemd320/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:ripemd320+branch:master) | :green_heart:* |
| `sha-1` [:exclamation:] | [SHA-1] | [![crates.io](https://img.shields.io/crates/v/sha-1.svg)](https://crates.io/crates/sha-1) | [![Documentation](https://docs.rs/sha-1/badge.svg)](https://docs.rs/sha-1) | [![build](https://github.com/rustcrypto/hashes/workflows/sha1/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sha1+branch:master) | :broken_heart: |
| `sha2` | [SHA-2] | [![crates.io](https://img.shields.io/crates/v/sha2.svg)](https://crates.io/crates/sha2) | [![Documentation](https://docs.rs/sha2/badge.svg)](https://docs.rs/sha2) | [![build](https://github.com/rustcrypto/hashes/workflows/sha2/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sha2+branch:master) | :green_heart: |
| `sha3` | [SHA-3] (Keccak) | [![crates.io](https://img.shields.io/crates/v/sha3.svg)](https://crates.io/crates/sha3) | [![Documentation](https://docs.rs/sha3/badge.svg)](https://docs.rs/sha3) | [![build](https://github.com/rustcrypto/hashes/workflows/sha3/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sha3+branch:master) | :green_heart: |
| `shabal` | [SHABAL] | [![crates.io](https://img.shields.io/crates/v/shabal.svg)](https://crates.io/crates/shabal) | [![Documentation](https://docs.rs/shabal/badge.svg)](https://docs.rs/shabal) | [![build](https://github.com/rustcrypto/hashes/workflows/shabal/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:shabal+branch:master) | :green_heart: |
| `sm3` | [SM3 (OSCCA GM/T 0004-2012)][SM3] | [![crates.io](https://img.shields.io/crates/v/sm3.svg)](https://crates.io/crates/sm3) | [![Documentation](https://docs.rs/sm3/badge.svg)](https://docs.rs/sm3) | [![build](https://github.com/rustcrypto/hashes/workflows/sm3/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sm3+branch:master) | :green_heart: |
| `streebog` | [Streebog] (GOST R 34.11-2012) | [![crates.io](https://img.shields.io/crates/v/streebog.svg)](https://crates.io/crates/streebog) | [![Documentation](https://docs.rs/streebog/badge.svg)](https://docs.rs/streebog) | [![build](https://github.com/rustcrypto/hashes/workflows/streebog/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:streebog+branch:master) | :yellow_heart: |
| `tiger` | [Tiger] | [![crates.io](https://img.shields.io/crates/v/tiger.svg)](https://crates.io/crates/tiger) | [![Documentation](https://docs.rs/tiger/badge.svg)](https://docs.rs/tiger) | [![build](https://github.com/rustcrypto/hashes/workflows/tiger/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:tiger+branch:master) | :green_heart: |
| `whirlpool` | [Whirlpool] | [![crates.io](https://img.shields.io/crates/v/whirlpool.svg)](https://crates.io/crates/whirlpool) | [![Documentation](https://docs.rs/whirlpool/badge.svg)](https://docs.rs/whirlpool) | [![build](https://github.com/rustcrypto/hashes/workflows/whirlpool/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:whirlpool+branch:master) | :green_heart: |
**Note:** For new applications, or where compatibility with other existing standards is not a primary concern, we strongly recommend to use either BLAKE2, SHA-2 or SHA-3.
NOTE: the [BLAKE3 crate](https://github.com/BLAKE3-team/BLAKE3) implements the `digest` (and `crypto-mac`) traits used by the rest of the hashes in this repository, but is maintained by the BLAKE3 team.
| Algorithm | Crate | Crates.io | Documentation | MSRV | [Security] |
|-----------|-------|:---------:|:-------------:|:----:|:----------:|
| [BLAKE2] | [`blake2`] | [![crates.io](https://img.shields.io/crates/v/blake2.svg)](https://crates.io/crates/blake2) | [![Documentation](https://docs.rs/blake2/badge.svg)](https://docs.rs/blake2) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [FSB] | [`fsb`] | [![crates.io](https://img.shields.io/crates/v/fsb.svg)](https://crates.io/crates/fsb) | [![Documentation](https://docs.rs/fsb/badge.svg)](https://docs.rs/fsb) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [GOST R 34.11-94][GOST94] | [`gost94`] | [![crates.io](https://img.shields.io/crates/v/gost94.svg)](https://crates.io/crates/gost94) | [![Documentation](https://docs.rs/gost94/badge.svg)](https://docs.rs/gost94) | ![MSRV 1.41][msrv-1.41] | :yellow_heart: |
| [Grøstl] (Groestl) | [`groestl`] | [![crates.io](https://img.shields.io/crates/v/groestl.svg)](https://crates.io/crates/groestl) | [![Documentation](https://docs.rs/groestl/badge.svg)](https://docs.rs/groestl) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [KangarooTwelve] | [`k12`] | [![crates.io](https://img.shields.io/crates/v/k12.svg)](https://crates.io/crates/k12) | [![Documentation](https://docs.rs/k12/badge.svg)](https://docs.rs/k12) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [MD2] | [`md2`] | [![crates.io](https://img.shields.io/crates/v/md2.svg)](https://crates.io/crates/md2) | [![Documentation](https://docs.rs/md2/badge.svg)](https://docs.rs/md2) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
| [MD4] | [`md4`] | [![crates.io](https://img.shields.io/crates/v/md4.svg)](https://crates.io/crates/md4) | [![Documentation](https://docs.rs/md4/badge.svg)](https://docs.rs/md4) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
| [MD5] | [`md-5`] [:exclamation:] | [![crates.io](https://img.shields.io/crates/v/md-5.svg)](https://crates.io/crates/md-5) | [![Documentation](https://docs.rs/md-5/badge.svg)](https://docs.rs/md-5) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
| [RIPEMD] | [`ripemd`] | [![crates.io](https://img.shields.io/crates/v/ripemd.svg)](https://crates.io/crates/ripemd) | [![Documentation](https://docs.rs/ripemd/badge.svg)](https://docs.rs/ripemd) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [SHA-1] | [`sha-1`] [:exclamation:] | [![crates.io](https://img.shields.io/crates/v/sha-1.svg)](https://crates.io/crates/sha-1) | [![Documentation](https://docs.rs/sha-1/badge.svg)](https://docs.rs/sha-1) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
| [SHA-2] | [`sha2`] | [![crates.io](https://img.shields.io/crates/v/sha2.svg)](https://crates.io/crates/sha2) | [![Documentation](https://docs.rs/sha2/badge.svg)](https://docs.rs/sha2) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [SHA-3] (Keccak) | [`sha3`] | [![crates.io](https://img.shields.io/crates/v/sha3.svg)](https://crates.io/crates/sha3) | [![Documentation](https://docs.rs/sha3/badge.svg)](https://docs.rs/sha3) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [SHABAL] | [`shabal`] | [![crates.io](https://img.shields.io/crates/v/shabal.svg)](https://crates.io/crates/shabal) | [![Documentation](https://docs.rs/shabal/badge.svg)](https://docs.rs/shabal) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [SM3] (OSCCA GM/T 0004-2012) | [`sm3`] | [![crates.io](https://img.shields.io/crates/v/sm3.svg)](https://crates.io/crates/sm3) | [![Documentation](https://docs.rs/sm3/badge.svg)](https://docs.rs/sm3) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [Streebog] (GOST R 34.11-2012) | [`streebog`] | [![crates.io](https://img.shields.io/crates/v/streebog.svg)](https://crates.io/crates/streebog) | [![Documentation](https://docs.rs/streebog/badge.svg)](https://docs.rs/streebog) | ![MSRV 1.41][msrv-1.41] | :yellow_heart: |
| [Tiger] | [`tiger`] | [![crates.io](https://img.shields.io/crates/v/tiger.svg)](https://crates.io/crates/tiger) | [![Documentation](https://docs.rs/tiger/badge.svg)](https://docs.rs/tiger) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
| [Whirlpool] | [`whirlpool`] | [![crates.io](https://img.shields.io/crates/v/whirlpool.svg)](https://crates.io/crates/whirlpool) | [![Documentation](https://docs.rs/whirlpool/badge.svg)](https://docs.rs/whirlpool) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
\* RIPEMD-256 provides only the same security as RIPEMD-128, and RIPEMD-320 provides only the same security as RIPEMD-160
NOTE: the [BLAKE3 crate](https://github.com/BLAKE3-team/BLAKE3) implements the `digest` traits used by the rest of the hashes in this repository, but is maintained by the BLAKE3 team.
[Security]: https://en.wikipedia.org/wiki/Hash_function_security_summary
[:exclamation:]: #crate-names
### Crate names
Whenever possible crates are published under the the same name as the crate
folder. Owners of `md5` and `sha1` crates declined
([1](https://github.com/stainless-steel/md5/pull/2),
[2](https://github.com/mitsuhiko/rust-sha1/issues/17)) to participate in this
project. This is why crates marked by :exclamation: are published under
`md-5` and `sha-1` names respectively.
### Crate Names
Whenever possible crates are published under the the same name as the crate folder.
Owners of `md5` and `sha1` declined ([1](https://github.com/stainless-steel/md5/pull/2), [2](https://github.com/mitsuhiko/rust-sha1/issues/17)) to participate in this project.
Those crates do not implement the [`digest`] traits, so they are not interoperable with the RustCrypto ecosystem.
This is why crates marked by :exclamation: are published under `md-5` and `sha-1` names, but the libraries themselves are named as `md5` and `sha1`, i.e. inside `use` statements you should use `sha1`/`md5`, not `sha_1`/`md_5`.
### Security Level Legend
The following describes the security level ratings associated with each
hash function (i.e. algorithms, not the specific implementation):
The following describes the security level ratings associated with each hash function (i.e. algorithms, not the specific implementation):
| Heart | Description |
|----------------|-------------|
|:--------------:|-------------|
| :green_heart: | No known successful attacks |
| :yellow_heart: | Theoretical break: security lower than claimed |
| :broken_heart: | Attack demonstrated in practice: avoid if at all possible |
See the [Security] page on Wikipedia for more information.
### Minimum Supported Rust Version (MSRV)
All crates in this repository support Rust 1.21 or higher. In future
minimally supported version of Rust can be changed, but it will be done with
a minor version bump.
### Minimum Supported Rust Version (MSRV) Policy
MSRV bumps are considered breaking changes and will be performed only with minor version bump.
## Usage
Let us demonstrate how to use crates in this repository using BLAKE2b as an
example.
First add `blake2` crate to your `Cargo.toml`:
Let us demonstrate how to use crates in this repository using SHA-2 as an example.
First add [`sha2`](https://docs.rs/sha2) crate to your `Cargo.toml`:
```toml
[dependencies]
blake2 = "0.9"
sha2 = "0.10"
```
Note that crates in this repository have an enabled by default `std` feature.
Note that all crates in this repository have an enabled by default `std` feature.
So if you plan to use the crate in `no_std` environments, don't forget to disable it:
```toml
[dependencies]
blake2 = { version="0.9", default-features = false }
sha2 = { version = "0.10", default-features = false }
```
`blake2` 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.
[`sha2`](https://docs.rs/sha2) and the other hash implementation crates re-export the [`digest`] crate and the [`Digest`] trait for convenience, so you don't have to include it in your `Cargo.toml` it as an explicit dependency.
Now you can write the following code:
```Rust
use blake2::{Blake2b, Digest};
```rust
use sha2::{Sha256, Digest};
let mut hasher = Blake2b::new();
let mut hasher = Sha256::new();
let data = b"Hello world!";
hasher.update(data);
// `update` can be called repeatedly and is generic over `AsRef<[u8]>`
@@ -103,64 +95,56 @@ 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]`.
In this example `hash` has type `GenericArray<u8, U32>`, which is a generic alternative to `[u8; 32]` defined in the [`generic-array`] crate.
Alternatively you can use chained approach, which is equivalent to the previous
example:
Alternatively, you can use chained approach, which is equivalent to the previous example:
```Rust
use blake2::{Blake2b, Digest};
```rust
use sha2::{Sha256, Digest};
let hash = Blake2b::new()
let hash = Sha256::new()
.chain(b"Hello world!")
.chain("String data")
.finalize();
println!("Result: {:x}", hash);
```
If the whole message is available you also can use convenience `digest` method:
If a complete message is available, then you also can use the convenience [`Digest::digest`] method:
```Rust
use blake2::{Blake2b, Digest};
```rust
use sha2::{Sha256, Digest};
let hash = Blake2b::digest(b"my message");
let hash = Sha256::digest(b"my message");
println!("Result: {:x}", hash);
```
### Hashing `Read`able objects
### 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 an enabled-by-default `std` feature):
If you want to hash data from a type which imlements the [`Read`] trait, you can rely on implementation of the [`Write`] trait (requires enabled-by-default `std` feature):
```Rust
use blake2::{Blake2b, Digest};
```rust
use sha2::{Sha256, Digest};
use std::{fs, io};
let mut file = fs::File::open(&path)?;
let mut hasher = Blake2b::new();
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);
```
### Hash-based Message Authentication Code (HMAC)
If you want to calculate [Hash-based Message Authentication Code][5] (HMAC),
you can use generic implementation from [`hmac`](https://docs.rs/hmac) crate,
which is a part of the [RustCrypto/MACs][6] repository.
If you want to calculate [Hash-based Message Authentication Code][HMAC] (HMAC), you can use the generic implementation from [`hmac`] crate, which is a part of the [RustCrypto/MACs] repository.
### Generic code
### Generic Code
You can write generic code over `Digest` (or other traits from `digest` crate)
trait which will work over different hash functions:
You can write generic code over the [`Digest`] trait (or other traits from the [`digest`] crate) which will work over different hash functions:
```Rust
use digest::Digest;
use blake2::Blake2b;
use sha2::Sha256;
```rust
use sha2::{Sha256, Sha512, Digest};
// Toy example, do not use it in practice!
// Instead use crates from: https://github.com/RustCrypto/password-hashing
@@ -172,15 +156,31 @@ fn hash_password<D: Digest>(password: &str, salt: &str, output: &mut [u8]) {
output.copy_from_slice(&hasher.finalize())
}
let mut buf1 = [0u8; 64];
hash_password::<Blake2b>("my_password", "abcd", &mut buf1);
let mut buf1 = [0u8; 32];
hash_password::<Sha256>("my_password", "abcd", &mut buf1);
let mut buf2 = [0u8; 32];
hash_password::<Sha256>("my_password", "abcd", &mut buf2);
let mut buf2 = [0u8; 64];
hash_password::<Sha512>("my_password", "abcd", &mut buf2);
```
If you want to use hash functions with trait objects, use `digest::DynDigest`
trait.
If you want to use hash functions with trait objects, you can use the [`DynDigest`] trait:
```rust
use sha2::{Sha256, Sha512, digest::DynDigest};
fn dyn_hash(hasher: &mut dyn DynDigest, data: &[u8]) -> Box<[u8]> {
hasher.update(data);
hasher.finalize_reset()
}
let mut sha256_hasher = Sha256::default();
let mut sha512_hasher = Sha512::default();
let res1 = dyn_hash(&mut sha256_hasher, b"foo");
let res2 = dyn_hash(&mut sha256_hasher, b"bar");
let res3 = dyn_hash(&mut sha512_hasher, b"foo");
let res4 = dyn_hash(&mut sha512_hasher, b"bar");
```
## License
@@ -193,25 +193,50 @@ 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.
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)
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
[deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg
[deps-link]: https://deps.rs/repo/github/RustCrypto/hashes
[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg
[//]: # (crates)
[`blake2`]: ./blake2
[`fsb`]: ./fsb
[`gost94`]: ./gost94
[`groestl`]: ./groestl
[`k12`]: ./k12
[`md2`]: ./md2
[`md4`]: ./md4
[`md-5`]: ./md-5
[`ripemd`]: ./ripemd
[`sha-1`]: ./sha1
[`sha2`]: ./sha2
[`sha3`]: ./sha3
[`shabal`]: ./shabal
[`sm3`]: ./sm3
[`streebog`]: ./streebog
[`tiger`]: ./tiger
[`whirlpool`]: ./whirlpool
[//]: # (footnotes)
[1]: https://en.wikipedia.org/wiki/Cryptographic_hash_function
[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
[`digest`]: https://docs.rs/digest
[`Digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html
[`Digest::digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html#tymethod.digest
[`DynDigest`]: https://docs.rs/digest/0.10.0/digest/trait.DynDigest.html
[`generic-array`]: https://docs.rs/generic-array
[HMAC]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
[`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
[`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
[`hmac`]: https://docs.rs/hmac
[RustCrypto/MACs]: https://github.com/RustCrypto/MACs
[//]: # (algorithms)
@@ -223,9 +248,7 @@ dual licensed as above, without any additional terms or conditions.
[MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography)
[MD4]: https://en.wikipedia.org/wiki/MD4
[MD5]: https://en.wikipedia.org/wiki/MD5
[RIPEMD-160]: https://en.wikipedia.org/wiki/RIPEMD
[RIPEMD-256]: https://en.wikipedia.org/wiki/RIPEMD
[RIPEMD-320]: https://en.wikipedia.org/wiki/RIPEMD
[RIPEMD]: https://en.wikipedia.org/wiki/RIPEMD
[SHA-1]: https://en.wikipedia.org/wiki/SHA-1
[SHA-2]: https://en.wikipedia.org/wiki/SHA-2
[SHA-3]: https://en.wikipedia.org/wiki/SHA-3

View File

@@ -5,6 +5,12 @@ 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.0 (2021-12-07)
### Changed
- Update to `digest` v0.10 and remove dependency on `crypto-mac` ([#217])
[#217]: https://github.com/RustCrypto/hashes/pull/217
## 0.9.2 (2021-08-25)
### Fixed
- Building with `simd_opt` on recent nightlies ([#301])

View File

@@ -1,6 +1,6 @@
[package]
name = "blake2"
version = "0.9.2"
name = "blake2" # Also update html_root_url in lib.rs when bumping this
version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
description = "BLAKE2 hash functions"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
@@ -12,18 +12,16 @@ keywords = ["crypto", "blake2", "hash", "digest"]
categories = ["cryptography", "no-std"]
[dependencies]
digest = "0.9"
crypto-mac = "0.8"
opaque-debug = "0.3"
digest = { version = "0.10", features = ["mac"] }
[dev-dependencies]
digest = { version = "0.9", features = ["dev"] }
crypto-mac = { version = "0.8", features = ["dev"] }
digest = { version = "0.10", features = ["dev"] }
hex-literal = "0.2"
[features]
default = ["std"]
std = ["digest/std", "crypto-mac/std"]
std = ["digest/std"]
reset = [] # Enable reset functionality
simd = []
simd_opt = ["simd"]
simd_asm = ["simd_opt"]

View File

@@ -1,4 +0,0 @@
#![no_std]
#![feature(test)]
digest::bench!(blake2::Blake2b);

View File

@@ -1,4 +0,0 @@
#![no_std]
#![feature(test)]
digest::bench!(blake2::Blake2s);

22
blake2/benches/mod.rs Normal file
View File

@@ -0,0 +1,22 @@
#![feature(test)]
extern crate test;
use blake2::{Blake2b512, Blake2s256};
use digest::bench_update;
use test::Bencher;
bench_update!(
Blake2b512::default();
blake2b512_10 10;
blake2b512_100 100;
blake2b512_1000 1000;
blake2b512_10000 10000;
);
bench_update!(
Blake2s256::default();
blake2s256_10 10;
blake2s256_100 100;
blake2s256_1000 1000;
blake2s256_10000 10000;
);

View File

@@ -1,47 +0,0 @@
use blake2::{Blake2b, Digest};
use std::env;
use std::fs;
use std::io::{self, Read};
const BUFFER_SIZE: usize = 1024;
/// Print digest result as hex string and name pair
fn print_result(sum: &[u8], name: &str) {
for byte in sum {
print!("{:02x}", byte);
}
println!("\t{}", name);
}
/// Compute digest value for given `Reader` and print it
/// On any error simply return without doing anything
fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
let mut sh = D::default();
let mut buffer = [0u8; BUFFER_SIZE];
loop {
let n = match reader.read(&mut buffer) {
Ok(n) => n,
Err(_) => return,
};
sh.update(&buffer[..n]);
if n == 0 || n < BUFFER_SIZE {
break;
}
}
print_result(&sh.finalize(), name);
}
fn main() {
let args = env::args();
// Process files listed in command line arguments one by one
// If no files provided process input from stdin
if args.len() > 1 {
for path in args.skip(1) {
if let Ok(mut file) = fs::File::open(&path) {
process::<Blake2b, _>(&mut file, &path);
}
}
} else {
process::<Blake2b, _>(&mut io::stdin(), "-");
}
}

View File

@@ -1,47 +0,0 @@
use blake2::{Blake2s, Digest};
use std::env;
use std::fs;
use std::io::{self, Read};
const BUFFER_SIZE: usize = 1024;
/// Print digest result as hex string and name pair
fn print_result(sum: &[u8], name: &str) {
for byte in sum {
print!("{:02x}", byte);
}
println!("\t{}", name);
}
/// Compute digest value for given `Reader` and print it
/// On any error simply return without doing anything
fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
let mut sh = D::default();
let mut buffer = [0u8; BUFFER_SIZE];
loop {
let n = match reader.read(&mut buffer) {
Ok(n) => n,
Err(_) => return,
};
sh.update(&buffer[..n]);
if n == 0 || n < BUFFER_SIZE {
break;
}
}
print_result(&sh.finalize(), name);
}
fn main() {
let args = env::args();
// Process files listed in command line arguments one by one
// If no files provided process input from stdin
if args.len() > 1 {
for path in args.skip(1) {
if let Ok(mut file) = fs::File::open(&path) {
process::<Blake2s, _>(&mut file, &path);
}
}
} else {
process::<Blake2s, _>(&mut io::stdin(), "-");
}
}

View File

@@ -1,414 +0,0 @@
macro_rules! blake2_impl {
(
$state:ident, $fix_state:ident, $word:ident, $vec:ident, $bytes:ident,
$block_size:ident, $R1:expr, $R2:expr, $R3:expr, $R4:expr, $IV:expr,
$vardoc:expr, $doc:expr,
) => {
use $crate::as_bytes::AsBytes;
use $crate::simd::{$vec, Vector4};
use core::{cmp, convert::TryInto, ops::Div};
use crypto_mac::{InvalidKeyLength, Mac, NewMac};
use digest::generic_array::typenum::{Unsigned, U4};
use digest::generic_array::GenericArray;
use digest::InvalidOutputSize;
use digest::{BlockInput, FixedOutputDirty, Reset, Update, VariableOutputDirty};
type Output = GenericArray<u8, $bytes>;
#[derive(Clone)]
#[doc=$vardoc]
pub struct $state {
m: [$word; 16],
h: [$vec; 2],
t: u64,
n: usize,
h0: [$vec; 2],
m0: [$word; 16],
t0: u64,
}
#[inline(always)]
fn iv0() -> $vec {
$vec::new($IV[0], $IV[1], $IV[2], $IV[3])
}
#[inline(always)]
fn iv1() -> $vec {
$vec::new($IV[4], $IV[5], $IV[6], $IV[7])
}
#[inline(always)]
fn quarter_round(v: &mut [$vec; 4], rd: u32, rb: u32, m: $vec) {
v[0] = v[0].wrapping_add(v[1]).wrapping_add(m.from_le());
v[3] = (v[3] ^ v[0]).rotate_right_const(rd);
v[2] = v[2].wrapping_add(v[3]);
v[1] = (v[1] ^ v[2]).rotate_right_const(rb);
}
#[inline(always)]
fn shuffle(v: &mut [$vec; 4]) {
v[1] = v[1].shuffle_left_1();
v[2] = v[2].shuffle_left_2();
v[3] = v[3].shuffle_left_3();
}
#[inline(always)]
fn unshuffle(v: &mut [$vec; 4]) {
v[1] = v[1].shuffle_right_1();
v[2] = v[2].shuffle_right_2();
v[3] = v[3].shuffle_right_3();
}
#[inline(always)]
fn round(v: &mut [$vec; 4], m: &[$word; 16], s: &[usize; 16]) {
quarter_round(v, $R1, $R2, $vec::gather(m, s[0], s[2], s[4], s[6]));
quarter_round(v, $R3, $R4, $vec::gather(m, s[1], s[3], s[5], s[7]));
shuffle(v);
quarter_round(v, $R1, $R2, $vec::gather(m, s[8], s[10], s[12], s[14]));
quarter_round(v, $R3, $R4, $vec::gather(m, s[9], s[11], s[13], s[15]));
unshuffle(v);
}
impl $state {
/// Creates a new hashing context with a key.
///
/// **WARNING!** If you plan to use it for variable output MAC, then
/// make sure to compare codes in constant time! It can be done
/// for example by using `subtle` crate.
pub fn new_keyed(key: &[u8], output_size: usize) -> Self {
Self::with_params(key, &[], &[], output_size)
}
/// Creates a new hashing context with the full set of sequential-mode parameters.
pub fn with_params(
key: &[u8],
salt: &[u8],
persona: &[u8],
output_size: usize,
) -> Self {
let kk = key.len();
assert!(kk <= $bytes::to_usize());
assert!(output_size <= $bytes::to_usize());
// The number of bytes needed to express two words.
let length = $bytes::to_usize() / 4;
assert!(salt.len() <= length);
assert!(persona.len() <= length);
// Build a parameter block
let mut p = [0 as $word; 8];
p[0] = 0x0101_0000 ^ ((kk as $word) << 8) ^ (output_size as $word);
// salt is two words long
if salt.len() < length {
let mut padded_salt =
GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
for i in 0..salt.len() {
padded_salt[i] = salt[i];
}
p[4] = $word::from_le_bytes(padded_salt[0..length / 2].try_into().unwrap());
p[5] = $word::from_le_bytes(
padded_salt[length / 2..padded_salt.len()]
.try_into()
.unwrap(),
);
} else {
p[4] = $word::from_le_bytes(salt[0..salt.len() / 2].try_into().unwrap());
p[5] =
$word::from_le_bytes(salt[salt.len() / 2..salt.len()].try_into().unwrap());
}
// persona is also two words long
if persona.len() < length {
let mut padded_persona =
GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
for i in 0..persona.len() {
padded_persona[i] = persona[i];
}
p[6] = $word::from_le_bytes(padded_persona[0..length / 2].try_into().unwrap());
p[7] = $word::from_le_bytes(
padded_persona[length / 2..padded_persona.len()]
.try_into()
.unwrap(),
);
} else {
p[6] = $word::from_le_bytes(persona[0..length / 2].try_into().unwrap());
p[7] = $word::from_le_bytes(
persona[length / 2..persona.len()].try_into().unwrap(),
);
}
let mut state = Self::with_parameter_block(&p);
if kk > 0 {
copy(key, state.m.as_mut_bytes());
state.t = 2 * $bytes::to_u64();
}
state.t0 = state.t;
state.m0 = state.m;
state
}
#[doc(hidden)]
pub fn with_parameter_block(p: &[$word; 8]) -> Self {
let nn = p[0] as u8 as usize;
let kk = (p[0] >> 8) as u8 as usize;
assert!(nn >= 1 && nn <= $bytes::to_usize());
assert!(kk <= $bytes::to_usize());
let h0 = [
iv0() ^ $vec::new(p[0], p[1], p[2], p[3]),
iv1() ^ $vec::new(p[4], p[5], p[6], p[7]),
];
$state {
m: [0; 16],
h: h0,
t: 0,
n: nn,
t0: 0,
m0: [0; 16],
h0,
}
}
/// Updates the hashing context with more data.
fn update(&mut self, data: &[u8]) {
let mut rest = data;
let block = 2 * $bytes::to_usize();
let off = self.t as usize % block;
if off != 0 || self.t == 0 {
let len = cmp::min(block - off, rest.len());
let part = &rest[..len];
rest = &rest[part.len()..];
copy(part, &mut self.m.as_mut_bytes()[off..]);
self.t = self
.t
.checked_add(part.len() as u64)
.expect("hash data length overflow");
}
while rest.len() >= block {
self.compress(0, 0);
let part = &rest[..block];
rest = &rest[part.len()..];
copy(part, &mut self.m.as_mut_bytes());
self.t = self
.t
.checked_add(part.len() as u64)
.expect("hash data length overflow");
}
let n = rest.len();
if n > 0 {
self.compress(0, 0);
copy(rest, &mut self.m.as_mut_bytes());
self.t = self
.t
.checked_add(rest.len() as u64)
.expect("hash data length overflow");
}
}
#[doc(hidden)]
pub fn finalize_last_node(mut self) -> Output {
self.finalize_with_flag(!0)
}
fn finalize_with_flag(&mut self, f1: $word) -> Output {
let off = self.t as usize % (2 * $bytes::to_usize());
if off != 0 {
self.m.as_mut_bytes()[off..].iter_mut().for_each(|b| *b = 0);
}
self.compress(!0, f1);
let buf = [self.h[0].to_le(), self.h[1].to_le()];
let mut out = GenericArray::default();
copy(buf.as_bytes(), &mut out);
out
}
fn compress(&mut self, f0: $word, f1: $word) {
use $crate::consts::SIGMA;
let m = &self.m;
let h = &mut self.h;
let t0 = self.t as $word;
let t1 = match $bytes::to_u8() {
64 => 0,
32 => (self.t >> 32) as $word,
_ => unreachable!(),
};
let mut v = [h[0], h[1], iv0(), iv1() ^ $vec::new(t0, t1, f0, f1)];
round(&mut v, m, &SIGMA[0]);
round(&mut v, m, &SIGMA[1]);
round(&mut v, m, &SIGMA[2]);
round(&mut v, m, &SIGMA[3]);
round(&mut v, m, &SIGMA[4]);
round(&mut v, m, &SIGMA[5]);
round(&mut v, m, &SIGMA[6]);
round(&mut v, m, &SIGMA[7]);
round(&mut v, m, &SIGMA[8]);
round(&mut v, m, &SIGMA[9]);
if $bytes::to_u8() == 64 {
round(&mut v, m, &SIGMA[0]);
round(&mut v, m, &SIGMA[1]);
}
h[0] = h[0] ^ (v[0] ^ v[2]);
h[1] = h[1] ^ (v[1] ^ v[3]);
}
}
impl Default for $state {
fn default() -> Self {
Self::new_keyed(&[], $bytes::to_usize())
}
}
impl BlockInput for $state {
type BlockSize = $block_size;
}
impl Update for $state {
fn update(&mut self, data: impl AsRef<[u8]>) {
self.update(data.as_ref());
}
}
impl VariableOutputDirty for $state {
fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
if output_size == 0 || output_size > $bytes::to_usize() {
return Err(InvalidOutputSize);
}
Ok(Self::new_keyed(&[], output_size))
}
fn output_size(&self) -> usize {
self.n
}
fn finalize_variable_dirty(&mut self, f: impl FnOnce(&[u8])) {
let n = self.n;
let res = self.finalize_with_flag(0);
f(&res[..n]);
}
}
impl Reset for $state {
fn reset(&mut self) {
self.t = self.t0;
self.m = self.m0;
self.h = self.h0;
}
}
opaque_debug::implement!($state);
digest::impl_write!($state);
#[derive(Clone)]
#[doc=$doc]
pub struct $fix_state {
state: $state,
}
impl $fix_state {
/// Creates a new hashing context with the full set of sequential-mode parameters.
pub fn with_params(key: &[u8], salt: &[u8], persona: &[u8]) -> Self {
let state = $state::with_params(key, salt, persona, $bytes::to_usize());
Self { state }
}
}
impl Default for $fix_state {
fn default() -> Self {
let state = $state::new_keyed(&[], $bytes::to_usize());
Self { state }
}
}
impl BlockInput for $fix_state {
type BlockSize = $block_size;
}
impl Update for $fix_state {
fn update(&mut self, data: impl AsRef<[u8]>) {
self.state.update(data.as_ref());
}
}
impl FixedOutputDirty for $fix_state {
type OutputSize = $bytes;
fn finalize_into_dirty(&mut self, out: &mut Output) {
out.copy_from_slice(&self.state.finalize_with_flag(0));
}
}
impl Reset for $fix_state {
fn reset(&mut self) {
self.state.reset()
}
}
impl NewMac for $fix_state {
type KeySize = $bytes;
fn new(key: &GenericArray<u8, $bytes>) -> Self {
let state = $state::new_keyed(key, $bytes::to_usize());
Self { state }
}
fn new_varkey(key: &[u8]) -> Result<Self, InvalidKeyLength> {
if key.len() > $bytes::to_usize() {
Err(InvalidKeyLength)
} else {
let state = $state::new_keyed(key, $bytes::to_usize());
Ok(Self { state })
}
}
}
impl Mac for $fix_state {
type OutputSize = $bytes;
fn update(&mut self, data: &[u8]) {
self.state.update(data);
}
fn reset(&mut self) {
<Self as Reset>::reset(self)
}
fn finalize(mut self) -> crypto_mac::Output<Self> {
crypto_mac::Output::new(self.state.finalize_with_flag(0))
}
}
opaque_debug::implement!($fix_state);
digest::impl_write!($fix_state);
fn copy(src: &[u8], dst: &mut [u8]) {
assert!(dst.len() >= src.len());
unsafe {
core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len());
}
}
};
}

View File

@@ -1,18 +0,0 @@
use crate::consts::BLAKE2B_IV;
use digest::generic_array::typenum::{U128, U64};
blake2_impl!(
VarBlake2b,
Blake2b,
u64,
u64x4,
U64,
U128,
32,
24,
16,
63,
BLAKE2B_IV,
"Blake2b instance with a variable output.",
"Blake2b instance with a fixed output.",
);

View File

@@ -1,18 +0,0 @@
use crate::consts::BLAKE2S_IV;
use digest::generic_array::typenum::{U32, U64};
blake2_impl!(
VarBlake2s,
Blake2s,
u32,
u32x4,
U32,
U64,
16,
12,
8,
7,
BLAKE2S_IV,
"Blake2s instance with a variable output.",
"Blake2s instance with a fixed output.",
);

View File

@@ -2,14 +2,14 @@
//!
//! # Usage
//!
//! `Blake2b` can be used in the following way:
//! [`Blake2b512`] and [`Blake2s256`] can be used in the following way:
//!
//! ```rust
//! use blake2::{Blake2b, Blake2s, Digest};
//! use blake2::{Blake2b512, Blake2s256, Digest};
//! use hex_literal::hex;
//!
//! // create a Blake2b object
//! let mut hasher = Blake2b::new();
//! // create a Blake2b512 object
//! let mut hasher = Blake2b512::new();
//!
//! // write input message
//! hasher.update(b"hello world");
@@ -21,8 +21,8 @@
//! c05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0
//! ")[..]);
//!
//! // same example for `Blake2s`:
//! let mut hasher = Blake2s::new();
//! // same example for Blake2s256:
//! let mut hasher = Blake2s256::new();
//! hasher.update(b"hello world");
//! let res = hasher.finalize();
//! assert_eq!(res[..], hex!("
@@ -34,45 +34,32 @@
//!
//! ## Variable output size
//!
//! If you need variable sized output you can use `VarBlake2b` and `VarBlake2s`
//! which support variable output sizes through `VariableOutput` trait. `Update`
//! trait has to be imported as well.
//! This implementation supports run and compile time variable sizes.
//!
//! Run time variable output example:
//! ```rust
//! use blake2::VarBlake2b;
//! use blake2::Blake2bVar;
//! use blake2::digest::{Update, VariableOutput};
//! use hex_literal::hex;
//!
//! let mut hasher = VarBlake2b::new(10).unwrap();
//! let mut hasher = Blake2bVar::new(10).unwrap();
//! hasher.update(b"my_input");
//! hasher.finalize_variable(|res| {
//! assert_eq!(res, [44, 197, 92, 132, 228, 22, 146, 78, 100, 0])
//! })
//! let mut buf = [0u8; 10];
//! hasher.finalize_variable(&mut buf).unwrap();
//! assert_eq!(buf, hex!("2cc55c84e416924e6400"));
//! ```
//!
//! ## Message Authentication Code (MAC)
//!
//! BLAKE2 can be used as a MAC without any additional constructs:
//!
//! Compile time variable output example:
//! ```rust
//! use blake2::Blake2b;
//! use blake2::crypto_mac::{Mac, NewMac};
//! use blake2::{Blake2b, Digest, digest::consts::U10};
//! use hex_literal::hex;
//!
//! let mut hasher = Blake2b::new_varkey(b"my key").unwrap();
//! hasher.update(b"hello world");
//! type Blake2b80 = Blake2b<U10>;
//!
//! // `result` has type `crypto_mac::Output` which is a thin wrapper around
//! // a byte array and provides a constant time equality check
//! let result = hasher.finalize();
//! // To get underlying array use the `into_bytes` method, but be careful,
//! // since incorrect use of the code value may permit timing attacks which
//! // defeat the security provided by the `crypto_mac::Output`
//! let code_bytes = result.into_bytes();
//!
//! // To verify the message it's recommended to use `verify` method
//! let mut hasher = Blake2b::new_varkey(b"my key").unwrap();
//! hasher.update(b"hello world");
//! // `verify` return `Ok(())` if code is correct, `Err(MacError)` otherwise
//! hasher.verify(&code_bytes).unwrap();
//! let mut hasher = Blake2b80::new();
//! hasher.update(b"my_input");
//! let res = hasher.finalize();
//! assert_eq!(res[..], hex!("2cc55c84e416924e6400")[..]);
//! ```
//!
//! # Acknowledgment
@@ -83,8 +70,9 @@
#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_root_url = "https://docs.rs/blake2/0.10.0"
)]
#![warn(missing_docs, rust_2018_idioms)]
#![cfg_attr(feature = "simd", feature(platform_intrinsics, repr_simd))]
@@ -94,19 +82,95 @@
#[cfg(feature = "std")]
extern crate std;
pub use digest::{self, Digest};
use core::{convert::TryInto, fmt, marker::PhantomData, ops::Div};
use digest::{
block_buffer::{Lazy, LazyBuffer},
consts::{U128, U32, U4, U64},
core_api::{
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper,
CtVariableCoreWrapper, OutputSizeUser, RtVariableCoreWrapper, TruncSide, UpdateCore,
VariableOutputCore,
},
crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser},
generic_array::{
typenum::{IsLessOrEqual, LeEq, NonZero, Unsigned},
ArrayLength, GenericArray,
},
FixedOutput, HashMarker, InvalidOutputSize, MacMarker, Output, Update,
};
#[cfg(feature = "reset")]
use digest::{FixedOutputReset, Reset};
mod as_bytes;
mod consts;
mod simd;
#[macro_use]
mod blake2;
mod macros;
mod blake2b;
mod blake2s;
use as_bytes::AsBytes;
use consts::{BLAKE2B_IV, BLAKE2S_IV};
use simd::{u32x4, u64x4, Vector4};
pub use crypto_mac;
pub use digest::{self, Digest};
blake2_impl!(
Blake2bVarCore,
"Blake2b",
u64,
u64x4,
U64,
U128,
32,
24,
16,
63,
BLAKE2B_IV,
"Blake2b instance with a variable output.",
"Blake2b instance with a fixed output.",
);
pub use crate::blake2b::{Blake2b, VarBlake2b};
pub use crate::blake2s::{Blake2s, VarBlake2s};
/// BLAKE2b which allows to choose output size at runtime.
pub type Blake2bVar = RtVariableCoreWrapper<Blake2bVarCore>;
/// Core hasher state of BLAKE2b generic over output size.
pub type Blake2bCore<OutSize> = CtVariableCoreWrapper<Blake2bVarCore, OutSize>;
/// BLAKE2b generic over output size.
pub type Blake2b<OutSize> = CoreWrapper<Blake2bCore<OutSize>>;
/// BLAKE2b-512 hasher state.
pub type Blake2b512 = Blake2b<U64>;
blake2_mac_impl!(Blake2bMac, Blake2bVarCore, U64, "Blake2b MAC function");
/// BLAKE2b-512 MAC state.
pub type Blake2bMac512 = Blake2bMac<U64>;
blake2_impl!(
Blake2sVarCore,
"Blake2s",
u32,
u32x4,
U32,
U64,
16,
12,
8,
7,
BLAKE2S_IV,
"Blake2s instance with a variable output.",
"Blake2s instance with a fixed output.",
);
/// BLAKE2s which allows to choose output size at runtime.
pub type Blake2sVar = RtVariableCoreWrapper<Blake2sVarCore>;
/// Core hasher state of BLAKE2s generic over output size.
pub type Blake2sCore<OutSize> = CtVariableCoreWrapper<Blake2sVarCore, OutSize>;
/// BLAKE2s generic over output size.
pub type Blake2s<OutSize> = CoreWrapper<Blake2sCore<OutSize>>;
/// BLAKE2s-256 hasher state.
pub type Blake2s256 = Blake2s<U32>;
blake2_mac_impl!(Blake2sMac, Blake2sVarCore, U32, "Blake2s MAC function");
/// BLAKE2s-256 MAC state.
pub type Blake2sMac256 = Blake2sMac<U32>;

430
blake2/src/macros.rs Normal file
View File

@@ -0,0 +1,430 @@
macro_rules! blake2_impl {
(
$name:ident, $alg_name:expr, $word:ident, $vec:ident, $bytes:ident,
$block_size:ident, $R1:expr, $R2:expr, $R3:expr, $R4:expr, $IV:expr,
$vardoc:expr, $doc:expr,
) => {
#[derive(Clone)]
#[doc=$vardoc]
pub struct $name {
h: [$vec; 2],
t: u64,
#[cfg(feature = "reset")]
h0: [$vec; 2],
}
impl $name {
#[inline(always)]
fn iv0() -> $vec {
$vec::new($IV[0], $IV[1], $IV[2], $IV[3])
}
#[inline(always)]
fn iv1() -> $vec {
$vec::new($IV[4], $IV[5], $IV[6], $IV[7])
}
/// Creates a new context with the full set of sequential-mode parameters.
pub fn new_with_params(
salt: &[u8],
persona: &[u8],
key_size: usize,
output_size: usize,
) -> Self {
assert!(key_size <= $bytes::to_usize());
assert!(output_size <= $bytes::to_usize());
// The number of bytes needed to express two words.
let length = $bytes::to_usize() / 4;
assert!(salt.len() <= length);
assert!(persona.len() <= length);
// Build a parameter block
let mut p = [0 as $word; 8];
p[0] = 0x0101_0000 ^ ((key_size as $word) << 8) ^ (output_size as $word);
// salt is two words long
if salt.len() < length {
let mut padded_salt =
GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
for i in 0..salt.len() {
padded_salt[i] = salt[i];
}
p[4] = $word::from_le_bytes(padded_salt[0..length / 2].try_into().unwrap());
p[5] = $word::from_le_bytes(
padded_salt[length / 2..padded_salt.len()]
.try_into()
.unwrap(),
);
} else {
p[4] = $word::from_le_bytes(salt[0..salt.len() / 2].try_into().unwrap());
p[5] =
$word::from_le_bytes(salt[salt.len() / 2..salt.len()].try_into().unwrap());
}
// persona is also two words long
if persona.len() < length {
let mut padded_persona =
GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
for i in 0..persona.len() {
padded_persona[i] = persona[i];
}
p[6] = $word::from_le_bytes(padded_persona[0..length / 2].try_into().unwrap());
p[7] = $word::from_le_bytes(
padded_persona[length / 2..padded_persona.len()]
.try_into()
.unwrap(),
);
} else {
p[6] = $word::from_le_bytes(persona[0..length / 2].try_into().unwrap());
p[7] = $word::from_le_bytes(
persona[length / 2..persona.len()].try_into().unwrap(),
);
}
let h = [
Self::iv0() ^ $vec::new(p[0], p[1], p[2], p[3]),
Self::iv1() ^ $vec::new(p[4], p[5], p[6], p[7]),
];
$name {
#[cfg(feature = "reset")]
h0: h.clone(),
h,
t: 0,
}
}
fn finalize_with_flag(
&mut self,
final_block: &GenericArray<u8, $block_size>,
flag: $word,
out: &mut Output<Self>,
) {
self.compress(final_block, !0, flag);
let buf = [self.h[0].to_le(), self.h[1].to_le()];
out.copy_from_slice(buf.as_bytes())
}
fn compress(&mut self, block: &Block<Self>, f0: $word, f1: $word) {
use $crate::consts::SIGMA;
#[inline(always)]
fn quarter_round(v: &mut [$vec; 4], rd: u32, rb: u32, m: $vec) {
v[0] = v[0].wrapping_add(v[1]).wrapping_add(m.from_le());
v[3] = (v[3] ^ v[0]).rotate_right_const(rd);
v[2] = v[2].wrapping_add(v[3]);
v[1] = (v[1] ^ v[2]).rotate_right_const(rb);
}
#[inline(always)]
fn shuffle(v: &mut [$vec; 4]) {
v[1] = v[1].shuffle_left_1();
v[2] = v[2].shuffle_left_2();
v[3] = v[3].shuffle_left_3();
}
#[inline(always)]
fn unshuffle(v: &mut [$vec; 4]) {
v[1] = v[1].shuffle_right_1();
v[2] = v[2].shuffle_right_2();
v[3] = v[3].shuffle_right_3();
}
#[inline(always)]
fn round(v: &mut [$vec; 4], m: &[$word; 16], s: &[usize; 16]) {
quarter_round(v, $R1, $R2, $vec::gather(m, s[0], s[2], s[4], s[6]));
quarter_round(v, $R3, $R4, $vec::gather(m, s[1], s[3], s[5], s[7]));
shuffle(v);
quarter_round(v, $R1, $R2, $vec::gather(m, s[8], s[10], s[12], s[14]));
quarter_round(v, $R3, $R4, $vec::gather(m, s[9], s[11], s[13], s[15]));
unshuffle(v);
}
let mut m: [$word; 16] = Default::default();
let n = core::mem::size_of::<$word>();
for (v, chunk) in m.iter_mut().zip(block.chunks_exact(n)) {
*v = $word::from_le_bytes(chunk.try_into().unwrap());
}
let h = &mut self.h;
let t0 = self.t as $word;
let t1 = match $bytes::to_u8() {
64 => 0,
32 => (self.t >> 32) as $word,
_ => unreachable!(),
};
let mut v = [
h[0],
h[1],
Self::iv0(),
Self::iv1() ^ $vec::new(t0, t1, f0, f1),
];
round(&mut v, &m, &SIGMA[0]);
round(&mut v, &m, &SIGMA[1]);
round(&mut v, &m, &SIGMA[2]);
round(&mut v, &m, &SIGMA[3]);
round(&mut v, &m, &SIGMA[4]);
round(&mut v, &m, &SIGMA[5]);
round(&mut v, &m, &SIGMA[6]);
round(&mut v, &m, &SIGMA[7]);
round(&mut v, &m, &SIGMA[8]);
round(&mut v, &m, &SIGMA[9]);
if $bytes::to_u8() == 64 {
round(&mut v, &m, &SIGMA[0]);
round(&mut v, &m, &SIGMA[1]);
}
h[0] = h[0] ^ (v[0] ^ v[2]);
h[1] = h[1] ^ (v[1] ^ v[3]);
}
}
impl HashMarker for $name {}
impl BlockSizeUser for $name {
type BlockSize = $block_size;
}
impl BufferKindUser for $name {
type BufferKind = Lazy;
}
impl UpdateCore for $name {
#[inline]
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
for block in blocks {
self.t += block.len() as u64;
self.compress(block, 0, 0);
}
}
}
impl OutputSizeUser for $name {
type OutputSize = $bytes;
}
impl VariableOutputCore for $name {
const TRUNC_SIDE: TruncSide = TruncSide::Left;
#[inline]
fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
if output_size > Self::OutputSize::USIZE {
return Err(InvalidOutputSize);
}
Ok(Self::new_with_params(&[], &[], 0, output_size))
}
#[inline]
fn finalize_variable_core(
&mut self,
buffer: &mut Buffer<Self>,
out: &mut Output<Self>,
) {
self.t += buffer.get_pos() as u64;
let block = buffer.pad_with_zeros();
self.finalize_with_flag(block, 0, out);
}
}
#[cfg(feature = "reset")]
impl Reset for $name {
fn reset(&mut self) {
self.h = self.h0;
self.t = 0;
}
}
impl AlgorithmName for $name {
#[inline]
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str($alg_name)
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(concat!(stringify!($name), " { ... }"))
}
}
};
}
macro_rules! blake2_mac_impl {
(
$name:ident, $hash:ty, $max_size:ty, $doc:expr
) => {
#[derive(Clone)]
#[doc=$doc]
pub struct $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
core: $hash,
buffer: LazyBuffer<<$hash as BlockSizeUser>::BlockSize>,
#[cfg(feature = "reset")]
key_block: Block<$hash>,
_out: PhantomData<OutSize>,
}
impl<OutSize> $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
/// Create new instance using provided key, salt, and persona.
///
/// Key length should not be bigger than block size, salt and persona
/// length should not be bigger than quarter of block size. If any
/// of those conditions is false the method will return an error.
#[inline]
pub fn new_with_salt_and_personal(
key: &[u8],
salt: &[u8],
persona: &[u8],
) -> Result<Self, InvalidLength> {
let kl = key.len();
let bs = <$hash as BlockSizeUser>::BlockSize::USIZE;
let qbs = bs / 4;
if kl > bs || salt.len() > qbs || persona.len() > qbs {
return Err(InvalidLength);
}
let mut key_block = Block::<$hash>::default();
key_block[..kl].copy_from_slice(key);
let buffer = LazyBuffer::new(&key_block);
Ok(Self {
core: <$hash>::new_with_params(salt, persona, key.len(), OutSize::USIZE),
buffer,
#[cfg(feature = "reset")]
key_block,
_out: PhantomData,
})
}
}
impl<OutSize> KeySizeUser for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
type KeySize = <$hash as BlockSizeUser>::BlockSize;
}
impl<OutSize> KeyInit for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
fn new(key: &Key<Self>) -> Self {
Self {
core: <$hash>::new_with_params(key, &[], key.len(), OutSize::USIZE),
buffer: LazyBuffer::new(key),
#[cfg(feature = "reset")]
key_block: key.clone(),
_out: PhantomData,
}
}
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
let kl = key.len();
if kl > <$hash as BlockSizeUser>::BlockSize::USIZE {
return Err(InvalidLength);
}
let mut key_block = Block::<$hash>::default();
key_block[..kl].copy_from_slice(key);
Ok(Self {
core: <$hash>::new_with_params(&[], &[], key.len(), OutSize::USIZE),
buffer: LazyBuffer::new(&key_block),
#[cfg(feature = "reset")]
key_block,
_out: PhantomData,
})
}
}
impl<OutSize> Update for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
#[inline]
fn update(&mut self, input: &[u8]) {
let Self { core, buffer, .. } = self;
buffer.digest_blocks(input, |blocks| core.update_blocks(blocks));
}
}
impl<OutSize> OutputSizeUser for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
type OutputSize = OutSize;
}
impl<OutSize> FixedOutput for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
#[inline]
fn finalize_into(mut self, out: &mut Output<Self>) {
let Self { core, buffer, .. } = &mut self;
let mut full_res = Default::default();
core.finalize_variable_core(buffer, &mut full_res);
out.copy_from_slice(&full_res[..OutSize::USIZE]);
}
}
#[cfg(feature = "reset")]
impl<OutSize> Reset for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
fn reset(&mut self) {
self.core.reset();
self.buffer = LazyBuffer::new(&self.key_block);
}
}
#[cfg(feature = "reset")]
impl<OutSize> FixedOutputReset for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
#[inline]
fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
let Self {
core,
buffer,
key_block,
..
} = self;
core.finalize_variable_core(buffer, out);
core.reset();
*buffer = LazyBuffer::new(key_block);
}
}
impl<OutSize> MacMarker for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
}
impl<OutSize> fmt::Debug for $name<OutSize>
where
OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
LeEq<OutSize, $max_size>: NonZero,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{} {{ ... }}", stringify!($name), OutSize::USIZE)
}
}
};
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,18 +0,0 @@
#![no_std]
use digest::dev::{digest_test, variable_test};
use digest::new_test;
new_test!(blake2b_fixed, "blake2b/fixed", blake2::Blake2b, digest_test);
new_test!(
blake2b_variable,
"blake2b/variable",
blake2::VarBlake2b,
variable_test
);
new_test!(
blake2s_variable,
"blake2s/variable",
blake2::VarBlake2s,
variable_test
);

View File

@@ -1,6 +1,7 @@
#![no_std]
#[cfg(not(feature = "reset"))]
use digest::new_mac_test as new_test;
#[cfg(feature = "reset")]
use digest::new_resettable_mac_test as new_test;
use crypto_mac::new_test;
new_test!(blake2b_mac, "blake2b/mac", blake2::Blake2b);
new_test!(blake2s_mac, "blake2s/mac", blake2::Blake2s);
new_test!(blake2b_mac, "blake2b/mac", blake2::Blake2bMac512);
new_test!(blake2s_mac, "blake2s/mac", blake2::Blake2sMac256);

19
blake2/tests/mod.rs Normal file
View File

@@ -0,0 +1,19 @@
#[cfg(feature = "reset")]
use digest::dev::{fixed_reset_test as fixed_fn, variable_reset_test as varaible_fn};
#[cfg(not(feature = "reset"))]
use digest::dev::{fixed_test as fixed_fn, variable_test as varaible_fn};
use digest::new_test;
new_test!(blake2b_fixed, "blake2b/fixed", blake2::Blake2b512, fixed_fn,);
new_test!(
blake2b_variable,
"blake2b/variable",
blake2::Blake2bVar,
varaible_fn,
);
new_test!(
blake2s_variable,
"blake2s/variable",
blake2::Blake2sVar,
varaible_fn,
);

View File

@@ -1,23 +1,40 @@
use blake2::{Blake2b, Blake2s, Digest};
use blake2::{digest::FixedOutput, Blake2bMac512, Blake2sMac256};
use hex_literal::hex;
#[test]
#[rustfmt::skip]
fn blake2s_persona() {
let key_bytes = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
let persona = "personal";
let persona_bytes = persona.as_bytes();
let ctx = Blake2s::with_params(&key_bytes, &[], persona_bytes);
let key= hex!("
000102030405060708090a0b0c0d0e0f
101112131415161718191a1b1c1d1e1f
");
let persona = b"personal";
let ctx = Blake2sMac256::new_with_salt_and_personal(&key, &[], persona).unwrap();
assert_eq!(
ctx.finalize().as_slice(),
&hex!("25a4ee63b594aed3f88a971e1877ef7099534f9097291f88fb86c79b5e70d022")[..]
ctx.finalize_fixed()[..],
hex!("
25a4ee63b594aed3f88a971e1877ef70
99534f9097291f88fb86c79b5e70d022
")[..],
);
}
#[test]
#[rustfmt::skip]
fn blake2b_persona() {
let key_bytes = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
let persona = "personal";
let persona_bytes = persona.as_bytes();
let ctx = Blake2b::with_params(&key_bytes, &[], persona_bytes);
assert_eq!(ctx.finalize().as_slice(), &hex!("03de3b295dcfc3b25b05abb09bc95fe3e9ff3073638badc68101d1e42019d0771dd07525a3aae8318e92c5e5d967ba92e4810d0021d7bf3b49da0b4b4a8a4e1f")[..]);
let key = hex!("
000102030405060708090a0b0c0d0e0f
101112131415161718191a1b1c1d1e1f
");
let persona = b"personal";
let ctx = Blake2bMac512::new_with_salt_and_personal(&key, &[], persona).unwrap();
assert_eq!(
ctx.finalize_fixed()[..],
hex!("
03de3b295dcfc3b25b05abb09bc95fe3
e9ff3073638badc68101d1e42019d077
1dd07525a3aae8318e92c5e5d967ba92
e4810d0021d7bf3b49da0b4b4a8a4e1f
")[..],
);
}

View File

@@ -5,6 +5,12 @@ 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.1.0 (2021-12-07)
### Changed
- Update to `digest` v0.10 ([#217])
[#217]: https://github.com/RustCrypto/hashes/pull/217
## 0.0.2 (2020-07-21)
- Fixed `Reset` implementation bug. Reduce crate size by using binary dump
of `PI` ([#300])

View File

@@ -1,7 +1,7 @@
[package]
name = "fsb"
description = "FSB hash function"
version = "0.0.2"
version = "0.1.0" # Also update html_root_url in lib.rs when bumping this
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@@ -11,15 +11,13 @@ keywords = ["crypto", "fsb", "hash", "digest"]
categories = ["cryptography", "no-std"]
[dependencies]
whirlpool = { version = "0.9", path = "../whirlpool", default-features = false }
digest = "0.9"
block-buffer = { version = "0.9", features = ["block-padding"] }
opaque-debug = "0.3"
digest = "0.10"
whirlpool = { version = "0.10", path = "../whirlpool", default-features = false }
[dev-dependencies]
digest = { version = "0.10", features = ["dev"] }
hex-literal = "0.2"
digest = { version = "0.9", features = ["dev"] }
[features]
asm = ["whirlpool/asm"]
std = ["whirlpool/std"]
default = ["std"]
std = ["digest/std"]

View File

@@ -1,6 +1,4 @@
Copyright (c) 2006-2009 Graydon Hoare
Copyright (c) 2009-2013 Mozilla Foundation
Copyright (c) 2016 Artyom Pavlov
Copyright (c) 2021 The RustCrypto Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated

View File

@@ -13,7 +13,7 @@ Pure Rust implementation of the [FSB hash function][1] family.
## Minimum Supported Rust Version
Rust **1.47** or higher.
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.

46
fsb/benches/mod.rs Normal file
View File

@@ -0,0 +1,46 @@
#![feature(test)]
extern crate test;
use digest::bench_update;
use fsb::{Fsb160, Fsb224, Fsb256, Fsb384, Fsb512};
use test::Bencher;
bench_update!(
Fsb160::default();
fsb160_10 10;
fsb160_100 100;
fsb160_1000 1000;
fsb160_10000 10000;
);
bench_update!(
Fsb224::default();
fsb224_10 10;
fsb224_100 100;
fsb224_1000 1000;
fsb224_10000 10000;
);
bench_update!(
Fsb256::default();
fsb256_10 10;
fsb256_100 100;
fsb256_1000 1000;
fsb256_10000 10000;
);
bench_update!(
Fsb384::default();
fsb384_10 10;
fsb384_100 100;
fsb384_1000 1000;
fsb384_10000 10000;
);
bench_update!(
Fsb512::default();
fsb512_10 10;
fsb512_100 100;
fsb512_1000 1000;
fsb512_10000 10000;
);

View File

@@ -39,38 +39,36 @@
#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_root_url = "https://docs.rs/fsb/0.1.0"
)]
#![deny(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]
#![allow(non_snake_case)]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::vec;
#[macro_use]
mod macros;
use core::fmt;
pub use digest::{self, Digest};
use whirlpool::Whirlpool;
use core::convert::TryInto;
// Double check this contains all values in the reference implementation
static PI: &[u8; 272384] = include_bytes!("pi.bin");
use block_buffer::BlockBuffer;
use digest::generic_array::GenericArray;
use digest::{BlockInput, FixedOutputDirty, Reset, Update};
use digest::{
block_buffer::Eager,
core_api::{
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
OutputSizeUser, Reset, UpdateCore,
},
generic_array::{typenum::Unsigned, GenericArray},
HashMarker, Output,
};
// FSB-160
fsb_impl!(
Fsb160,
Fsb160Core,
160,
U60,
U20,
@@ -79,12 +77,14 @@ fsb_impl!(
640,
653,
1120,
"FSB-160 hash function."
"FSB-160 hasher state",
"Core FSB-160 hasher state",
);
// FSB-224
fsb_impl!(
Fsb224,
Fsb224Core,
224,
U84,
U28,
@@ -93,12 +93,14 @@ fsb_impl!(
896,
907,
1568,
"FSB-224 hash function."
"FSB-224 hasher state",
"Core FSB-224 hasher state",
);
// FSB-256
fsb_impl!(
Fsb256,
Fsb256Core,
256,
U96,
U32,
@@ -107,12 +109,14 @@ fsb_impl!(
1024,
1061,
1792,
"FSB-256 hash function."
"FSB-256 hasher state",
"Core FSB-256 hasher state",
);
// FSB-384
fsb_impl!(
Fsb384,
Fsb384Core,
384,
U115,
U48,
@@ -121,12 +125,14 @@ fsb_impl!(
1472,
1483,
2392,
"FSB-384 hash function."
"FSB-384 hasher state",
"Core FSB-384 hasher state",
);
// FSB-512
fsb_impl!(
Fsb512,
Fsb512Core,
512,
U155,
U64,
@@ -135,5 +141,6 @@ fsb_impl!(
1984,
1987,
3224,
"FSB-512 hash function."
"FSB-512 hasher state",
"Core FSB-512 hasher state",
);

View File

@@ -1,57 +1,94 @@
macro_rules! fsb_impl {
(
$state:ident, $state_num:expr, $blocksize:ident, $outputsize:ident, $n:expr, $w:expr,
$r:expr, $p:expr, $s:expr, $doc:expr
$full_state:ident, $state:ident, $state_num:expr, $blocksize:ident, $outputsize:ident, $n:expr, $w:expr,
$r:expr, $p:expr, $s:expr, $full_doc:expr, $doc:expr,
) => {
use digest::consts::{$blocksize, $outputsize};
#[derive(Clone)]
#[doc=$doc]
pub struct $state {
/// bit size of the message till the current moment (the bit size is represented by a 64 bit
/// number)
bit_length: u64,
/// size of the message being processed
buffer: BlockBuffer<$blocksize>,
/// value of the input vector
hash: [u8; $r / 8],
blocks_len: u64,
state: [u8; $r / 8],
}
impl HashMarker for $state {}
impl BlockSizeUser for $state {
type BlockSize = $blocksize;
}
impl OutputSizeUser for $state {
type OutputSize = $outputsize;
}
impl BufferKindUser for $state {
type BufferKind = Eager;
}
impl UpdateCore for $state {
#[inline]
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
self.blocks_len += blocks.len() as u64;
for block in blocks {
Self::compress(&mut self.state, Self::convert(block));
}
}
}
impl FixedOutputCore for $state {
#[inline]
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
let block_bytes = self.blocks_len * Self::BlockSize::U64;
let bit_len = 8 * (block_bytes + buffer.get_pos() as u64);
let mut h = self.state;
buffer.len64_padding_be(bit_len, |b| Self::compress(&mut h, Self::convert(b)));
let res = whirlpool::Whirlpool::digest(&h[..]);
let n = out.len();
out.copy_from_slice(&res[..n]);
}
}
impl Default for $state {
#[inline]
fn default() -> Self {
Self {
blocks_len: 0u64,
state: [0u8; $r / 8],
}
}
}
impl Reset for $state {
#[inline]
fn reset(&mut self) {
*self = Default::default();
}
}
impl AlgorithmName for $state {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(stringify!($full_state))
}
}
impl fmt::Debug for $state {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(concat!(stringify!($state), " { ... }"))
}
}
#[doc=$full_doc]
pub type $full_state = CoreWrapper<$state>;
impl $state {
// constants
const SIZE_OUTPUT_COMPRESS: usize = $r / 8;
const SIZE_INPUT_COMPRESS: usize = $s / 8;
const HASH_OUTPUT_SIZE: usize = $state_num / 8;
const SIZE_MSG_CHUNKS: usize = Self::SIZE_INPUT_COMPRESS - Self::SIZE_OUTPUT_COMPRESS;
const SIZE_VECTORS: usize = $p / 8 + 1;
const SHIFT: u8 = 8 - ($p % 8) as u8;
fn update_len(&mut self, len: u64) {
self.bit_length += len * 8;
}
fn finalize_inner(&mut self) {
let hash = &mut self.hash;
let pos = self.buffer.position();
if pos < Self::SIZE_MSG_CHUNKS - 8 {
let mut padding = vec![0; Self::SIZE_MSG_CHUNKS - pos - 8];
padding[0] = 128u8;
padding.extend_from_slice(&Self::helper_transform_usize(self.bit_length));
self.buffer
.input_block(&padding, |b| Self::compress(hash, Self::convert(b)));
} else {
let mut padding = vec![0; Self::SIZE_MSG_CHUNKS - pos];
padding[0] = 128u8;
self.buffer
.input_block(&padding, |b| Self::compress(hash, Self::convert(b)));
let mut second_padding = vec![0; Self::SIZE_MSG_CHUNKS - 8];
second_padding
.extend_from_slice(&Self::helper_transform_usize(self.bit_length));
self.buffer
.input_block(&second_padding, |b| Self::compress(hash, Self::convert(b)));
}
}
fn define_iv(index: usize) -> [u8; Self::SIZE_VECTORS] {
let mut subset_pi: [u8; Self::SIZE_VECTORS] = [0u8; Self::SIZE_VECTORS];
subset_pi.copy_from_slice(
@@ -72,21 +109,21 @@ macro_rules! fsb_impl {
/// $(W_i)_{i\in[0;w-1]}$ between $0$ and $n - 1$. The value of each $W_i$ is computed
/// from the inputs bits like this:
/// $W_i = i \times (n / w) + IV_i + M_i \times 2^{r / w}.
fn computing_W_indices(
fn computing_w_indices(
input_vector: &[u8; Self::SIZE_OUTPUT_COMPRESS],
message: &[u8; Self::SIZE_MSG_CHUNKS],
) -> [u32; $w] {
let mut W_indices: [u32; $w] = [0; $w];
let mut wind: [u32; $w] = [0; $w];
let divided_message: [u8; $w] = Self::dividing_bits(message, ($s - $r) / $w);
for i in 0..($w) {
let message_i = divided_message[i] as u32;
W_indices[i] = (i * $n / $w) as u32
wind[i] = (i * $n / $w) as u32
+ input_vector[i] as u32
+ (message_i << ($r / $w) as u8);
}
W_indices
wind
}
/// This function servers the purpose presented in table 3, of breaking a bit array into
@@ -133,7 +170,7 @@ macro_rules! fsb_impl {
) {
let mut initial_vector = [0u8; Self::SIZE_OUTPUT_COMPRESS];
let w_indices = Self::computing_W_indices(hash, message_block);
let w_indices = Self::computing_w_indices(hash, message_block);
for w_index in w_indices.iter() {
let chosen_vec = w_index / $r as u32;
let shift_value = w_index % $r as u32;
@@ -149,18 +186,6 @@ macro_rules! fsb_impl {
*hash = initial_vector;
}
fn final_compression(
initial_vector: [u8; Self::SIZE_OUTPUT_COMPRESS],
) -> [u8; Self::HASH_OUTPUT_SIZE] {
// Now we use Whirpool
let mut result = [0u8; Self::HASH_OUTPUT_SIZE];
let mut hasher = Whirlpool::new();
Update::update(&mut hasher, &initial_vector);
result.copy_from_slice(&hasher.finalize()[..Self::HASH_OUTPUT_SIZE]);
result
}
fn shift_and_truncate(
array: &mut [u8; Self::SIZE_VECTORS],
shift_value: u32,
@@ -170,9 +195,7 @@ macro_rules! fsb_impl {
let mut truncated = [0u8; Self::SIZE_OUTPUT_COMPRESS];
if shift_value == 0 {
array[..Self::SIZE_OUTPUT_COMPRESS]
.try_into()
.expect("SIZE_VECTORS is always bigger than SIZE_OUTPUT_COMPRESS")
truncated.copy_from_slice(&array[..Self::SIZE_OUTPUT_COMPRESS]);
} else if shift_value <= (bits_in_cue as u32) {
let bytes_to_shift = 1;
let starting_byte = (array_len - bytes_to_shift) as usize;
@@ -183,8 +206,6 @@ macro_rules! fsb_impl {
truncated[position] ^= array[position - 1] >> (8 - shift_value);
truncated[position] ^= array[position] << shift_value;
}
truncated
} else {
// First we need to decide which is the last byte and bit that will go to the first position.
// Then, we build our truncated array from there. Recall that the last byte is not complete,
@@ -246,8 +267,6 @@ macro_rules! fsb_impl {
}
}
}
truncated
} else {
truncated[..bytes_to_shift].clone_from_slice(
&array[starting_byte..(starting_byte + bytes_to_shift)],
@@ -261,76 +280,14 @@ macro_rules! fsb_impl {
truncated[position] ^= array[index] << (8 - bits_in_cue);
truncated[position] ^= array[index + 1] >> bits_in_cue;
}
truncated
}
}
}
// I'm trying to avoid use unsafe code for this transformation. We are certain that the bit
// size of the buffer can be represented in 8 bytes.
fn helper_transform_usize(x: u64) -> [u8; 8] {
let b1: u8 = ((x >> 56) & 0xff) as u8;
let b2: u8 = ((x >> 48) & 0xff) as u8;
let b3: u8 = ((x >> 40) & 0xff) as u8;
let b4: u8 = ((x >> 32) & 0xff) as u8;
let b5: u8 = ((x >> 24) & 0xff) as u8;
let b6: u8 = ((x >> 16) & 0xff) as u8;
let b7: u8 = ((x >> 8) & 0xff) as u8;
let b8: u8 = (x & 0xff) as u8;
[b1, b2, b3, b4, b5, b6, b7, b8]
truncated
}
fn convert(block: &GenericArray<u8, $blocksize>) -> &[u8; Self::SIZE_MSG_CHUNKS] {
#[allow(unsafe_code)]
unsafe {
&*(block.as_ptr() as *const [u8; Self::SIZE_MSG_CHUNKS])
}
unsafe { &*(block.as_ptr() as *const [u8; Self::SIZE_MSG_CHUNKS]) }
}
}
impl Default for $state {
fn default() -> Self {
Self {
bit_length: 0u64,
buffer: BlockBuffer::default(),
hash: [0u8; $r / 8],
}
}
}
impl BlockInput for $state {
type BlockSize = $blocksize;
}
impl Update for $state {
fn update(&mut self, input: impl AsRef<[u8]>) {
let input = input.as_ref();
self.update_len(input.len() as u64);
let hash = &mut self.hash;
self.buffer
.input_block(input, |b| $state::compress(hash, $state::convert(b)));
}
}
impl FixedOutputDirty for $state {
type OutputSize = $outputsize;
fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, Self::OutputSize>) {
self.finalize_inner();
let final_whirpool = $state::final_compression(self.hash);
out.copy_from_slice(&final_whirpool)
}
}
impl Reset for $state {
fn reset(&mut self) {
self.buffer.reset();
self.hash = [0u8; $r / 8];
self.bit_length = 0;
}
}
opaque_debug::implement!($state);
digest::impl_write!($state);
};
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,8 +0,0 @@
use digest::dev::digest_test;
use digest::new_test;
new_test!(fsb160_main, "fsb160", fsb::Fsb160, digest_test);
new_test!(fsb224_main, "fsb224", fsb::Fsb224, digest_test);
new_test!(fsb256_main, "fsb256", fsb::Fsb256, digest_test);
new_test!(fsb384_main, "fsb384", fsb::Fsb384, digest_test);
new_test!(fsb512_main, "fsb512", fsb::Fsb512, digest_test);

68
fsb/tests/mod.rs Normal file
View File

@@ -0,0 +1,68 @@
use digest::dev::{feed_rand_16mib, fixed_reset_test};
use digest::new_test;
use fsb::{Digest, Fsb160, Fsb224, Fsb256, Fsb384, Fsb512};
use hex_literal::hex;
new_test!(fsb160_main, "fsb160", Fsb160, fixed_reset_test);
new_test!(fsb224_main, "fsb224", Fsb224, fixed_reset_test);
new_test!(fsb256_main, "fsb256", Fsb256, fixed_reset_test);
new_test!(fsb384_main, "fsb384", Fsb384, fixed_reset_test);
new_test!(fsb512_main, "fsb512", Fsb512, fixed_reset_test);
#[test]
fn fsb160_rand() {
let mut h = Fsb160::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("454b28a8d158ad63ff59e3f761919c7581ee78d3")[..]
);
}
#[test]
fn fsb224_rand() {
let mut h = Fsb224::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("80ef345c462dc88261355eaf44ee2bb7277d01db77b46b2828a918b6")[..]
);
}
#[test]
fn fsb256_rand() {
let mut h = Fsb256::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("301cbfd7031de3568bf4c4ffa86c2295bde89937acc8ee470446b8c55b88334a")[..]
);
}
#[test]
#[rustfmt::skip]
fn fsb384_rand() {
let mut h = Fsb384::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("
d11c0ea4ef363916ad8c2a4d8b4758bf0c36e4de93f2bbaeba037b0726c83179
0ec4e5d9d3e9d66e0810d391a00bf60e
")[..]
);
}
#[test]
#[rustfmt::skip]
fn fsb512_rand() {
let mut h = Fsb512::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("
eb15b6c3626e38141e4f17b3b89d7deed007c4ae727452010601bc4e16deef82
f81415566defb1aba3db9b1b14746bd81cf3689a0f79e6d00434ff4ca19b3e66
")[..]
);
}

View File

@@ -5,6 +5,12 @@ 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.0 (2021-12-07)
### Changed
- Update to `digest` v0.10 ([#217])
[#217]: https://github.com/RustCrypto/hashes/pull/217
## 0.9.1 (2020-11-03)
### Fixed
- Arithmetic overflow bug. ([#193])

View File

@@ -1,6 +1,6 @@
[package]
name = "gost94"
version = "0.9.1"
version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
description = "GOST R 34.11-94 hash function"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
@@ -12,12 +12,10 @@ keywords = ["crypto", "gost94", "gost", "hash", "digest"]
categories = ["cryptography", "no-std"]
[dependencies]
digest = "0.9"
block-buffer = { version = "0.9", features = ["block-padding"] }
opaque-debug = "0.3"
digest = "0.10"
[dev-dependencies]
digest = { version = "0.9", features = ["dev"] }
digest = { version = "0.10", features = ["dev"] }
hex-literal = "0.2"
[features]

View File

@@ -1,4 +0,0 @@
#![no_std]
#![feature(test)]
digest::bench!(gost94::Gost94Test);

14
gost94/benches/mod.rs Normal file
View File

@@ -0,0 +1,14 @@
#![feature(test)]
extern crate test;
use digest::bench_update;
use gost94::Gost94Test;
use test::Bencher;
bench_update!(
Gost94Test::default();
md2_10 10;
md2_100 100;
md2_1000 1000;
md2_10000 10000;
);

View File

@@ -1,47 +0,0 @@
use gost94::{Digest, Gost94CryptoPro};
use std::env;
use std::fs;
use std::io::{self, Read};
const BUFFER_SIZE: usize = 1024;
/// Print digest result as hex string and name pair
fn print_result(sum: &[u8], name: &str) {
for byte in sum {
print!("{:02x}", byte);
}
println!("\t{}", name);
}
/// Compute digest value for given `Reader` and print it
/// On any error simply return without doing anything
fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
let mut sh = D::default();
let mut buffer = [0u8; BUFFER_SIZE];
loop {
let n = match reader.read(&mut buffer) {
Ok(n) => n,
Err(_) => return,
};
sh.update(&buffer[..n]);
if n == 0 || n < BUFFER_SIZE {
break;
}
}
print_result(&sh.finalize(), name);
}
fn main() {
let args = env::args();
// Process files listed in command line arguments one by one
// If no files provided process input from stdin
if args.len() > 1 {
for path in args.skip(1) {
if let Ok(mut file) = fs::File::open(&path) {
process::<Gost94CryptoPro, _>(&mut file, &path);
}
}
} else {
process::<Gost94CryptoPro, _>(&mut io::stdin(), "-");
}
}

View File

@@ -1,47 +0,0 @@
use gost94::{Digest, Gost94Test};
use std::env;
use std::fs;
use std::io::{self, Read};
const BUFFER_SIZE: usize = 1024;
/// Print digest result as hex string and name pair
fn print_result(sum: &[u8], name: &str) {
for byte in sum {
print!("{:02x}", byte);
}
println!("\t{}", name);
}
/// Compute digest value for given `Reader` and print it
/// On any error simply return without doing anything
fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
let mut sh = D::default();
let mut buffer = [0u8; BUFFER_SIZE];
loop {
let n = match reader.read(&mut buffer) {
Ok(n) => n,
Err(_) => return,
};
sh.update(&buffer[..n]);
if n == 0 || n < BUFFER_SIZE {
break;
}
}
print_result(&sh.finalize(), name);
}
fn main() {
let args = env::args();
// Process files listed in command line arguments one by one
// If no files provided process input from stdin
if args.len() > 1 {
for path in args.skip(1) {
if let Ok(mut file) = fs::File::open(&path) {
process::<Gost94Test, _>(&mut file, &path);
}
}
} else {
process::<Gost94Test, _>(&mut io::stdin(), "-");
}
}

View File

@@ -1,12 +0,0 @@
const S_CRYPTO_PRO: SBox = [
[10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15],
[5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8],
[7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13],
[4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3],
[7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5],
[7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3],
[13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11],
[1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12],
];
gost94_impl!(Gost94CryptoPro, S_CRYPTO_PRO);

View File

@@ -1,19 +1,23 @@
#![allow(clippy::many_single_char_names)]
use block_buffer::block_padding::ZeroPadding;
use block_buffer::BlockBuffer;
use core::convert::TryInto;
use digest::{consts::U32, generic_array::GenericArray};
use digest::{BlockInput, FixedOutputDirty, Reset, Update};
use core::{convert::TryInto, fmt};
use digest::{
block_buffer::Eager,
consts::U32,
core_api::{
AlgorithmName, Block as TBlock, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore,
OutputSizeUser, Reset, UpdateCore,
},
generic_array::{typenum::Unsigned, GenericArray},
HashMarker, Output,
};
pub(crate) type Block = [u8; 32];
use crate::params::{Block, Gost94Params, SBox};
const C: Block = [
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00,
0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff,
];
pub type SBox = [[u8; 16]; 8];
fn sbox(a: u32, s: &SBox) -> u32 {
let mut v = 0;
@@ -114,15 +118,23 @@ fn psi(block: &mut Block) {
block.copy_from_slice(&out);
}
#[inline(always)]
fn adc(a: &mut u64, b: u64, carry: &mut u64) {
let ret = (*a as u128) + (b as u128) + (*carry as u128);
*a = ret as u64;
*carry = (ret >> 64) as u64;
}
/// Core GOST94 algorithm generic over parameters.
#[derive(Clone)]
struct Gost94State {
s: SBox,
pub struct Gost94Core<P: Gost94Params> {
h: Block,
n: [u64; 4],
sigma: [u64; 4],
_m: core::marker::PhantomData<P>,
}
impl Gost94State {
impl<P: Gost94Params> Gost94Core<P> {
fn shuffle(&mut self, m: &Block, s: &Block) {
let mut res = Block::default();
res.copy_from_slice(s);
@@ -141,23 +153,23 @@ impl Gost94State {
let mut s = Block::default();
s.copy_from_slice(&self.h);
let k = p(x(&self.h, m));
encrypt(&mut s[0..8], k, &self.s);
encrypt(&mut s[0..8], k, &P::S_BOX);
let u = a(self.h);
let v = a(a(*m));
let k = p(x(&u, &v));
encrypt(&mut s[8..16], k, &self.s);
encrypt(&mut s[8..16], k, &P::S_BOX);
let mut u = a(u);
x_mut(&mut u, &C);
let v = a(a(v));
let k = p(x(&u, &v));
encrypt(&mut s[16..24], k, &self.s);
encrypt(&mut s[16..24], k, &P::S_BOX);
let u = a(u);
let v = a(a(v));
let k = p(x(&u, &v));
encrypt(&mut s[24..32], k, &self.s);
encrypt(&mut s[24..32], k, &P::S_BOX);
self.shuffle(m, &s);
}
@@ -178,92 +190,88 @@ impl Gost94State {
adc(&mut self.n[3], 0, &mut carry);
}
fn process_block(&mut self, block: &GenericArray<u8, U32>) {
#[inline(always)]
fn compress(&mut self, block: &GenericArray<u8, U32>) {
let block = unsafe { &*(block.as_ptr() as *const [u8; 32]) };
self.f(block);
self.update_sigma(block);
}
}
/// GOST94
#[derive(Clone)]
pub struct Gost94 {
buffer: BlockBuffer<U32>,
state: Gost94State,
h0: Block,
}
impl<P: Gost94Params> HashMarker for Gost94Core<P> {}
impl Gost94 {
/// Create new [`Gost94`] instance with given S-Box and IV
pub fn new(s: SBox, h: Block) -> Self {
let n = Default::default();
let sigma = Default::default();
Gost94 {
buffer: Default::default(),
h0: h,
state: Gost94State { s, h, n, sigma },
}
}
}
impl BlockInput for Gost94 {
impl<P: Gost94Params> BlockSizeUser for Gost94Core<P> {
type BlockSize = U32;
}
impl Update for Gost94 {
fn update(&mut self, input: impl AsRef<[u8]>) {
let input = input.as_ref();
let s = &mut self.state;
s.update_n(input.len());
self.buffer.input_block(input, |d| s.process_block(d));
impl<P: Gost94Params> BufferKindUser for Gost94Core<P> {
type BufferKind = Eager;
}
impl<P: Gost94Params> OutputSizeUser for Gost94Core<P> {
type OutputSize = U32;
}
impl<P: Gost94Params> UpdateCore for Gost94Core<P> {
#[inline]
fn update_blocks(&mut self, blocks: &[TBlock<Self>]) {
let len = Self::BlockSize::USIZE * blocks.len();
self.update_n(len);
blocks.iter().for_each(|b| self.compress(b));
}
}
impl FixedOutputDirty for Gost94 {
type OutputSize = U32;
fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, U32>) {
let self_state = &mut self.state;
if self.buffer.position() != 0 {
let block = self
.buffer
.pad_with::<ZeroPadding>()
.expect("we never use input_lazy");
self_state.process_block(block);
impl<P: Gost94Params> FixedOutputCore for Gost94Core<P> {
#[inline]
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
if buffer.get_pos() != 0 {
self.update_n(buffer.get_pos());
self.compress(buffer.pad_with_zeros());
}
let mut buf = Block::default();
for (o, v) in buf.chunks_exact_mut(8).zip(self_state.n.iter()) {
for (o, v) in buf.chunks_exact_mut(8).zip(self.n.iter()) {
o.copy_from_slice(&v.to_le_bytes());
}
self_state.f(&buf);
self.f(&buf);
for (o, v) in buf.chunks_exact_mut(8).zip(self_state.sigma.iter()) {
for (o, v) in buf.chunks_exact_mut(8).zip(self.sigma.iter()) {
o.copy_from_slice(&v.to_le_bytes());
}
self_state.f(&buf);
self.f(&buf);
out.copy_from_slice(&self.state.h);
out.copy_from_slice(&self.h);
}
}
impl Reset for Gost94 {
impl<P: Gost94Params> Default for Gost94Core<P> {
#[inline]
fn default() -> Self {
Self {
h: P::H0,
n: Default::default(),
sigma: Default::default(),
_m: Default::default(),
}
}
}
impl<P: Gost94Params> Reset for Gost94Core<P> {
#[inline]
fn reset(&mut self) {
self.buffer.reset();
self.state.n = Default::default();
self.state.h = self.h0;
self.state.sigma = Default::default();
*self = Default::default();
}
}
#[inline(always)]
fn adc(a: &mut u64, b: u64, carry: &mut u64) {
let ret = (*a as u128) + (b as u128) + (*carry as u128);
*a = ret as u64;
*carry = (ret >> 64) as u64;
impl<P: Gost94Params> AlgorithmName for Gost94Core<P> {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(P::NAME)
}
}
opaque_debug::implement!(Gost94);
digest::impl_write!(Gost94);
impl<P: Gost94Params> fmt::Debug for Gost94Core<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(P::NAME)?;
f.write_str("Core { .. }")
}
}

View File

@@ -1,22 +1,21 @@
//! An implementation of the [GOST R 34.11-94][1] cryptographic hash algorithm.
//!
//! # Usage
//!
//! ```rust
//! use gost94::{Gost94Test, Digest};
//! use gost94::{Gost94CryptoPro, Digest};
//! use hex_literal::hex;
//!
//! // create a Gost94 hasher instance with test S-box
//! let mut hasher = Gost94Test::new();
//! // create Gost94 hasher instance with CryptoPro params
//! let mut hasher = Gost94CryptoPro::new();
//!
//! // process input message
//! hasher.update(b"hello world");
//! hasher.update("The quick brown fox jumps over the lazy dog");
//!
//! // acquire hash digest in the form of GenericArray,
//! // which in this case is equivalent to [u8; 32]
//! let result = hasher.finalize();
//! assert_eq!(result[..], hex!("
//! 1bb6ce69d2e895a78489c87a0712a2f40258d1fae3a4666c23f8f487bef0e22a
//! 9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76
//! "));
//! ```
//!
@@ -27,25 +26,28 @@
#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_root_url = "https://docs.rs/gost94/0.10.0"
)]
#![warn(missing_docs, rust_2018_idioms)]
#[cfg(feature = "std")]
extern crate std;
#[macro_use]
mod macros;
use digest::core_api::CoreWrapper;
mod cryptopro;
mod gost94;
mod s2015;
mod test_param;
mod gost94_core;
/// GOST94 parameters.
pub mod params;
pub use digest::{self, Digest};
pub use crate::cryptopro::Gost94CryptoPro;
pub use crate::gost94::Gost94;
pub use crate::s2015::Gost94s2015;
pub use crate::test_param::Gost94Test;
pub use gost94_core::Gost94Core;
/// GOST94 hash function with CryptoPro parameters.
pub type Gost94CryptoPro = CoreWrapper<Gost94Core<params::CryptoProParam>>;
/// GOST94 hash function with S-box defined in GOST R 34.12-2015.
pub type Gost94s2015 = CoreWrapper<Gost94Core<params::S2015Param>>;
/// GOST94 hash function with test parameters.
pub type Gost94Test = CoreWrapper<Gost94Core<params::TestParam>>;

View File

@@ -1,48 +0,0 @@
macro_rules! gost94_impl {
($state:ident, $sbox:expr) => {
use digest::{consts::U32, BlockInput, FixedOutputDirty, Reset, Update};
use $crate::gost94::{Block, Gost94, SBox};
/// GOST94 state
#[derive(Clone)]
pub struct $state {
sh: Gost94,
}
impl Default for $state {
fn default() -> Self {
$state {
sh: Gost94::new($sbox, Block::default()),
}
}
}
impl BlockInput for $state {
type BlockSize = U32;
}
impl Update for $state {
fn update(&mut self, input: impl AsRef<[u8]>) {
let input = input.as_ref();
self.sh.update(input);
}
}
impl FixedOutputDirty for $state {
type OutputSize = U32;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
self.sh.finalize_into_dirty(out)
}
}
impl Reset for $state {
fn reset(&mut self) {
self.sh.reset()
}
}
opaque_debug::implement!($state);
digest::impl_write!($state);
};
}

70
gost94/src/params.rs Normal file
View File

@@ -0,0 +1,70 @@
pub(crate) type Block = [u8; 32];
pub(crate) type SBox = [[u8; 16]; 8];
/// Trait for storing parameter constants.
// TODO: replace with const generics
pub trait Gost94Params {
/// S-box value.
const S_BOX: SBox;
/// Initialization vector value.
const H0: Block;
/// Algorithm name
const NAME: &'static str;
}
/// CryptoPro parameters.
#[derive(Copy, Clone, Default)]
pub struct CryptoProParam;
impl Gost94Params for CryptoProParam {
const S_BOX: SBox = [
[10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15],
[5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8],
[7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13],
[4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3],
[7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5],
[7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3],
[13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11],
[1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12],
];
const H0: Block = [0; 32];
const NAME: &'static str = "Gost94CryptoPro";
}
/// S-Box defined in GOST R 34.12-2015.
#[derive(Copy, Clone, Default)]
pub struct S2015Param;
impl Gost94Params for S2015Param {
const S_BOX: SBox = [
[12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1],
[6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15],
[11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0],
[12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11],
[7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12],
[5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0],
[8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7],
[1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2],
];
const H0: Block = [0; 32];
const NAME: &'static str = "Gost94s2015";
}
/// Test parameters.
#[derive(Copy, Clone, Default)]
pub struct TestParam;
impl Gost94Params for TestParam {
const S_BOX: SBox = [
[4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3],
[14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9],
[5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11],
[7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3],
[6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2],
[4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14],
[13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12],
[1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12],
];
const H0: Block = [0; 32];
const NAME: &'static str = "Gost94Test";
}

View File

@@ -1,12 +0,0 @@
const S2015: SBox = [
[12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1],
[6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15],
[11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0],
[12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11],
[7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12],
[5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0],
[8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7],
[1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2],
];
gost94_impl!(Gost94s2015, S2015);

View File

@@ -1,12 +0,0 @@
const S_TEST: SBox = [
[4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3],
[14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9],
[5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11],
[7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3],
[6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2],
[4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14],
[13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12],
[1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12],
];
gost94_impl!(Gost94Test, S_TEST);

Binary file not shown.

View File

@@ -1 +0,0 @@
†“(z¦/”x÷Ë1.À†klNJAèôÿÍ'ÝUO

Binary file not shown.

View File

@@ -1,34 +1,41 @@
use digest::dev::{digest_test, one_million_a};
use digest::dev::{feed_rand_16mib, fixed_reset_test};
use digest::new_test;
use gost94::{Digest, Gost94CryptoPro, Gost94Test};
use hex_literal::hex;
new_test!(gost94_test_main, "test", gost94::Gost94Test, digest_test);
new_test!(gost94_test_main, "test", Gost94Test, fixed_reset_test);
new_test!(
gost94_cryptopro_main,
"cryptopro",
gost94::Gost94CryptoPro,
digest_test
Gost94CryptoPro,
fixed_reset_test
);
#[test]
fn gost94_test_1million_a() {
let output = include_bytes!("data/test_one_million_a.bin");
one_million_a::<gost94::Gost94Test>(output);
fn gost94_test_rand() {
let mut h = Gost94Test::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("fdd1b9f220898c117f82d664716795e12f5e9f458ee8cd71d014329438db5089")[..]
);
}
#[test]
fn gost94_cryptopro_1million_a() {
let output = include_bytes!("data/cryptopro_one_million_a.bin");
one_million_a::<gost94::Gost94CryptoPro>(output);
fn gost94_cryptopro_rand() {
let mut h = Gost94CryptoPro::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("1d539ea8a318df8c13d304fcfd9beeec188bb48683d9d7f4c4a3750cff6ef22a")[..]
);
}
/// Test vectors from:
/// https://github.com/gost-engine/engine/blob/master/test/01-digest.t
#[test]
fn gost_engine_tests() {
use digest::Digest;
use hex_literal::hex;
let mut h = gost94::Gost94CryptoPro::new();
let mut h = Gost94CryptoPro::new();
for _ in 0..128 {
h.update(b"12345670");
}
@@ -70,9 +77,7 @@ fn gost_engine_tests() {
#[test]
fn arithmetic_overflow_regression() {
use digest::Digest;
let mut h = gost94::Gost94Test::default();
let mut h = Gost94Test::default();
h.update(&include_bytes!("data/arithmetic_overflow.bin")[..]);
h.finalize().as_slice();
}

View File

@@ -5,6 +5,12 @@ 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.0 (2021-12-07)
### Changed
- Update to `digest` v0.10 and significantly improve performance ([#217])
[#217]: https://github.com/RustCrypto/hashes/pull/217
## 0.9.0 (2020-06-12)
### Changed
- Bump `opaque-debug` to v0.3.0 ([#168])

View File

@@ -1,6 +1,6 @@
[package]
name = "groestl"
version = "0.9.0"
version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
description = "Grøstl hash function"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
@@ -12,12 +12,10 @@ keywords = ["crypto", "groestl", "grostl", "hash", "digest"]
categories = ["cryptography", "no-std"]
[dependencies]
digest = "0.9"
block-buffer = "0.9"
opaque-debug = "0.3"
digest = "0.10"
[dev-dependencies]
digest = { version = "0.9", features = ["dev"] }
digest = { version = "0.10", features = ["dev"] }
hex-literal = "0.2"
[features]

View File

@@ -1,4 +1,4 @@
Copyright (c) 2017 Gulshan Singh
Copyright (c) 2020 RustCrypto Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated

View File

@@ -1,4 +0,0 @@
#![no_std]
#![feature(test)]
digest::bench!(groestl::Groestl256);

View File

@@ -1,4 +0,0 @@
#![no_std]
#![feature(test)]
digest::bench!(groestl::Groestl512);

22
groestl/benches/mod.rs Normal file
View File

@@ -0,0 +1,22 @@
#![feature(test)]
extern crate test;
use digest::bench_update;
use groestl::{Groestl256, Groestl512};
use test::Bencher;
bench_update!(
Groestl256::default();
groestl256_10 10;
groestl256_100 100;
groestl256_1000 1000;
groestl256_10000 10000;
);
bench_update!(
Groestl512::default();
groestl512_10 10;
groestl512_100 100;
groestl512_1000 1000;
groestl512_10000 10000;
);

View File

@@ -0,0 +1,99 @@
#![allow(clippy::needless_range_loop)]
use crate::table::TABLE;
use core::{convert::TryInto, u64};
pub(crate) const COLS: usize = 16;
const ROUNDS: u64 = 14;
type Block = super::Block<super::GroestlLongVarCore>;
#[inline(always)]
fn column(x: &[u64; COLS], c: [usize; 8]) -> u64 {
let mut t = 0;
for i in 0..8 {
let sl = 8 * (7 - i);
let idx = ((x[c[i]] >> sl) & 0xFF) as usize;
t ^= TABLE[i][idx];
}
t
}
#[inline(always)]
fn rndq(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
for i in 0..COLS {
x[i] ^= u64::MAX.wrapping_sub((i as u64) << 4) ^ r;
}
[
column(&x, [1, 3, 5, 11, 0, 2, 4, 6]),
column(&x, [2, 4, 6, 12, 1, 3, 5, 7]),
column(&x, [3, 5, 7, 13, 2, 4, 6, 8]),
column(&x, [4, 6, 8, 14, 3, 5, 7, 9]),
column(&x, [5, 7, 9, 15, 4, 6, 8, 10]),
column(&x, [6, 8, 10, 0, 5, 7, 9, 11]),
column(&x, [7, 9, 11, 1, 6, 8, 10, 12]),
column(&x, [8, 10, 12, 2, 7, 9, 11, 13]),
column(&x, [9, 11, 13, 3, 8, 10, 12, 14]),
column(&x, [10, 12, 14, 4, 9, 11, 13, 15]),
column(&x, [11, 13, 15, 5, 10, 12, 14, 0]),
column(&x, [12, 14, 0, 6, 11, 13, 15, 1]),
column(&x, [13, 15, 1, 7, 12, 14, 0, 2]),
column(&x, [14, 0, 2, 8, 13, 15, 1, 3]),
column(&x, [15, 1, 3, 9, 14, 0, 2, 4]),
column(&x, [0, 2, 4, 10, 15, 1, 3, 5]),
]
}
#[inline(always)]
fn rndp(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
for i in 0..COLS {
x[i] ^= ((i as u64) << 60) ^ r;
}
[
column(&x, [0, 1, 2, 3, 4, 5, 6, 11]),
column(&x, [1, 2, 3, 4, 5, 6, 7, 12]),
column(&x, [2, 3, 4, 5, 6, 7, 8, 13]),
column(&x, [3, 4, 5, 6, 7, 8, 9, 14]),
column(&x, [4, 5, 6, 7, 8, 9, 10, 15]),
column(&x, [5, 6, 7, 8, 9, 10, 11, 0]),
column(&x, [6, 7, 8, 9, 10, 11, 12, 1]),
column(&x, [7, 8, 9, 10, 11, 12, 13, 2]),
column(&x, [8, 9, 10, 11, 12, 13, 14, 3]),
column(&x, [9, 10, 11, 12, 13, 14, 15, 4]),
column(&x, [10, 11, 12, 13, 14, 15, 0, 5]),
column(&x, [11, 12, 13, 14, 15, 0, 1, 6]),
column(&x, [12, 13, 14, 15, 0, 1, 2, 7]),
column(&x, [13, 14, 15, 0, 1, 2, 3, 8]),
column(&x, [14, 15, 0, 1, 2, 3, 4, 9]),
column(&x, [15, 0, 1, 2, 3, 4, 5, 10]),
]
}
pub(crate) fn compress(h: &mut [u64; COLS], block: &Block) {
let mut q = [0u64; COLS];
for (chunk, v) in block.chunks_exact(8).zip(q.iter_mut()) {
*v = u64::from_be_bytes(chunk.try_into().unwrap());
}
let mut p = [0u64; COLS];
for i in 0..COLS {
p[i] = h[i] ^ q[i];
}
for i in 0..ROUNDS {
q = rndq(q, i);
}
for i in 0..ROUNDS {
p = rndp(p, i << 56);
}
for i in 0..COLS {
h[i] ^= q[i] ^ p[i];
}
}
pub(crate) fn p(h: &[u64; COLS]) -> [u64; COLS] {
let mut p = *h;
for i in 0..ROUNDS {
p = rndp(p, i << 56);
}
for i in 0..COLS {
p[i] ^= h[i];
}
p
}

View File

@@ -0,0 +1,83 @@
#![allow(clippy::needless_range_loop)]
use crate::table::TABLE;
use core::{convert::TryInto, u64};
type Block = super::Block<super::GroestlShortVarCore>;
pub(crate) const COLS: usize = 8;
const ROUNDS: u64 = 10;
#[inline(always)]
fn column(x: &[u64; COLS], c: [usize; 8]) -> u64 {
let mut t = 0;
for i in 0..8 {
let sl = 8 * (7 - i);
let idx = ((x[c[i]] >> sl) & 0xFF) as usize;
t ^= TABLE[i][idx];
}
t
}
#[inline(always)]
fn rndq(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
for i in 0..COLS {
x[i] ^= u64::MAX.wrapping_sub((i as u64) << 4) ^ r;
}
[
column(&x, [1, 3, 5, 7, 0, 2, 4, 6]),
column(&x, [2, 4, 6, 0, 1, 3, 5, 7]),
column(&x, [3, 5, 7, 1, 2, 4, 6, 0]),
column(&x, [4, 6, 0, 2, 3, 5, 7, 1]),
column(&x, [5, 7, 1, 3, 4, 6, 0, 2]),
column(&x, [6, 0, 2, 4, 5, 7, 1, 3]),
column(&x, [7, 1, 3, 5, 6, 0, 2, 4]),
column(&x, [0, 2, 4, 6, 7, 1, 3, 5]),
]
}
#[inline(always)]
fn rndp(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
for i in 0..COLS {
x[i] ^= ((i as u64) << 60) ^ r;
}
[
column(&x, [0, 1, 2, 3, 4, 5, 6, 7]),
column(&x, [1, 2, 3, 4, 5, 6, 7, 0]),
column(&x, [2, 3, 4, 5, 6, 7, 0, 1]),
column(&x, [3, 4, 5, 6, 7, 0, 1, 2]),
column(&x, [4, 5, 6, 7, 0, 1, 2, 3]),
column(&x, [5, 6, 7, 0, 1, 2, 3, 4]),
column(&x, [6, 7, 0, 1, 2, 3, 4, 5]),
column(&x, [7, 0, 1, 2, 3, 4, 5, 6]),
]
}
pub(crate) fn compress(h: &mut [u64; COLS], block: &Block) {
let mut q = [0u64; COLS];
for (chunk, v) in block.chunks_exact(8).zip(q.iter_mut()) {
*v = u64::from_be_bytes(chunk.try_into().unwrap());
}
let mut p = [0u64; COLS];
for i in 0..COLS {
p[i] = h[i] ^ q[i];
}
for i in 0..ROUNDS {
q = rndq(q, i);
}
for i in 0..ROUNDS {
p = rndp(p, i << 56);
}
for i in 0..COLS {
h[i] ^= q[i] ^ p[i];
}
}
pub(crate) fn p(h: &[u64; COLS]) -> [u64; COLS] {
let mut p = *h;
for i in 0..ROUNDS {
p = rndp(p, i << 56);
}
for i in 0..COLS {
p[i] ^= h[i];
}
p
}

View File

@@ -1,55 +0,0 @@
pub const SBOX: [u8; 256] = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
];
pub const C_P: [u8; 128] = [
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
pub const C_Q: [u8; 128] = [
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xef, 0xdf, 0xcf, 0xbf, 0xaf, 0x9f, 0x8f, 0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f, 0x0f,
];
pub const B: [[u8; 8]; 8] = [
[2, 2, 3, 4, 5, 3, 5, 7],
[7, 2, 2, 3, 4, 5, 3, 5],
[5, 7, 2, 2, 3, 4, 5, 3],
[3, 5, 7, 2, 2, 3, 4, 5],
[5, 3, 5, 7, 2, 2, 3, 4],
[4, 5, 3, 5, 7, 2, 2, 3],
[3, 4, 5, 3, 5, 7, 2, 2],
[2, 3, 4, 5, 3, 5, 7, 2],
];
pub const SHIFTS_P: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
pub const SHIFTS_Q: [u8; 8] = [1, 3, 5, 7, 0, 2, 4, 6];
pub const SHIFTS_P_WIDE: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 11];
pub const SHIFTS_Q_WIDE: [u8; 8] = [1, 3, 5, 11, 0, 2, 4, 6];

View File

@@ -1,76 +0,0 @@
use block_buffer::BlockBuffer;
use core::ops::Div;
use digest::generic_array::typenum::{Quot, U8};
use digest::generic_array::{ArrayLength, GenericArray};
use crate::state::{xor_generic_array, GroestlState};
#[derive(Clone)]
pub struct Groestl<BlockSize>
where
BlockSize: ArrayLength<u8> + Div<U8> + Default,
BlockSize::ArrayType: Copy,
Quot<BlockSize, U8>: ArrayLength<u8>,
{
buffer: BlockBuffer<BlockSize>,
state: GroestlState<BlockSize>,
pub output_size: usize,
}
impl<BlockSize> Groestl<BlockSize>
where
BlockSize: ArrayLength<u8> + Div<U8> + Default,
BlockSize::ArrayType: Copy,
Quot<BlockSize, U8>: ArrayLength<u8>,
Self: Clone,
{
pub fn new(output_size: usize) -> Result<Self, digest::InvalidOutputSize> {
match BlockSize::to_usize() {
128 => {
if output_size <= 32 || output_size > 64 {
return Err(digest::InvalidOutputSize);
}
}
64 => {
if output_size == 0 || output_size > 32 {
return Err(digest::InvalidOutputSize);
}
}
_ => unreachable!(),
};
let state = GroestlState::new(output_size);
Ok(Groestl {
buffer: Default::default(),
state,
output_size,
})
}
pub fn process(&mut self, input: &[u8]) {
let s = &mut self.state;
self.buffer.input_block(input, |b| s.compress(b));
}
pub fn finalize(&mut self) -> GenericArray<u8, BlockSize> {
let res = {
let state = &mut self.state;
let l = if self.buffer.remaining() <= 8 {
state.num_blocks + 2
} else {
state.num_blocks + 1
};
self.buffer.len64_padding_be(l, |b| state.compress(b));
xor_generic_array(&state.p(&state.state), &state.state)
};
self.buffer = Default::default();
self.state = GroestlState::new(self.output_size);
res
}
pub fn reset(&mut self) {
self.state = GroestlState::new(self.output_size);
self.buffer.reset();
}
}

217
groestl/src/lib.rs Executable file → Normal file
View File

@@ -2,14 +2,7 @@
//!
//! # Usage
//!
//! Groestl can produce a digest of any size between 1 and 64 bytes inclusive.
//! This crate defines the common digest sizes (`Groestl224`, `Groestl256`,
//! `Groestl384`, and `Groestl512`), but allows you to specify a custom size
//! with the `GroestlSmall` and `GroestlBig` structs. `GroestlSmall` allows you
//! to specify a digest size between 1 and 32 inclusive, and `GroestlBig` allows
//! you to specify a digest size between 33 and 64 inclusive.
//!
//! ```rust
//! ```
//! use groestl::{Digest, Groestl256};
//! use hex_literal::hex;
//!
@@ -34,33 +27,199 @@
#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_root_url = "https://docs.rs/groestl/0.10.0"
)]
#![deny(unsafe_code)]
#![warn(rust_2018_idioms)]
#[cfg(feature = "std")]
extern crate std;
pub use digest::{self, Digest};
mod consts;
mod groestl;
mod matrix;
mod state;
#[macro_use]
mod macros;
use core::fmt;
use digest::{
block_buffer::Eager,
core_api::{
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper,
CtVariableCoreWrapper, OutputSizeUser, RtVariableCoreWrapper, TruncSide, UpdateCore,
VariableOutputCore,
},
generic_array::typenum::{Unsigned, U128, U28, U32, U48, U64},
HashMarker, InvalidOutputSize, Output,
};
use crate::groestl::Groestl;
use digest::consts::{U128, U28, U32, U48, U64};
use digest::generic_array::typenum::Unsigned;
use digest::{BlockInput, FixedOutputDirty, InvalidOutputSize, Reset, Update, VariableOutputDirty};
mod compress1024;
mod compress512;
mod table;
impl_groestl!(Groestl512, U64, U128);
impl_groestl!(Groestl384, U48, U128);
impl_groestl!(Groestl256, U32, U64);
impl_groestl!(Groestl224, U28, U64);
/// Lowest-level core hasher state of the short Groestl variant.
#[derive(Clone)]
pub struct GroestlShortVarCore {
state: [u64; compress512::COLS],
blocks_len: u64,
}
impl_variable_groestl!(GroestlBig, U128, 32, 64);
impl_variable_groestl!(GroestlSmall, U64, 0, 32);
impl HashMarker for GroestlShortVarCore {}
impl BlockSizeUser for GroestlShortVarCore {
type BlockSize = U64;
}
impl BufferKindUser for GroestlShortVarCore {
type BufferKind = Eager;
}
impl UpdateCore for GroestlShortVarCore {
#[inline]
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
self.blocks_len += blocks.len() as u64;
for block in blocks {
compress512::compress(&mut self.state, block);
}
}
}
impl OutputSizeUser for GroestlShortVarCore {
type OutputSize = U32;
}
impl VariableOutputCore for GroestlShortVarCore {
const TRUNC_SIDE: TruncSide = TruncSide::Right;
#[inline]
fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
if output_size > Self::OutputSize::USIZE {
return Err(InvalidOutputSize);
}
let mut state = [0; compress512::COLS];
state[compress512::COLS - 1] = 8 * output_size as u64;
let blocks_len = 0;
Ok(Self { state, blocks_len })
}
#[inline]
fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
let blocks_len = if buffer.remaining() <= 8 {
self.blocks_len + 2
} else {
self.blocks_len + 1
};
buffer.len64_padding_be(blocks_len, |b| compress512::compress(&mut self.state, b));
let res = compress512::p(&self.state);
let n = compress512::COLS / 2;
for (chunk, v) in out.chunks_exact_mut(8).zip(res[n..].iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
}
}
impl AlgorithmName for GroestlShortVarCore {
#[inline]
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GroestlShort")
}
}
impl fmt::Debug for GroestlShortVarCore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GroestlShortVarCore { ... }")
}
}
/// Short Groestl variant which allows to choose output size at runtime.
pub type GroestlShortVar = RtVariableCoreWrapper<GroestlShortVarCore>;
/// Core hasher state of the short Groestl variant generic over output size.
pub type GroestlShortCore<OutSize> = CtVariableCoreWrapper<GroestlShortVarCore, OutSize>;
/// Hasher state of the short Groestl variant generic over output size.
pub type GroestlShort<OutSize> = CoreWrapper<GroestlShortCore<OutSize>>;
/// Groestl-224 hasher state.
pub type Groestl224 = CoreWrapper<GroestlShortCore<U28>>;
/// Groestl-256 hasher state.
pub type Groestl256 = CoreWrapper<GroestlShortCore<U32>>;
/// Lowest-level core hasher state of the long Groestl variant.
#[derive(Clone)]
pub struct GroestlLongVarCore {
state: [u64; compress1024::COLS],
blocks_len: u64,
}
impl HashMarker for GroestlLongVarCore {}
impl BlockSizeUser for GroestlLongVarCore {
type BlockSize = U128;
}
impl BufferKindUser for GroestlLongVarCore {
type BufferKind = Eager;
}
impl UpdateCore for GroestlLongVarCore {
#[inline]
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
self.blocks_len += blocks.len() as u64;
for block in blocks {
compress1024::compress(&mut self.state, block);
}
}
}
impl OutputSizeUser for GroestlLongVarCore {
type OutputSize = U64;
}
impl VariableOutputCore for GroestlLongVarCore {
const TRUNC_SIDE: TruncSide = TruncSide::Right;
#[inline]
fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
if output_size > Self::OutputSize::USIZE {
return Err(InvalidOutputSize);
}
let mut state = [0; compress1024::COLS];
state[compress1024::COLS - 1] = 8 * output_size as u64;
let blocks_len = 0;
Ok(Self { state, blocks_len })
}
#[inline]
fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
let blocks_len = if buffer.remaining() <= 8 {
self.blocks_len + 2
} else {
self.blocks_len + 1
};
buffer.len64_padding_be(blocks_len, |b| compress1024::compress(&mut self.state, b));
let res = compress1024::p(&self.state);
let n = compress1024::COLS / 2;
for (chunk, v) in out.chunks_exact_mut(8).zip(res[n..].iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
}
}
impl AlgorithmName for GroestlLongVarCore {
#[inline]
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GroestlLong")
}
}
impl fmt::Debug for GroestlLongVarCore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("GroestlLongVarCore { ... }")
}
}
/// Long Groestl variant which allows to choose output size at runtime.
pub type GroestlLongVar = RtVariableCoreWrapper<GroestlLongVarCore>;
/// Core hasher state of the long Groestl variant generic over output size.
pub type GroestlLongCore<OutSize> = CtVariableCoreWrapper<GroestlLongVarCore, OutSize>;
/// Hasher state of the long Groestl variant generic over output size.
pub type GroestlLong<OutSize> = CoreWrapper<GroestlLongCore<OutSize>>;
/// Groestl-384 hasher state.
pub type Groestl384 = CoreWrapper<GroestlLongCore<U48>>;
/// Groestl-512 hasher state.
pub type Groestl512 = CoreWrapper<GroestlLongCore<U64>>;

View File

@@ -1,95 +0,0 @@
macro_rules! impl_groestl {
($state:ident, $output:ident, $block:ident) => {
#[derive(Clone)]
pub struct $state {
groestl: Groestl<$block>,
}
impl Default for $state {
fn default() -> Self {
$state {
groestl: Groestl::new($output::to_usize()).unwrap(),
}
}
}
impl BlockInput for $state {
type BlockSize = $block;
}
impl Update for $state {
fn update(&mut self, input: impl AsRef<[u8]>) {
let input = input.as_ref();
self.groestl.process(input);
}
}
impl FixedOutputDirty for $state {
type OutputSize = $output;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
let block = self.groestl.finalize();
let n = block.len() - Self::OutputSize::to_usize();
out.copy_from_slice(&block[n..])
}
}
impl Reset for $state {
fn reset(&mut self) {
self.groestl.reset()
}
}
opaque_debug::implement!($state);
digest::impl_write!($state);
};
}
macro_rules! impl_variable_groestl {
($state:ident, $block:ident, $min:expr, $max:expr) => {
#[derive(Clone)]
pub struct $state {
groestl: Groestl<$block>,
}
impl BlockInput for $state {
type BlockSize = $block;
}
impl Update for $state {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.groestl.process(input.as_ref());
}
}
impl VariableOutputDirty for $state {
fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
if output_size == $min || output_size > $max {
return Err(InvalidOutputSize);
}
Ok($state {
groestl: Groestl::new(output_size).unwrap(),
})
}
fn output_size(&self) -> usize {
self.groestl.output_size
}
fn finalize_variable_dirty(&mut self, f: impl FnOnce(&[u8])) {
let block = self.groestl.finalize();
let n = block.len() - self.groestl.output_size;
f(&block[n..]);
}
}
impl Reset for $state {
fn reset(&mut self) {
self.groestl.reset()
}
}
opaque_debug::implement!($state);
digest::impl_write!($state);
};
}

View File

@@ -1,87 +0,0 @@
use core::ops::{Index, IndexMut};
use digest::generic_array::{ArrayLength, GenericArray};
#[derive(Debug, Eq, PartialEq)]
pub struct Matrix<R: ArrayLength<GenericArray<u8, C>>, C: ArrayLength<u8>> {
pub state: GenericArray<GenericArray<u8, C>, R>,
}
impl<R, C> Default for Matrix<R, C>
where
R: ArrayLength<GenericArray<u8, C>>,
C: ArrayLength<u8>,
{
fn default() -> Self {
Matrix {
state: GenericArray::default(),
}
}
}
impl<R, C> Index<usize> for Matrix<R, C>
where
R: ArrayLength<GenericArray<u8, C>>,
C: ArrayLength<u8>,
{
type Output = GenericArray<u8, C>;
fn index(&self, index: usize) -> &Self::Output {
&self.state[index]
}
}
impl<R, C> IndexMut<usize> for Matrix<R, C>
where
R: ArrayLength<GenericArray<u8, C>>,
C: ArrayLength<u8>,
{
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.state[index]
}
}
fn poly_mul(a: u8, b: usize) -> usize {
let mut val = match a {
2 => b << 1,
3 => b ^ poly_mul(2, b),
4 => b << 2,
5 => b ^ poly_mul(4, b),
7 => b ^ poly_mul(2, b) ^ poly_mul(4, b),
_ => unreachable!(),
};
if val >= 512 {
val ^= 0x11b << 1;
}
if val >= 256 {
val ^= 0x11b;
}
val
}
impl<R, C> Matrix<R, C>
where
R: ArrayLength<GenericArray<u8, C>>,
C: ArrayLength<u8>,
{
pub fn rows(&self) -> usize {
R::to_usize()
}
pub fn cols(&self) -> usize {
C::to_usize()
}
pub fn mul_array(&self, a: &[[u8; 8]; 8]) -> Self {
let mut res = Matrix::default();
for i in 0..8 {
for j in 0..self.cols() {
for k in 0..8 {
res[i][j] ^= poly_mul(a[i][k], self[k][j] as usize) as u8;
}
}
}
res
}
}

View File

@@ -1,293 +0,0 @@
use core::ops::Div;
use crate::consts::{B, C_P, C_Q, SBOX, SHIFTS_P, SHIFTS_P_WIDE, SHIFTS_Q, SHIFTS_Q_WIDE};
use crate::matrix::Matrix;
use digest::generic_array::typenum::{Quot, U8};
use digest::generic_array::{ArrayLength, GenericArray};
#[derive(Copy, Clone)]
pub struct GroestlState<BlockSize>
where
BlockSize: ArrayLength<u8> + Div<U8>,
BlockSize::ArrayType: Copy,
Quot<BlockSize, U8>: ArrayLength<u8>,
{
pub state: GenericArray<u8, BlockSize>,
rounds: u8,
pub num_blocks: u64,
}
pub fn xor_generic_array<L: ArrayLength<u8>>(
a1: &GenericArray<u8, L>,
a2: &GenericArray<u8, L>,
) -> GenericArray<u8, L> {
let mut res = GenericArray::default();
for i in 0..L::to_usize() {
res[i] = a1[i] ^ a2[i];
}
res
}
fn gcd(a: usize, b: usize) -> usize {
if b == 0 {
return a;
}
gcd(b, a % b)
}
impl<BlockSize> GroestlState<BlockSize>
where
BlockSize: ArrayLength<u8> + Div<U8>,
BlockSize::ArrayType: Copy,
Quot<BlockSize, U8>: ArrayLength<u8>,
{
pub fn new(output_size: usize) -> Self {
let block_bytes = BlockSize::to_usize();
let output_bits = output_size * 8;
let mut state = GenericArray::default();
let n = output_bits as u64;
state[block_bytes - 8..].copy_from_slice(&n.to_be_bytes());
let rounds = match block_bytes {
128 => 14,
64 => 10,
_ => unreachable!(),
};
GroestlState {
state,
rounds,
num_blocks: 0,
}
}
fn wide(&self) -> bool {
match BlockSize::to_usize() {
128 => true,
64 => false,
_ => unreachable!(),
}
}
pub fn compress(&mut self, input_block: &GenericArray<u8, BlockSize>) {
self.state = xor_generic_array(
&xor_generic_array(
&self.p(&xor_generic_array(&self.state, input_block)),
&self.q(input_block),
),
&self.state,
);
self.num_blocks += 1;
}
fn block_to_matrix(
&self,
block: &GenericArray<u8, BlockSize>,
) -> Matrix<U8, Quot<BlockSize, U8>> {
let mut matrix = Matrix::<U8, Quot<BlockSize, U8>>::default();
let rows = matrix.rows();
for i in 0..matrix.cols() {
for j in 0..rows {
matrix[j][i] = block[i * rows + j];
}
}
matrix
}
fn matrix_to_block(
&self,
matrix: &Matrix<U8, Quot<BlockSize, U8>>,
) -> GenericArray<u8, BlockSize> {
let mut block = GenericArray::default();
let rows = matrix.rows();
for i in 0..matrix.cols() {
for j in 0..rows {
block[i * rows + j] = matrix[j][i];
}
}
block
}
pub fn p(&self, block: &GenericArray<u8, BlockSize>) -> GenericArray<u8, BlockSize> {
let shifts = if self.wide() { SHIFTS_P_WIDE } else { SHIFTS_P };
let mut matrix = self.block_to_matrix(block);
for round in 0..self.rounds {
self.add_round_constant(&mut matrix, C_P, round);
self.sub_bytes(&mut matrix);
self.shift_bytes(&mut matrix, shifts);
matrix = matrix.mul_array(&B);
}
self.matrix_to_block(&matrix)
}
fn q(&self, block: &GenericArray<u8, BlockSize>) -> GenericArray<u8, BlockSize> {
let shifts = if self.wide() { SHIFTS_Q_WIDE } else { SHIFTS_Q };
let mut matrix = self.block_to_matrix(block);
for round in 0..self.rounds {
self.add_round_constant(&mut matrix, C_Q, round);
self.sub_bytes(&mut matrix);
self.shift_bytes(&mut matrix, shifts);
matrix = matrix.mul_array(&B);
}
self.matrix_to_block(&matrix)
}
fn add_round_constant(
&self,
matrix: &mut Matrix<U8, Quot<BlockSize, U8>>,
c: [u8; 128],
round: u8,
) {
for i in 0..matrix.rows() {
for j in 0..matrix.cols() {
matrix[i][j] ^= c[i * 16 + j];
if (c[0] == 0x00 && i == 0) || (c[0] == 0xff && i == 7) {
matrix[i][j] ^= round;
}
}
}
}
fn sub_bytes(&self, matrix: &mut Matrix<U8, Quot<BlockSize, U8>>) {
for i in 0..matrix.rows() {
for j in 0..matrix.cols() {
matrix[i][j] = SBOX[matrix[i][j] as usize];
}
}
}
fn shift_bytes(&self, matrix: &mut Matrix<U8, Quot<BlockSize, U8>>, shifts: [u8; 8]) {
let cols = matrix.cols();
for i in 0..matrix.rows() {
let shift = shifts[i] as usize;
if shift == 0 {
continue;
}
let d = gcd(shift, cols);
for j in 0..d {
let mut k = j;
let tmp = matrix[i][k];
loop {
let pos = k.wrapping_add(shift) % cols;
if pos == j {
break;
}
matrix[i][k] = matrix[i][pos];
k = pos;
}
matrix[i][k] = tmp;
}
}
}
}
#[cfg(test)]
mod test {
use super::{xor_generic_array, GroestlState};
use crate::consts::{C_P, C_Q, SHIFTS_P};
use digest::generic_array::typenum::U64;
use digest::generic_array::GenericArray;
fn get_padding_block() -> GenericArray<u8, U64> {
let padding_block: [u8; 64] = [
128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1,
];
GenericArray::clone_from_slice(&padding_block)
}
#[test]
fn test_shift_bytes() {
let s = GroestlState::<U64>::new(32);
let mut block = GenericArray::default();
for i in 0..64 {
block[i] = i as u8;
}
let mut matrix = s.block_to_matrix(&block);
s.shift_bytes(&mut matrix, SHIFTS_P);
let block = s.matrix_to_block(&matrix);
let expected = [
0, 9, 18, 27, 36, 45, 54, 63, 8, 17, 26, 35, 44, 53, 62, 7, 16, 25, 34, 43, 52, 61, 6,
15, 24, 33, 42, 51, 60, 5, 14, 23, 32, 41, 50, 59, 4, 13, 22, 31, 40, 49, 58, 3, 12,
21, 30, 39, 48, 57, 2, 11, 20, 29, 38, 47, 56, 1, 10, 19, 28, 37, 46, 55,
];
assert_eq!(&block[..], &expected[..]);
}
#[test]
fn test_p() {
let padding_chunk = get_padding_block();
let s = GroestlState::<U64>::new(32);
let block = xor_generic_array(&s.state, GenericArray::from_slice(&padding_chunk));
let p_block = s.p(&block);
let expected = [
247, 236, 141, 217, 73, 225, 112, 216, 1, 155, 85, 192, 152, 168, 174, 72, 112, 253,
159, 53, 7, 6, 8, 115, 58, 242, 7, 115, 148, 150, 157, 25, 18, 220, 11, 5, 178, 10,
110, 94, 44, 56, 110, 67, 107, 234, 102, 163, 243, 212, 49, 25, 46, 17, 170, 84, 5, 76,
239, 51, 4, 107, 94, 20,
];
assert_eq!(&p_block[..], &expected[..]);
}
#[test]
fn test_q() {
let padding_chunk = get_padding_block();
let s = GroestlState::<U64>::new(32);
let q_block = s.q(GenericArray::from_slice(&padding_chunk));
let expected = [
189, 183, 105, 133, 208, 106, 34, 36, 82, 37, 180, 250, 229, 59, 230, 223, 215, 245,
53, 117, 167, 139, 150, 186, 210, 17, 220, 57, 116, 134, 209, 51, 124, 108, 84, 91, 79,
103, 148, 27, 135, 183, 144, 226, 59, 242, 87, 81, 109, 211, 84, 185, 192, 172, 88,
210, 8, 121, 31, 242, 158, 227, 207, 13,
];
assert_eq!(&q_block[..], &expected[..]);
}
#[test]
fn test_block_to_matrix() {
let s = GroestlState::<U64>::new(32);
let mut block1 = GenericArray::default();
for i in 0..block1.len() {
block1[i] = i as u8;
}
let m = s.block_to_matrix(&block1);
let block2 = s.matrix_to_block(&m);
assert_eq!(block1, block2);
}
#[test]
fn test_add_round_constant() {
let padding_chunk = get_padding_block();
let s = GroestlState::<U64>::new(32);
let mut m = s.block_to_matrix(GenericArray::from_slice(&padding_chunk));
s.add_round_constant(&mut m, C_P, 0);
let b = s.matrix_to_block(&m);
let expected = [
128, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0,
0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0,
0, 112, 0, 0, 0, 0, 0, 0, 1,
];
assert_eq!(&b[..], &expected[..]);
let mut m = s.block_to_matrix(GenericArray::from_slice(&padding_chunk));
s.add_round_constant(&mut m, C_Q, 0);
let b = s.matrix_to_block(&m);
let expected = [
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e,
];
assert_eq!(&b[..], &expected[..]);
}
}

11
groestl/src/table.rs Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,28 +0,0 @@
#![no_std]
use digest::{dev::digest_test, new_test};
new_test!(
groestl_224_main,
"groestl224",
groestl::Groestl224,
digest_test
);
new_test!(
groestl_256_main,
"groestl256",
groestl::Groestl256,
digest_test
);
new_test!(
groestl_384_main,
"groestl384",
groestl::Groestl384,
digest_test
);
new_test!(
groestl_512_main,
"groestl512",
groestl::Groestl512,
digest_test
);

57
groestl/tests/mod.rs Executable file
View File

@@ -0,0 +1,57 @@
use digest::dev::{feed_rand_16mib, fixed_reset_test};
use digest::new_test;
use groestl::{Digest, Groestl224, Groestl256, Groestl384, Groestl512};
use hex_literal::hex;
new_test!(groestl_224_main, "groestl224", Groestl224, fixed_reset_test);
new_test!(groestl_256_main, "groestl256", Groestl256, fixed_reset_test);
new_test!(groestl_384_main, "groestl384", Groestl384, fixed_reset_test);
new_test!(groestl_512_main, "groestl512", Groestl512, fixed_reset_test);
#[test]
fn groestl224_rand() {
let mut h = Groestl224::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("2000744c2f85a7fb4733e97da8db00069dd6defa9186dac3461dfeb8")[..]
);
}
#[test]
fn groestl256_rand() {
let mut h = Groestl256::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("aac71c789678f627a6474605322ae98d1647e47f405d00b1461b90ee5f0cfbc4")[..]
);
}
#[test]
#[rustfmt::skip]
fn groestl384_rand() {
let mut h = Groestl384::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("
dab78eea895a6dde0c53dc02fc79c7986f5d6811618ca6e5922f01e8aca9bfeb
20ed5eda4130bf0ab474ac0b6f0290f8
")[..]
);
}
#[test]
#[rustfmt::skip]
fn groestl512_rand() {
let mut h = Groestl512::new();
feed_rand_16mib(&mut h);
assert_eq!(
h.finalize()[..],
hex!("
7e4d8257c217c7ae59331126e0f984f145e9789862de7c099675ac29e46424ef
e93543974fa7113190d492f607f629a03db35ec5551abcb2785ae145fd3c543f
")[..],
);
}

View File

@@ -5,6 +5,12 @@ 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.2.0 (2021-12-07)
### Changed
- Update to `digest` v0.10 ([#217])
[#217]: https://github.com/RustCrypto/hashes/pull/217
## 0.1.0 (2020-06-09)
### Changed
- Update to `digest` v0.9 release; MSRV 1.41+ ([#155])

View File

@@ -1,6 +1,6 @@
[package]
name = "k12"
version = "0.1.0"
version = "0.2.0" # Also update html_root_url in lib.rs when bumping this
description = "Experimental pure Rust implementation of the KangarooTwelve hash function"
authors = ["Diggory Hardy <github1@dhardy.name>"]
license = "Apache-2.0 OR MIT"
@@ -12,10 +12,10 @@ keywords = ["crypto", "hash", "digest"]
categories = ["cryptography", "no-std"]
[dependencies]
digest = { version = "0.9", features = ["alloc"] }
digest = { version = "0.10", features = ["alloc"] }
[dev-dependencies]
digest = { version = "0.9", features = ["alloc", "dev"] }
digest = { version = "0.10", features = ["alloc", "dev"] }
hex-literal = "0.2"
[features]

View File

@@ -1,12 +0,0 @@
#![no_std]
#![feature(test)]
extern crate test;
use digest::Update;
use test::Bencher;
digest::bench!(bench1_10, k12::KangarooTwelve, 10);
digest::bench!(bench2_100, k12::KangarooTwelve, 100);
digest::bench!(bench3_1000, k12::KangarooTwelve, 1000);
digest::bench!(bench4_10000, k12::KangarooTwelve, 10000);

Some files were not shown because too many files have changed in this diff Show More