1 // Copyright 2023 Google LLC 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 /// Trait for SHA2 256-bit hash implementation. 16 pub trait Sha256 { 17 /// Computes the SHA2 256-bit hash. sha256(input: &[u8]) -> [u8; 32]18 fn sha256(input: &[u8]) -> [u8; 32]; 19 } 20 21 /// Trait for SHA2 512-bit hash implementation. 22 pub trait Sha512 { 23 /// Computes the SHA2 512-bit hash. sha512(input: &[u8]) -> [u8; 64]24 fn sha512(input: &[u8]) -> [u8; 64]; 25 } 26 27 /// Utilities for testing. Implementations can use the test cases and functions provided to test 28 /// their implementation. 29 #[cfg(feature = "testing")] 30 pub mod testing { 31 32 extern crate alloc; 33 extern crate std; 34 use super::{Sha256, Sha512}; 35 pub use crate::testing::prelude::*; 36 use crate::CryptoProvider; 37 use alloc::vec::Vec; 38 use core::{marker::PhantomData, str::FromStr}; 39 use hex::FromHex; 40 pub use hex_literal::hex; 41 use rstest_reuse::template; 42 43 /// Test vectors from SHA256ShortMsg.rsp in 44 /// <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs> sha256_cavp_short_vector_test<C: CryptoProvider>(_marker: PhantomData<C>)45 pub fn sha256_cavp_short_vector_test<C: CryptoProvider>(_marker: PhantomData<C>) { 46 sha256_cavp_vector_test::<C>(include_str!("testdata/SHA256ShortMsg.rsp")); 47 } 48 49 /// Test vectors from SHA256LongMsg.rsp in 50 /// <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs> sha256_cavp_long_vector_test<C: CryptoProvider>(_marker: PhantomData<C>)51 pub fn sha256_cavp_long_vector_test<C: CryptoProvider>(_marker: PhantomData<C>) { 52 sha256_cavp_vector_test::<C>(include_str!("testdata/SHA256LongMsg.rsp")); 53 } 54 55 /// Test vectors from SHA512ShortMsg.rsp in 56 /// <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs> sha512_cavp_short_vector_test<C: CryptoProvider>(_marker: PhantomData<C>)57 pub fn sha512_cavp_short_vector_test<C: CryptoProvider>(_marker: PhantomData<C>) { 58 sha512_cavp_vector_test::<C>(include_str!("testdata/SHA512ShortMsg.rsp")); 59 } 60 61 /// Test vectors from SHA512LongMsg.rsp in 62 /// <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs> sha512_cavp_long_vector_test<C: CryptoProvider>(_marker: PhantomData<C>)63 pub fn sha512_cavp_long_vector_test<C: CryptoProvider>(_marker: PhantomData<C>) { 64 sha512_cavp_vector_test::<C>(include_str!("testdata/SHA512LongMsg.rsp")); 65 } 66 67 /// Test vectors an rsp file in 68 /// <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs> sha256_cavp_vector_test<C: CryptoProvider>(cavp_file_contents: &str)69 fn sha256_cavp_vector_test<C: CryptoProvider>(cavp_file_contents: &str) { 70 sha_cavp_vector_test(<C::Sha256 as Sha256>::sha256, cavp_file_contents) 71 } 72 73 /// Test vectors an rsp file in 74 /// <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#shavs> sha512_cavp_vector_test<C: CryptoProvider>(cavp_file_contents: &str)75 fn sha512_cavp_vector_test<C: CryptoProvider>(cavp_file_contents: &str) { 76 sha_cavp_vector_test(<C::Sha512 as Sha512>::sha512, cavp_file_contents) 77 } 78 sha_cavp_vector_test<const N: usize>( hash_func: impl Fn(&[u8]) -> [u8; N], cavp_file_contents: &str, )79 fn sha_cavp_vector_test<const N: usize>( 80 hash_func: impl Fn(&[u8]) -> [u8; N], 81 cavp_file_contents: &str, 82 ) { 83 let test_cases = cavp_file_contents.split("\n\n").filter_map(|chunk| { 84 let mut len: Option<usize> = None; 85 let mut msg: Option<Vec<u8>> = None; 86 let mut md: Option<Vec<u8>> = None; 87 for line in chunk.split('\n') { 88 if line.starts_with('#') || line.is_empty() || line == std::format!("[L = {N}]") { 89 continue; 90 } else if let Some(len_str) = line.strip_prefix("Len = ") { 91 len = Some(FromStr::from_str(len_str).unwrap()); 92 } else if let Some(hex_str) = line.strip_prefix("Msg = ") { 93 msg = Some(Vec::<u8>::from_hex(hex_str).unwrap()); 94 } else if let Some(hex_str) = line.strip_prefix("MD = ") { 95 md = Some(Vec::<u8>::from_hex(hex_str).unwrap()); 96 } else { 97 panic!("Unexpected line in test file: `{}`", line); 98 } 99 } 100 if let (Some(len), Some(msg), Some(md)) = (len, msg, md) { 101 Some((len, msg, md)) 102 } else { 103 None 104 } 105 }); 106 for (len, mut msg, md) in test_cases { 107 if len == 0 { 108 // Truncate len = 0, since the test file has "Msg = 00" in there that should be 109 // ignored. 110 msg.truncate(0); 111 } 112 assert_eq!(msg.len(), len / 8); 113 let md_arr: [u8; N] = md.try_into().unwrap(); 114 assert_eq!(hash_func(&msg), md_arr); 115 } 116 } 117 118 /// Generates the test cases to validate the SHA2 implementation. 119 /// For example, to test `MyCryptoProvider`: 120 /// 121 /// ``` 122 /// use crypto_provider::sha2::testing::*; 123 /// 124 /// mod tests { 125 /// #[apply(sha2_test_cases)] 126 /// fn sha2_tests(testcase: CryptoProviderTestCase<MyCryptoProvider>) { 127 /// testcase(PhantomData::<MyCryptoProvider>); 128 /// } 129 /// } 130 /// ``` 131 #[template] 132 #[export] 133 #[rstest] 134 #[case::sha256_cavp_short_vector(sha256_cavp_short_vector_test)] 135 #[case::sha256_cavp_long_vector(sha256_cavp_long_vector_test)] sha2_test_cases<C: CryptoProvider>(#[case] testcase: CryptoProviderTestCase<C>)136 fn sha2_test_cases<C: CryptoProvider>(#[case] testcase: CryptoProviderTestCase<C>) {} 137 } 138