diff --git a/src/keypair.rs b/src/keypair.rs index a9d17c2..e9362f9 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -23,20 +23,20 @@ use crate::key_exchange::tripledh::DiffieHellman; feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "S: serde::Deserialize<'de>", - serialize = "S: serde::Serialize" + deserialize = "SK: serde::Deserialize<'de>", + serialize = "SK: serde::Serialize" )) )] #[derive_where(Clone)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk, S)] -pub struct KeyPair> { +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk, SK)] +pub struct KeyPair> { pk: PublicKey, - sk: S, + sk: SK, } -impl KeyPair { +impl KeyPair { /// Creates a new [`KeyPair`] from the given keys. - pub fn new(sk: S, pk: PublicKey) -> Self { + pub fn new(sk: SK, pk: PublicKey) -> Self { Self { pk, sk } } @@ -46,7 +46,7 @@ impl KeyPair { } /// The private key component - pub fn private(&self) -> &S { + pub fn private(&self) -> &SK { &self.sk } } @@ -171,9 +171,9 @@ impl<'de, KG: KeGroup> serde::Deserialize<'de> for PrivateKey { #[cfg(feature = "serde")] impl serde::Serialize for PrivateKey { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: SK) -> Result where - S: serde::Serializer, + SK: serde::Serializer, { KG::serialize_sk(self.0).serialize(serializer) } @@ -217,9 +217,9 @@ impl<'de, KG: KeGroup> serde::Deserialize<'de> for PublicKey { #[cfg(feature = "serde")] impl serde::Serialize for PublicKey { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: SK) -> Result where - S: serde::Serializer, + SK: serde::Serializer, { KG::serialize_pk(self.0).serialize(serializer) } diff --git a/src/lib.rs b/src/lib.rs index 3543c99..f9e7ec0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1192,7 +1192,7 @@ pub use crate::messages::{ pub use crate::opaque::{ ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult, - ClientRegistrationStartResult, Identifiers, ServerLogin, ServerLoginFinishResult, - ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration, ServerRegistrationLen, - ServerRegistrationStartResult, ServerSetup, + ClientRegistrationStartResult, Identifiers, KeyMaterialInfo, ServerLogin, + ServerLoginFinishResult, ServerLoginStartParameters, ServerLoginStartResult, + ServerRegistration, ServerRegistrationLen, ServerRegistrationStartResult, ServerSetup, }; diff --git a/src/messages.rs b/src/messages.rs index 38beab9..160a321 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -115,21 +115,21 @@ pub struct CredentialRequest { feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "S: serde::Deserialize<'de>, , \ + deserialize = "SK: serde::Deserialize<'de>, , \ CS::KeGroup>>::KE2Builder: serde::Deserialize<'de>", - serialize = "S: serde::Serialize, , \ + serialize = "SK: serde::Serialize, , \ CS::KeGroup>>::KE2Builder: serde::Serialize" )) )] #[derive_where(Clone)] #[derive_where( Debug, Eq, PartialEq; - S, + SK, voprf::EvaluationElement, , CS::KeGroup>>::KE2Builder, )] -pub struct ServerLoginBuilder { - pub(crate) server_s_sk: S, +pub struct ServerLoginBuilder { + pub(crate) server_s_sk: SK, pub(crate) evaluation_element: voprf::EvaluationElement, pub(crate) masking_nonce: Zeroizing>, pub(crate) masked_response: MaskedResponse, @@ -138,7 +138,7 @@ pub struct ServerLoginBuilder { pub(crate) ke2_builder: , CS::KeGroup>>::KE2Builder, } -impl ServerLoginBuilder { +impl ServerLoginBuilder { /// The returned data here has to be processed and the result given as an /// input to [`ServerLoginBuilder::build()`]. To understand what kind of /// output is expected here and how to process it, refer to the @@ -150,7 +150,7 @@ impl ServerLoginBuilder { } /// The handle to the corresponding [`ServerSetup`]s private key. - pub fn private_key(&self) -> &S { + pub fn private_key(&self) -> &SK { &self.server_s_sk } @@ -325,9 +325,9 @@ impl RegistrationUpload { } // Creates a dummy instance used for faking a [CredentialResponse] - pub(crate) fn dummy( + pub(crate) fn dummy( rng: &mut R, - server_setup: &ServerSetup, + server_setup: &ServerSetup, ) -> Self { let mut masking_key = Output::>::default(); rng.fill_bytes(&mut masking_key); diff --git a/src/opaque.rs b/src/opaque.rs index 797468e..ab357da 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -11,7 +11,7 @@ use core::ops::{Add, Deref}; use derive_where::derive_where; -use digest::Output; +use digest::{Output, OutputSizeUser}; use generic_array::sequence::Concat; use generic_array::typenum::{Sum, Unsigned, U2}; use generic_array::{ArrayLength, GenericArray}; @@ -60,15 +60,19 @@ const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair"; feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "S: serde::Deserialize<'de>", - serialize = "S: serde::Serialize" + deserialize = "SK: serde::Deserialize<'de>, OS: serde::Deserialize<'de>", + serialize = "SK: serde::Serialize, OS: serde::Serialize" )) )] #[derive_where(Clone)] -#[derive_where(Debug, Eq, PartialEq; ::Pk, ::Sk, S)] -pub struct ServerSetup::KeGroup>> { - oprf_seed: Zeroizing>>, - keypair: KeyPair, +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Pk, ::Sk, SK, OS)] +pub struct ServerSetup< + CS: CipherSuite, + SK: Clone = PrivateKey<::KeGroup>, + OS: Clone = Zeroizing>>, +> { + oprf_seed: OS, + keypair: KeyPair, pub(crate) fake_keypair: KeyPair, } @@ -162,10 +166,85 @@ impl ServerSetup> { } /// Length of [`ServerSetup`] in bytes for serialization. -pub type ServerSetupLen> = - Sum>, S::Len>, ::SkLen>; +pub type ServerSetupLen< + CS: CipherSuite, + SK: PrivateKeySerialization, + OS: OprfSeedSerialization, SK::Error>, +> = Sum, ::SkLen>; -impl ServerSetup { +impl ServerSetup { + /// Create [`ServerSetup`] with the given keypair and OPRF seed. + /// + /// This function should not be used to restore a previously-existing + /// instance of [`ServerSetup`]. Instead, use [`ServerSetup::serialize`] and + /// [`ServerSetup::deserialize`] for this purpose. + pub fn new_with_key_pair_and_seed( + rng: &mut R, + keypair: KeyPair, + oprf_seed: OS, + ) -> Self { + Self { + oprf_seed, + keypair, + fake_keypair: KeyPair::::generate_random::(rng), + } + } + + /// The information required to generate the key material for + /// [`ServerRegistration::start_with_key_material()`] and + /// [`ServerLogin::builder_with_key_material()`]. + pub fn key_material_info<'ci>( + &self, + credential_identifier: &'ci [u8], + ) -> KeyMaterialInfo<'ci, OS> { + KeyMaterialInfo { + ikm: self.oprf_seed.clone(), + info: [credential_identifier, STR_OPRF_KEY], + } + } + + /// Serialization into bytes + pub fn serialize(&self) -> GenericArray> + where + SK: PrivateKeySerialization, + OS: OprfSeedSerialization, SK::Error>, + // ServerSetup: Hash + KeSk + KeSk + OS::Len: Add, + Sum: ArrayLength + Add<::SkLen>, + ServerSetupLen: ArrayLength, + { + self.oprf_seed + .serialize() + .concat(SK::serialize_key_pair(&self.keypair)) + .concat(self.fake_keypair.private().serialize()) + } + + /// Deserialization from bytes + pub fn deserialize(input: &[u8]) -> Result> + where + SK: PrivateKeySerialization, + OS: OprfSeedSerialization, SK::Error>, + { + let seed_len = OS::Len::USIZE; + let key_len = ::SkLen::USIZE; + let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup") + .map_err(ProtocolError::into_custom)?; + + Ok(Self { + oprf_seed: OS::deserialize(&checked_slice[..seed_len])?, + keypair: SK::deserialize_key_pair(&checked_slice[seed_len..seed_len + key_len])?, + fake_keypair: PrivateKey::deserialize_key_pair(&checked_slice[seed_len + key_len..]) + .map_err(ProtocolError::into_custom)?, + }) + } + + /// Returns the keypair + pub fn keypair(&self) -> &KeyPair { + &self.keypair + } +} + +impl ServerSetup { /// Create [`ServerSetup`] with the given keypair /// /// This function should not be used to restore a previously-existing @@ -173,7 +252,7 @@ impl ServerSetup { /// [`ServerSetup::deserialize`] for this purpose. pub fn new_with_key_pair( rng: &mut R, - keypair: KeyPair, + keypair: KeyPair, ) -> Self { let mut oprf_seed = GenericArray::default(); rng.fill_bytes(&mut oprf_seed); @@ -184,46 +263,48 @@ impl ServerSetup { fake_keypair: KeyPair::::generate_random::(rng), } } +} + +/// A trait to facilitate +/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize). +pub trait OprfSeedSerialization: Sized { + /// Serialization size in bytes. + type Len: ArrayLength; /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> - where - S: PrivateKeySerialization, - // ServerSetup: Hash + KeSk + KeSk - OutputSize>: Add, - Sum>, S::Len>: - ArrayLength + Add<::SkLen>, - ServerSetupLen: ArrayLength, - { - self.oprf_seed - .deref() - .clone() - .concat(S::serialize_key_pair(&self.keypair)) - .concat(self.fake_keypair.private().serialize()) - } + fn serialize(&self) -> GenericArray; /// Deserialization from bytes - pub fn deserialize(input: &[u8]) -> Result> - where - S: PrivateKeySerialization, - { - let seed_len = OutputSize::>::USIZE; - let key_len = ::SkLen::USIZE; - let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup") + fn deserialize(input: &[u8]) -> Result>; +} + +impl OprfSeedSerialization for Zeroizing> { + type Len = H::OutputSize; + + fn serialize(&self) -> GenericArray { + self.deref().clone() + } + + fn deserialize(input: &[u8]) -> Result> { + check_slice_size(input, H::OutputSize::USIZE, "oprf_seed") .map_err(ProtocolError::into_custom)?; - Ok(Self { - oprf_seed: Zeroizing::new(GenericArray::clone_from_slice(&checked_slice[..seed_len])), - keypair: S::deserialize_key_pair(&checked_slice[seed_len..seed_len + key_len])?, - fake_keypair: PrivateKey::deserialize_key_pair(&checked_slice[seed_len + key_len..]) - .map_err(ProtocolError::into_custom)?, - }) + Ok(Zeroizing::new(GenericArray::clone_from_slice(input))) } +} - /// Returns the keypair - pub fn keypair(&self) -> &KeyPair { - &self.keypair - } +/// The information required to generate the key material for +/// [`ServerRegistration::start_with_key_material()`] and +/// [`ServerLogin::builder_with_key_material()`]. +/// +/// Use an HKDF, with the input key material [`ikm`](Self::ikm), expand +/// operation with [`info`](Self::info) with an output length +/// of [`CS::OprfCs::ScalarLen`](Group::ScalarLen). +pub struct KeyMaterialInfo<'ci, OS: Clone> { + /// Input key material for the HKDF. + pub ikm: OS, + /// Info for the HKDF expand operation. + pub info: [&'ci [u8]; 2], } // Registration @@ -370,14 +451,16 @@ impl ServerRegistration { Ok(Self(RegistrationUpload::deserialize(input)?)) } - /// From the client's "blinded" password, returns a response to be sent back - /// to the client, as well as a [`ServerRegistration`] - pub fn start( - server_setup: &ServerSetup, + /// Create a [`RegistrationResponse`] with a remote OPRF seed. To generate + /// the `key_material` see [`ServerSetup::key_material_info()`]. + /// + /// See [`ServerRegistration::start()`] for the regular path. + pub fn start_with_key_material( + server_setup: &ServerSetup, + key_material: GenericArray as Group>::ScalarLen>, message: RegistrationRequest, - credential_identifier: &[u8], ) -> Result, ProtocolError> { - let oprf_key = oprf_key_from_seed::(&server_setup.oprf_seed, credential_identifier)?; + let oprf_key = oprf_key_from_key_material::(key_material)?; let server = voprf::OprfServer::new_with_key(&oprf_key)?; let evaluation_element = server.blind_evaluate(&message.blinded_element); @@ -385,13 +468,29 @@ impl ServerRegistration { Ok(ServerRegistrationStartResult { message: RegistrationResponse { evaluation_element, - server_s_pk: server_setup.keypair.public().clone(), + server_s_pk: server_setup.keypair().public().clone(), }, #[cfg(test)] oprf_key, }) } + /// From the client's "blinded" password, returns a response to be sent back + /// to the client, as well as a [`ServerRegistration`] + pub fn start( + server_setup: &ServerSetup, + message: RegistrationRequest, + credential_identifier: &[u8], + ) -> Result, ProtocolError> { + let KeyMaterialInfo { + ikm: oprf_seed, + info, + } = server_setup.key_material_info(credential_identifier); + let key_material = oprf_key_material::(&oprf_seed, &info)?; + + Self::start_with_key_material(server_setup, key_material, message) + } + /// From the client's cryptographic identifiers, fully populates and returns /// a [`ServerRegistration`] pub fn finish(message: RegistrationUpload) -> Self { @@ -399,9 +498,9 @@ impl ServerRegistration { } // Creates a dummy instance used for faking a [CredentialResponse] - pub(crate) fn dummy( + pub(crate) fn dummy( rng: &mut R, - server_setup: &ServerSetup, + server_setup: &ServerSetup, ) -> Self { Self(RegistrationUpload::dummy(rng, server_setup)) } @@ -598,20 +697,23 @@ impl ServerLogin { }) } - /// Create a [`ServerLoginBuilder`] to use with a remote private key. + /// Create a [`ServerLoginBuilder`] with a remote OPRF seed and private key. + /// To generate the `key_material` see + /// [`ServerSetup::key_material_info()`]. /// - /// See [`ServerLogin::start()`] for the regular path. - pub fn builder( + /// See [`ServerLogin::start()`] for the regular path. Or + /// [`ServerLogin::builder()`] with just a remote private key. + pub fn builder_with_key_material( rng: &mut R, - server_setup: &ServerSetup, + server_setup: &ServerSetup, + key_material: GenericArray as Group>::ScalarLen>, password_file: Option>, credential_request: CredentialRequest, - credential_identifier: &[u8], ServerLoginStartParameters { context, identifiers, }: ServerLoginStartParameters, - ) -> Result, ProtocolError> + ) -> Result, ProtocolError> where // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>>, @@ -652,7 +754,7 @@ impl ServerLogin { let credential_request_bytes = CredentialRequest::::serialize_iter(&blinded_element, &ke1_message); - let oprf_key = oprf_key_from_seed::(&server_setup.oprf_seed, credential_identifier)?; + let oprf_key = oprf_key_from_key_material::(key_material)?; let server = voprf::OprfServer::new_with_key(&oprf_key).map_err(ProtocolError::from)?; let evaluation_element = server.blind_evaluate(&credential_request.blinded_element); @@ -682,8 +784,42 @@ impl ServerLogin { }) } - pub(crate) fn build( - builder: ServerLoginBuilder, + /// Create a [`ServerLoginBuilder`] to use with a remote private key. + /// + /// See [`ServerLogin::start()`] for the regular path. + pub fn builder( + rng: &mut R, + server_setup: &ServerSetup, + password_file: Option>, + credential_request: CredentialRequest, + credential_identifier: &[u8], + params: ServerLoginStartParameters, + ) -> Result, ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add>>, + Sum>>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { + let KeyMaterialInfo { + ikm: oprf_seed, + info, + } = server_setup.key_material_info(credential_identifier); + let key_material = oprf_key_material::(&oprf_seed, &info)?; + + Self::builder_with_key_material( + rng, + server_setup, + key_material, + password_file, + credential_request, + params, + ) + } + + pub(crate) fn build( + builder: ServerLoginBuilder, input: , CS::KeGroup>>::KE2BuilderInput, ) -> Result, ProtocolError> { let result = CS::KeyExchange::build_ke2(builder.ke2_builder.clone(), input)?; @@ -966,23 +1102,26 @@ fn get_password_derived_key( Ok(hkdf.finalize()) } -fn oprf_key_from_seed( +fn oprf_key_material( oprf_seed: &Output>, - credential_identifier: &[u8], -) -> Result as Group>::ScalarLen>, ProtocolError> { + info: &[&[u8]], +) -> Result as Group>::ScalarLen>, InternalError> { let mut ikm = GenericArray::<_, as Group>::ScalarLen>::default(); Hkdf::>::from_prk(oprf_seed) .ok() - .and_then(|hkdf| { - hkdf.expand_multi_info(&[credential_identifier, STR_OPRF_KEY], &mut ikm) - .ok() - }) + .and_then(|hkdf| hkdf.expand_multi_info(info, &mut ikm).ok()) .ok_or(InternalError::HkdfError)?; + Ok(ikm) +} + +fn oprf_key_from_key_material( + input: GenericArray as Group>::ScalarLen>, +) -> Result as Group>::ScalarLen>, InternalError> { Ok(OprfGroup::::serialize_scalar(voprf::derive_key::< CS::OprfCs, >( - ikm.as_slice(), + input.as_slice(), &GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR), voprf::Mode::Oprf, )?)) diff --git a/src/tests/remote_key.rs b/src/tests/remote_key.rs index 6fbff2e..21d33f3 100644 --- a/src/tests/remote_key.rs +++ b/src/tests/remote_key.rs @@ -17,6 +17,7 @@ use cryptoki::mechanism::Mechanism; use cryptoki::object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle}; use cryptoki::session::{Session, UserType}; use cryptoki::types::AuthPin; +use digest::OutputSizeUser; use elliptic_curve::group::Curve; use elliptic_curve::pkcs8::der::asn1::{OctetString, OctetStringRef}; use elliptic_curve::pkcs8::der::{Decode, Encode}; @@ -24,7 +25,7 @@ use elliptic_curve::pkcs8::{AssociatedOid, ObjectIdentifier}; use elliptic_curve::point::{AffineCoordinates, DecompressPoint}; use elliptic_curve::sec1::{ModulusSize, Tag, ToEncodedPoint}; use elliptic_curve::{AffinePoint, CurveArithmetic, FieldBytesSize, Group, ProjectivePoint}; -use generic_array::typenum::Sum; +use generic_array::typenum::{Sum, Unsigned}; use generic_array::{ArrayLength, GenericArray}; use p256::NistP256; use p384::NistP384; @@ -32,7 +33,7 @@ use p521::NistP521; use rand::rngs::OsRng; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use crate::ciphersuite::OprfHash; +use crate::ciphersuite::{OprfGroup, OprfHash}; use crate::envelope::NonceLen; use crate::hash::OutputSize; use crate::key_exchange::group::KeGroup; @@ -60,7 +61,11 @@ fn p256() { type Ksf = Identity; } - test::(Mechanism::EccKeyPairGen, NistP256::OID); + test::( + Mechanism::EccKeyPairGen, + NistP256::OID, + Mechanism::Sha256Hmac, + ); } #[test] @@ -74,7 +79,11 @@ fn p384() { type Ksf = Identity; } - test::(Mechanism::EccKeyPairGen, NistP384::OID); + test::( + Mechanism::EccKeyPairGen, + NistP384::OID, + Mechanism::Sha384Hmac, + ); } #[test] @@ -88,7 +97,11 @@ fn p521() { type Ksf = Identity; } - test::(Mechanism::EccKeyPairGen, NistP521::OID); + test::( + Mechanism::EccKeyPairGen, + NistP521::OID, + Mechanism::Sha512Hmac, + ); } #[test] @@ -108,6 +121,7 @@ fn curve25519() { // implementation. See https://github.com/softhsm/SoftHSMv2/issues/647. Mechanism::EccEdwardsKeyPairGen, ObjectIdentifier::new("1.3.101.110").unwrap(), + Mechanism::Sha512Hmac, ); } @@ -122,8 +136,11 @@ trait Pkcs11DiffieHellman { ) -> GenericArray; } -fn test>(mechanism: Mechanism, oid: ObjectIdentifier) -where +fn test>( + dh_mechanism: Mechanism, + oid: ObjectIdentifier, + hmac_mechanism: Mechanism, +) where RemoteKey: Pkcs11DiffieHellman, ::Sk: DiffieHellman, // MaskedResponse: (Nonce + Hash) + KePk @@ -147,10 +164,11 @@ where Sum::PkLen>: ArrayLength + Add>>, Sum::PkLen>, OutputSize>>: ArrayLength, { - let (remote_key, pk) = pkcs11_generate_key_pair(mechanism, oid); + let (remote_key, pk) = pkcs11_generate_key_pair(dh_mechanism, oid); let keypair = KeyPair::new(RemoteKey(remote_key), pk); - let server_setup = ServerSetup::new_with_key_pair(&mut OsRng, keypair); + let oprf_seed = pkcs11_generate_oprf_seed( as OutputSizeUser>::OutputSize::U64); + let server_setup = ServerSetup::new_with_key_pair_and_seed(&mut OsRng, keypair, oprf_seed); const PASSWORD: &str = "password"; @@ -158,7 +176,13 @@ where message, state: client, } = ClientRegistration::::start(&mut OsRng, PASSWORD.as_bytes()).unwrap(); - let message = ServerRegistration::start(&server_setup, message, &[]) + let key_material_info = server_setup.key_material_info(&[]); + let key_material = pkcs11_hkdf::( + key_material_info.ikm, + hmac_mechanism, + Vec::from_iter(key_material_info.info.into_iter().flatten().copied()), + ); + let message = ServerRegistration::start_with_key_material(&server_setup, key_material, message) .unwrap() .message; let message = client @@ -176,12 +200,18 @@ where message, state: client, } = ClientLogin::::start(&mut OsRng, PASSWORD.as_bytes()).unwrap(); - let builder = ServerLogin::builder( + let key_material_info = server_setup.key_material_info(&[]); + let key_material = pkcs11_hkdf::( + key_material_info.ikm, + hmac_mechanism, + Vec::from_iter(key_material_info.info.into_iter().flatten().copied()), + ); + let builder = ServerLogin::builder_with_key_material( &mut OsRng, &server_setup, + key_material, Some(file), message, - &[], ServerLoginStartParameters::default(), ) .unwrap(); @@ -265,6 +295,53 @@ fn pkcs11_generate_key_pair( (remote_key, pk) } +fn pkcs11_generate_oprf_seed(length: u64) -> ObjectHandle { + SESSION + .lock() + .unwrap() + .generate_key( + &Mechanism::GenericSecretKeyGen, + &[Attribute::Token(false), Attribute::ValueLen(length.into())], + ) + .unwrap() +} + +// SoftHSM, nor any other popular HSM at the time of writing, supports HKDF. So +// we instead implement HKDF by hand on top of the HSMs HMAC, which is supported +// by almost all HSMs and still protects the OPRF seed. +fn pkcs11_hkdf( + hmac: ObjectHandle, + mechanism: Mechanism, + info: Vec, +) -> GenericArray as voprf::Group>::ScalarLen> { + let mut okm = GenericArray::default(); + let mut prev: Option> = None; + let chunk_len = as OutputSizeUser>::OutputSize::USIZE; + + if okm.len() > chunk_len * 255 { + panic!("invalid length"); + } + + let session = SESSION.lock().unwrap(); + + for (block_n, block) in (0..).zip(okm.chunks_mut(chunk_len)) { + let mut data = Vec::new(); + + if let Some(ref prev) = prev { + data.extend(prev.as_slice()) + }; + + data.extend(&info); + data.extend(&[block_n + 1]); + + let output = session.sign(&mechanism, hmac, &data).unwrap(); + block.copy_from_slice(&output[..block.len()]); + prev = Some(output); + } + + okm +} + impl Pkcs11DiffieHellman for RemoteKey { fn pkcs11_diffie_hellman( &self,