Skip to main content

embedded_cal/
hash.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// SPDX-FileCopyrightText: Inria-AIO, Cryspen, and Christian Amsüss
3
4/// Hashing of byte streams.
5pub trait HashProvider {
6    type Algorithm: HashAlgorithm;
7    /// State in which is carried between rounds of feeding data.
8    ///
9    /// As construction is not fallible, this can not be a handle into a limited pool. (Cf.
10    /// architecture requirements: "Incomplete operations should not block the system").
11    ///
12    /// If hardware exists that can only hash efficiently in an internal state, this needs to be an
13    /// encapsulation of that state, as construction is not fallible. As this is likely a costly
14    /// process, such implementations are encouraged to implement [`Self::hash`] in an optimized
15    /// way. (Also, if such a hardware actually exists, please open an issue about it).
16    // FIXME: Link to stable FAQ position once that is more website/documentation shape and not
17    // just a GitHub Markdown document.
18    type State: Sized + Clone;
19    /// Output of a hashing operation.
20    ///
21    /// This needs to be sufficiently large to contain any selected hash's output. When collecting
22    /// multiple hash results of the same algorithm in limited space (i.e., in situations when it
23    /// makes sense to store 8 SHA-512 outputs or 16 SHA-256 outputs), it can make sense to copy
24    /// data out rather than storing the `HashResult` type to free the space. (See also project FAQ
25    /// on output sizes).
26    // FIXME: Link to stable FAQ position once that is more website/documentation shape and not
27    // just a GitHub Markdown document.
28    type Output: AsRef<[u8]>;
29
30    // Spitballing here to convey the idea and check whether ownership and lifetimes can work this
31    // way. FIXME: Pick terminology from existing crates.
32
33    fn init(&mut self, algorithm: Self::Algorithm) -> Self::State;
34    fn update(&mut self, instance: &mut Self::State, data: &[u8]);
35    // FIXME: (How) do we best carry around that the results's AsRef is exactly the .len() of the
36    // algorithm?
37    fn finalize(&mut self, instance: Self::State) -> Self::Output;
38
39    /// Hash contiguous in-memory data in a single pass.
40    ///
41    /// This method is provided, but implementations are encouraged to provide optimized versions
42    /// if an actual speed-up can be gained; conversely, users are encouraged to use this if data
43    /// is already present in this form.
44    ///
45    /// Optimized versions are expected to be rare, though, so don't go out of your way using it:
46    /// Only buffer the full data, or create special cases for when there actually is just one item
47    /// in an iterator, without testing and possibly consulting with the back-end authors first.
48    fn hash(&mut self, algorithm: Self::Algorithm, data: &[u8]) -> Self::Output {
49        let mut state = self.init(algorithm);
50        self.update(&mut state, data);
51        self.finalize(state)
52    }
53}
54
55/// A hash algorithm identifier.
56///
57/// While const traits are not stable yet, implementers should prepare for the constructors and
58/// other methods to be `const` functions.
59#[allow(
60    clippy::len_without_is_empty,
61    reason = "Lint only makes sense when length can reasonably be zero, which is not the case here."
62)]
63// FIXME: Are all of those requirements good?
64pub trait HashAlgorithm: Sized + PartialEq + Eq + core::fmt::Debug + Clone {
65    // No MAX_LEN *yet*, but we might add one as we implement HMAC more widely.
66
67    /// Output length of the hash algorithm.
68    fn len(&self) -> usize;
69
70    /// Selects a hash algorithm from its COSE number.
71    ///
72    /// The algorithm number comes from the ["COSE Algorithms"
73    /// registry](https://www.iana.org/assignments/cose/cose.xhtml#algorithms) maintained by IANA.
74    ///
75    /// This works from `Into<i128>` because the numeric range of CBOR integers is effectively that
76    /// of a i65 (the sign is in the data type); inlining will take care of systems not *actually*
77    /// materializing any i128 comparisons, let alone arithmetic.
78    #[inline]
79    #[allow(
80        unused_variables,
81        reason = "Argument names are part of the documentation"
82    )]
83    fn from_cose_number(number: impl Into<i128>) -> Option<Self> {
84        None
85    }
86
87    /// Selects a hash algorithm from a Suite ID out of the IANA Named Information Hash Algorith
88    /// Registry
89    ///
90    /// <https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg>
91    ///
92    /// Note that while the number is expressed as a [`u8`], the actual usable value space is
93    /// 0..=63 excluding the reserved 32; implementations must return None for values outside this
94    /// range.
95    #[inline]
96    #[allow(
97        unused_variables,
98        reason = "Argument names are part of the documentation"
99    )]
100    fn from_ni_id(number: u8) -> Option<Self> {
101        None
102    }
103
104    /// Selects a hash algorithm from a Hash Name String out of the IANA Named Information Hash
105    /// Algorith Registry
106    ///
107    /// <https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg>
108    #[inline]
109    #[allow(
110        unused_variables,
111        reason = "Argument names are part of the documentation"
112    )]
113    fn from_ni_name(name: &str) -> Option<Self> {
114        None
115    }
116}
117
118// FIXME: Should we introduce a feature to no build those all the time?
119pub fn test_hash_algorithm_sha256<HA: HashAlgorithm>() {
120    // FIXME see from_cose_number comment
121    let cose_neg10 = HA::from_cose_number(-16);
122    let ni_1 = HA::from_ni_id(1);
123    let ni_named = HA::from_ni_name("sha-256");
124
125    // Those are not *strictly* required, because there's no rule that any backend needs to
126    // recognize all identifiers, but those should be widespread enough.
127    assert_eq!(cose_neg10, ni_1);
128    assert_eq!(cose_neg10, ni_named);
129
130    // When we actually want to test for test vectors here, we'll need to take a &mut Hashing
131    // rather than just the algorithm.
132}