blob: 397fb9d48cc7e3b9326e3962fc90d3aa44503ae7 [file] [log] [blame]
Cindy Lin6ec3c2b2024-05-16 07:39:23 +00001// 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
17use bssl_crypto::digest;
18use bssl_crypto::hmac::{HmacSha256, HmacSha512};
19use mls_rs_core::crypto::CipherSuite;
20use thiserror::Error;
21
22/// Errors returned from hash functions and HMACs.
23#[derive(Debug, Error)]
24pub 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)]
33pub enum Hash {
34 /// SHA-256.
35 Sha256,
36 /// SHA-384.
37 Sha384,
38 /// SHA-512.
39 Sha512,
40}
41
42impl Hash {
43 /// Creates a new Hash.
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`.
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`.
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))]
77mod 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]
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]
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]
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]
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]
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]
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}