use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::Reference;
use crate::schema::{NonEmptyString, RunxSchema};
#[must_use]
pub fn hex_lower(bytes: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut encoded = String::with_capacity(bytes.len() * 2);
for byte in bytes {
encoded.push(HEX[(byte >> 4) as usize] as char);
encoded.push(HEX[(byte & 0x0f) as usize] as char);
}
encoded
}
#[must_use]
pub fn sha256_hex(bytes: &[u8]) -> String {
hex_lower(&Sha256::digest(bytes))
}
#[must_use]
pub fn sha256_prefixed(bytes: &[u8]) -> String {
format!("sha256:{}", sha256_hex(bytes))
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(rename_all = "snake_case")]
pub enum FingerprintAlgorithm {
Sha256,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
#[serde(deny_unknown_fields)]
pub struct Fingerprint {
pub algorithm: FingerprintAlgorithm,
pub canonicalization: NonEmptyString,
pub value: NonEmptyString,
pub derived_from: Vec<Reference>,
}
#[cfg(test)]
mod tests {
use super::{hex_lower, sha256_hex, sha256_prefixed};
#[test]
fn hex_lower_encodes_bytes() {
assert_eq!(hex_lower(&[0xde, 0xad, 0xbe, 0xef]), "deadbeef");
}
#[test]
fn sha256_hex_hashes_bytes() {
assert_eq!(
sha256_hex(b"runx"),
"8186b7035bea2f66ebe27c1f5cf7de4e94ef935e259a2f3160352adffc752f28",
);
}
#[test]
fn sha256_prefixed_includes_algorithm_tag() {
assert_eq!(
sha256_prefixed(b"runx"),
"sha256:8186b7035bea2f66ebe27c1f5cf7de4e94ef935e259a2f3160352adffc752f28",
);
}
}