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 use crate::OpenSslHash; 16 use crypto_provider::hkdf::InvalidLength; 17 use std::marker::PhantomData; 18 19 /// openssl based hkdf implementation 20 pub struct Hkdf<H: OpenSslHash> { 21 _marker: PhantomData<H>, 22 salt: Option<Vec<u8>>, 23 ikm: Vec<u8>, 24 } 25 26 impl<H: OpenSslHash> crypto_provider::hkdf::Hkdf for Hkdf<H> { new(salt: Option<&[u8]>, ikm: &[u8]) -> Self27 fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self { 28 Self { 29 _marker: Default::default(), 30 salt: salt.map(Vec::from), 31 ikm: Vec::from(ikm), 32 } 33 } 34 expand_multi_info( &self, info_components: &[&[u8]], okm: &mut [u8], ) -> Result<(), InvalidLength>35 fn expand_multi_info( 36 &self, 37 info_components: &[&[u8]], 38 okm: &mut [u8], 39 ) -> Result<(), InvalidLength> { 40 // open ssl will panic in the case of a 0 length okm, but expected behavior is no-op 41 if okm.is_empty() { 42 return Ok(()); 43 } 44 let mut ctx = openssl::pkey_ctx::PkeyCtx::new_id(openssl::pkey::Id::HKDF) 45 .expect("hkdf context should not fail"); 46 let md = H::get_md(); 47 ctx.derive_init().expect("hkdf derive init should not fail"); 48 ctx.set_hkdf_md(md).expect("hkdf set md should not fail"); 49 self.salt 50 .as_ref() 51 .map(|salt| ctx.set_hkdf_salt(salt.as_slice())); 52 ctx.set_hkdf_key(self.ikm.as_slice()) 53 .expect("should be able to set key"); 54 ctx.add_hkdf_info(&info_components.concat()) 55 .expect("should be able to add info"); 56 ctx.derive(Some(okm)).map_err(|_| InvalidLength).map(|_| ()) 57 } 58 expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength>59 fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> { 60 self.expand_multi_info(&[info], okm) 61 } 62 } 63 64 #[cfg(test)] 65 mod tests { 66 use crate::Openssl; 67 use core::marker::PhantomData; 68 use crypto_provider::hkdf::testing::*; 69 70 #[apply(hkdf_test_cases)] hkdf_tests(testcase: CryptoProviderTestCase<Openssl>)71 fn hkdf_tests(testcase: CryptoProviderTestCase<Openssl>) { 72 testcase(PhantomData); 73 } 74 } 75