Skip to main content

prikk_hash/
lib.rs

1#![forbid(unsafe_code)]
2#![warn(missing_docs)]
3
4//! Hash primitives used by Prikk.
5//!
6//! `sha256` runs on the audited `sha2` crate. Prikk originally shipped a first-party SHA-256
7//! implementation for the initial source drop; DC-50 concluded the ROI no longer favoured
8//! maintaining it (identity-bearing maintenance cost, a measured ~5.8x throughput gap, and no
9//! remaining supply-chain benefit once `sha2` was already trusted for Ed25519 signing via
10//! `ed25519-dalek`), and DC-55 performed the swap after an equivalence campaign proved it changed
11//! no existing identity. The outgoing implementation is retained test-only, frozen, as an
12//! independent differential reference — see `tests::frozen_outgoing`.
13//!
14//! `#![forbid(unsafe_code)]` remains true of this crate's own source, but hashing now happens
15//! inside `sha2`, whose accelerated backends use `unsafe` internally for CPU-specific
16//! instructions. This crate no longer provides an unsafe-free guarantee for hashing by itself.
17
18use sha2::{Digest, Sha256};
19
20/// A 32-byte SHA-256 digest.
21pub type Sha256Digest = [u8; 32];
22
23/// Compute SHA-256 for a byte slice.
24#[must_use]
25pub fn sha256(input: &[u8]) -> Sha256Digest {
26    let mut hasher = Sha256::new();
27    hasher.update(input);
28    hasher.finalize().into()
29}
30
31/// Convert bytes to lowercase hex.
32#[must_use]
33pub fn to_hex(bytes: &[u8]) -> String {
34    let mut out = String::with_capacity(bytes.len() * 2);
35    for byte in bytes {
36        out.push(hex_char(byte >> 4));
37        out.push(hex_char(byte & 0x0f));
38    }
39    out
40}
41
42fn hex_char(value: u8) -> char {
43    match value {
44        0..=9 => char::from(b'0' + value),
45        10..=15 => char::from(b'a' + (value - 10)),
46        _ => '?',
47    }
48}
49
50#[cfg(test)]
51mod tests;