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
//!Minimal `no_std` hashing library
//!
//!## Provided Algorithms
//!
//!These are also names of features to be used to include algorithm code.
//!
//!- `sha1`
//!- `md5`
//!- `sha256`
//!- `sha512`
//!
//!It also includes generic hmac implementation

#![no_std]
#![warn(missing_docs)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]

mod fmt;
pub use fmt::DigestFmt;

///Hashing algorithm interface
pub trait Digest {
    ///Output type
    type OutputType: AsRef<[u8]> + AsMut<[u8]> + Copy;
    ///Block type
    type BlockType: AsRef<[u8]> + AsMut<[u8]> + Copy;

    ///Creates new instance.
    fn new() -> Self;
    ///Resets algorithm's state.
    fn reset(&mut self);
    ///Hashes input
    fn update(&mut self, input: &[u8]);
    ///Finalizes algorithm and returns output.
    fn result(&mut self) -> Self::OutputType;
}

///Represents key used to sign content in `hmac` algorithm.
///
///Comparing to `hmac` function it allows to pre-compute key and just sign input directly.
///
///`Digest` is only used in methods, making user to be responsible for using correct algorithm.
pub struct HmacKey<D: Digest> {
    key: D::BlockType,
}

impl<D: Digest> HmacKey<D> {
    ///Creates new hmac key, using provided secret.
    ///
    ///If `secret` size is above that of `Digest::BlockType` then it is hashed,
    ///reducing potential quality of hmac properties.
    pub fn new(secret: &[u8]) -> Self {
        let mut inner: D::BlockType = unsafe {
            core::mem::MaybeUninit::zeroed().assume_init()
        };
        let key = inner.as_mut();

        if secret.len() <= key.len() {
            key[..secret.len()].copy_from_slice(secret);
        } else {
        let mut algo = D::new();
            algo.update(secret);
            let hash = algo.result();
            let hash = hash.as_ref();
            key[..hash.len()].copy_from_slice(hash);
            algo.reset();
        }

        for byte in key.iter_mut() {
            *byte ^= 0x36;
        }

        Self {
            key: inner,
        }
    }

    ///Signs provided `input` with the key.
    pub fn sign(&self, input: &[u8]) -> D::OutputType {
        let mut key = self.key;
        let key = key.as_mut();

        //inner
        let mut algo = D::new();
        algo.update(key);
        algo.update(input);
        let inner_result = algo.result();
        algo.reset();

        //outer
        for byte in key.iter_mut() {
            *byte ^= 0x36 ^ 0x5C;
        }
        algo.update(key);
        algo.update(inner_result.as_ref());
        algo.result()
    }
}

///Creates HMAC using provided `Digest` algorithm.
///
///- `input` - Data to hash.
///- `secret` - Data to derive HMAC's key.
pub fn hmac<D: Digest>(input: &[u8], secret: &[u8]) -> D::OutputType {
    let key = HmacKey::<D>::new(secret);
    key.sign(input)
}

#[cfg(feature = "sha1")]
mod sha1;
#[cfg(feature = "sha1")]
pub use sha1::{sha1, Sha1};
#[cfg(feature = "md5")]
mod md5;
#[cfg(feature = "md5")]
pub use md5::{md5, Md5};

#[cfg(feature = "sha256")]
mod sha256;
#[cfg(feature = "sha256")]
pub use sha256::{sha256, Sha256};

#[cfg(feature = "sha512")]
mod sha512;
#[cfg(feature = "sha512")]
pub use sha512::{sha512, Sha512};