Initial import and port from C++ code.
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emsha"
|
||||||
|
version = "1.0.0"
|
||||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "emsha"
|
||||||
|
description = "embedded secure hashing"
|
||||||
|
repository = "https://git.wntrmute.dev/wntrmute/emsha-rs"
|
||||||
|
categories = ["cryptography", "no-std", "embedded"]
|
||||||
|
keywords = ["sha256", "hmac", "hash", "embedded", "no_std"]
|
||||||
|
license-file = "LICENSE"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2024"
|
||||||
|
publish = ["kellnr"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
205
LICENSE
Normal file
205
LICENSE
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
WNTRMUTE HEAVY INDUSTRIES LICENSE
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Copyright 2025 K. Isom <kyle@wntrmute.dev>. All rights reserved.
|
||||||
|
|
||||||
|
No license is granted to use, copy, modify, or distribute this
|
||||||
|
software unless express written permission is obtained from the
|
||||||
|
copyright holder.
|
||||||
|
|
||||||
|
If the copyright holder grants express written permission for use, the
|
||||||
|
software shall be licensed under the terms of the Apache License,
|
||||||
|
Version 2.0 (the "License"), a copy of which is provided below/
|
||||||
|
attached. You may not use this software except in compliance with the
|
||||||
|
License if permission is granted. The copyright holder reserves the
|
||||||
|
right to amend future versions of the software with additional
|
||||||
|
provisions.
|
||||||
|
|
||||||
|
Copyright 2025 K. Isom <kyle@imap.cc>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# emsha: embedded secure hashing
|
||||||
|
|
||||||
|
This library is an HMAC-SHA-256 Rust library designed for embedded
|
||||||
|
systems. It is built following the JPL [Power of
|
||||||
|
Ten](http://spinroot.com/gerard/pdf/P10.pdf) rules. It was written in
|
||||||
|
response to a need for a standalone HMAC-SHA-256 package that could run
|
||||||
|
on several platforms, including several memory- constrained embedded
|
||||||
|
platforms. It works without using the Rust standard environment.
|
||||||
|
|
||||||
|
I had previously written a [C++ version](https://git.wntrmute.dev/sc/emsha);
|
||||||
|
porting it to Rust seemed a natural way to keep trying to learn the
|
||||||
|
language.
|
||||||
|
|
||||||
|
Note: it requires the 2024 edition of Rust due to its use of
|
||||||
|
`core::error::Error`.
|
||||||
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
max_width = 72
|
||||||
36
src/common.rs
Normal file
36
src/common.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#[inline]
|
||||||
|
pub(crate) fn rotr32(x: u32, n: u8) -> u32 {
|
||||||
|
(x >> n) | (x << (32 - n))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)] // the name is taken from the RFC
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn sha_Σ0(x: u32) -> u32 {
|
||||||
|
rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)] // the name is taken from the RFC
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn sha_Σ1(x: u32) -> u32 {
|
||||||
|
rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn sha_σ0(x: u32) -> u32 {
|
||||||
|
rotr32(x, 7) ^ rotr32(x, 18) ^ (x >> 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn sha_σ1(x: u32) -> u32 {
|
||||||
|
rotr32(x, 17) ^ rotr32(x, 19) ^ (x >> 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn sha_ch(x: u32, y: u32, z: u32) -> u32 {
|
||||||
|
(x & y) ^ ((!x) & z)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn sha_maj(x: u32, y: u32, z: u32) -> u32 {
|
||||||
|
(x & y) ^ (x & z) ^ (y & z)
|
||||||
|
}
|
||||||
164
src/hmac.rs
Normal file
164
src/hmac.rs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
//! HMAC-SHA256 implementation.
|
||||||
|
|
||||||
|
use crate::{sha256, Code, Error, Hash, Result};
|
||||||
|
use core::fmt;
|
||||||
|
use core::fmt::Formatter;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum State {
|
||||||
|
IPad,
|
||||||
|
OPad,
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const KEY_LENGTH: usize = 64;
|
||||||
|
const IPAD: u8 = 0x36;
|
||||||
|
const OPAD: u8 = 0x5c;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct HMAC_SHA256 {
|
||||||
|
state: State,
|
||||||
|
ctx: sha256::SHA256,
|
||||||
|
k: [u8; KEY_LENGTH],
|
||||||
|
buf: [u8; sha256::SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for HMAC_SHA256 {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "HMAC-SHA256<{:?}, ", self.ctx.hresult)?;
|
||||||
|
if self.state != State::Finished {
|
||||||
|
write!(f, "in")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "complete>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HMAC_SHA256 {
|
||||||
|
pub fn new(k: &[u8]) -> Result<Self> {
|
||||||
|
let mut ik: [u8; KEY_LENGTH] = [0; KEY_LENGTH];
|
||||||
|
let mut ctx = sha256::SHA256::default();
|
||||||
|
|
||||||
|
if k.len() > KEY_LENGTH {
|
||||||
|
ctx.update(k)?;
|
||||||
|
ctx.finalize(&mut ik[..sha256::SIZE])?;
|
||||||
|
ctx.reset()?;
|
||||||
|
} else {
|
||||||
|
ik[0..k.len()].copy_from_slice(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut h = Self {
|
||||||
|
state: State::IPad,
|
||||||
|
ctx,
|
||||||
|
k: ik,
|
||||||
|
buf: [0; sha256::SIZE],
|
||||||
|
};
|
||||||
|
|
||||||
|
h.reset()?;
|
||||||
|
|
||||||
|
Ok(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_digest(&self, digest: &mut [u8]) -> Result<()> {
|
||||||
|
if digest.len() < sha256::SIZE {
|
||||||
|
return Err(Error::with(Code::BufferTooSmall));
|
||||||
|
}
|
||||||
|
digest[..sha256::SIZE].copy_from_slice(&self.buf);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for HMAC_SHA256 {
|
||||||
|
fn reset(&mut self) -> Result<()> {
|
||||||
|
// Following a reset, both SHA-256 contexts and result buffer
|
||||||
|
// should be zero'd out for a clean slate. The HMAC state
|
||||||
|
// should be reset accordingly.
|
||||||
|
self.ctx.reset()?;
|
||||||
|
self.buf.fill(0);
|
||||||
|
|
||||||
|
// Set up the k0 ⊕ inner padding construction and write it
|
||||||
|
// into the SHA-256 context.
|
||||||
|
let mut k: [u8; KEY_LENGTH] = [0; KEY_LENGTH];
|
||||||
|
let mut i: usize = 0;
|
||||||
|
while i < KEY_LENGTH {
|
||||||
|
k[i] = self.k[i] ^ IPAD;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.update(&k)?;
|
||||||
|
|
||||||
|
// This key is considered sensitive material and should be
|
||||||
|
// wiped.
|
||||||
|
k.fill(0);
|
||||||
|
|
||||||
|
self.state = State::IPad;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: &[u8]) -> Result<()> {
|
||||||
|
debug_assert_eq!(self.state, State::IPad);
|
||||||
|
if self.state != State::IPad {
|
||||||
|
return Err(Error::with(Code::InvalidState));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.update(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(&mut self, digest: &mut [u8]) -> Result<()> {
|
||||||
|
debug_assert_eq!(digest.len(), sha256::SIZE);
|
||||||
|
if digest.len() != sha256::SIZE {
|
||||||
|
return Err(Error::with(Code::BufferTooSmall));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.state == State::Finished {
|
||||||
|
self.copy_digest(digest)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert_eq!(self.state, State::IPad);
|
||||||
|
if self.state != State::IPad {
|
||||||
|
return Err(Error::with(Code::InvalidState));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.finalize(&mut self.buf)?;
|
||||||
|
self.ctx.reset()?;
|
||||||
|
|
||||||
|
let mut k: [u8; KEY_LENGTH] = [0; KEY_LENGTH];
|
||||||
|
let mut i: usize = 0;
|
||||||
|
|
||||||
|
// Set up the k0 ⊕ outer padding construction and write it into
|
||||||
|
// the SHA-256 context.
|
||||||
|
while i < KEY_LENGTH {
|
||||||
|
k[i] = self.k[i] ^ OPAD;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
self.ctx.update(&k)?;
|
||||||
|
k.fill(0);
|
||||||
|
self.state = State::OPad;
|
||||||
|
|
||||||
|
self.ctx.update(&self.buf)?;
|
||||||
|
self.ctx.finalize(&mut self.buf)?;
|
||||||
|
self.state = State::Finished;
|
||||||
|
|
||||||
|
self.copy_digest(digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result(&self, digest: &mut [u8]) -> Result<()> {
|
||||||
|
debug_assert_eq!(digest.len(), sha256::SIZE);
|
||||||
|
if digest.len() != sha256::SIZE {
|
||||||
|
return Err(Error::with(Code::BufferTooSmall));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.state != State::Finished {
|
||||||
|
return Err(Error::with(Code::HashNotFinalized));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.copy_digest(digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size() -> usize {
|
||||||
|
sha256::SIZE
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/lib.rs
Normal file
95
src/lib.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
//! emsha is the embedded hashing library. It aims to work even in
|
||||||
|
//! nostdenv environments.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::error;
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Error {
|
||||||
|
reason: Code,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.reason {
|
||||||
|
Code::Unknown => write!(f, "unknown error"),
|
||||||
|
Code::OK => write!(f, "OK"),
|
||||||
|
Code::TestFailure => write!(f, "test failure"),
|
||||||
|
Code::InvalidState => write!(f, "invalid state"),
|
||||||
|
Code::InputTooLong => write!(f, "input is too long"),
|
||||||
|
Code::BufferTooSmall => {
|
||||||
|
write!(f, "output buffer too small")
|
||||||
|
}
|
||||||
|
Code::HashNotFinalized => {
|
||||||
|
write!(f, "hash has not been finalized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn with(reason: Code) -> Self {
|
||||||
|
Self { reason }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Code {
|
||||||
|
Unknown,
|
||||||
|
OK,
|
||||||
|
TestFailure,
|
||||||
|
InvalidState,
|
||||||
|
InputTooLong,
|
||||||
|
BufferTooSmall,
|
||||||
|
HashNotFinalized,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
pub trait Hash {
|
||||||
|
/// Bring the Hash back to its initial state.
|
||||||
|
///
|
||||||
|
/// That is, the idea is that
|
||||||
|
///
|
||||||
|
/// `hash.reset();`
|
||||||
|
/// `hash.update(...);`
|
||||||
|
/// `hash.result(...);`
|
||||||
|
///
|
||||||
|
/// is idempotent, assuming the inputs to update and result are
|
||||||
|
/// constant. The implications of this for a given implementer
|
||||||
|
/// should be described in that type's documentation, but in
|
||||||
|
/// general, it has the effect of preserving any initial state
|
||||||
|
/// while removing any data written to the Hash via the update
|
||||||
|
/// method.
|
||||||
|
fn reset(&mut self) -> Result<()>;
|
||||||
|
|
||||||
|
/// Write message data into the Hash.
|
||||||
|
fn update(&mut self, msg: &[u8]) -> Result<()>;
|
||||||
|
|
||||||
|
/// Carry out any final operations on the Hash.
|
||||||
|
///
|
||||||
|
/// After a call to finalize, no more data can be written.
|
||||||
|
/// Additionally, it transfers out the resulting hash into its
|
||||||
|
/// argument.
|
||||||
|
fn finalize(&mut self, digest: &mut [u8]) -> Result<()>;
|
||||||
|
|
||||||
|
/// Transfers out the hash to the argument.
|
||||||
|
///
|
||||||
|
/// The Hash must keep enough state for repeated calls to result
|
||||||
|
/// to work.
|
||||||
|
fn result(&self, digest: &mut [u8]) -> Result<()>;
|
||||||
|
|
||||||
|
/// Return the output size of the Hash.
|
||||||
|
///
|
||||||
|
/// This is how large the buffers written to by result should
|
||||||
|
/// be.
|
||||||
|
fn size() -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
pub mod hmac;
|
||||||
|
pub mod sha256;
|
||||||
393
src/sha256.rs
Normal file
393
src/sha256.rs
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
use crate::{Code, Error, Hash, Result};
|
||||||
|
use core::fmt;
|
||||||
|
use core::fmt::Formatter;
|
||||||
|
|
||||||
|
const K: [u32; 64] = [
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
|
||||||
|
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
|
||||||
|
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
|
||||||
|
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||||
|
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
|
||||||
|
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||||||
|
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||||||
|
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
|
||||||
|
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
|
||||||
|
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
|
||||||
|
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||||
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
|
];
|
||||||
|
|
||||||
|
const H0: [u32; 8] = [
|
||||||
|
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F,
|
||||||
|
0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
|
||||||
|
];
|
||||||
|
|
||||||
|
const MB_SIZE: usize = 64;
|
||||||
|
|
||||||
|
pub const SIZE: usize = 32;
|
||||||
|
|
||||||
|
fn u32_to_chunk_in_place(x: u32, chunk: &mut [u8]) {
|
||||||
|
assert!(chunk.len() >= 4);
|
||||||
|
chunk[0] = ((x & 0xff000000) >> 24) as u8;
|
||||||
|
chunk[1] = ((x & 0x00ff0000) >> 16) as u8;
|
||||||
|
chunk[2] = ((x & 0x0000ff00) >> 8) as u8;
|
||||||
|
chunk[3] = (x & 0x000000ff) as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SHA256 {
|
||||||
|
mlen: u64,
|
||||||
|
i_hash: [u32; 8],
|
||||||
|
|
||||||
|
pub(crate) hresult: Code,
|
||||||
|
complete: bool,
|
||||||
|
|
||||||
|
mbi: usize,
|
||||||
|
mb: [u8; MB_SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SHA256 {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "SHA256<{:?}, ", self.hresult)?;
|
||||||
|
if !self.complete {
|
||||||
|
write!(f, "in")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "complete>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SHA256 {
|
||||||
|
fn default() -> SHA256 {
|
||||||
|
SHA256::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SHA256 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
mlen: 0,
|
||||||
|
i_hash: H0,
|
||||||
|
hresult: Code::OK,
|
||||||
|
complete: false,
|
||||||
|
mbi: 0,
|
||||||
|
mb: [0; MB_SIZE],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chunk_to_u32(&self, offset: usize) -> u32 {
|
||||||
|
let mut chunk: u32 = 0;
|
||||||
|
let mut i: usize = offset;
|
||||||
|
|
||||||
|
while i < offset + 4 {
|
||||||
|
chunk <<= 8;
|
||||||
|
chunk += self.mb[i] as u32;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_message_block(&mut self) {
|
||||||
|
let mut w: [u32; MB_SIZE] = [0; MB_SIZE];
|
||||||
|
let mut chunk = 0;
|
||||||
|
let mut i: usize = 0;
|
||||||
|
|
||||||
|
while i < 16 {
|
||||||
|
w[i] = self.chunk_to_u32(chunk);
|
||||||
|
chunk += 4;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mbi = 0;
|
||||||
|
i = 16;
|
||||||
|
while i < 64 {
|
||||||
|
w[i] = sha_σ1(w[i - 2])
|
||||||
|
.wrapping_add(w[i - 7])
|
||||||
|
.wrapping_add(sha_σ0(w[i - 15]))
|
||||||
|
.wrapping_add(w[i - 16]);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut a = self.i_hash[0];
|
||||||
|
let mut b = self.i_hash[1];
|
||||||
|
let mut c = self.i_hash[2];
|
||||||
|
let mut d = self.i_hash[3];
|
||||||
|
let mut e = self.i_hash[4];
|
||||||
|
let mut f = self.i_hash[5];
|
||||||
|
let mut g = self.i_hash[6];
|
||||||
|
let mut h = self.i_hash[7];
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while i < 64 {
|
||||||
|
let t1 = h
|
||||||
|
.wrapping_add(sha_Σ1(e))
|
||||||
|
.wrapping_add(sha_ch(e, f, g))
|
||||||
|
.wrapping_add(K[i])
|
||||||
|
.wrapping_add(w[i]);
|
||||||
|
let t2 = sha_Σ0(a).wrapping_add(sha_maj(a, b, c));
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = d.wrapping_add(t1);
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = t1.wrapping_add(t2);
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
self.i_hash[0] = self.i_hash[0].wrapping_add(a);
|
||||||
|
self.i_hash[1] = self.i_hash[1].wrapping_add(b);
|
||||||
|
self.i_hash[2] = self.i_hash[2].wrapping_add(c);
|
||||||
|
self.i_hash[3] = self.i_hash[3].wrapping_add(d);
|
||||||
|
self.i_hash[4] = self.i_hash[4].wrapping_add(e);
|
||||||
|
self.i_hash[5] = self.i_hash[5].wrapping_add(f);
|
||||||
|
self.i_hash[6] = self.i_hash[6].wrapping_add(g);
|
||||||
|
self.i_hash[7] = self.i_hash[7].wrapping_add(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_length(&mut self, δ: usize) -> Result<()> {
|
||||||
|
let mδ = self.mlen + δ as u64;
|
||||||
|
if mδ < self.mlen {
|
||||||
|
return Err(Error::with(Code::InputTooLong));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mlen = mδ;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_message(&mut self, pc: u8) -> Result<()> {
|
||||||
|
if self.mbi < (MB_SIZE - 8) {
|
||||||
|
self.mb[self.mbi] = pc;
|
||||||
|
self.mbi += 1;
|
||||||
|
} else {
|
||||||
|
let mut pc_add = false;
|
||||||
|
|
||||||
|
if self.mbi < MB_SIZE - 1 {
|
||||||
|
self.mb[self.mbi] = pc;
|
||||||
|
self.mbi += 1;
|
||||||
|
pc_add = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while self.mbi < MB_SIZE {
|
||||||
|
self.mb[self.mbi] = 0;
|
||||||
|
self.mbi += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_message_block();
|
||||||
|
if !pc_add {
|
||||||
|
self.mb[self.mbi] = pc;
|
||||||
|
self.mbi += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumption: updating the message block has not left the
|
||||||
|
// context in a corrupted state.
|
||||||
|
debug_assert_eq!(self.hresult, Code::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
while self.mbi < (MB_SIZE - 8) {
|
||||||
|
self.mb[self.mbi] = 0;
|
||||||
|
self.mbi += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LSTART: usize = MB_SIZE - 8;
|
||||||
|
self.mb[LSTART] = (self.mlen >> 56) as u8;
|
||||||
|
self.mb[LSTART + 1] =
|
||||||
|
((self.mlen & 0x00ff000000000000) >> 48) as u8;
|
||||||
|
self.mb[LSTART + 2] =
|
||||||
|
((self.mlen & 0x0000ff0000000000) >> 40) as u8;
|
||||||
|
self.mb[LSTART + 3] =
|
||||||
|
((self.mlen & 0x000000ff00000000) >> 32) as u8;
|
||||||
|
self.mb[LSTART + 4] =
|
||||||
|
((self.mlen & 0x00000000ff000000) >> 24) as u8;
|
||||||
|
self.mb[LSTART + 5] =
|
||||||
|
((self.mlen & 0x0000000000ff0000) >> 16) as u8;
|
||||||
|
self.mb[LSTART + 6] =
|
||||||
|
((self.mlen & 0x000000000000ff00) >> 8) as u8;
|
||||||
|
self.mb[LSTART + 7] = (self.mlen & 0x00000000000000ff) as u8;
|
||||||
|
|
||||||
|
self.update_message_block();
|
||||||
|
|
||||||
|
// Assumption: updating the message block has not left the
|
||||||
|
// context in a corrupted state.
|
||||||
|
debug_assert_eq!(self.hresult, Code::OK);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_out_digest(&self, digest: &mut [u8]) {
|
||||||
|
u32_to_chunk_in_place(self.i_hash[0], digest);
|
||||||
|
u32_to_chunk_in_place(self.i_hash[1], &mut digest[4..]);
|
||||||
|
u32_to_chunk_in_place(self.i_hash[2], &mut digest[8..]);
|
||||||
|
u32_to_chunk_in_place(self.i_hash[3], &mut digest[12..]);
|
||||||
|
u32_to_chunk_in_place(self.i_hash[4], &mut digest[16..]);
|
||||||
|
u32_to_chunk_in_place(self.i_hash[5], &mut digest[20..]);
|
||||||
|
u32_to_chunk_in_place(self.i_hash[6], &mut digest[24..]);
|
||||||
|
u32_to_chunk_in_place(self.i_hash[7], &mut digest[28..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for SHA256 {
|
||||||
|
fn reset(&mut self) -> Result<()> {
|
||||||
|
// The message block is set to the initial hash vector.
|
||||||
|
self.i_hash[0] = H0[0];
|
||||||
|
self.i_hash[1] = H0[1];
|
||||||
|
self.i_hash[2] = H0[2];
|
||||||
|
self.i_hash[3] = H0[3];
|
||||||
|
self.i_hash[4] = H0[4];
|
||||||
|
self.i_hash[5] = H0[5];
|
||||||
|
self.i_hash[6] = H0[6];
|
||||||
|
self.i_hash[7] = H0[7];
|
||||||
|
|
||||||
|
self.mb.fill(0);
|
||||||
|
self.mbi = 0;
|
||||||
|
self.hresult = Code::OK;
|
||||||
|
self.complete = false;
|
||||||
|
self.mlen = 0;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: &[u8]) -> Result<()> {
|
||||||
|
if msg.is_empty() {
|
||||||
|
self.hresult = Code::OK;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.hresult != Code::OK {
|
||||||
|
return Err(Error::with(self.hresult));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.complete {
|
||||||
|
return Err(Error::with(Code::InvalidState));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i: usize = 0;
|
||||||
|
while i < msg.len() {
|
||||||
|
self.mb[self.mbi] = msg[i];
|
||||||
|
self.mbi += 1;
|
||||||
|
self.add_length(8)?;
|
||||||
|
if self.mbi == MB_SIZE {
|
||||||
|
self.update_message_block();
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.hresult != Code::OK {
|
||||||
|
Err(Error::with(self.hresult))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(&mut self, digest: &mut [u8]) -> Result<()> {
|
||||||
|
if digest.len() < SIZE {
|
||||||
|
return Err(Error::with(Code::BufferTooSmall));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.hresult != Code::OK {
|
||||||
|
return Err(Error::with(self.hresult));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.complete {
|
||||||
|
return Err(Error::with(Code::InvalidState));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pad_message(0x80)?;
|
||||||
|
|
||||||
|
let mut i: usize = 0;
|
||||||
|
while i < self.mb.len() {
|
||||||
|
self.mb[i] = 0;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.complete = true;
|
||||||
|
self.mlen = 0;
|
||||||
|
|
||||||
|
self.copy_out_digest(digest);
|
||||||
|
self.hresult = Code::OK;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result(&self, digest: &mut [u8]) -> Result<()> {
|
||||||
|
if digest.len() < SIZE {
|
||||||
|
return Err(Error::with(Code::BufferTooSmall));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.hresult != Code::OK {
|
||||||
|
return Err(Error::with(self.hresult));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.complete {
|
||||||
|
return Err(Error::with(Code::HashNotFinalized));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.copy_out_digest(digest);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size() -> usize {
|
||||||
|
SIZE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_test(input: &[u8], expected: &[u8]) -> Result<()> {
|
||||||
|
let mut h = SHA256::default();
|
||||||
|
let mut d: [u8; SIZE] = [0; SIZE];
|
||||||
|
|
||||||
|
h.update(input)?;
|
||||||
|
h.finalize(&mut d)?;
|
||||||
|
assert_eq!(d, expected);
|
||||||
|
|
||||||
|
d = [0; SIZE];
|
||||||
|
h.result(&mut d)?;
|
||||||
|
assert_eq!(d, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_reset_cycle() -> Result<()> {
|
||||||
|
let mut h = SHA256::default();
|
||||||
|
let mut d: [u8; SIZE] = [0; SIZE];
|
||||||
|
|
||||||
|
h.update(b"hello, world")?;
|
||||||
|
h.finalize(&mut d)?;
|
||||||
|
assert_eq!(&d, HELLO_WORLD);
|
||||||
|
|
||||||
|
d = [0; SIZE];
|
||||||
|
h.reset()?;
|
||||||
|
h.finalize(&mut d)?;
|
||||||
|
assert_eq!(&d, EMPTY_VECTOR);
|
||||||
|
|
||||||
|
h.reset()?;
|
||||||
|
h.update(b"hello, world")?;
|
||||||
|
h.finalize(&mut d)?;
|
||||||
|
assert_eq!(&d, HELLO_WORLD);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMPTY_VECTOR: &[u8; SIZE] = &[
|
||||||
|
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
|
||||||
|
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
|
||||||
|
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
||||||
|
];
|
||||||
|
|
||||||
|
const HELLO_WORLD: &[u8; SIZE] = &[
|
||||||
|
0x09, 0xca, 0x7e, 0x4e, 0xaa, 0x6e, 0x8a, 0xe9, 0xc7, 0xd2, 0x61,
|
||||||
|
0x16, 0x71, 0x29, 0x18, 0x48, 0x83, 0x64, 0x4d, 0x07, 0xdf, 0xba,
|
||||||
|
0x7c, 0xbf, 0xbc, 0x4c, 0x8a, 0x2e, 0x08, 0x36, 0x0d, 0x5b,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// self_test runs a quick test cycle, and can be used to validate
|
||||||
|
/// correctness in systems on startup.
|
||||||
|
pub fn self_test() -> Result<()> {
|
||||||
|
run_test(b"", EMPTY_VECTOR)?;
|
||||||
|
run_test(b"hello, world", HELLO_WORLD)?;
|
||||||
|
run_reset_cycle()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
43
tests/common.rs
Normal file
43
tests/common.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
const LUT: [&str; 256] = [
|
||||||
|
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a",
|
||||||
|
"0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15",
|
||||||
|
"16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20",
|
||||||
|
"21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b",
|
||||||
|
"2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36",
|
||||||
|
"37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41",
|
||||||
|
"42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c",
|
||||||
|
"4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57",
|
||||||
|
"58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62",
|
||||||
|
"63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d",
|
||||||
|
"6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78",
|
||||||
|
"79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83",
|
||||||
|
"84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e",
|
||||||
|
"8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
|
||||||
|
"9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4",
|
||||||
|
"a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
|
||||||
|
"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba",
|
||||||
|
"bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5",
|
||||||
|
"c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0",
|
||||||
|
"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db",
|
||||||
|
"dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6",
|
||||||
|
"e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1",
|
||||||
|
"f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc",
|
||||||
|
"fd", "fe", "ff",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn hexstr<const N: usize, const M: usize>(
|
||||||
|
input: &[u8; N],
|
||||||
|
output: &mut [u8; M],
|
||||||
|
) {
|
||||||
|
assert_eq!(M, N * 2);
|
||||||
|
let mut i: usize = 0;
|
||||||
|
let mut o: usize = 0;
|
||||||
|
|
||||||
|
while i < N {
|
||||||
|
let lutc = LUT[input[i] as usize].as_bytes();
|
||||||
|
output[o] = lutc[0];
|
||||||
|
output[o + 1] = lutc[1];
|
||||||
|
o += 2;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
116
tests/hmac.rs
Normal file
116
tests/hmac.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
mod common;
|
||||||
|
use common::hexstr;
|
||||||
|
use emsha::{hmac, sha256, Hash, Result};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hmac_00() -> Result<()> {
|
||||||
|
let k: [u8; 20] = [
|
||||||
|
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||||
|
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||||
|
];
|
||||||
|
let input = b"Hi There";
|
||||||
|
let output = b"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7";
|
||||||
|
let mut digest: [u8; sha256::SIZE] = [0; sha256::SIZE];
|
||||||
|
let mut hdigest: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let mut h = hmac::HMAC_SHA256::new(&k)?;
|
||||||
|
h.update(input)?;
|
||||||
|
h.finalize(&mut digest)?;
|
||||||
|
hexstr(&digest, &mut hdigest);
|
||||||
|
|
||||||
|
assert_eq!(&hdigest, output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hmac_01() -> Result<()> {
|
||||||
|
let k: [u8; 4] = [0x4a, 0x65, 0x66, 0x65];
|
||||||
|
let input = b"what do ya want for nothing?";
|
||||||
|
let output = b"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843";
|
||||||
|
let mut digest: [u8; sha256::SIZE] = [0; sha256::SIZE];
|
||||||
|
let mut hdigest: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let mut h = hmac::HMAC_SHA256::new(&k)?;
|
||||||
|
h.update(input)?;
|
||||||
|
h.finalize(&mut digest)?;
|
||||||
|
hexstr(&digest, &mut hdigest);
|
||||||
|
|
||||||
|
assert_eq!(&hdigest, output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hmac_02() -> Result<()> {
|
||||||
|
let k: [u8; 20] = [
|
||||||
|
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||||
|
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||||
|
];
|
||||||
|
let input = &[0xddu8; 50];
|
||||||
|
let output = b"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe";
|
||||||
|
let mut digest: [u8; sha256::SIZE] = [0; sha256::SIZE];
|
||||||
|
let mut hdigest: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let mut h = hmac::HMAC_SHA256::new(&k)?;
|
||||||
|
h.update(input)?;
|
||||||
|
h.finalize(&mut digest)?;
|
||||||
|
hexstr(&digest, &mut hdigest);
|
||||||
|
|
||||||
|
assert_eq!(&hdigest, output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hmac_03() -> Result<()> {
|
||||||
|
let k: [u8; 25] = [
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
|
||||||
|
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
|
||||||
|
0x15, 0x16, 0x17, 0x18, 0x19,
|
||||||
|
];
|
||||||
|
let input = &[0xcdu8; 50];
|
||||||
|
let output = b"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b";
|
||||||
|
let mut digest: [u8; sha256::SIZE] = [0; sha256::SIZE];
|
||||||
|
let mut hdigest: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let mut h = hmac::HMAC_SHA256::new(&k)?;
|
||||||
|
h.update(input)?;
|
||||||
|
h.finalize(&mut digest)?;
|
||||||
|
hexstr(&digest, &mut hdigest);
|
||||||
|
|
||||||
|
assert_eq!(&hdigest, output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hmac_04() -> Result<()> {
|
||||||
|
let k = [0xaau8; 131];
|
||||||
|
let input =
|
||||||
|
b"Test Using Larger Than Block-Size Key - Hash Key First";
|
||||||
|
let output = b"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54";
|
||||||
|
let mut digest: [u8; sha256::SIZE] = [0; sha256::SIZE];
|
||||||
|
let mut hdigest: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let mut h = hmac::HMAC_SHA256::new(&k)?;
|
||||||
|
h.update(input)?;
|
||||||
|
h.finalize(&mut digest)?;
|
||||||
|
hexstr(&digest, &mut hdigest);
|
||||||
|
|
||||||
|
assert_eq!(&hdigest, output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hmac_05() -> Result<()> {
|
||||||
|
let k = [0xaau8; 131];
|
||||||
|
let input = b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.";
|
||||||
|
let output = b"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2";
|
||||||
|
let mut digest: [u8; sha256::SIZE] = [0; sha256::SIZE];
|
||||||
|
let mut hdigest: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let mut h = hmac::HMAC_SHA256::new(&k)?;
|
||||||
|
h.update(input)?;
|
||||||
|
h.finalize(&mut digest)?;
|
||||||
|
hexstr(&digest, &mut hdigest);
|
||||||
|
|
||||||
|
assert_eq!(&hdigest, output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
77
tests/sha256.rs
Normal file
77
tests/sha256.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
mod common;
|
||||||
|
use common::hexstr;
|
||||||
|
use emsha::sha256;
|
||||||
|
use emsha::{Hash, Result};
|
||||||
|
|
||||||
|
pub(crate) struct HashTest<'a> {
|
||||||
|
pub(crate) output: &'a [u8],
|
||||||
|
pub(crate) input: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> HashTest<'a> {
|
||||||
|
pub fn new(output: &'a [u8], input: &'a [u8]) -> Self {
|
||||||
|
Self { output, input }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_self_test() -> Result<()> {
|
||||||
|
sha256::self_test()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_golden_tests() -> Result<()> {
|
||||||
|
let golden_tests: &[HashTest] = &[
|
||||||
|
HashTest::new(b"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", b""),
|
||||||
|
HashTest::new(b"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", b"a"),
|
||||||
|
HashTest::new(b"fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603", b"ab"),
|
||||||
|
HashTest::new(b"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", b"abc"),
|
||||||
|
HashTest::new(b"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", b"abcd"),
|
||||||
|
HashTest::new(b"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c", b"abcde"),
|
||||||
|
HashTest::new(b"bef57ec7f53a6d40beb640a780a639c83bc29ac8a9816f1fc6c5c6dcd93c4721", b"abcdef"),
|
||||||
|
HashTest::new(b"7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a", b"abcdefg"),
|
||||||
|
HashTest::new(b"9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c02190ecf1e430635ab", b"abcdefgh"),
|
||||||
|
HashTest::new(b"19cc02f26df43cc571bc9ed7b0c4d29224a3ec229529221725ef76d021c8326f", b"abcdefghi"),
|
||||||
|
HashTest::new(b"72399361da6a7754fec986dca5b7cbaf1c810a28ded4abaf56b2106d06cb78b0", b"abcdefghij"),
|
||||||
|
HashTest::new(b"a144061c271f152da4d151034508fed1c138b8c976339de229c3bb6d4bbb4fce", b"Discard medicine more than two years old."),
|
||||||
|
HashTest::new(b"6dae5caa713a10ad04b46028bf6dad68837c581616a1589a265a11288d4bb5c4", b"He who has a shady past knows that nice guys finish last."),
|
||||||
|
HashTest::new(b"ae7a702a9509039ddbf29f0765e70d0001177914b86459284dab8b348c2dce3f", b"I wouldn't marry him with a ten foot pole."),
|
||||||
|
HashTest::new(b"6748450b01c568586715291dfa3ee018da07d36bb7ea6f180c1af6270215c64f", b"Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"),
|
||||||
|
HashTest::new(b"14b82014ad2b11f661b5ae6a99b75105c2ffac278cd071cd6c05832793635774", b"The days of the digital watch are numbered. -Tom Stoppard"),
|
||||||
|
HashTest::new(b"7102cfd76e2e324889eece5d6c41921b1e142a4ac5a2692be78803097f6a48d8", b"Nepal premier won't resign."),
|
||||||
|
HashTest::new(b"23b1018cd81db1d67983c5f7417c44da9deb582459e378d7a068552ea649dc9f", b"For every action there is an equal and opposite government program."),
|
||||||
|
HashTest::new(b"8001f190dfb527261c4cfcab70c98e8097a7a1922129bc4096950e57c7999a5a", b"His money is twice tainted: 'taint yours and 'taint mine."),
|
||||||
|
HashTest::new(b"8c87deb65505c3993eb24b7a150c4155e82eee6960cf0c3a8114ff736d69cad5", b"There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"),
|
||||||
|
HashTest::new(b"bfb0a67a19cdec3646498b2e0f751bddc41bba4b7f30081b0b932aad214d16d7", b"It's a tiny change to the code and not completely disgusting. - Bob Manchek"),
|
||||||
|
HashTest::new(b"7f9a0b9bf56332e19f5a0ec1ad9c1425a153da1c624868fda44561d6b74daf36", b"size: a.out: bad magic"),
|
||||||
|
HashTest::new(b"b13f81b8aad9e3666879af19886140904f7f429ef083286195982a7588858cfc", b"The major problem is with sendmail. -Mark Horton"),
|
||||||
|
HashTest::new(b"b26c38d61519e894480c70c8374ea35aa0ad05b2ae3d6674eec5f52a69305ed4", b"Give me a rock, paper and scissors and I will move the world. CCFestoon"),
|
||||||
|
HashTest::new(b"049d5e26d4f10222cd841a119e38bd8d2e0d1129728688449575d4ff42b842c1", b"If the enemy is within range, then so are you."),
|
||||||
|
HashTest::new(b"0e116838e3cc1c1a14cd045397e29b4d087aa11b0853fc69ec82e90330d60949", b"It's well we cannot hear the screams/That we create in others' dreams."),
|
||||||
|
HashTest::new(b"4f7d8eb5bcf11de2a56b971021a444aa4eafd6ecd0f307b5109e4e776cd0fe46", b"You remind me of a TV show, but that's all right: I watch it anyway."),
|
||||||
|
HashTest::new(b"61c0cc4c4bd8406d5120b3fb4ebc31ce87667c162f29468b3c779675a85aebce", b"C is as portable as Stonehedge!!"),
|
||||||
|
HashTest::new(b"1fb2eb3688093c4a3f80cd87a5547e2ce940a4f923243a79a2a1e242220693ac", b"Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"),
|
||||||
|
HashTest::new(b"395585ce30617b62c80b93e8208ce866d4edc811a177fdb4b82d3911d8696423", b"The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"),
|
||||||
|
HashTest::new(b"4f9b189a13d030838269dce846b16a1ce9ce81fe63e65de2f636863336a98fe6", b"How can you write a big system without C++? -Paul Glick"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut i: usize = 0;
|
||||||
|
let mut h = sha256::SHA256::default();
|
||||||
|
let mut d: [u8; 32] = [0; 32];
|
||||||
|
let mut s: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
while i < golden_tests.len() {
|
||||||
|
eprintln!("golden test: {:}", i);
|
||||||
|
h.update(golden_tests[i].input)?;
|
||||||
|
h.finalize(&mut d)?;
|
||||||
|
hexstr(&d, &mut s);
|
||||||
|
assert_eq!(s, golden_tests[i].output);
|
||||||
|
h.reset()?;
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user