1use std::fmt::Debug;
23
24pub trait Digest: Clone + Sized {
25 const DIGEST_NAME: &'static str;
26 const OUTPUT_LEN: usize;
27 const BLOCK_LEN: usize;
28
29 type Output: Copy + Eq + Sized + Send + AsRef<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug;
30
31 fn new() -> Self;
32 fn with_output_slice(slice: &[u8]) -> Option<Self> {
33 if slice.len() != Self::OUTPUT_LEN {
34 return None;
35 }
36 let mut buf = [0u8; 32];
37 buf.copy_from_slice(slice);
38 todo!()
39 }
40
41 fn digest(data: impl AsRef<[u8]>) -> Self::Output { Self::digest_concat([data]) }
42 fn digest_concat(data: impl IntoIterator<Item = impl AsRef<[u8]>>) -> Self::Output {
43 let mut engine = Self::new();
44 for item in data {
45 engine.input(item);
46 }
47 engine.finalize()
48 }
49 fn input(&mut self, data: impl AsRef<[u8]>);
50 fn finalize(self) -> Self::Output;
51}
52
53pub trait Digest32: Digest<Output = [u8; 32]> {}
54pub trait Digest64: Digest<Output = [u8; 64]> {}
55
56pub trait KeyedDigest: Digest {
57 type Key;
58 fn with_key(key: Self::Key) -> Self;
59}
60
61pub trait HmacDigest<D: Digest>: Digest {
62 fn with_key(key: impl AsRef<[u8]>) -> Self;
63}
64
65#[cfg(feature = "sha2")]
66mod sha2 {
67 use ::sha2::digest::{FixedOutput, Update};
68
69 use super::*;
70
71 impl Digest for Sha256 {
72 const DIGEST_NAME: &'static str = "SHA256";
73 const OUTPUT_LEN: usize = 32;
74 const BLOCK_LEN: usize = 64;
75 type Output = [u8; 32];
76
77 fn new() -> Self { Sha256::default() }
78
79 fn input(&mut self, data: impl AsRef<[u8]>) { self.update(data.as_ref()); }
80
81 fn finalize(self) -> Self::Output {
82 let mut buf = [0u8; 32];
83 let out = &*self.finalize_fixed();
84 buf.copy_from_slice(out);
85 buf
86 }
87 }
88}
89
90#[cfg(feature = "sha2")]
91pub use ::sha2::Sha256;
92
93#[derive(Clone, Eq, PartialEq, Hash, Debug)]
94pub struct Hmac<D: Digest> {
95 iengine: D,
96 oengine: D,
97}
98
99impl<D: Digest> Hmac<D> {
100 pub fn keyed(key: impl AsRef<[u8]>) -> Self {
101 let mut ipad = [0x36u8; 128];
102 let mut opad = [0x5cu8; 128];
103 let mut iengine = D::new();
104 let mut oengine = D::new();
105
106 let key = key.as_ref();
107 if key.len() > D::BLOCK_LEN {
108 let hash = D::digest(key);
109 for (b_i, b_h) in ipad.iter_mut().zip(hash.as_ref()) {
110 *b_i ^= *b_h;
111 }
112 for (b_o, b_h) in opad.iter_mut().zip(hash.as_ref()) {
113 *b_o ^= *b_h;
114 }
115 } else {
116 for (b_i, b_h) in ipad.iter_mut().zip(key) {
117 *b_i ^= *b_h;
118 }
119 for (b_o, b_h) in opad.iter_mut().zip(key) {
120 *b_o ^= *b_h;
121 }
122 };
123
124 iengine.input(&ipad[..D::BLOCK_LEN]);
125 oengine.input(&opad[..D::BLOCK_LEN]);
126
127 Self { iengine, oengine }
128 }
129}
130
131impl<D: Digest> Digest for Hmac<D> {
132 const DIGEST_NAME: &'static str = "HMAC";
133 const OUTPUT_LEN: usize = D::OUTPUT_LEN;
134 const BLOCK_LEN: usize = D::BLOCK_LEN;
135 type Output = D::Output;
136
137 fn new() -> Self { Self::keyed([]) }
138
139 fn input(&mut self, data: impl AsRef<[u8]>) { self.iengine.input(data); }
140
141 fn finalize(mut self) -> Self::Output {
142 let ihash = self.iengine.finalize();
143 self.oengine.input(ihash.as_ref());
144 self.oengine.finalize()
145 }
146}
147
148impl<D: Digest> HmacDigest<D> for Hmac<D> {
149 fn with_key(key: impl AsRef<[u8]>) -> Self { Self::keyed(key) }
150}