hash_based_signatures/
utils.rs

1use crate::signature::HashType;
2use anyhow::{bail, Result};
3use data_encoding::HEXLOWER;
4use ring::digest::{digest, SHA256};
5use ring::hmac::Key;
6use std::cmp::min;
7
8/// Convert a `&[u8]` to a [u8; 32]
9///
10/// # Panics
11/// Panics if the input does not have length 32.
12pub fn slice_to_hash(input_slice: &[u8]) -> HashType {
13    assert_eq!(input_slice.len(), 32);
14    let mut result = [0u8; 32];
15    result.copy_from_slice(input_slice);
16    result
17}
18
19/// Hashes arbitrary bytes using SHA256
20pub fn hash(data: &[u8]) -> HashType {
21    slice_to_hash(digest(&SHA256, data).as_ref())
22}
23
24/// Computes the HMAC-SHA256 using the given key and data
25pub fn hmac(key: &HashType, data: &[u8]) -> HashType {
26    let hmac_key = Key::new(ring::hmac::HMAC_SHA256, key);
27    slice_to_hash(ring::hmac::sign(&hmac_key, data).as_ref())
28}
29
30pub fn string_to_hash_maybe(hash_string: &str) -> Result<HashType> {
31    let decoded = HEXLOWER.decode(hash_string.as_bytes())?;
32    if decoded.len() != 32 {
33        bail!("Invalid number of bytes!");
34    }
35    Ok(slice_to_hash(&decoded))
36}
37
38pub fn string_to_hash(hash_string: &str) -> HashType {
39    string_to_hash_maybe(hash_string).expect("Could not decode")
40}
41
42/// Gets the `bits` least significant bits of `index`,
43/// sorted from most significant to least significant.
44pub fn get_least_significant_bits(index: usize, bits: usize) -> Vec<bool> {
45    let mut result = Vec::new();
46    for i in (0..bits).rev() {
47        result.push((index & (1 << i)) != 0)
48    }
49    result
50}
51
52// Converts a vector of bits to an unsigned integer, corresponding to
53// the big-endian interpretation of the bit string.
54//
55// # Panics
56// Panics if the number of bits is bigger than 8.
57pub fn bits_to_unsigned_int(bits: &[bool]) -> u8 {
58    assert!(bits.len() <= 8);
59    let mut result = 0;
60    for i in 0..bits.len() {
61        if bits[i] {
62            result = result | (1 << (bits.len() - 1 - i));
63        }
64    }
65    result
66}
67
68// Converts a slice of bits to a vector of u8s
69pub fn bits_to_unsigned_ints(bits: &[bool]) -> Vec<u8> {
70    let size = (bits.len() as f32 / 8.0).ceil() as usize;
71    let mut result = Vec::with_capacity(size);
72    for i in 0..size {
73        let end_bit = min((i + 1) * 8, bits.len());
74        result.push(bits_to_unsigned_int(&bits[i * 8..end_bit]));
75    }
76    result
77}
78
79#[cfg(test)]
80mod tests {
81    use crate::signature::HashType;
82    use crate::utils::{
83        bits_to_unsigned_int, bits_to_unsigned_ints, get_least_significant_bits, string_to_hash,
84    };
85
86    #[test]
87    fn test_get_least_significant_bits() {
88        assert_eq!(
89            get_least_significant_bits(10, 5),
90            vec![false, true, false, true, false]
91        )
92    }
93
94    #[test]
95    fn test_bits_to_unsigned_int() {
96        assert_eq!(bits_to_unsigned_int(&[false, true, false, true, false]), 10)
97    }
98
99    #[test]
100    fn test_bits_to_unsigned_ints() {
101        assert_eq!(
102            bits_to_unsigned_ints(&[
103                false, false, false, false, true, false, true, false, true, false
104            ]),
105            vec![10, 2]
106        )
107    }
108
109    fn get_test_hash() -> (HashType, String) {
110        let mut hash = [0u8; 32];
111        for i in 0..32 {
112            hash[i] = i as u8;
113        }
114        (
115            hash,
116            String::from("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"),
117        )
118    }
119
120    #[test]
121    fn test_string_to_hash() {
122        let (test_hash, test_hash_string) = get_test_hash();
123        assert_eq!(string_to_hash(&test_hash_string), test_hash);
124    }
125
126    #[test]
127    #[should_panic]
128    fn test_string_to_hash_invalid_wrong_length() {
129        string_to_hash(&String::from("I have the wrong length"));
130    }
131
132    #[test]
133    #[should_panic]
134    fn test_string_to_hash_invalid_no_hex() {
135        let hash_string = "Right length but no valid hash!                                 ";
136        string_to_hash(&String::from(hash_string));
137    }
138}