Skip to main content

cesr/core/matter/code/
digest.rs

1use super::cesr_code::CesrCode;
2use super::matter_code::MatterCode;
3use super::sealed::Sealed;
4use crate::core::matter::error::ValidationError;
5#[cfg(feature = "alloc")]
6#[allow(
7    unused_imports,
8    reason = "alloc prelude items; subset used per cfg/feature combination"
9)]
10use alloc::string::ToString;
11
12/// CESR codes for supported cryptographic digest algorithms.
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
14#[allow(
15    non_camel_case_types,
16    reason = "digest algorithm names use underscores by convention"
17)]
18pub enum DigestCode {
19    /// BLAKE3 with 256-bit (32-byte) output.
20    Blake3_256,
21    /// `BLAKE2b` with 256-bit (32-byte) output.
22    Blake2b_256,
23    /// BLAKE2s with 256-bit (32-byte) output.
24    Blake2s_256,
25    /// SHA-3 with 256-bit (32-byte) output.
26    SHA3_256,
27    /// SHA-2 (SHA-256) with 256-bit (32-byte) output.
28    SHA2_256,
29    /// BLAKE3 with 512-bit (64-byte) output.
30    Blake3_512,
31    /// `BLAKE2b` with 512-bit (64-byte) output.
32    Blake2b_512,
33    /// SHA-3 with 512-bit (64-byte) output.
34    SHA3_512,
35    /// SHA-2 (SHA-512) with 512-bit (64-byte) output.
36    SHA2_512,
37}
38
39impl Sealed for DigestCode {}
40
41impl CesrCode for DigestCode {
42    fn to_matter_code(&self) -> MatterCode {
43        match self {
44            Self::Blake3_256 => MatterCode::Blake3_256,
45            Self::Blake2b_256 => MatterCode::Blake2b_256,
46            Self::Blake2s_256 => MatterCode::Blake2s_256,
47            Self::SHA3_256 => MatterCode::SHA3_256,
48            Self::SHA2_256 => MatterCode::SHA2_256,
49            Self::Blake3_512 => MatterCode::Blake3_512,
50            Self::Blake2b_512 => MatterCode::Blake2b_512,
51            Self::SHA3_512 => MatterCode::SHA3_512,
52            Self::SHA2_512 => MatterCode::SHA2_512,
53        }
54    }
55
56    fn as_str(&self) -> &'static str {
57        match self {
58            Self::Blake3_256 => "E",
59            Self::Blake2b_256 => "F",
60            Self::Blake2s_256 => "G",
61            Self::SHA3_256 => "H",
62            Self::SHA2_256 => "I",
63            Self::Blake3_512 => "0D",
64            Self::Blake2b_512 => "0E",
65            Self::SHA3_512 => "0F",
66            Self::SHA2_512 => "0G",
67        }
68    }
69}
70
71impl TryFrom<MatterCode> for DigestCode {
72    type Error = ValidationError;
73
74    fn try_from(code: MatterCode) -> Result<Self, Self::Error> {
75        match code {
76            MatterCode::Blake3_256 => Ok(Self::Blake3_256),
77            MatterCode::Blake2b_256 => Ok(Self::Blake2b_256),
78            MatterCode::Blake2s_256 => Ok(Self::Blake2s_256),
79            MatterCode::SHA3_256 => Ok(Self::SHA3_256),
80            MatterCode::SHA2_256 => Ok(Self::SHA2_256),
81            MatterCode::Blake3_512 => Ok(Self::Blake3_512),
82            MatterCode::Blake2b_512 => Ok(Self::Blake2b_512),
83            MatterCode::SHA3_512 => Ok(Self::SHA3_512),
84            MatterCode::SHA2_512 => Ok(Self::SHA2_512),
85            _ => Err(ValidationError::UnknownMatterCode(code.to_string())),
86        }
87    }
88}
89
90impl From<DigestCode> for MatterCode {
91    fn from(code: DigestCode) -> Self {
92        code.to_matter_code()
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use crate::core::matter::code::MatterCode;
100
101    #[test]
102    fn digest_code_to_matter_code_roundtrip() {
103        let codes = [
104            (DigestCode::Blake3_256, MatterCode::Blake3_256),
105            (DigestCode::Blake2b_256, MatterCode::Blake2b_256),
106            (DigestCode::Blake2s_256, MatterCode::Blake2s_256),
107            (DigestCode::SHA3_256, MatterCode::SHA3_256),
108            (DigestCode::SHA2_256, MatterCode::SHA2_256),
109            (DigestCode::Blake3_512, MatterCode::Blake3_512),
110            (DigestCode::Blake2b_512, MatterCode::Blake2b_512),
111            (DigestCode::SHA3_512, MatterCode::SHA3_512),
112            (DigestCode::SHA2_512, MatterCode::SHA2_512),
113        ];
114        for (dc, mc) in codes {
115            assert_eq!(dc.to_matter_code(), mc);
116            assert_eq!(DigestCode::try_from(mc).unwrap(), dc);
117            assert_eq!(MatterCode::from(dc), mc);
118        }
119    }
120
121    #[test]
122    fn digest_code_rejects_non_digest() {
123        assert!(DigestCode::try_from(MatterCode::Ed25519).is_err());
124        assert!(DigestCode::try_from(MatterCode::Ed25519Seed).is_err());
125        assert!(DigestCode::try_from(MatterCode::Short).is_err());
126    }
127
128    #[test]
129    fn digest_code_as_str() {
130        assert_eq!(DigestCode::Blake3_256.as_str(), "E");
131        assert_eq!(DigestCode::SHA2_256.as_str(), "I");
132        assert_eq!(DigestCode::Blake3_512.as_str(), "0D");
133        assert_eq!(DigestCode::SHA2_512.as_str(), "0G");
134    }
135
136    #[test]
137    fn placeholder_blake3_256_is_44_dummy_chars() {
138        let ph = DigestCode::Blake3_256
139            .placeholder()
140            .expect("digest codes are fixed-size");
141        assert_eq!(ph, "#".repeat(44));
142    }
143
144    #[test]
145    fn placeholder_all_digest_codes_fixed_and_hash_filled() {
146        use crate::core::matter::sizage::SizeType;
147        for code in [
148            DigestCode::Blake3_256,
149            DigestCode::Blake2b_256,
150            DigestCode::Blake2s_256,
151            DigestCode::SHA3_256,
152            DigestCode::SHA2_256,
153            DigestCode::Blake3_512,
154            DigestCode::Blake2b_512,
155            DigestCode::SHA3_512,
156            DigestCode::SHA2_512,
157        ] {
158            let ph = code.placeholder().expect("digest codes are fixed-size");
159            let SizeType::Fixed(n) = code.get_sizage().fs else {
160                unreachable!("digest codes are fixed-size")
161            };
162            assert_eq!(ph.len(), usize::from(n));
163            assert!(!ph.is_empty() && ph.bytes().all(|b| b == b'#'));
164        }
165    }
166}