diff --git a/.github/workflows/jh.yml b/.github/workflows/jh.yml new file mode 100644 index 000000000..e72676247 --- /dev/null +++ b/.github/workflows/jh.yml @@ -0,0 +1,67 @@ +name: jh + +on: + pull_request: + paths: + - ".github/workflows/jh.yml" + - "jh/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: jh + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + set-msrv: + uses: RustCrypto/actions/.github/workflows/set-msrv.yml@master + with: + msrv: 1.57.0 + + build: + needs: set-msrv + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - ${{needs.set-msrv.outputs.msrv}} + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v3 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - uses: RustCrypto/actions/cargo-hack-install@master + - run: cargo hack build --target ${{ matrix.target }} --each-feature --exclude-features default,std + + test: + needs: set-msrv + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - ${{needs.set-msrv.outputs.msrv}} + - stable + steps: + - uses: actions/checkout@v3 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - uses: RustCrypto/actions/cargo-hack-install@master + - run: cargo hack test --feature-powerset + + minimal-versions: + uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + with: + working-directory: ${{ github.workflow }} diff --git a/Cargo.toml b/Cargo.toml index 7ee1c07fb..790a09c7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ exclude = [ "ascon-hash", "belt-hash", + "jh", "k12", "skein", ] diff --git a/README.md b/README.md index 887353d1e..8603430be 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Additionally all crates do not require the standard library (i.e. `no_std` capab | [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: | +| [JH] | [`jh`] | [![crates.io](https://img.shields.io/crates/v/jh.svg)](https://crates.io/crates/jh) | [![Documentation](https://docs.rs/jh/badge.svg)](https://docs.rs/jh) | ![MSRV 1.57][msrv-1.57] | :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: | @@ -244,6 +245,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [`fsb`]: ./fsb [`gost94`]: ./gost94 [`groestl`]: ./groestl +[`jh`]: ./jh [`k12`]: ./k12 [`md2`]: ./md2 [`md4`]: ./md4 @@ -284,6 +286,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [FSB]: https://en.wikipedia.org/wiki/Fast_syndrome-based_hash [GOST94]: https://en.wikipedia.org/wiki/GOST_(hash_function) [Grøstl]: https://en.wikipedia.org/wiki/Grøstl +[JH]: https://www3.ntu.edu.sg/home/wuhj/research/jh [KangarooTwelve]: https://keccak.team/kangarootwelve.html [MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography) [MD4]: https://en.wikipedia.org/wiki/MD4 diff --git a/jh/CHANGELOG.md b/jh/CHANGELOG.md new file mode 100644 index 000000000..43a72ca02 --- /dev/null +++ b/jh/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2023-06-11) +- Initial release ([#484]) + +[#484]: https://github.com/RustCrypto/hashes/pull/484 diff --git a/jh/Cargo.toml b/jh/Cargo.toml new file mode 100644 index 000000000..cd2c77a92 --- /dev/null +++ b/jh/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "jh" +version = "0.1.0" +description = "Pure Rust implementation of the JH cryptographic hash function" +authors = ["RustCrypto Developers"] +license = "MIT or Apache-2.0" +edition = "2021" +rust-version = "1.57" +readme = "README.md" +documentation = "https://docs.rs/jh" +repository = "https://github.com/RustCrypto/hashes" +keywords = ["crypto", "jh", "hash", "digest"] +categories = ["cryptography", "no-std"] + +[dependencies] +digest = "0.10" +hex-literal = "0.4" +simd = { package = "ppv-lite86", version = "0.2.6" } + +[dev-dependencies] +digest = { version = "0.10", features = ["dev"] } diff --git a/jh/LICENSE-APACHE b/jh/LICENSE-APACHE new file mode 100644 index 000000000..1eb321535 --- /dev/null +++ b/jh/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2019 The CryptoCorrosion Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/jh/LICENSE-MIT b/jh/LICENSE-MIT new file mode 100644 index 000000000..50c61807c --- /dev/null +++ b/jh/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/jh/README.md b/jh/README.md new file mode 100644 index 000000000..8e6cb0749 --- /dev/null +++ b/jh/README.md @@ -0,0 +1,56 @@ +# RustCrypto: JH + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Pure Rust implementation of the [JH] cryptographic hash function. + +[Documentation][docs-link] + +## Minimum Supported Rust Version + +Rust **1.57** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/jh.svg +[crate-link]: https://crates.io/crates/jh +[docs-image]: https://docs.rs/jh/badge.svg +[docs-link]: https://docs.rs/jh/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.57+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes +[build-image]: https://github.com/RustCrypto/hashes/workflows/jh/badge.svg?branch=master +[build-link]: https://github.com/RustCrypto/hashes/actions?query=workflow%jh + +[//]: # (general links) + +[JH]: https://en.wikipedia.org/wiki/JH_(hash_function) diff --git a/jh/benches/digest_bench.rs b/jh/benches/digest_bench.rs new file mode 100644 index 000000000..6382575c0 --- /dev/null +++ b/jh/benches/digest_bench.rs @@ -0,0 +1,14 @@ +#![feature(test)] +extern crate test; + +use digest::bench_update; +use jh::Jh256; +use test::Bencher; + +bench_update!( + Jh256::default(); + jh_256_10 10; + jh_256_100 100; + jh_256_1000 1000; + jh_256_10000 10000; +); diff --git a/jh/benches/machine.rs b/jh/benches/machine.rs new file mode 100644 index 000000000..6583eaf44 --- /dev/null +++ b/jh/benches/machine.rs @@ -0,0 +1,38 @@ +#![feature(test)] +extern crate test; + +use simd::{vec128_storage, x86_64, Machine}; +use test::Bencher; + +macro_rules! mach_bench { + ($MachName:ident, $feature:expr, $enable:expr) => { + #[allow(non_snake_case)] + #[bench] + pub fn $MachName(b: &mut Bencher) { + if !$enable { + return; + } + let m = unsafe { x86_64::$MachName::instance() }; + let mut state = [vec128_storage::default(); 8]; + let input = [0; 64]; + #[target_feature(enable = $feature)] + unsafe fn runner( + m: M, + state: &mut [vec128_storage; 8], + input: *const [u8; 64], + ) { + for _ in 0..160 { + jh::f8_impl(m, state, input as *const _); + } + } + b.iter(|| unsafe { runner(m, &mut state, &input) }); + b.bytes = 10240; + } + }; +} + +mach_bench!(SSE2, "sse2", is_x86_feature_detected!("sse2")); +mach_bench!(SSSE3, "ssse3", is_x86_feature_detected!("ssse3")); +mach_bench!(SSE41, "sse4.1", is_x86_feature_detected!("sse4.1")); +mach_bench!(AVX, "avx", is_x86_feature_detected!("avx")); +mach_bench!(AVX2, "avx2", is_x86_feature_detected!("avx2")); diff --git a/jh/src/compressor.rs b/jh/src/compressor.rs new file mode 100644 index 000000000..f75cde8a0 --- /dev/null +++ b/jh/src/compressor.rs @@ -0,0 +1,186 @@ +#![allow(non_upper_case_globals)] + +use core::ptr; +use digest::generic_array::{typenum::U64, GenericArray}; +use simd::{dispatch, vec128_storage, AndNot, Machine, Swap64, VZip, Vec2}; + +#[rustfmt::skip] +macro_rules! unroll7 { + ($j:ident, $body:block) => { + { const $j: usize = 0; $body } + { const $j: usize = 1; $body } + { const $j: usize = 2; $body } + { const $j: usize = 3; $body } + { const $j: usize = 4; $body } + { const $j: usize = 5; $body } + { const $j: usize = 6; $body } + }; +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct X8( + M::u128x1, + M::u128x1, + M::u128x1, + M::u128x1, + M::u128x1, + M::u128x1, + M::u128x1, + M::u128x1, +); + +impl X8 { + #[inline(always)] + fn zip(self) -> (M::u128x2, M::u128x2, M::u128x2, M::u128x2) { + ( + [self.0, self.1].vzip(), + [self.2, self.3].vzip(), + [self.4, self.5].vzip(), + [self.6, self.7].vzip(), + ) + } + + #[inline(always)] + fn unzip((a, b, c, d): (M::u128x2, M::u128x2, M::u128x2, M::u128x2)) -> Self { + X8( + a.extract(0), + a.extract(1), + b.extract(0), + b.extract(1), + c.extract(0), + c.extract(1), + d.extract(0), + d.extract(1), + ) + } +} + +/// two Sboxes computed in parallel; each Sbox implements S0 and S1, selected by a constant bit +#[inline(always)] +fn ss(state: X8, mut k: M::u128x2) -> X8 { + let mut m = state.zip(); + // TODO: replace ! with andnot ops? + m.3 = !m.3; + m.0 ^= m.2.andnot(k); + k ^= m.0 & m.1; + m.0 ^= m.3 & m.2; + m.3 ^= m.1.andnot(m.2); + m.1 ^= m.0 & m.2; + m.2 ^= m.3.andnot(m.0); + m.0 ^= m.1 | m.3; + m.3 ^= m.1 & m.2; + m.2 ^= k; + m.1 ^= k & m.0; + X8::unzip(m) +} + +#[inline(always)] +fn l(mut y: X8) -> X8 { + y.1 ^= y.2; + y.3 ^= y.4; + y.5 ^= y.6 ^ y.0; + y.7 ^= y.0; + y.0 ^= y.3; + y.2 ^= y.5; + y.4 ^= y.7 ^ y.1; + y.6 ^= y.1; + y +} + +union X2Bytes { + x2: M::u128x2, + bytes: [u8; 32], +} + +#[inline(always)] +#[doc(hidden)] +pub fn f8_impl(mach: M, state: &mut [vec128_storage; 8], data: *const u8) { + #[allow(clippy::cast_ptr_alignment)] + let data = data as *const M::u128x1; + let mut y = X8::( + mach.unpack(state[0]), + mach.unpack(state[1]), + mach.unpack(state[2]), + mach.unpack(state[3]), + mach.unpack(state[4]), + mach.unpack(state[5]), + mach.unpack(state[6]), + mach.unpack(state[7]), + ); + unsafe { + y.0 ^= ptr::read_unaligned(data); + y.1 ^= ptr::read_unaligned(data.offset(1)); + y.2 ^= ptr::read_unaligned(data.offset(2)); + y.3 ^= ptr::read_unaligned(data.offset(3)); + } + for rc in crate::consts::E8_BITSLICE_ROUNDCONSTANT.chunks_exact(7) { + unroll7!(j, { + y = ss(y, unsafe { X2Bytes:: { bytes: rc[j] }.x2 }); + y = l(y); + let f = match j { + 0 => M::u128x1::swap1, + 1 => M::u128x1::swap2, + 2 => M::u128x1::swap4, + 3 => M::u128x1::swap8, + 4 => M::u128x1::swap16, + 5 => M::u128x1::swap32, + 6 => M::u128x1::swap64, + _ => unreachable!(), + }; + y = X8(y.0, f(y.1), y.2, f(y.3), y.4, f(y.5), y.6, f(y.7)); + }); + } + unsafe { + y.4 ^= ptr::read_unaligned(data); + y.5 ^= ptr::read_unaligned(data.offset(1)); + y.6 ^= ptr::read_unaligned(data.offset(2)); + y.7 ^= ptr::read_unaligned(data.offset(3)); + } + *state = [ + y.0.into(), + y.1.into(), + y.2.into(), + y.3.into(), + y.4.into(), + y.5.into(), + y.6.into(), + y.7.into(), + ]; +} + +dispatch!(mach, M, { + fn f8(state: &mut [vec128_storage; 8], data: *const u8) { + f8_impl(mach, state, data); + } +}); + +pub(crate) union Compressor { + cv: [vec128_storage; 8], + bytes: [u8; 128], +} + +impl Compressor { + #[inline] + pub(crate) fn new(bytes: [u8; 128]) -> Self { + Compressor { bytes } + } + + #[inline] + pub(crate) fn update(&mut self, data: &GenericArray) { + f8(unsafe { &mut self.cv }, data.as_ptr()); + } + + #[inline] + pub(crate) fn finalize(&self) -> &[u8; 128] { + unsafe { &self.bytes } + } +} + +impl Clone for Compressor { + fn clone(&self) -> Self { + Self { + bytes: unsafe { self.bytes }, + } + } +} diff --git a/jh/src/consts.rs b/jh/src/consts.rs new file mode 100644 index 000000000..49e5003be --- /dev/null +++ b/jh/src/consts.rs @@ -0,0 +1,74 @@ +use hex_literal::hex; + +pub(crate) const JH224_H0: [u8; 128] = hex!( + "2dfedd62f99a98acae7cacd619d634e7a4831005bc301216b86038c6c9661494" + "66d9899f2580706fce9ea31b1d9b1adc11e8325f7b366e10f994857f02fa06c1" + "1b4f1b5cd8c840b397f6a17f6e738099dcdf93a5adeaa3d3a431e8dec9539a68" + "22b4a98aec86a1e4d574ac959ce56cf015960deab5ab2bbf9611dcf0dd64ea6e" +); + +pub(crate) const JH256_H0: [u8; 128] = hex!( + "eb98a3412c20d3eb92cdbe7b9cb245c11c93519160d4c7fa260082d67e508a03" + "a4239e267726b945e0fb1a48d41a9477cdb5ab26026b177a56f024420fff2fa8" + "71a396897f2e4d751d144908f77de262277695f776248f9487d5b6574780296c" + "5c5e272dac8e0d6c518450c657057a0f7be4d367702412ea89e3ab13d31cd769" +); + +pub(crate) const JH384_H0: [u8; 128] = hex!( + "481e3bc6d813398a6d3b5e894ade879b63faea68d480ad2e332ccb21480f8267" + "98aec84d9082b928d455ea304111424936f555b2924847ecc7250a93baf43ce1" + "569b7f8a27db454c9efcbd496397af0e589fc27d26aa80cd80c08b8c9deb2eda" + "8a7981e8f8d5373af43967adddd17a71a9b4d3bda475d394976c3fba9842737f" +); + +pub(crate) const JH512_H0: [u8; 128] = hex!( + "6fd14b963e00aa17636a2e057a15d5438a225e8d0c97ef0be9341259f2b3c361" + "891da0c1536f801e2aa9056bea2b6d80588eccdb2075baa6a90f3a76baf83bf7" + "0169e60541e34a6946b58a8e2e6fe65a1047a7d0c1843c243b6e71b12d5ac199" + "cf57f6ec9db1f856a706887c5716b156e3c2fcdfe68517fb545a4678cc8cdd4b" +); + +pub(crate) const E8_BITSLICE_ROUNDCONSTANT: [[u8; 32]; 42] = [ + hex!("72d5dea2df15f8677b84150ab723155781abd6904d5a87f64e9f4fc5c3d12b40"), + hex!("ea983ae05c45fa9c03c5d29966b2999a660296b4f2bb538ab556141a88dba231"), + hex!("03a35a5c9a190edb403fb20a87c144101c051980849e951d6f33ebad5ee7cddc"), + hex!("10ba139202bf6b41dc786515f7bb27d00a2c813937aa78503f1abfd2410091d3"), + hex!("422d5a0df6cc7e90dd629f9c92c097ce185ca70bc72b44acd1df65d663c6fc23"), + hex!("976e6c039ee0b81a2105457e446ceca8eef103bb5d8e61fafd9697b294838197"), + hex!("4a8e8537db03302f2a678d2dfb9f6a958afe7381f8b8696c8ac77246c07f4214"), + hex!("c5f4158fbdc75ec475446fa78f11bb8052de75b7aee488bc82b8001e98a6a3f4"), + hex!("8ef48f33a9a36315aa5f5624d5b7f989b6f1ed207c5ae0fd36cae95a06422c36"), + hex!("ce2935434efe983d533af974739a4ba7d0f51f596f4e81860e9dad81afd85a9f"), + hex!("a7050667ee34626a8b0b28be6eb9172747740726c680103fe0a07e6fc67e487b"), + hex!("0d550aa54af8a4c091e3e79f978ef19e8676728150608dd47e9e5a41f3e5b062"), + hex!("fc9f1fec4054207ae3e41a00cef4c9844fd794f59dfa95d8552e7e1124c354a5"), + hex!("5bdf7228bdfe6e2878f57fe20fa5c4b205897cefee49d32e447e9385eb28597f"), + hex!("705f6937b324314a5e8628f11dd6e465c71b770451b920e774fe43e823d4878a"), + hex!("7d29e8a3927694f2ddcb7a099b30d9c11d1b30fb5bdc1be0da24494ff29c82bf"), + hex!("a4e7ba31b470bfff0d324405def8bc483baefc3253bbd339459fc3c1e0298ba0"), + hex!("e5c905fdf7ae090f947034124290f134a271b701e344ed95e93b8e364f2f984a"), + hex!("88401d63a06cf61547c1444b8752afff7ebb4af1e20ac6304670b6c5cc6e8ce6"), + hex!("a4d5a456bd4fca00da9d844bc83e18ae7357ce453064d1ade8a6ce68145c2567"), + hex!("a3da8cf2cb0ee11633e906589a94999a1f60b220c26f847bd1ceac7fa0d18518"), + hex!("32595ba18ddd19d3509a1cc0aaa5b4469f3d6367e4046bbaf6ca19ab0b56ee7e"), + hex!("1fb179eaa9282174e9bdf7353b3651ee1d57ac5a7550d3763a46c2fea37d7001"), + hex!("f735c1af98a4d84278edec209e6b677941836315ea3adba8fac33b4d32832c83"), + hex!("a7403b1f1c2747f35940f034b72d769ae73e4e6cd2214ffdb8fd8d39dc5759ef"), + hex!("8d9b0c492b49ebda5ba2d74968f3700d7d3baed07a8d5584f5a5e9f0e4f88e65"), + hex!("a0b8a2f436103b530ca8079e753eec5a9168949256e8884f5bb05c55f8babc4c"), + hex!("e3bb3b99f387947b75daf4d6726b1c5d64aeac28dc34b36d6c34a550b828db71"), + hex!("f861e2f2108d512ae3db643359dd75fc1cacbcf143ce3fa267bbd13c02e843b0"), + hex!("330a5bca8829a1757f34194db416535c923b94c30e794d1e797475d7b6eeaf3f"), + hex!("eaa8d4f7be1a39215cf47e094c23275126a32453ba323cd244a3174a6da6d5ad"), + hex!("b51d3ea6aff2c90883593d98916b3c564cf87ca17286604d46e23ecc086ec7f6"), + hex!("2f9833b3b1bc765e2bd666a5efc4e62a06f4b6e8bec1d43674ee8215bcef2163"), + hex!("fdc14e0df453c969a77d5ac4065858267ec1141606e0fa167e90af3d28639d3f"), + hex!("d2c9f2e3009bd20c5faace30b7d40c30742a5116f2e032980deb30d8e3cef89a"), + hex!("4bc59e7bb5f17992ff51e66e048668d39b234d57e6966731cce6a6f3170a7505"), + hex!("b17681d913326cce3c175284f805a262f42bcbb378471547ff46548223936a48"), + hex!("38df58074e5e6565f2fc7c89fc86508e31702e44d00bca86f04009a23078474e"), + hex!("65a0ee39d1f73883f75ee937e42c3abd2197b2260113f86fa344edd1ef9fdee7"), + hex!("8ba0df15762592d93c85f7f612dc42bed8a7ec7cab27b07e538d7ddaaa3ea8de"), + hex!("aa25ce93bd0269d85af643fd1a7308f9c05fefda174a19a5974d66334cfd216a"), + hex!("35b49831db411570ea1e0fbbedcd549b9ad063a151974072f6759dbf91476fe2"), +]; diff --git a/jh/src/lib.rs b/jh/src/lib.rs new file mode 100644 index 000000000..9dcea8940 --- /dev/null +++ b/jh/src/lib.rs @@ -0,0 +1,147 @@ +//! Implementation of the [JH] cryptographic hash function. +//! +//! There are 4 standard versions of the JH hash function: +//! +//! * [JH-224][Jh224] +//! * [JH-256][Jh256] +//! * [JH-384][Jh384] +//! * [JH-512][Jh512] +//! +//! # Examples +//! +//! Hash functionality is usually accessed via the [`Digest`] trait: +//! +//! ``` +//! use hex_literal::hex; +//! use jh::{Digest, Jh256}; +//! +//! // create a JH-256 object +//! let mut hasher = Jh256::new(); +//! +//! // write input message +//! hasher.update(b"hello"); +//! +//! // read hash digest +//! let result = hasher.finalize(); +//! +//! let expected = hex!("94fd3f4c564957c6754265676bf8b244c707d3ffb294e18af1f2e4f9b8306089"); +//! assert_eq!(result[..], expected[..]); +//! ``` +//! Also see [RustCrypto/hashes] readme. +//! +//! [JH]: https://en.wikipedia.org/wiki/JH_(hash_function) +//! [RustCrypto/hashes]: https://github.com/RustCrypto/hashes +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" +)] +#![warn(missing_docs, rust_2018_idioms)] + +mod compressor; +mod consts; + +// This function is exported only for benchmarks +pub use compressor::f8_impl; + +pub use digest::{self, Digest}; + +use crate::compressor::Compressor; +use core::fmt; +use digest::{ + block_buffer::Eager, + core_api::{ + AlgorithmName, Block, Buffer, BufferKindUser, CoreWrapper, CtVariableCoreWrapper, + TruncSide, UpdateCore, VariableOutputCore, + }, + crypto_common::{BlockSizeUser, OutputSizeUser}, + generic_array::typenum::{Unsigned, U28, U32, U48, U64}, + HashMarker, InvalidOutputSize, Output, +}; + +/// Core JH hasher state +#[derive(Clone)] +pub struct JhCore { + state: Compressor, + block_len: u64, +} + +impl HashMarker for JhCore {} + +impl BlockSizeUser for JhCore { + type BlockSize = U64; +} + +impl BufferKindUser for JhCore { + type BufferKind = Eager; +} + +impl OutputSizeUser for JhCore { + type OutputSize = U64; +} + +impl UpdateCore for JhCore { + #[inline] + fn update_blocks(&mut self, blocks: &[Block]) { + self.block_len = self.block_len.wrapping_add(blocks.len() as u64); + for b in blocks { + self.state.update(b); + } + } +} + +impl VariableOutputCore for JhCore { + const TRUNC_SIDE: TruncSide = TruncSide::Right; + + #[inline] + fn new(output_size: usize) -> Result { + let h0 = match output_size { + 28 => consts::JH224_H0, + 32 => consts::JH256_H0, + 48 => consts::JH384_H0, + 64 => consts::JH512_H0, + _ => return Err(InvalidOutputSize), + }; + Ok(Self { + state: Compressor::new(h0), + block_len: 0, + }) + } + + #[inline] + fn finalize_variable_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let Self { state, block_len } = self; + let bit_len = block_len + .wrapping_mul(Self::BlockSize::U64) + .wrapping_add(buffer.get_pos() as u64) + .wrapping_mul(8); + if buffer.get_pos() == 0 { + buffer.len64_padding_be(bit_len, |b| state.update(b)); + } else { + buffer.digest_pad(0x80, &[], |b| state.update(b)); + buffer.digest_pad(0, &bit_len.to_be_bytes(), |b| state.update(b)); + } + out.copy_from_slice(&self.state.finalize()[64..]); + } +} + +impl AlgorithmName for JhCore { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Jh") + } +} + +impl fmt::Debug for JhCore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("JhCore { ... }") + } +} + +/// Jh-224 hasher state +pub type Jh224 = CoreWrapper>; +/// Jh-256 hasher state +pub type Jh256 = CoreWrapper>; +/// Jh-384 hasher state +pub type Jh384 = CoreWrapper>; +/// Jh-512 hasher state +pub type Jh512 = CoreWrapper>; diff --git a/jh/tests/data/LongMsgKAT_224.blb b/jh/tests/data/LongMsgKAT_224.blb new file mode 100644 index 000000000..4a1a49e45 Binary files /dev/null and b/jh/tests/data/LongMsgKAT_224.blb differ diff --git a/jh/tests/data/LongMsgKAT_256.blb b/jh/tests/data/LongMsgKAT_256.blb new file mode 100644 index 000000000..38aca3404 Binary files /dev/null and b/jh/tests/data/LongMsgKAT_256.blb differ diff --git a/jh/tests/data/LongMsgKAT_384.blb b/jh/tests/data/LongMsgKAT_384.blb new file mode 100644 index 000000000..94e5f8b48 Binary files /dev/null and b/jh/tests/data/LongMsgKAT_384.blb differ diff --git a/jh/tests/data/LongMsgKAT_512.blb b/jh/tests/data/LongMsgKAT_512.blb new file mode 100644 index 000000000..d9b0f9f8f Binary files /dev/null and b/jh/tests/data/LongMsgKAT_512.blb differ diff --git a/jh/tests/data/ShortMsgKAT_224.blb b/jh/tests/data/ShortMsgKAT_224.blb new file mode 100644 index 000000000..2eb8250d7 Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_224.blb differ diff --git a/jh/tests/data/ShortMsgKAT_256.blb b/jh/tests/data/ShortMsgKAT_256.blb new file mode 100644 index 000000000..f58e731c7 Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_256.blb differ diff --git a/jh/tests/data/ShortMsgKAT_384.blb b/jh/tests/data/ShortMsgKAT_384.blb new file mode 100644 index 000000000..ced802851 Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_384.blb differ diff --git a/jh/tests/data/ShortMsgKAT_512.blb b/jh/tests/data/ShortMsgKAT_512.blb new file mode 100644 index 000000000..1b600a945 Binary files /dev/null and b/jh/tests/data/ShortMsgKAT_512.blb differ diff --git a/jh/tests/lib.rs b/jh/tests/lib.rs new file mode 100644 index 000000000..9e80a807b --- /dev/null +++ b/jh/tests/lib.rs @@ -0,0 +1,11 @@ +use digest::{dev::fixed_test, new_test}; + +new_test!(long_224, "LongMsgKAT_224", jh::Jh224, fixed_test); +new_test!(long_256, "LongMsgKAT_256", jh::Jh256, fixed_test); +new_test!(long_384, "LongMsgKAT_384", jh::Jh384, fixed_test); +new_test!(long_512, "LongMsgKAT_512", jh::Jh512, fixed_test); + +new_test!(short_224, "ShortMsgKAT_224", jh::Jh224, fixed_test); +new_test!(short_256, "ShortMsgKAT_256", jh::Jh256, fixed_test); +new_test!(short_384, "ShortMsgKAT_384", jh::Jh384, fixed_test); +new_test!(short_512, "ShortMsgKAT_512", jh::Jh512, fixed_test);