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