solana_hmac_sha256/
lib.rs

1use solana_nostd_sha256::{hashv, HASH_LENGTH};
2
3/// # HmacSha256
4/// 
5/// Creates an HMAC with an updatable digest similar to how the standard `hmac` library works but using the raw Solana Sha256 syscall under the hood.
6#[derive(Clone, Debug)]
7pub struct HmacSha256 {
8    outer_key_pad: [u8; 64],
9    inner_key_pad: [u8; 64],
10    digest: Vec<u8>,
11}
12
13const BLOCK_SIZE: usize = 64;
14
15impl HmacSha256 {
16    pub fn new(key: &[u8]) -> Self {
17        let mut key_block = [0u8; BLOCK_SIZE];
18
19        if key.len() > BLOCK_SIZE {
20            let hashed_key = hashv(&[key]);
21            key_block[..HASH_LENGTH].copy_from_slice(&hashed_key);
22            for i in HASH_LENGTH..BLOCK_SIZE {
23                key_block[i] = 0;
24            }
25        } else {
26            key_block[..key.len()].copy_from_slice(key);
27        }
28
29        let mut inner_key_pad = [0u8; BLOCK_SIZE];
30        let mut outer_key_pad = [0u8; BLOCK_SIZE];
31
32        for i in 0..BLOCK_SIZE {
33            inner_key_pad[i] = key_block[i] ^ 0x36;
34            outer_key_pad[i] = key_block[i] ^ 0x5C;
35        }
36
37        let digest = inner_key_pad.to_vec();
38
39        Self {
40            digest,
41            outer_key_pad,
42            inner_key_pad,
43        }
44    }
45
46    #[inline(always)]
47    pub fn update(&mut self, data: &[u8]) {
48        self.digest.extend_from_slice(data);
49    }
50
51    #[inline(always)]
52    pub fn reset(&mut self) {
53        self.digest.clear();
54        self.digest.extend_from_slice(&self.inner_key_pad);
55    }
56
57    pub fn finalize(&self) -> [u8; 32] {
58        let inner_hash = hashv(&[&self.digest]);
59        hashv(&[&self.outer_key_pad, &inner_hash])
60    }
61
62    pub fn finalize_reset(&mut self) -> [u8; 32] {
63        let result = self.finalize();
64        self.reset();
65        result
66    }
67}
68
69/// #hmac_sha256
70/// 
71/// Quickly create a single hmac_sha256 hash
72pub fn hmac_sha256(key: &[u8], message: &[u8]) -> [u8; 32] {
73    let mut key_block = [0u8; BLOCK_SIZE];
74
75    if key.len() > BLOCK_SIZE {
76        let hashed_key = hashv(&[key]);
77        key_block[..HASH_LENGTH].copy_from_slice(&hashed_key);
78        for i in HASH_LENGTH..BLOCK_SIZE {
79            key_block[i] = 0;
80        }
81    } else {
82        key_block[..key.len()].copy_from_slice(key);
83    }
84
85    let mut inner_key_pad = [0u8; BLOCK_SIZE];
86    let mut outer_key_pad = [0u8; BLOCK_SIZE];
87
88    for i in 0..BLOCK_SIZE {
89        inner_key_pad[i] = key_block[i] ^ 0x36;
90        outer_key_pad[i] = key_block[i] ^ 0x5C;
91    }
92    let inner_hash = hashv(&[&inner_key_pad, message]);
93    hashv(&[&outer_key_pad, &inner_hash])
94}
95
96
97#[cfg(test)]
98mod tests {
99    use crate::{hmac_sha256, HmacSha256};
100
101    const HASH_TEST_TEST: [u8;32] = [0x88, 0xcd, 0x21, 0x08, 0xb5, 0x34, 0x7d, 0x97, 0x3c, 0xf3, 0x9c, 0xdf, 0x90, 0x53, 0xd7, 0xdd, 0x42, 0x70, 0x48, 0x76, 0xd8, 0xc9, 0xa9, 0xbd, 0x8e, 0x2d, 0x16, 0x82, 0x59, 0xd3, 0xdd, 0xf7];
102
103    #[test]
104    fn hmac_sha256_test() {
105        let h = hmac_sha256(b"test", b"test");
106        assert_eq!(h, HASH_TEST_TEST)
107    }
108
109    #[test]
110    fn hmac_sha256_digest_type(){
111        let mut h = HmacSha256::new(b"test");
112        h.update(b"test");
113        assert_eq!(h.finalize(), HASH_TEST_TEST)
114    }
115
116}