// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT) use alloc::vec::Vec; use mls_rs_core::crypto::CipherSuite; use nss_gk_api::hash; use nss_gk_api::hmac; #[derive(Debug)] #[cfg_attr(feature = "std", derive(thiserror::Error))] pub enum HashError { #[cfg_attr(feature = "std", error("invalid hmac length"))] InvalidHmacLength, #[cfg_attr(feature = "std", error("unsupported cipher suite"))] UnsupportedCipherSuite, #[cfg_attr(feature = "std", error("internal error"))] InternalError, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u16)] pub enum Hash { Sha256, Sha384, Sha512, } impl Hash { pub fn new(cipher_suite: CipherSuite) -> Result { match cipher_suite { CipherSuite::CURVE25519_AES128 | CipherSuite::P256_AES128 | CipherSuite::CURVE25519_CHACHA => Ok(Hash::Sha256), CipherSuite::P384_AES256 => Ok(Hash::Sha384), CipherSuite::CURVE448_AES256 | CipherSuite::CURVE448_CHACHA | CipherSuite::P521_AES256 => Ok(Hash::Sha512), _ => Err(HashError::UnsupportedCipherSuite), } } pub fn hash(&self, data: &[u8]) -> Vec { match self { Hash::Sha256 => hash::hash(hash::HashAlgorithm::SHA2_256, data).expect("InternalError"), Hash::Sha384 => hash::hash(hash::HashAlgorithm::SHA2_384, data).expect("InternalError"), Hash::Sha512 => hash::hash(hash::HashAlgorithm::SHA2_512, data).expect("InternalError"), } } pub fn mac(&self, key: &[u8], data: &[u8]) -> Result, HashError> { match self { Hash::Sha256 => Ok(hmac::hmac(&hmac::HmacAlgorithm::HMAC_SHA2_256, key, data) .map_err(|_| HashError::InternalError)?), Hash::Sha384 => Ok(hmac::hmac(&hmac::HmacAlgorithm::HMAC_SHA2_384, key, data) .map_err(|_| HashError::InternalError)?), Hash::Sha512 => Ok(hmac::hmac(&hmac::HmacAlgorithm::HMAC_SHA2_512, key, data) .map_err(|_| HashError::InternalError)?), } } } mod test { use crate::Hash; use alloc::vec::Vec; use serde::Deserialize; #[derive(Deserialize)] struct TestCase { #[allow(dead_code)] pub ciphersuite: u16, pub hash_function: u8, #[serde(with = "hex::serde")] pub message: Vec, #[serde(with = "hex::serde")] pub hash: Vec, } #[allow(dead_code)] fn run_test_case(t: TestCase) { let hash_fun = match t.hash_function { 1 => Hash::Sha256, 2 => Hash::Sha384, _default => Hash::Sha256, }; let message = t.message; let hash_result = hash_fun.hash(message.as_slice()); assert_eq!(hash_result, t.hash); } #[test] fn test_algo_test_cases() { let test_case_file = include_str!("../test_data/test_hash.json"); let test_cases: Vec = serde_json::from_str(test_case_file).unwrap(); for case in test_cases { run_test_case(case); } } }