cypher/
digest.rs

1// Set of libraries for privacy-preserving networking apps
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2023 by
6//     Dr. Maxim Orlovsky <orlovsky@cyphernet.org>
7//
8// Copyright 2022-2023 Cyphernet DAO, Switzerland
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use 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}