1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Hash functions and hash-based message authentication codes.
16 
17 use bssl_crypto::digest;
18 use bssl_crypto::hmac::{HmacSha256, HmacSha512};
19 use mls_rs_core::crypto::CipherSuite;
20 use thiserror::Error;
21 
22 /// Errors returned from hash functions and HMACs.
23 #[derive(Debug, Error)]
24 pub enum HashError {
25     /// Error returned when unsupported cipher suite is requested.
26     #[error("unsupported cipher suite")]
27     UnsupportedCipherSuite,
28 }
29 
30 /// Hash function and HMAC implementations backed by BoringSSL.
31 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
32 #[repr(u16)]
33 pub enum Hash {
34     /// SHA-256.
35     Sha256,
36     /// SHA-384.
37     Sha384,
38     /// SHA-512.
39     Sha512,
40 }
41 
42 impl Hash {
43     /// Creates a new Hash.
new(cipher_suite: CipherSuite) -> Result<Self, HashError>44     pub fn new(cipher_suite: CipherSuite) -> Result<Self, HashError> {
45         match cipher_suite {
46             CipherSuite::CURVE25519_AES128
47             | CipherSuite::P256_AES128
48             | CipherSuite::CURVE25519_CHACHA => Ok(Hash::Sha256),
49             CipherSuite::P384_AES256 => Ok(Hash::Sha384),
50             CipherSuite::CURVE448_AES256
51             | CipherSuite::CURVE448_CHACHA
52             | CipherSuite::P521_AES256 => Ok(Hash::Sha512),
53             _ => Err(HashError::UnsupportedCipherSuite),
54         }
55     }
56 
57     /// Hashes `data`.
hash(&self, data: &[u8]) -> Vec<u8>58     pub fn hash(&self, data: &[u8]) -> Vec<u8> {
59         match self {
60             Hash::Sha256 => digest::Sha256::hash(data).to_vec(),
61             Hash::Sha384 => digest::Sha384::hash(data).to_vec(),
62             Hash::Sha512 => digest::Sha512::hash(data).to_vec(),
63         }
64     }
65 
66     /// Computes the HMAC of `data` using `key`.
mac(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, HashError>67     pub fn mac(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, HashError> {
68         match self {
69             Hash::Sha256 => Ok(HmacSha256::mac(key, data).to_vec()),
70             Hash::Sha384 => Err(HashError::UnsupportedCipherSuite),
71             Hash::Sha512 => Ok(HmacSha512::mac(key, data).to_vec()),
72         }
73     }
74 }
75 
76 #[cfg(all(not(mls_build_async), test))]
77 mod test {
78     use super::{Hash, HashError};
79     use crate::test_helpers::decode_hex;
80     use assert_matches::assert_matches;
81     use mls_rs_core::crypto::CipherSuite;
82 
83     // bssl_crypto::hmac test vectors.
84 
85     #[test]
sha256()86     fn sha256() {
87         let hash = Hash::new(CipherSuite::P256_AES128).unwrap();
88         assert_eq!(
89             hash.hash(&decode_hex::<4>("74ba2521")),
90             decode_hex::<32>("b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e")
91         );
92     }
93 
94     #[test]
sha384()95     fn sha384() {
96         let hash = Hash::new(CipherSuite::P384_AES256).unwrap();
97         assert_eq!(
98             hash.hash(b"abc"),
99             decode_hex::<48>("cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7")
100         );
101     }
102 
103     #[test]
sha512()104     fn sha512() {
105         let hash = Hash::new(CipherSuite::CURVE448_CHACHA).unwrap();
106         assert_eq!(
107             hash.hash(&decode_hex::<4>("23be86d5")),
108             decode_hex::<64>(concat!(
109                 "76d42c8eadea35a69990c63a762f330614a4699977f058adb988f406fb0be8f2",
110                 "ea3dce3a2bbd1d827b70b9b299ae6f9e5058ee97b50bd4922d6d37ddc761f8eb"
111             ))
112         );
113     }
114 
115     #[test]
hmac_sha256()116     fn hmac_sha256() {
117         let expected = vec![
118             0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
119             0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
120             0x2e, 0x32, 0xcf, 0xf7,
121         ];
122         let key: [u8; 20] = [0x0b; 20];
123         let data = b"Hi There";
124 
125         let hmac = Hash::new(CipherSuite::CURVE25519_AES128).unwrap();
126         assert_eq!(expected, hmac.mac(&key, data).unwrap());
127     }
128 
129     #[test]
hmac_sha384()130     fn hmac_sha384() {
131         let key: [u8; 20] = [0x0b; 20];
132         let data = b"Hi There";
133 
134         let hmac = Hash::new(CipherSuite::P384_AES256).unwrap();
135         assert_matches!(hmac.mac(&key, data), Err(HashError::UnsupportedCipherSuite));
136     }
137 
138     #[test]
hmac_sha512()139     fn hmac_sha512() {
140         let expected = vec![
141             135, 170, 124, 222, 165, 239, 97, 157, 79, 240, 180, 36, 26, 29, 108, 176, 35, 121,
142             244, 226, 206, 78, 194, 120, 122, 208, 179, 5, 69, 225, 124, 222, 218, 168, 51, 183,
143             214, 184, 167, 2, 3, 139, 39, 78, 174, 163, 244, 228, 190, 157, 145, 78, 235, 97, 241,
144             112, 46, 105, 108, 32, 58, 18, 104, 84,
145         ];
146         let key: [u8; 20] = [0x0b; 20];
147         let data = b"Hi There";
148 
149         let hmac = Hash::new(CipherSuite::CURVE448_CHACHA).unwrap();
150         assert_eq!(expected, hmac.mac(&key, data).unwrap());
151     }
152 }
153