1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project 2*e1997b9aSAndroid Build Coastguard Worker // 3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at 6*e1997b9aSAndroid Build Coastguard Worker // 7*e1997b9aSAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0 8*e1997b9aSAndroid Build Coastguard Worker // 9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License. 14*e1997b9aSAndroid Build Coastguard Worker 15*e1997b9aSAndroid Build Coastguard Worker //! Key derivation function. 16*e1997b9aSAndroid Build Coastguard Worker 17*e1997b9aSAndroid Build Coastguard Worker use bssl_crypto::digest; 18*e1997b9aSAndroid Build Coastguard Worker use bssl_crypto::hkdf::{HkdfSha256, HkdfSha512, Prk, Salt}; 19*e1997b9aSAndroid Build Coastguard Worker use mls_rs_core::crypto::CipherSuite; 20*e1997b9aSAndroid Build Coastguard Worker use mls_rs_core::error::IntoAnyError; 21*e1997b9aSAndroid Build Coastguard Worker use mls_rs_crypto_traits::{KdfId, KdfType}; 22*e1997b9aSAndroid Build Coastguard Worker use thiserror::Error; 23*e1997b9aSAndroid Build Coastguard Worker 24*e1997b9aSAndroid Build Coastguard Worker /// Errors returned from KDF. 25*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, Error)] 26*e1997b9aSAndroid Build Coastguard Worker pub enum KdfError { 27*e1997b9aSAndroid Build Coastguard Worker /// Error returned when the input key material (IKM) is too short. 28*e1997b9aSAndroid Build Coastguard Worker #[error("KDF IKM of length {len}, expected length at least {min_len}")] 29*e1997b9aSAndroid Build Coastguard Worker TooShortIkm { 30*e1997b9aSAndroid Build Coastguard Worker /// Invalid IKM length. 31*e1997b9aSAndroid Build Coastguard Worker len: usize, 32*e1997b9aSAndroid Build Coastguard Worker /// Minimum IKM length. 33*e1997b9aSAndroid Build Coastguard Worker min_len: usize, 34*e1997b9aSAndroid Build Coastguard Worker }, 35*e1997b9aSAndroid Build Coastguard Worker /// Error returned when the pseudorandom key (PRK) is too short. 36*e1997b9aSAndroid Build Coastguard Worker #[error("KDF PRK of length {len}, expected length at least {min_len}")] 37*e1997b9aSAndroid Build Coastguard Worker TooShortPrk { 38*e1997b9aSAndroid Build Coastguard Worker /// Invalid PRK length. 39*e1997b9aSAndroid Build Coastguard Worker len: usize, 40*e1997b9aSAndroid Build Coastguard Worker /// Minimum PRK length. 41*e1997b9aSAndroid Build Coastguard Worker min_len: usize, 42*e1997b9aSAndroid Build Coastguard Worker }, 43*e1997b9aSAndroid Build Coastguard Worker /// Error returned when the output key material (OKM) requested it too long. 44*e1997b9aSAndroid Build Coastguard Worker #[error("KDF OKM of length {len} requested, expected length at most {max_len}")] 45*e1997b9aSAndroid Build Coastguard Worker TooLongOkm { 46*e1997b9aSAndroid Build Coastguard Worker /// Invalid OKM length. 47*e1997b9aSAndroid Build Coastguard Worker len: usize, 48*e1997b9aSAndroid Build Coastguard Worker /// Maximum OKM length. 49*e1997b9aSAndroid Build Coastguard Worker max_len: usize, 50*e1997b9aSAndroid Build Coastguard Worker }, 51*e1997b9aSAndroid Build Coastguard Worker /// Error returned when unsupported cipher suite is requested. 52*e1997b9aSAndroid Build Coastguard Worker #[error("unsupported cipher suite")] 53*e1997b9aSAndroid Build Coastguard Worker UnsupportedCipherSuite, 54*e1997b9aSAndroid Build Coastguard Worker } 55*e1997b9aSAndroid Build Coastguard Worker 56*e1997b9aSAndroid Build Coastguard Worker impl IntoAnyError for KdfError { into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self>57*e1997b9aSAndroid Build Coastguard Worker fn into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self> { 58*e1997b9aSAndroid Build Coastguard Worker Ok(self.into()) 59*e1997b9aSAndroid Build Coastguard Worker } 60*e1997b9aSAndroid Build Coastguard Worker } 61*e1997b9aSAndroid Build Coastguard Worker 62*e1997b9aSAndroid Build Coastguard Worker /// KdfType implementation backed by BoringSSL. 63*e1997b9aSAndroid Build Coastguard Worker #[derive(Clone)] 64*e1997b9aSAndroid Build Coastguard Worker pub struct Kdf(KdfId); 65*e1997b9aSAndroid Build Coastguard Worker 66*e1997b9aSAndroid Build Coastguard Worker impl Kdf { 67*e1997b9aSAndroid Build Coastguard Worker /// Creates a new Kdf. new(cipher_suite: CipherSuite) -> Option<Self>68*e1997b9aSAndroid Build Coastguard Worker pub fn new(cipher_suite: CipherSuite) -> Option<Self> { 69*e1997b9aSAndroid Build Coastguard Worker KdfId::new(cipher_suite).map(Self) 70*e1997b9aSAndroid Build Coastguard Worker } 71*e1997b9aSAndroid Build Coastguard Worker } 72*e1997b9aSAndroid Build Coastguard Worker 73*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] 74*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))] 75*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(not(target_arch = "wasm32"), mls_build_async), maybe_async::must_be_async)] 76*e1997b9aSAndroid Build Coastguard Worker impl KdfType for Kdf { 77*e1997b9aSAndroid Build Coastguard Worker type Error = KdfError; 78*e1997b9aSAndroid Build Coastguard Worker extract(&self, salt: &[u8], ikm: &[u8]) -> Result<Vec<u8>, KdfError>79*e1997b9aSAndroid Build Coastguard Worker async fn extract(&self, salt: &[u8], ikm: &[u8]) -> Result<Vec<u8>, KdfError> { 80*e1997b9aSAndroid Build Coastguard Worker if ikm.is_empty() { 81*e1997b9aSAndroid Build Coastguard Worker return Err(KdfError::TooShortIkm { len: 0, min_len: 1 }); 82*e1997b9aSAndroid Build Coastguard Worker } 83*e1997b9aSAndroid Build Coastguard Worker 84*e1997b9aSAndroid Build Coastguard Worker let salt = if salt.is_empty() { Salt::None } else { Salt::NonEmpty(salt) }; 85*e1997b9aSAndroid Build Coastguard Worker 86*e1997b9aSAndroid Build Coastguard Worker match self.0 { 87*e1997b9aSAndroid Build Coastguard Worker KdfId::HkdfSha256 => { 88*e1997b9aSAndroid Build Coastguard Worker Ok(HkdfSha256::extract(ikm, salt).as_bytes()[..self.extract_size()].to_vec()) 89*e1997b9aSAndroid Build Coastguard Worker } 90*e1997b9aSAndroid Build Coastguard Worker KdfId::HkdfSha512 => { 91*e1997b9aSAndroid Build Coastguard Worker Ok(HkdfSha512::extract(ikm, salt).as_bytes()[..self.extract_size()].to_vec()) 92*e1997b9aSAndroid Build Coastguard Worker } 93*e1997b9aSAndroid Build Coastguard Worker _ => Err(KdfError::UnsupportedCipherSuite), 94*e1997b9aSAndroid Build Coastguard Worker } 95*e1997b9aSAndroid Build Coastguard Worker } 96*e1997b9aSAndroid Build Coastguard Worker expand(&self, prk: &[u8], info: &[u8], len: usize) -> Result<Vec<u8>, KdfError>97*e1997b9aSAndroid Build Coastguard Worker async fn expand(&self, prk: &[u8], info: &[u8], len: usize) -> Result<Vec<u8>, KdfError> { 98*e1997b9aSAndroid Build Coastguard Worker if prk.len() < self.extract_size() { 99*e1997b9aSAndroid Build Coastguard Worker return Err(KdfError::TooShortPrk { len: prk.len(), min_len: self.extract_size() }); 100*e1997b9aSAndroid Build Coastguard Worker } 101*e1997b9aSAndroid Build Coastguard Worker 102*e1997b9aSAndroid Build Coastguard Worker match self.0 { 103*e1997b9aSAndroid Build Coastguard Worker KdfId::HkdfSha256 => match Prk::new::<digest::Sha256>(prk) { 104*e1997b9aSAndroid Build Coastguard Worker Some(hkdf) => { 105*e1997b9aSAndroid Build Coastguard Worker let mut out = vec![0; len]; 106*e1997b9aSAndroid Build Coastguard Worker match hkdf.expand_into(info, &mut out) { 107*e1997b9aSAndroid Build Coastguard Worker Ok(_) => Ok(out), 108*e1997b9aSAndroid Build Coastguard Worker Err(_) => { 109*e1997b9aSAndroid Build Coastguard Worker Err(KdfError::TooLongOkm { len, max_len: HkdfSha256::MAX_OUTPUT_LEN }) 110*e1997b9aSAndroid Build Coastguard Worker } 111*e1997b9aSAndroid Build Coastguard Worker } 112*e1997b9aSAndroid Build Coastguard Worker } 113*e1997b9aSAndroid Build Coastguard Worker None => Err(KdfError::TooShortPrk { len: prk.len(), min_len: self.extract_size() }), 114*e1997b9aSAndroid Build Coastguard Worker }, 115*e1997b9aSAndroid Build Coastguard Worker KdfId::HkdfSha512 => match Prk::new::<digest::Sha512>(prk) { 116*e1997b9aSAndroid Build Coastguard Worker Some(hkdf) => { 117*e1997b9aSAndroid Build Coastguard Worker let mut out = vec![0; len]; 118*e1997b9aSAndroid Build Coastguard Worker match hkdf.expand_into(info, &mut out) { 119*e1997b9aSAndroid Build Coastguard Worker Ok(_) => Ok(out), 120*e1997b9aSAndroid Build Coastguard Worker Err(_) => { 121*e1997b9aSAndroid Build Coastguard Worker Err(KdfError::TooLongOkm { len, max_len: HkdfSha512::MAX_OUTPUT_LEN }) 122*e1997b9aSAndroid Build Coastguard Worker } 123*e1997b9aSAndroid Build Coastguard Worker } 124*e1997b9aSAndroid Build Coastguard Worker } 125*e1997b9aSAndroid Build Coastguard Worker None => Err(KdfError::TooShortPrk { len: prk.len(), min_len: self.extract_size() }), 126*e1997b9aSAndroid Build Coastguard Worker }, 127*e1997b9aSAndroid Build Coastguard Worker _ => Err(KdfError::UnsupportedCipherSuite), 128*e1997b9aSAndroid Build Coastguard Worker } 129*e1997b9aSAndroid Build Coastguard Worker } 130*e1997b9aSAndroid Build Coastguard Worker extract_size(&self) -> usize131*e1997b9aSAndroid Build Coastguard Worker fn extract_size(&self) -> usize { 132*e1997b9aSAndroid Build Coastguard Worker self.0.extract_size() 133*e1997b9aSAndroid Build Coastguard Worker } 134*e1997b9aSAndroid Build Coastguard Worker kdf_id(&self) -> u16135*e1997b9aSAndroid Build Coastguard Worker fn kdf_id(&self) -> u16 { 136*e1997b9aSAndroid Build Coastguard Worker self.0 as u16 137*e1997b9aSAndroid Build Coastguard Worker } 138*e1997b9aSAndroid Build Coastguard Worker } 139*e1997b9aSAndroid Build Coastguard Worker 140*e1997b9aSAndroid Build Coastguard Worker #[cfg(all(not(mls_build_async), test))] 141*e1997b9aSAndroid Build Coastguard Worker mod test { 142*e1997b9aSAndroid Build Coastguard Worker use super::{Kdf, KdfError, KdfType}; 143*e1997b9aSAndroid Build Coastguard Worker use crate::test_helpers::decode_hex; 144*e1997b9aSAndroid Build Coastguard Worker use assert_matches::assert_matches; 145*e1997b9aSAndroid Build Coastguard Worker use bssl_crypto::hkdf::{HkdfSha256, HkdfSha512}; 146*e1997b9aSAndroid Build Coastguard Worker use mls_rs_core::crypto::CipherSuite; 147*e1997b9aSAndroid Build Coastguard Worker 148*e1997b9aSAndroid Build Coastguard Worker #[test] sha256()149*e1997b9aSAndroid Build Coastguard Worker fn sha256() { 150*e1997b9aSAndroid Build Coastguard Worker // https://www.rfc-editor.org/rfc/rfc5869.html#appendix-A.1 151*e1997b9aSAndroid Build Coastguard Worker let salt: [u8; 13] = decode_hex("000102030405060708090a0b0c"); 152*e1997b9aSAndroid Build Coastguard Worker let ikm: [u8; 22] = decode_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); 153*e1997b9aSAndroid Build Coastguard Worker let info: [u8; 10] = decode_hex("f0f1f2f3f4f5f6f7f8f9"); 154*e1997b9aSAndroid Build Coastguard Worker let expected_prk: [u8; 32] = 155*e1997b9aSAndroid Build Coastguard Worker decode_hex("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"); 156*e1997b9aSAndroid Build Coastguard Worker let expected_okm: [u8; 42] = decode_hex( 157*e1997b9aSAndroid Build Coastguard Worker "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", 158*e1997b9aSAndroid Build Coastguard Worker ); 159*e1997b9aSAndroid Build Coastguard Worker 160*e1997b9aSAndroid Build Coastguard Worker let kdf = Kdf::new(CipherSuite::CURVE25519_AES128).unwrap(); 161*e1997b9aSAndroid Build Coastguard Worker let prk = kdf.extract(&salt, &ikm).unwrap(); 162*e1997b9aSAndroid Build Coastguard Worker assert_eq!(prk, expected_prk); 163*e1997b9aSAndroid Build Coastguard Worker assert_eq!(kdf.expand(&prk, &info, 42).unwrap(), expected_okm); 164*e1997b9aSAndroid Build Coastguard Worker } 165*e1997b9aSAndroid Build Coastguard Worker 166*e1997b9aSAndroid Build Coastguard Worker #[test] sha512()167*e1997b9aSAndroid Build Coastguard Worker fn sha512() { 168*e1997b9aSAndroid Build Coastguard Worker // https://github.com/C2SP/wycheproof/blob/cd27d6419bedd83cbd24611ec54b6d4bfdb0cdca/testvectors/hkdf_sha512_test.json#L141 169*e1997b9aSAndroid Build Coastguard Worker let salt: [u8; 16] = decode_hex("1d6f3b38a1e607b5e6bcd4af1800a9d3"); 170*e1997b9aSAndroid Build Coastguard Worker let ikm: [u8; 16] = decode_hex("5d3db20e8238a90b62a600fa57fdb318"); 171*e1997b9aSAndroid Build Coastguard Worker let info: [u8; 20] = decode_hex("2bc5f39032b6fc87da69ba8711ce735b169646fd"); 172*e1997b9aSAndroid Build Coastguard Worker let expected_okm: [u8; 42] = decode_hex( 173*e1997b9aSAndroid Build Coastguard Worker "8c3cf7122dcb5eb7efaf02718f1faf70bca20dcb75070e9d0871a413a6c05fc195a75aa9ffc349d70aae", 174*e1997b9aSAndroid Build Coastguard Worker ); 175*e1997b9aSAndroid Build Coastguard Worker 176*e1997b9aSAndroid Build Coastguard Worker let kdf = Kdf::new(CipherSuite::CURVE448_CHACHA).unwrap(); 177*e1997b9aSAndroid Build Coastguard Worker let prk = kdf.extract(&salt, &ikm).unwrap(); 178*e1997b9aSAndroid Build Coastguard Worker assert_eq!(kdf.expand(&prk, &info, 42).unwrap(), expected_okm); 179*e1997b9aSAndroid Build Coastguard Worker } 180*e1997b9aSAndroid Build Coastguard Worker 181*e1997b9aSAndroid Build Coastguard Worker #[test] sha256_extract_short_ikm()182*e1997b9aSAndroid Build Coastguard Worker fn sha256_extract_short_ikm() { 183*e1997b9aSAndroid Build Coastguard Worker let kdf = Kdf::new(CipherSuite::CURVE25519_AES128).unwrap(); 184*e1997b9aSAndroid Build Coastguard Worker assert_matches!(kdf.extract(b"salty", b""), Err(KdfError::TooShortIkm { .. })); 185*e1997b9aSAndroid Build Coastguard Worker } 186*e1997b9aSAndroid Build Coastguard Worker 187*e1997b9aSAndroid Build Coastguard Worker #[test] sha256_expand_short_prk()188*e1997b9aSAndroid Build Coastguard Worker fn sha256_expand_short_prk() { 189*e1997b9aSAndroid Build Coastguard Worker let prk_short: [u8; 16] = decode_hex("077709362c2e32df0ddc3f0dc47bba63"); 190*e1997b9aSAndroid Build Coastguard Worker let info: [u8; 10] = decode_hex("f0f1f2f3f4f5f6f7f8f9"); 191*e1997b9aSAndroid Build Coastguard Worker 192*e1997b9aSAndroid Build Coastguard Worker let kdf = Kdf::new(CipherSuite::CURVE25519_AES128).unwrap(); 193*e1997b9aSAndroid Build Coastguard Worker assert_matches!(kdf.expand(&prk_short, &info, 42), Err(KdfError::TooShortPrk { .. })); 194*e1997b9aSAndroid Build Coastguard Worker } 195*e1997b9aSAndroid Build Coastguard Worker 196*e1997b9aSAndroid Build Coastguard Worker #[test] sha256_expand_long_okm()197*e1997b9aSAndroid Build Coastguard Worker fn sha256_expand_long_okm() { 198*e1997b9aSAndroid Build Coastguard Worker // https://www.rfc-editor.org/rfc/rfc5869.html#appendix-A.1 199*e1997b9aSAndroid Build Coastguard Worker let prk: [u8; 32] = 200*e1997b9aSAndroid Build Coastguard Worker decode_hex("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"); 201*e1997b9aSAndroid Build Coastguard Worker let info: [u8; 10] = decode_hex("f0f1f2f3f4f5f6f7f8f9"); 202*e1997b9aSAndroid Build Coastguard Worker 203*e1997b9aSAndroid Build Coastguard Worker let kdf = Kdf::new(CipherSuite::CURVE25519_AES128).unwrap(); 204*e1997b9aSAndroid Build Coastguard Worker assert_matches!( 205*e1997b9aSAndroid Build Coastguard Worker kdf.expand(&prk, &info, HkdfSha256::MAX_OUTPUT_LEN + 1), 206*e1997b9aSAndroid Build Coastguard Worker Err(KdfError::TooLongOkm { .. }) 207*e1997b9aSAndroid Build Coastguard Worker ); 208*e1997b9aSAndroid Build Coastguard Worker } 209*e1997b9aSAndroid Build Coastguard Worker 210*e1997b9aSAndroid Build Coastguard Worker #[test] sha512_extract_short_ikm()211*e1997b9aSAndroid Build Coastguard Worker fn sha512_extract_short_ikm() { 212*e1997b9aSAndroid Build Coastguard Worker let kdf = Kdf::new(CipherSuite::CURVE448_CHACHA).unwrap(); 213*e1997b9aSAndroid Build Coastguard Worker assert_matches!(kdf.extract(b"salty", b""), Err(KdfError::TooShortIkm { .. })); 214*e1997b9aSAndroid Build Coastguard Worker } 215*e1997b9aSAndroid Build Coastguard Worker 216*e1997b9aSAndroid Build Coastguard Worker #[test] sha512_expand_short_prk()217*e1997b9aSAndroid Build Coastguard Worker fn sha512_expand_short_prk() { 218*e1997b9aSAndroid Build Coastguard Worker let prk_short: [u8; 16] = decode_hex("077709362c2e32df0ddc3f0dc47bba63"); 219*e1997b9aSAndroid Build Coastguard Worker let info: [u8; 10] = decode_hex("f0f1f2f3f4f5f6f7f8f9"); 220*e1997b9aSAndroid Build Coastguard Worker 221*e1997b9aSAndroid Build Coastguard Worker let kdf = Kdf::new(CipherSuite::CURVE448_CHACHA).unwrap(); 222*e1997b9aSAndroid Build Coastguard Worker assert_matches!(kdf.expand(&prk_short, &info, 42), Err(KdfError::TooShortPrk { .. })); 223*e1997b9aSAndroid Build Coastguard Worker } 224*e1997b9aSAndroid Build Coastguard Worker 225*e1997b9aSAndroid Build Coastguard Worker #[test] sha512_expand_long_okm()226*e1997b9aSAndroid Build Coastguard Worker fn sha512_expand_long_okm() { 227*e1997b9aSAndroid Build Coastguard Worker // https://github.com/C2SP/wycheproof/blob/cd27d6419bedd83cbd24611ec54b6d4bfdb0cdca/testvectors/hkdf_sha512_test.json#L141 228*e1997b9aSAndroid Build Coastguard Worker let salt: [u8; 16] = decode_hex("1d6f3b38a1e607b5e6bcd4af1800a9d3"); 229*e1997b9aSAndroid Build Coastguard Worker let ikm: [u8; 16] = decode_hex("5d3db20e8238a90b62a600fa57fdb318"); 230*e1997b9aSAndroid Build Coastguard Worker let info: [u8; 20] = decode_hex("2bc5f39032b6fc87da69ba8711ce735b169646fd"); 231*e1997b9aSAndroid Build Coastguard Worker 232*e1997b9aSAndroid Build Coastguard Worker let kdf_sha512 = Kdf::new(CipherSuite::CURVE448_CHACHA).unwrap(); 233*e1997b9aSAndroid Build Coastguard Worker let prk = kdf_sha512.extract(&salt, &ikm).unwrap(); 234*e1997b9aSAndroid Build Coastguard Worker assert_matches!( 235*e1997b9aSAndroid Build Coastguard Worker kdf_sha512.expand(&prk, &info, HkdfSha512::MAX_OUTPUT_LEN + 1), 236*e1997b9aSAndroid Build Coastguard Worker Err(KdfError::TooLongOkm { .. }) 237*e1997b9aSAndroid Build Coastguard Worker ); 238*e1997b9aSAndroid Build Coastguard Worker } 239*e1997b9aSAndroid Build Coastguard Worker 240*e1997b9aSAndroid Build Coastguard Worker #[test] unsupported_cipher_suites()241*e1997b9aSAndroid Build Coastguard Worker fn unsupported_cipher_suites() { 242*e1997b9aSAndroid Build Coastguard Worker let ikm: [u8; 22] = decode_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); 243*e1997b9aSAndroid Build Coastguard Worker let salt: [u8; 13] = decode_hex("000102030405060708090a0b0c"); 244*e1997b9aSAndroid Build Coastguard Worker 245*e1997b9aSAndroid Build Coastguard Worker assert_matches!( 246*e1997b9aSAndroid Build Coastguard Worker Kdf::new(CipherSuite::P384_AES256).unwrap().extract(&salt, &ikm), 247*e1997b9aSAndroid Build Coastguard Worker Err(KdfError::UnsupportedCipherSuite) 248*e1997b9aSAndroid Build Coastguard Worker ); 249*e1997b9aSAndroid Build Coastguard Worker } 250*e1997b9aSAndroid Build Coastguard Worker } 251