Add extensible hash primitives: SHA1, SHA256, SHA512
Introduces a `crypto` feature (on by default) that wires the RustCrypto
sha1/sha2 crates into a small `HashAlgo` registry. `register_primitives`
iterates `crypto::ALGOS` and installs one Forth host word per algorithm,
each with the stack effect
( c-addr u -- c-addr2 u2 )
reading `u` bytes from `c-addr` and writing the digest into a shared
`HASH_SCRATCH` region in linear memory (carved out between the float
stack and the dictionary).
Adding a new hash is a one-line entry in `ALGOS`. `register_host_primitive`
is now `pub` so downstream crates can extend the VM with their own I/O
host words without forking WAFER — kelvar (a deterministic password
manager on WAFER) is the first consumer.
- 4 unit tests (lib-level sha1/256/512 + registry sanity)
- 5 integration tests (in-VM `SHA1`/`SHA256`/`SHA512` against RFC-3174,
FIPS-180, and the first-round S/KEY seed used by `hel`)
- All 437 existing lib tests still pass; `wafer-web` still builds for
`wasm32-unknown-unknown` with the feature enabled
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
//! Cryptographic hash primitives for WAFER.
|
||||
//!
|
||||
//! Provides a small registry of hash algorithms ([`ALGOS`]) and a Forth
|
||||
//! word per algorithm. Each word has stack effect:
|
||||
//!
|
||||
//! ```text
|
||||
//! ( c-addr u -- c-addr2 u2 )
|
||||
//! ```
|
||||
//!
|
||||
//! It hashes the `u` bytes starting at `c-addr` and writes the digest into
|
||||
//! the [`crate::memory::HASH_SCRATCH_BASE`] region. The output buffer is
|
||||
//! shared and overwritten by every subsequent hash call — copy the bytes
|
||||
//! out before the next invocation if you need to keep them.
|
||||
//!
|
||||
//! The registry is designed to grow: add a new entry to [`ALGOS`] and the
|
||||
//! word is registered automatically by
|
||||
//! [`crate::outer::ForthVM::register_crypto_words`].
|
||||
|
||||
use sha1::{Digest as Sha1Digest, Sha1};
|
||||
use sha2::{Sha256, Sha512};
|
||||
|
||||
/// One hash algorithm registered as a Forth word.
|
||||
pub struct HashAlgo {
|
||||
/// Forth word name (uppercase by convention).
|
||||
pub name: &'static str,
|
||||
/// Digest length in bytes.
|
||||
pub digest_len: usize,
|
||||
/// Hash function.
|
||||
pub hash: fn(&[u8]) -> Vec<u8>,
|
||||
}
|
||||
|
||||
fn sha1_hash(input: &[u8]) -> Vec<u8> {
|
||||
let mut h = Sha1::new();
|
||||
h.update(input);
|
||||
h.finalize().to_vec()
|
||||
}
|
||||
|
||||
fn sha256_hash(input: &[u8]) -> Vec<u8> {
|
||||
let mut h = Sha256::new();
|
||||
h.update(input);
|
||||
h.finalize().to_vec()
|
||||
}
|
||||
|
||||
fn sha512_hash(input: &[u8]) -> Vec<u8> {
|
||||
let mut h = Sha512::new();
|
||||
h.update(input);
|
||||
h.finalize().to_vec()
|
||||
}
|
||||
|
||||
/// All hash algorithms registered as Forth words.
|
||||
pub const ALGOS: &[HashAlgo] = &[
|
||||
HashAlgo {
|
||||
name: "SHA1",
|
||||
digest_len: 20,
|
||||
hash: sha1_hash,
|
||||
},
|
||||
HashAlgo {
|
||||
name: "SHA256",
|
||||
digest_len: 32,
|
||||
hash: sha256_hash,
|
||||
},
|
||||
HashAlgo {
|
||||
name: "SHA512",
|
||||
digest_len: 64,
|
||||
hash: sha512_hash,
|
||||
},
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn hex(bytes: &[u8]) -> String {
|
||||
let mut s = String::with_capacity(bytes.len() * 2);
|
||||
for b in bytes {
|
||||
s.push_str(&format!("{b:02x}"));
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sha1_rfc3174_abc() {
|
||||
assert_eq!(hex(&sha1_hash(b"abc")), "a9993e364706816aba3e25717850c26c9cd0d89d");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sha256_fips180_abc() {
|
||||
assert_eq!(
|
||||
hex(&sha256_hash(b"abc")),
|
||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sha512_fips180_abc() {
|
||||
assert_eq!(
|
||||
hex(&sha512_hash(b"abc")),
|
||||
concat!(
|
||||
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a",
|
||||
"2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_lengths_match() {
|
||||
assert_eq!(ALGOS[0].digest_len, sha1_hash(b"").len());
|
||||
assert_eq!(ALGOS[1].digest_len, sha256_hash(b"").len());
|
||||
assert_eq!(ALGOS[2].digest_len, sha512_hash(b"").len());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user