diff --git a/Cargo.toml b/Cargo.toml index 4c70e3dd2..f46794bd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "sha1", "sha2", "sha3", + "shabal", "streebog", "whirlpool", ] diff --git a/README.md b/README.md index 1999fba23..4eecbad99 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ BLAKE2, SHA-2 or SHA-3. | [SHA-1](https://en.wikipedia.org/wiki/SHA-1) [:exclamation:](#crate-names) | | [![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) | :broken_heart: | | [SHA-2](https://en.wikipedia.org/wiki/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) | :green_heart: | | [SHA-3](https://en.wikipedia.org/wiki/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) | :green_heart: | +| [SHABAL](https://www.cs.rit.edu/~ark/20090927/Round2Candidates/Shabal.pdf) | | [![crates.io](https://img.shields.io/crates/v/shabal.svg)](https://crates.io/crates/shabal) | [![Documentation](https://docs.rs/shabal.svg)](https://docs.rs/shabal) | :green_heart: | | [Streebog](https://en.wikipedia.org/wiki/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) | :yellow_heart: | | [Whirlpool](https://en.wikipedia.org/wiki/Whirlpool_(cryptography)) | | [![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) | :green_heart: | diff --git a/shabal/Cargo.toml b/shabal/Cargo.toml new file mode 100644 index 000000000..82242a883 --- /dev/null +++ b/shabal/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "shabal" +version = "0.2.0" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +description = "Shabal hash functions" +documentation = "https://docs.rs/shabal" +repository = "https://github.com/RustCrypto/hashes" +keywords = ["crypto", "shabal", "hash", "digest"] +categories = ["cryptography", "no-std"] + +[dependencies] +digest = "0.8" +block-buffer = "0.7" +opaque-debug = "0.2" + +[dev-dependencies] +digest = { version = "0.8", features = ["dev"] } +hex-literal = "0.1" + +[features] +default = ["std"] +std = ["digest/std"] + +[badges] +travis-ci = { repository = "RustCrypto/hashes" } \ No newline at end of file diff --git a/shabal/LICENSE-APACHE b/shabal/LICENSE-APACHE new file mode 100644 index 000000000..78173fa2e --- /dev/null +++ b/shabal/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 [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/shabal/LICENSE-MIT b/shabal/LICENSE-MIT new file mode 100644 index 000000000..66cf75563 --- /dev/null +++ b/shabal/LICENSE-MIT @@ -0,0 +1,27 @@ +Copyright (c) 2006-2009 Graydon Hoare +Copyright (c) 2009-2013 Mozilla Foundation +Copyright (c) 2016 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/shabal/README.md b/shabal/README.md new file mode 100644 index 000000000..820e9b74a --- /dev/null +++ b/shabal/README.md @@ -0,0 +1,40 @@ +[![Build Status](https://travis-ci.org/spebern/shabal-rs.svg?branch=master)](https://travis-ci.org/spebern/shabal-rs) [![dependency status](https://deps.rs/repo/github/spebern/shabal-rs/status.svg)](https://deps.rs/repo/github/spebern/shabal-rs) + +# shabal + +An implementation of the [Shabal][1] cryptographic hash algorithm. + +There are 5 standard algorithms specified in the Shabal standard: + +* `Shabal192`, which is the `Shabal` algorithm with the result truncated to 192 bits +* `Shabal224`, which is the `Shabal` algorithm with the result truncated to 224 bits +* `Shabal256`, which is the `Shabal` algorithm with the result truncated to 256 bits. +* `Shabal384`, which is the `Shabal` algorithm with the result truncated to 384 bits. +* `Shabal512`, which is the `Shabal` algorithm with the result not truncated. + +There is a single Shabal algorithm. All variants have different intialisation and apart +Shabal512 truncate the result. + +## Usage + +```rust +use shabal::{Shabal256, Digest}; + +// create a Shabal256 hasher instance +let mut hasher = Shabal256::new(); + +// process input message +hasher.input(b"helloworld"); + +// acquire hash digest in the form of GenericArray, +// which in this case is equivalent to [u8; 32] +let result = hasher.result(); +assert_eq!(result[..], hex!("d945dee21ffca23ac232763aa9cac6c15805f144db9d6c97395437e01c8595a8")); +``` + +Also see [RustCrypto/hashes][2] readme. + +[1]: https://www.cs.rit.edu/~ark/20090927/Round2Candidates/Shabal.pdf +[2]: https://github.com/RustCrypto/hashes + +License: MIT OR Apache-2.0 diff --git a/shabal/benches/shabal.rs b/shabal/benches/shabal.rs new file mode 100644 index 000000000..6d04faebe --- /dev/null +++ b/shabal/benches/shabal.rs @@ -0,0 +1,7 @@ +#![no_std] +#![feature(test)] +#[macro_use] +extern crate digest; +extern crate shabal; + +bench!(shabal::Shabal256); diff --git a/shabal/examples/shabal256sum.rs b/shabal/examples/shabal256sum.rs new file mode 100644 index 000000000..88621a38a --- /dev/null +++ b/shabal/examples/shabal256sum.rs @@ -0,0 +1,49 @@ +extern crate shabal; + +use shabal::{Digest, Shabal256}; +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(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.input(&buffer[..n]); + if n == 0 || n < BUFFER_SIZE { + break; + } + } + print_result(&sh.result(), 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::(&mut file, &path); + } + } + } else { + process::(&mut io::stdin(), "-"); + } +} diff --git a/shabal/examples/shabal512sum.rs b/shabal/examples/shabal512sum.rs new file mode 100644 index 000000000..f280652a1 --- /dev/null +++ b/shabal/examples/shabal512sum.rs @@ -0,0 +1,49 @@ +extern crate shabal; + +use shabal::{Digest, Shabal512}; +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(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.input(&buffer[..n]); + if n == 0 || n < BUFFER_SIZE { + break; + } + } + print_result(&sh.result(), 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::(&mut file, &path); + } + } + } else { + process::(&mut io::stdin(), "-"); + } +} diff --git a/shabal/src/consts.rs b/shabal/src/consts.rs new file mode 100644 index 000000000..27fddcdbe --- /dev/null +++ b/shabal/src/consts.rs @@ -0,0 +1,264 @@ +pub const A_INIT_512: [u32; 12] = [ + 0x2072_8DFD, + 0x46C0_BD53, + 0xE782_B699, + 0x5530_4632, + 0x71B4_EF90, + 0x0EA9_E82C, + 0xDBB9_30F1, + 0xFAD0_6B8B, + 0xBE0C_AE40, + 0x8BD1_4410, + 0x76D2_ADAC, + 0x28AC_AB7F, +]; + +pub const B_INIT_512: [u32; 16] = [ + 0xC109_9CB7, + 0x07B3_85F3, + 0xE744_2C26, + 0xCC8A_D640, + 0xEB6F_56C7, + 0x1EA8_1AA9, + 0x73B9_D314, + 0x1DE8_5D08, + 0x4891_0A5A, + 0x893B_22DB, + 0xC5A0_DF44, + 0xBBC4_324E, + 0x72D2_F240, + 0x7594_1D99, + 0x6D8B_DE82, + 0xA1A7_502B, +]; + +pub const C_INIT_512: [u32; 16] = [ + 0xD9BF_68D1, + 0x58BA_D750, + 0x5602_8CB2, + 0x8134_F359, + 0xB5D4_69D8, + 0x941A_8CC2, + 0x418B_2A6E, + 0x0405_2780, + 0x7F07_D787, + 0x5194_358F, + 0x3C60_D665, + 0xBE97_D79A, + 0x950C_3434, + 0xAED9_A06D, + 0x2537_DC8D, + 0x7CDB_5969, +]; + +pub const A_INIT_384: [u32; 12] = [ + 0xC8FC_A331, + 0xE55C_504E, + 0x003E_BF26, + 0xBB6B_8D83, + 0x7B04_48C1, + 0x41B8_2789, + 0x0A7C_9601, + 0x8D65_9CFF, + 0xB6E2_673E, + 0xCA54_C77B, + 0x1460_FD7E, + 0x3FCB_8F2D, +]; + +pub const B_INIT_384: [u32; 16] = [ + 0x5272_91FC, + 0x2A16_455F, + 0x78E6_27E5, + 0x944F_169F, + 0x1CA6_F016, + 0xA854_EA25, + 0x8DB9_8ABE, + 0xF2C6_2641, + 0x3011_7DCB, + 0xCF5C_4309, + 0x9371_1A25, + 0xF9F6_71B8, + 0xB01D_2116, + 0x333F_4B89, + 0xB285_D165, + 0x8682_9B36, +]; + +pub const C_INIT_384: [u32; 16] = [ + 0xF764_B11A, + 0x7617_2146, + 0xCEF6_934D, + 0xC6D2_8399, + 0xFE09_5F61, + 0x5E60_18B4, + 0x5048_ECF5, + 0x5135_3261, + 0x6E6E_36DC, + 0x6313_0DAD, + 0xA9C6_9BD6, + 0x1E90_EA0C, + 0x7C35_073B, + 0x28D9_5E6D, + 0xAA34_0E0D, + 0xCB3D_EE70, +]; + +pub const A_INIT_256: [u32; 12] = [ + 0x52F8_4552, + 0xE54B_7999, + 0x2D8E_E3EC, + 0xB964_5191, + 0xE007_8B86, + 0xBB7C_44C9, + 0xD2B5_C1CA, + 0xB0D2_EB8C, + 0x14CE_5A45, + 0x22AF_50DC, + 0xEFFD_BC6B, + 0xEB21_B74A, +]; + +pub const B_INIT_256: [u32; 16] = [ + 0xB555_C6EE, + 0x3E71_0596, + 0xA72A_652F, + 0x9301_515F, + 0xDA28_C1FA, + 0x696F_D868, + 0x9CB6_BF72, + 0x0AFE_4002, + 0xA6E0_3615, + 0x5138_C1D4, + 0xBE21_6306, + 0xB38B_8890, + 0x3EA8_B96B, + 0x3299_ACE4, + 0x3092_4DD4, + 0x55CB_34A5, +]; + +pub const C_INIT_256: [u32; 16] = [ + 0xB405_F031, + 0xC423_3EBA, + 0xB373_3979, + 0xC0DD_9D55, + 0xC51C_28AE, + 0xA327_B8E1, + 0x56C5_6167, + 0xED61_4433, + 0x88B5_9D60, + 0x60E2_CEBA, + 0x758B_4B8B, + 0x83E8_2A7F, + 0xBC96_8828, + 0xE6E0_0BF7, + 0xBA83_9E55, + 0x9B49_1C60, +]; + +pub const A_INIT_224: [u32; 12] = [ + 0xA520_1467, + 0xA9B8_D94A, + 0xD4CE_D997, + 0x6837_9D7B, + 0xA7FC_73BA, + 0xF1A2_546B, + 0x6067_82BF, + 0xE0BC_FD0F, + 0x2F25_374E, + 0x069A_149F, + 0x5E2D_FF25, + 0xFAEC_F061, +]; + +pub const B_INIT_224: [u32; 16] = [ + 0xEC99_05D8, + 0xF218_50CF, + 0xC0A7_46C8, + 0x21DA_D498, + 0x3515_6EEB, + 0x088C_97F2, + 0x2630_3E40, + 0x8A2D_4FB5, + 0xFEEE_44B6, + 0x8A1E_9573, + 0x7B81_111A, + 0xCBC1_39F0, + 0xA351_3861, + 0x1D2C_362E, + 0x918C_580E, + 0xB58E_1B9C, +]; + +pub const C_INIT_224: [u32; 16] = [ + 0xE4B5_73A1, + 0x4C1A_0880, + 0x1E90_7C51, + 0x0480_7EFD, + 0x3AD8_CDE5, + 0x16B2_1302, + 0x0251_2C53, + 0x2204_CB18, + 0x9940_5F2D, + 0xE5B6_48A1, + 0x70AB_1D43, + 0xA10C_25C2, + 0x16F1_AC05, + 0x38BB_EB56, + 0x9B01_DC60, + 0xB109_6D83, +]; + +pub const A_INIT_192: [u32; 12] = [ + 0xFD74_9ED4, + 0xB798_E530, + 0x3390_4B6F, + 0x46BD_A85E, + 0x0769_34B4, + 0x454B_4058, + 0x77F7_4527, + 0xFB4C_F465, + 0x6293_1DA9, + 0xE778_C8DB, + 0x22B3_998E, + 0xAC15_CFB9, +]; + +pub const B_INIT_192: [u32; 16] = [ + 0x58BC_BAC4, + 0xEC47_A08E, + 0xAEE9_33B2, + 0xDFCB_C824, + 0xA794_4804, + 0xBF65_BDB0, + 0x5A9D_4502, + 0x5997_9AF7, + 0xC5CE_A54E, + 0x4B6B_8150, + 0x16E7_1909, + 0x7D63_2319, + 0x9305_73A0, + 0xF34C_63D1, + 0xCAF9_14B4, + 0xFDD6_612C, +]; + +pub const C_INIT_192: [u32; 16] = [ + 0x6155_0878, + 0x89EF_2B75, + 0xA166_0C46, + 0x7EF3_855B, + 0x7297_B58C, + 0x1BC6_7793, + 0x7FB1_C723, + 0xB66F_C640, + 0x1A48_B71C, + 0xF097_6D17, + 0x088C_E80A, + 0xA454_EDF3, + 0x1C09_6BF4, + 0xAC76_224B, + 0x5215_781C, + 0xCD5D_2669, +]; diff --git a/shabal/src/lib.rs b/shabal/src/lib.rs new file mode 100644 index 000000000..30dfc5703 --- /dev/null +++ b/shabal/src/lib.rs @@ -0,0 +1,54 @@ +//! An implementation of the [Shabal][1] cryptographic hash algorithm. +//! +//! There are 5 standard algorithms specified in the Shabal standard: +//! +//! * `Shabal192`, which is the `Shabal` algorithm with the result truncated to 192 bits +//! * `Shabal224`, which is the `Shabal` algorithm with the result truncated to 224 bits +//! * `Shabal256`, which is the `Shabal` algorithm with the result truncated to 256 bits. +//! * `Shabal384`, which is the `Shabal` algorithm with the result truncated to 384 bits. +//! * `Shabal512`, which is the `Shabal` algorithm with the result not truncated. +//! +//! There is a single Shabal algorithm. All variants have different intialisation and apart +//! Shabal512 truncate the result. +//! +//! # Usage +//! +//! ```rust +//! # #[macro_use] extern crate hex_literal; +//! # extern crate shabal; +//! # fn main() { +//! use shabal::{Shabal256, Digest}; +//! +//! // create a Shabal256 hasher instance +//! let mut hasher = Shabal256::new(); +//! +//! // process input message +//! hasher.input(b"helloworld"); +//! +//! // acquire hash digest in the form of GenericArray, +//! // which in this case is equivalent to [u8; 32] +//! let result = hasher.result(); +//! assert_eq!(result[..], hex!("d945dee21ffca23ac232763aa9cac6c15805f144db9d6c97395437e01c8595a8")); +//! # } +//! ``` +//! +//! Also see [RustCrypto/hashes][2] readme. +//! +//! [1]: https://www.cs.rit.edu/~ark/20090927/Round2Candidates/Shabal.pdf +//! [2]: https://github.com/RustCrypto/hashes +#![no_std] +#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")] + +extern crate block_buffer; +#[macro_use] +extern crate opaque_debug; +#[macro_use] +pub extern crate digest; +#[cfg(feature = "std")] +extern crate std; + +mod consts; +mod shabal; + +pub use digest::Digest; +pub use shabal::{Shabal192, Shabal224, Shabal256, Shabal384, Shabal512}; diff --git a/shabal/src/shabal.rs b/shabal/src/shabal.rs new file mode 100644 index 000000000..602d76f5c --- /dev/null +++ b/shabal/src/shabal.rs @@ -0,0 +1,502 @@ +use block_buffer::block_padding::Iso7816; +use block_buffer::byteorder::{ByteOrder, LE}; +use block_buffer::BlockBuffer; +use digest::generic_array::typenum::{U24, U28, U32, U48, U64}; +use digest::generic_array::GenericArray; +pub use digest::Digest; +use digest::{BlockInput, FixedOutput, Input, Reset}; + +use consts::{ + A_INIT_192, A_INIT_224, A_INIT_256, A_INIT_384, A_INIT_512, B_INIT_192, B_INIT_224, B_INIT_256, + B_INIT_384, B_INIT_512, C_INIT_192, C_INIT_224, C_INIT_256, C_INIT_384, C_INIT_512, +}; + +type BlockSize = U64; +type Block = GenericArray; + +/// A structure that represents that state of a digest computation for the +/// Shabal family of digest functions +#[derive(Clone)] +struct EngineState { + a: [u32; 12], + b: [u32; 16], + c: [u32; 16], + whigh: u32, + wlow: u32, +} + +impl EngineState { + fn new(a: &[u32; 12], b: &[u32; 16], c: &[u32; 16]) -> Self { + Self { + a: *a, + b: *b, + c: *c, + wlow: 1, + whigh: 0, + } + } + + fn process_block(&mut self, block: &Block) { + let block = unsafe { &*(block.as_ptr() as *const [u8; 64]) }; + compress(self, block); + } + + fn process_final_block(&mut self, block: &Block) { + let block = unsafe { &*(block.as_ptr() as *const [u8; 64]) }; + compress_final(self, block); + } + + #[inline] + fn add_m(&mut self, m: &[u32; 16]) { + for (b, m) in self.b.iter_mut().zip(m) { + *b = b.wrapping_add(*m); + } + } + + #[inline] + fn sub_m(&mut self, m: &[u32; 16]) { + for (c, m) in self.c.iter_mut().zip(m) { + *c = c.wrapping_sub(*m); + } + } + + #[inline] + fn inc_w(&mut self) { + self.wlow = self.wlow.wrapping_add(1); + if self.wlow == 0 { + self.whigh = self.whigh.wrapping_add(1); + } + } + + #[inline] + fn xor_w(&mut self) { + self.a[0] ^= self.wlow; + self.a[1] ^= self.whigh; + } + + #[inline] + fn perm(&mut self, m: &[u32; 16]) { + for b in self.b.iter_mut() { + *b = b.wrapping_shl(17) | b.wrapping_shr(15); + } + self.perm_blocks(m); + + let a = &mut self.a; + let c = &self.c; + a[0] = a[0] + .wrapping_add(c[11]) + .wrapping_add(c[15]) + .wrapping_add(c[3]); + a[1] = a[1] + .wrapping_add(c[12]) + .wrapping_add(c[0]) + .wrapping_add(c[4]); + a[2] = a[2] + .wrapping_add(c[13]) + .wrapping_add(c[1]) + .wrapping_add(c[5]); + a[3] = a[3] + .wrapping_add(c[14]) + .wrapping_add(c[2]) + .wrapping_add(c[6]); + a[4] = a[4] + .wrapping_add(c[15]) + .wrapping_add(c[3]) + .wrapping_add(c[7]); + a[5] = a[5] + .wrapping_add(c[0]) + .wrapping_add(c[4]) + .wrapping_add(c[8]); + a[6] = a[6] + .wrapping_add(c[1]) + .wrapping_add(c[5]) + .wrapping_add(c[9]); + a[7] = a[7] + .wrapping_add(c[2]) + .wrapping_add(c[6]) + .wrapping_add(c[10]); + a[8] = a[8] + .wrapping_add(c[3]) + .wrapping_add(c[7]) + .wrapping_add(c[11]); + a[9] = a[9] + .wrapping_add(c[4]) + .wrapping_add(c[8]) + .wrapping_add(c[12]); + a[10] = a[10] + .wrapping_add(c[5]) + .wrapping_add(c[9]) + .wrapping_add(c[13]); + a[11] = a[11] + .wrapping_add(c[6]) + .wrapping_add(c[10]) + .wrapping_add(c[14]); + } + + #[inline] + #[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] + fn perm_elt( + &mut self, + xa0: usize, + xa1: usize, + xb0: usize, + xb1: usize, + xb2: usize, + xb3: usize, + xc0: usize, + xm: u32, + ) { + let a = &mut self.a; + let b = &mut self.b; + let xc = self.c[xc0]; + + a[xa0] = (a[xa0] + ^ ((a[xa1].wrapping_shl(15u32) | a[xa1].wrapping_shr(17u32)).wrapping_mul(5u32)) + ^ xc) + .wrapping_mul(3u32) + ^ b[xb1] + ^ (b[xb2] & !b[xb3]) + ^ xm; + b[xb0] = !((b[xb0].wrapping_shl(1) | b[xb0].wrapping_shr(31)) ^ a[xa0]); + } + + #[inline] + fn perm_blocks(&mut self, m: &[u32; 16]) { + self.perm_elt(0, 11, 0, 13, 9, 6, 8, m[0]); + self.perm_elt(1, 0, 1, 14, 10, 7, 7, m[1]); + self.perm_elt(2, 1, 2, 15, 11, 8, 6, m[2]); + self.perm_elt(3, 2, 3, 0, 12, 9, 5, m[3]); + self.perm_elt(4, 3, 4, 1, 13, 10, 4, m[4]); + self.perm_elt(5, 4, 5, 2, 14, 11, 3, m[5]); + self.perm_elt(6, 5, 6, 3, 15, 12, 2, m[6]); + self.perm_elt(7, 6, 7, 4, 0, 13, 1, m[7]); + self.perm_elt(8, 7, 8, 5, 1, 14, 0, m[8]); + self.perm_elt(9, 8, 9, 6, 2, 15, 15, m[9]); + self.perm_elt(10, 9, 10, 7, 3, 0, 14, m[10]); + self.perm_elt(11, 10, 11, 8, 4, 1, 13, m[11]); + self.perm_elt(0, 11, 12, 9, 5, 2, 12, m[12]); + self.perm_elt(1, 0, 13, 10, 6, 3, 11, m[13]); + self.perm_elt(2, 1, 14, 11, 7, 4, 10, m[14]); + self.perm_elt(3, 2, 15, 12, 8, 5, 9, m[15]); + self.perm_elt(4, 3, 0, 13, 9, 6, 8, m[0]); + self.perm_elt(5, 4, 1, 14, 10, 7, 7, m[1]); + self.perm_elt(6, 5, 2, 15, 11, 8, 6, m[2]); + self.perm_elt(7, 6, 3, 0, 12, 9, 5, m[3]); + self.perm_elt(8, 7, 4, 1, 13, 10, 4, m[4]); + self.perm_elt(9, 8, 5, 2, 14, 11, 3, m[5]); + self.perm_elt(10, 9, 6, 3, 15, 12, 2, m[6]); + self.perm_elt(11, 10, 7, 4, 0, 13, 1, m[7]); + self.perm_elt(0, 11, 8, 5, 1, 14, 0, m[8]); + self.perm_elt(1, 0, 9, 6, 2, 15, 15, m[9]); + self.perm_elt(2, 1, 10, 7, 3, 0, 14, m[10]); + self.perm_elt(3, 2, 11, 8, 4, 1, 13, m[11]); + self.perm_elt(4, 3, 12, 9, 5, 2, 12, m[12]); + self.perm_elt(5, 4, 13, 10, 6, 3, 11, m[13]); + self.perm_elt(6, 5, 14, 11, 7, 4, 10, m[14]); + self.perm_elt(7, 6, 15, 12, 8, 5, 9, m[15]); + self.perm_elt(8, 7, 0, 13, 9, 6, 8, m[0]); + self.perm_elt(9, 8, 1, 14, 10, 7, 7, m[1]); + self.perm_elt(10, 9, 2, 15, 11, 8, 6, m[2]); + self.perm_elt(11, 10, 3, 0, 12, 9, 5, m[3]); + self.perm_elt(0, 11, 4, 1, 13, 10, 4, m[4]); + self.perm_elt(1, 0, 5, 2, 14, 11, 3, m[5]); + self.perm_elt(2, 1, 6, 3, 15, 12, 2, m[6]); + self.perm_elt(3, 2, 7, 4, 0, 13, 1, m[7]); + self.perm_elt(4, 3, 8, 5, 1, 14, 0, m[8]); + self.perm_elt(5, 4, 9, 6, 2, 15, 15, m[9]); + self.perm_elt(6, 5, 10, 7, 3, 0, 14, m[10]); + self.perm_elt(7, 6, 11, 8, 4, 1, 13, m[11]); + self.perm_elt(8, 7, 12, 9, 5, 2, 12, m[12]); + self.perm_elt(9, 8, 13, 10, 6, 3, 11, m[13]); + self.perm_elt(10, 9, 14, 11, 7, 4, 10, m[14]); + self.perm_elt(11, 10, 15, 12, 8, 5, 9, m[15]); + } + + #[inline] + fn swap_b_c(&mut self) { + ::core::mem::swap(&mut self.b, &mut self.c); + } +} + +/// A structure that keeps track of the state of the Shabal operation and +/// contains the logic necessary to perform the final calculations. +#[derive(Clone)] +struct Engine256 { + buffer: BlockBuffer, + state: EngineState, +} + +impl Engine256 { + fn new(a: &[u32; 12], b: &[u32; 16], c: &[u32; 16]) -> Engine256 { + Engine256 { + buffer: Default::default(), + state: EngineState::new(a, b, c), + } + } + + fn input(&mut self, input: &[u8]) { + let state = &mut self.state; + self.buffer.input(input, |input| state.process_block(input)); + } + + fn finish(&mut self) { + let state = &mut self.state; + let block = self.buffer.pad_with::().unwrap(); + state.process_final_block(block); + } + + fn reset(&mut self, a: &[u32; 12], b: &[u32; 16], c: &[u32; 16]) { + self.state = EngineState::new(a, b, c); + self.buffer.reset(); + } +} + +/// The Shabal hash algorithm with the Shabal-512 initial hash value. +#[derive(Clone)] +pub struct Shabal512 { + engine: Engine256, +} + +impl Default for Shabal512 { + fn default() -> Self { + Self { + engine: Engine256::new(&A_INIT_512, &B_INIT_512, &C_INIT_512), + } + } +} + +impl BlockInput for Shabal512 { + type BlockSize = BlockSize; +} + +impl Input for Shabal512 { + fn input>(&mut self, input: B) { + self.engine.input(input.as_ref()); + } +} + +impl FixedOutput for Shabal512 { + type OutputSize = U64; + + fn fixed_result(mut self) -> GenericArray { + self.engine.finish(); + let mut out = GenericArray::default(); + LE::write_u32_into(&self.engine.state.b[0..16], out.as_mut_slice()); + out + } +} + +impl Reset for Shabal512 { + fn reset(&mut self) { + self.engine.reset(&A_INIT_512, &B_INIT_512, &C_INIT_512); + } +} + +/// The Shabal hash algorithm with the Shabal-384 initial hash value. The result +/// is truncated to 384 bits. +#[derive(Clone)] +pub struct Shabal384 { + engine: Engine256, +} + +impl Default for Shabal384 { + fn default() -> Self { + Self { + engine: Engine256::new(&A_INIT_384, &B_INIT_384, &C_INIT_384), + } + } +} + +impl BlockInput for Shabal384 { + type BlockSize = BlockSize; +} + +impl Input for Shabal384 { + fn input>(&mut self, input: B) { + self.engine.input(input.as_ref()); + } +} + +impl FixedOutput for Shabal384 { + type OutputSize = U48; + + fn fixed_result(mut self) -> GenericArray { + self.engine.finish(); + let mut out = GenericArray::default(); + LE::write_u32_into(&self.engine.state.b[4..16], out.as_mut_slice()); + out + } +} + +impl Reset for Shabal384 { + fn reset(&mut self) { + self.engine.reset(&A_INIT_384, &B_INIT_384, &C_INIT_384); + } +} + +/// The Shabal hash algorithm with the Shabal-256 initial hash value. The result +/// is truncated to 256 bits. +#[derive(Clone)] +pub struct Shabal256 { + engine: Engine256, +} + +impl Default for Shabal256 { + fn default() -> Self { + Self { + engine: Engine256::new(&A_INIT_256, &B_INIT_256, &C_INIT_256), + } + } +} + +impl BlockInput for Shabal256 { + type BlockSize = BlockSize; +} + +impl Input for Shabal256 { + fn input>(&mut self, input: B) { + self.engine.input(input.as_ref()); + } +} + +impl FixedOutput for Shabal256 { + type OutputSize = U32; + + fn fixed_result(mut self) -> GenericArray { + self.engine.finish(); + let mut out = GenericArray::default(); + LE::write_u32_into(&self.engine.state.b[8..16], out.as_mut_slice()); + out + } +} + +impl Reset for Shabal256 { + fn reset(&mut self) { + self.engine.reset(&A_INIT_256, &B_INIT_256, &C_INIT_256); + } +} + +/// The Shabal hash algorithm with the Shabal-224 initial hash value. The result +/// is truncated to 224 bits. +#[derive(Clone)] +pub struct Shabal224 { + engine: Engine256, +} + +impl Default for Shabal224 { + fn default() -> Self { + Self { + engine: Engine256::new(&A_INIT_224, &B_INIT_224, &C_INIT_224), + } + } +} + +impl BlockInput for Shabal224 { + type BlockSize = BlockSize; +} + +impl Input for Shabal224 { + fn input>(&mut self, input: B) { + self.engine.input(input.as_ref()); + } +} + +impl FixedOutput for Shabal224 { + type OutputSize = U28; + + fn fixed_result(mut self) -> GenericArray { + self.engine.finish(); + let mut out = GenericArray::default(); + LE::write_u32_into(&self.engine.state.b[9..16], out.as_mut_slice()); + out + } +} + +impl Reset for Shabal224 { + fn reset(&mut self) { + self.engine.reset(&A_INIT_224, &B_INIT_224, &C_INIT_224); + } +} + +/// The Shabal hash algorithm with the Shabal-192 initial hash value. The result +/// is truncated to 192 bits. +#[derive(Clone)] +pub struct Shabal192 { + engine: Engine256, +} + +impl Default for Shabal192 { + fn default() -> Self { + Self { + engine: Engine256::new(&A_INIT_192, &B_INIT_192, &C_INIT_192), + } + } +} + +impl BlockInput for Shabal192 { + type BlockSize = BlockSize; +} + +impl Input for Shabal192 { + fn input>(&mut self, input: B) { + self.engine.input(input.as_ref()); + } +} + +impl FixedOutput for Shabal192 { + type OutputSize = U24; + + fn fixed_result(mut self) -> GenericArray { + self.engine.finish(); + let mut out = GenericArray::default(); + LE::write_u32_into(&self.engine.state.b[10..16], out.as_mut_slice()); + out + } +} + +impl Reset for Shabal192 { + fn reset(&mut self) { + self.engine.reset(&A_INIT_192, &B_INIT_192, &C_INIT_192); + } +} + +impl_opaque_debug!(Shabal512); +impl_opaque_debug!(Shabal384); +impl_opaque_debug!(Shabal256); +impl_opaque_debug!(Shabal224); +impl_opaque_debug!(Shabal192); + +impl_write!(Shabal512); +impl_write!(Shabal384); +impl_write!(Shabal256); +impl_write!(Shabal224); +impl_write!(Shabal192); + +#[inline] +fn read_m(input: &[u8; 64]) -> [u32; 16] { + let mut m = [0; 16]; + LE::read_u32_into(input, &mut m); + m +} + +fn compress(state: &mut EngineState, input: &[u8; 64]) { + let m = read_m(input); + state.add_m(&m); + state.xor_w(); + state.perm(&m); + state.sub_m(&m); + state.swap_b_c(); + state.inc_w(); +} + +fn compress_final(state: &mut EngineState, input: &[u8; 64]) { + let m = read_m(input); + state.add_m(&m); + state.xor_w(); + state.perm(&m); + for _ in 0..3 { + state.swap_b_c(); + state.xor_w(); + state.perm(&m); + } +} diff --git a/shabal/tests/data/shabal192.blb b/shabal/tests/data/shabal192.blb new file mode 100644 index 000000000..b205df8a2 Binary files /dev/null and b/shabal/tests/data/shabal192.blb differ diff --git a/shabal/tests/data/shabal192_one_million_a.bin b/shabal/tests/data/shabal192_one_million_a.bin new file mode 100644 index 000000000..f0f88ef08 --- /dev/null +++ b/shabal/tests/data/shabal192_one_million_a.bin @@ -0,0 +1 @@ +���@I����>F����z�HI5@ \ No newline at end of file diff --git a/shabal/tests/data/shabal224.blb b/shabal/tests/data/shabal224.blb new file mode 100644 index 000000000..29de7decf Binary files /dev/null and b/shabal/tests/data/shabal224.blb differ diff --git a/shabal/tests/data/shabal224_one_million_a.bin b/shabal/tests/data/shabal224_one_million_a.bin new file mode 100644 index 000000000..536fb4ba4 --- /dev/null +++ b/shabal/tests/data/shabal224_one_million_a.bin @@ -0,0 +1 @@ +4�3�ȅ��\+ͦ��Γ�x�o_�� \ No newline at end of file diff --git a/shabal/tests/data/shabal256.blb b/shabal/tests/data/shabal256.blb new file mode 100644 index 000000000..e978cb199 Binary files /dev/null and b/shabal/tests/data/shabal256.blb differ diff --git a/shabal/tests/data/shabal256_one_million_a.bin b/shabal/tests/data/shabal256_one_million_a.bin new file mode 100644 index 000000000..e6c070a1a --- /dev/null +++ b/shabal/tests/data/shabal256_one_million_a.bin @@ -0,0 +1,2 @@ + +�!�5D� ���㙘g����H��bx�e� \ No newline at end of file diff --git a/shabal/tests/data/shabal384.blb b/shabal/tests/data/shabal384.blb new file mode 100644 index 000000000..266410a76 Binary files /dev/null and b/shabal/tests/data/shabal384.blb differ diff --git a/shabal/tests/data/shabal384_one_million_a.bin b/shabal/tests/data/shabal384_one_million_a.bin new file mode 100644 index 000000000..5d5f5492d --- /dev/null +++ b/shabal/tests/data/shabal384_one_million_a.bin @@ -0,0 +1 @@ +�xՋ��!����v#T�~ջ�<��i�{��穭xm0��85�{<��p� \ No newline at end of file diff --git a/shabal/tests/data/shabal512.blb b/shabal/tests/data/shabal512.blb new file mode 100644 index 000000000..d874809f7 Binary files /dev/null and b/shabal/tests/data/shabal512.blb differ diff --git a/shabal/tests/data/shabal512_one_million_a.bin b/shabal/tests/data/shabal512_one_million_a.bin new file mode 100644 index 000000000..1c26f7e06 Binary files /dev/null and b/shabal/tests/data/shabal512_one_million_a.bin differ diff --git a/shabal/tests/lib.rs b/shabal/tests/lib.rs new file mode 100644 index 000000000..6de08fd39 --- /dev/null +++ b/shabal/tests/lib.rs @@ -0,0 +1,42 @@ +#![no_std] +#[macro_use] +extern crate digest; +extern crate shabal; + +use digest::dev::{digest_test, one_million_a}; + +new_test!(shabal192_main, "shabal192", shabal::Shabal192, digest_test); +new_test!(shabal224_main, "shabal224", shabal::Shabal224, digest_test); +new_test!(shabal256_main, "shabal256", shabal::Shabal256, digest_test); +new_test!(shabal384_main, "shabal384", shabal::Shabal384, digest_test); +new_test!(shabal512_main, "shabal512", shabal::Shabal512, digest_test); + +#[test] +fn sha192_1million_a() { + let output = include_bytes!("data/shabal192_one_million_a.bin"); + one_million_a::(output); +} + +#[test] +fn sha224_1million_a() { + let output = include_bytes!("data/shabal224_one_million_a.bin"); + one_million_a::(output); +} + +#[test] +fn sha256_1million_a() { + let output = include_bytes!("data/shabal256_one_million_a.bin"); + one_million_a::(output); +} + +#[test] +fn sha384_1million_a() { + let output = include_bytes!("data/shabal384_one_million_a.bin"); + one_million_a::(output); +} + +#[test] +fn sha512_1million_a() { + let output = include_bytes!("data/shabal512_one_million_a.bin"); + one_million_a::(output); +}