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}