Skip to main content

pask_wire/
digest.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 Wilder Management Inc. (d/b/a Wilder Robotics) <rob@wilder-robotics.com>
3// pask-wire is licensed Apache-2.0. No commercial agreement is required to use,
4// modify or redistribute it; see LICENSING.md in the workspace root.
5
6use alloc::string::String;
7use core::fmt::Write;
8use sha2::{Digest, Sha256};
9
10use crate::{Error, Result};
11
12/// Computes a lowercase `sha256:<hex>` digest.
13#[must_use]
14pub fn sha256_prefixed(bytes: &[u8]) -> String {
15    let digest = Sha256::digest(bytes);
16    let mut output = String::with_capacity(71);
17    output.push_str("sha256:");
18    for byte in digest {
19        write!(&mut output, "{byte:02x}").expect("writing to a String cannot fail");
20    }
21    output
22}
23
24/// Validates the exact lowercase `sha256:<64 hex digits>` representation.
25///
26/// # Errors
27///
28/// Returns [`Error::Validation`] when the prefix, length, case, or digits are invalid.
29pub fn validate_sha256(value: &str) -> Result<()> {
30    let Some(hex) = value.strip_prefix("sha256:") else {
31        return Err(Error::Validation("digest prefix must be sha256:"));
32    };
33    if hex.len() != 64
34        || !hex
35            .as_bytes()
36            .iter()
37            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(byte))
38    {
39        return Err(Error::Validation(
40            "digest must contain 64 lowercase hexadecimal digits",
41        ));
42    }
43    Ok(())
44}