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