1use core::ops::Add;
6
7use derive_where::derive_where;
8use digest::block_api::{CoreProxy, SmallBlockSizeUser};
9use digest::{Digest, Mac, Output, OutputSizeUser, Update};
10use generic_array::typenum::{IsLess, Le, NonZero, Sum, U1, U2, U32, U256, Unsigned};
11use generic_array::{ArrayLength, GenericArray};
12use hkdf::SimpleHkdf as Hkdf;
13use hkdf::SimpleHkdfExtract as HkdfExtract;
14use hmac::{KeyInit, SimpleHmac};
15use rand::{CryptoRng, Rng};
16
17use super::{
18 Deserialize, GenerateKe1Result, KeyExchange, Serialize, SerializedContext,
19 SerializedCredentialRequest, SerializedCredentialResponse, SerializedIdentifiers,
20};
21use crate::ciphersuite::{CipherSuite, KeGroup, KeHash};
22use crate::errors::{InternalError, ProtocolError};
23use crate::hash::{Hash, OutputSize, ProxyHash};
24use crate::key_exchange::group::Group;
25use crate::keypair::{KeyPair, PrivateKey, PublicKey};
26use crate::serialization::{ConcatExt, SliceExt, UpdateExt, i2osp};
27
28pub(crate) type NonceLen = U32;
34pub(super) static STR_CONTEXT: &[u8] = b"OPAQUEv1-";
35static STR_CLIENT_MAC: &[u8] = b"ClientMAC";
36static STR_HANDSHAKE_SECRET: &[u8] = b"HandshakeSecret";
37static STR_SERVER_MAC: &[u8] = b"ServerMAC";
38static STR_SESSION_KEY: &[u8] = b"SessionKey";
39static STR_OPAQUE: &[u8] = b"OPAQUE-";
40
41pub trait DiffieHellman<G: Group> {
49 fn diffie_hellman(&self, pk: &G::Pk) -> GenericArray<u8, G::PkLen>;
51}
52
53#[cfg_attr(
55 feature = "serde",
56 derive(serde::Deserialize, serde::Serialize),
57 serde(bound(
58 deserialize = "G::Sk: serde::Deserialize<'de>",
59 serialize = "G::Sk: serde::Serialize"
60 ))
61)]
62#[derive_where(Clone, ZeroizeOnDrop)]
63#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Sk)]
64pub struct Ke1State<G: Group> {
65 pub(super) client_e_sk: PrivateKey<G>,
66 pub(super) client_nonce: GenericArray<u8, NonceLen>,
67}
68
69#[cfg_attr(
71 feature = "serde",
72 derive(serde::Deserialize, serde::Serialize),
73 serde(bound(
74 deserialize = "G::Pk: serde::Deserialize<'de>",
75 serialize = "G::Pk: serde::Serialize"
76 ))
77)]
78#[derive_where(Clone, ZeroizeOnDrop)]
79#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk)]
80pub struct Ke1Message<G: Group> {
81 pub(super) client_nonce: GenericArray<u8, NonceLen>,
82 #[derive_where(skip(Zeroize))]
83 pub(super) client_e_pk: PublicKey<G>,
84}
85
86pub(super) struct DerivedKeys<H: OutputSizeUser> {
93 pub(super) session_key: Output<H>,
94 pub(super) km2: Output<H>,
95 pub(super) km3: Output<H>,
96 #[cfg(test)]
97 pub(super) handshake_secret: Output<H>,
98}
99
100pub(super) struct Ke2BuilderCommon<G: Group, H: Hash>
103where
104 H::Core: ProxyHash,
105 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
106 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
107 OutputSize<H>: ArrayLength,
108 G::Sk: DiffieHellman<G>,
109{
110 pub(super) server_nonce: GenericArray<u8, NonceLen>,
111 pub(super) transcript_hasher: H,
112 pub(super) client_e_pk: PublicKey<G>,
113 pub(super) server_e_pk: PublicKey<G>,
114 pub(super) shared_secret_1: GenericArray<u8, G::PkLen>,
115 pub(super) shared_secret_3: GenericArray<u8, G::PkLen>,
116}
117
118pub(super) fn generate_ke1<
126 R: Rng + CryptoRng,
127 KE: KeyExchange<KE1State = Ke1State<G>, KE1Message = Ke1Message<G>>,
128 G: Group,
129>(
130 rng: &mut R,
131) -> Result<GenerateKe1Result<KE>, ProtocolError> {
132 let client_e_kp = KeyPair::<G>::derive_random(rng);
133 let client_nonce = generate_nonce::<R>(rng);
134
135 let ke1_message = Ke1Message {
136 client_nonce,
137 client_e_pk: client_e_kp.public().clone(),
138 };
139
140 Ok(GenerateKe1Result {
141 state: Ke1State {
142 client_e_sk: client_e_kp.private().clone(),
143 client_nonce,
144 },
145 message: ke1_message,
146 })
147}
148
149pub(super) fn generate_nonce<R: Rng + CryptoRng>(rng: &mut R) -> GenericArray<u8, NonceLen> {
151 let mut nonce_bytes = GenericArray::default();
152 rng.fill_bytes(&mut nonce_bytes);
153 nonce_bytes
154}
155
156pub(super) fn transcript<CS: CipherSuite, KE: Group>(
157 context: &SerializedContext<'_>,
158 identifiers: &SerializedIdentifiers<'_, KeGroup<CS>>,
159 credential_request: &SerializedCredentialRequest<CS>,
160 ke1_message: &Ke1MessageIter<KE>,
161 credential_response: &SerializedCredentialResponse<CS>,
162 server_nonce: GenericArray<u8, NonceLen>,
163 server_e_pk: &GenericArray<u8, KE::PkLen>,
164) -> KeHash<CS> {
165 KeHash::<CS>::new()
166 .chain_iter(context.iter())
167 .chain_iter(identifiers.client.iter())
168 .chain_iter(credential_request.iter())
169 .chain_iter(ke1_message.iter())
170 .chain_iter(identifiers.server.iter())
171 .chain_iter(credential_response.iter())
172 .chain(server_nonce)
173 .chain(server_e_pk)
174}
175
176pub(super) fn ke2_builder_common<'a, G, H, CS, R>(
179 rng: &mut R,
180 credential_request: SerializedCredentialRequest<CS>,
181 ke1_message: Ke1Message<G>,
182 credential_response: SerializedCredentialResponse<CS>,
183 client_s_pk: PublicKey<G>,
184 identifiers: SerializedIdentifiers<'a, KeGroup<CS>>,
185 context: SerializedContext<'a>,
186) -> Result<Ke2BuilderCommon<G, H>, ProtocolError>
187where
188 G: Group,
189 H: Hash,
190 R: Rng + CryptoRng,
191 CS: CipherSuite,
192 H::Core: ProxyHash,
193 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
194 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
195 OutputSize<H>: ArrayLength,
196 G::Sk: DiffieHellman<G>,
197 CS::KeyExchange: KeyExchange<Group = G, Hash = H>,
198{
199 let server_ephemeral = KeyPair::<G>::derive_random(rng);
200 let server_nonce = generate_nonce::<R>(rng);
201 let server_e_pk_bytes = server_ephemeral.public().serialize();
202
203 let ke1_iter = ke1_message.to_iter();
204 let client_e_pk = ke1_message.client_e_pk.clone();
205
206 let transcript_hasher = transcript(
207 &context,
208 &identifiers,
209 &credential_request,
210 &ke1_iter,
211 &credential_response,
212 server_nonce,
213 &server_e_pk_bytes,
214 );
215
216 let shared_secret_1 = server_ephemeral
217 .private()
218 .ke_diffie_hellman(&ke1_message.client_e_pk);
219 let shared_secret_3 = server_ephemeral.private().ke_diffie_hellman(&client_s_pk);
220
221 Ok(Ke2BuilderCommon {
222 server_nonce,
223 transcript_hasher,
224 client_e_pk,
225 server_e_pk: server_ephemeral.public().clone(),
226 shared_secret_1,
227 shared_secret_3,
228 })
229}
230
231pub(super) fn derive_keys<'a, H: Hash>(
234 ikms: impl Iterator<Item = &'a [u8]>,
235 hashed_derivation_transcript: &[u8],
236) -> Result<DerivedKeys<H>, ProtocolError>
237where
238 H::Core: ProxyHash,
239 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
240 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
241 OutputSize<H>: ArrayLength,
242{
243 let mut hkdf = HkdfExtract::<H>::new(None);
244
245 for ikm in ikms {
246 hkdf.input_ikm(ikm);
247 }
248
249 let (_, extracted_ikm) = hkdf.finalize();
250 let handshake_secret = derive_secrets::<H>(
251 &extracted_ikm,
252 STR_HANDSHAKE_SECRET,
253 hashed_derivation_transcript,
254 )?;
255 let session_key = derive_secrets::<H>(
256 &extracted_ikm,
257 STR_SESSION_KEY,
258 hashed_derivation_transcript,
259 )?;
260
261 let km2 = hkdf_expand_label::<H>(&handshake_secret, STR_SERVER_MAC, b"")?;
262 let km3 = hkdf_expand_label::<H>(&handshake_secret, STR_CLIENT_MAC, b"")?;
263
264 Ok(DerivedKeys {
265 session_key,
266 km2,
267 km3,
268 #[cfg(test)]
269 handshake_secret,
270 })
271}
272
273pub(super) fn compute_ke2_macs<H: Hash>(
276 transcript_hasher: &mut H,
277 derived_keys: &DerivedKeys<H>,
278 transcript_digest: &[u8],
279) -> Result<(Output<H>, Output<H>), ProtocolError>
280where
281 H::Core: ProxyHash,
282 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
283 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
284 OutputSize<H>: ArrayLength,
285{
286 let mut mac_hasher =
287 SimpleHmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
288 Mac::update(&mut mac_hasher, transcript_digest);
289 let mac = mac_hasher.finalize().into_bytes();
290
291 Update::update(transcript_hasher, &mac);
292 let finalized_transcript = transcript_hasher.clone().finalize();
293
294 let mut expected_mac_hasher =
295 SimpleHmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
296 Mac::update(&mut expected_mac_hasher, &finalized_transcript);
297 let expected_mac = expected_mac_hasher.finalize().into_bytes();
298
299 Ok((mac, expected_mac))
300}
301
302pub(super) fn finalize_ke3_transcript<'a, H: Hash>(
308 transcript_hasher: &mut H,
309 shared_secrets: impl Iterator<Item = &'a [u8]>,
310 server_mac: &Output<H>,
311) -> Result<(DerivedKeys<H>, Output<H>), ProtocolError>
312where
313 H::Core: ProxyHash,
314 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
315 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
316 OutputSize<H>: ArrayLength,
317{
318 let transcript_digest = transcript_hasher.clone().finalize();
319 let derived_keys = derive_keys::<H>(shared_secrets, &transcript_digest)?;
320 let mut server_mac_hasher =
321 SimpleHmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
322 Mac::update(&mut server_mac_hasher, &transcript_digest);
323 server_mac_hasher
324 .verify(server_mac)
325 .map_err(|_| ProtocolError::InvalidLoginError)?;
326
327 Update::update(transcript_hasher, server_mac);
328 let finalized_transcript = transcript_hasher.clone().finalize();
329
330 let mut client_mac_hasher =
331 SimpleHmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
332 Mac::update(&mut client_mac_hasher, &finalized_transcript);
333
334 let client_mac = client_mac_hasher.finalize().into_bytes();
335
336 Ok((derived_keys, client_mac))
337}
338
339fn hkdf_expand_label<H: Hash>(
340 secret: &[u8],
341 label: &[u8],
342 context: &[u8],
343) -> Result<Output<H>, ProtocolError>
344where
345 H::Core: ProxyHash,
346 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
347 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
348 OutputSize<H>: ArrayLength,
349{
350 let h = Hkdf::<H>::from_prk(secret).map_err(|_| InternalError::HkdfError)?;
351 hkdf_expand_label_extracted(&h, label, context)
352}
353
354fn hkdf_expand_label_extracted<H: Hash>(
355 hkdf: &Hkdf<H>,
356 label: &[u8],
357 context: &[u8],
358) -> Result<Output<H>, ProtocolError>
359where
360 H::Core: ProxyHash,
361 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
362 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
363 OutputSize<H>: ArrayLength,
364{
365 let mut okm = GenericArray::default().into_ha0_4();
366
367 let length = i2osp::<U2>(OutputSize::<H>::USIZE)?;
368 let label_length = i2osp::<U1>(STR_OPAQUE.len() + label.len())?;
369 let context_len = i2osp::<U1>(context.len())?;
370
371 let hkdf_label = [
372 length.as_slice(),
373 &label_length,
374 STR_OPAQUE,
375 label,
376 &context_len,
377 context,
378 ];
379
380 hkdf.expand_multi_info(&hkdf_label, &mut okm)
381 .map_err(|_| InternalError::HkdfError)?;
382 Ok(okm)
383}
384
385fn derive_secrets<H: Hash>(
386 hkdf: &Hkdf<H>,
387 label: &[u8],
388 hashed_derivation_transcript: &[u8],
389) -> Result<Output<H>, ProtocolError>
390where
391 H::Core: ProxyHash,
392 <<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
393 Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
394 OutputSize<H>: ArrayLength,
395{
396 hkdf_expand_label_extracted::<H>(hkdf, label, hashed_derivation_transcript)
397}
398
399impl<G: Group> Deserialize for Ke1State<G> {
402 fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
403 Ok(Self {
404 client_e_sk: PrivateKey::deserialize_take(bytes)?,
405 client_nonce: bytes.take_array("client nonce")?,
406 })
407 }
408}
409
410impl<G: Group> Serialize for Ke1State<G>
411where
412 G::SkLen: Add<NonceLen>,
414 Sum<G::SkLen, NonceLen>: ArrayLength,
415{
416 type Len = Sum<G::SkLen, NonceLen>;
417
418 fn serialize(&self) -> GenericArray<u8, Self::Len> {
419 self.client_e_sk.serialize().cat(self.client_nonce)
420 }
421}
422
423impl<G: Group> Deserialize for Ke1Message<G> {
424 fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
425 Ok(Self {
426 client_nonce: input.take_array("client nonce")?,
427 client_e_pk: PublicKey::deserialize_take(input)?,
428 })
429 }
430}
431
432impl<G: Group> Serialize for Ke1Message<G>
433where
434 NonceLen: Add<G::PkLen>,
436 Sum<NonceLen, G::PkLen>: ArrayLength,
437{
438 type Len = Sum<NonceLen, G::PkLen>;
439
440 fn serialize(&self) -> GenericArray<u8, Self::Len> {
441 self.client_nonce.cat(self.client_e_pk.serialize())
442 }
443}
444
445impl<G: Group> Ke1Message<G> {
446 pub(crate) fn to_iter(&self) -> Ke1MessageIter<G> {
447 Ke1MessageIter {
448 client_nonce: self.client_nonce,
449 client_e_pk: self.client_e_pk.serialize(),
450 }
451 }
452}
453
454#[cfg_attr(
455 feature = "serde",
456 derive(serde::Deserialize, serde::Serialize),
457 serde(bound = "")
458)]
459#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
460pub(crate) struct Ke1MessageIter<G: Group> {
461 client_nonce: GenericArray<u8, NonceLen>,
462 client_e_pk: GenericArray<u8, G::PkLen>,
463}
464
465pub(crate) type Ke1MessageIterLen<G: Group> = Sum<NonceLen, G::PkLen>;
466
467impl<G: Group> Ke1MessageIter<G> {
468 pub(crate) fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
469 [self.client_nonce.as_slice(), self.client_e_pk.as_slice()].into_iter()
470 }
471
472 pub(crate) fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
473 Ok(Ke1MessageIter {
474 client_nonce: input.take_array("client nonce")?,
475 client_e_pk: input.take_array("client ephemeral public key")?,
476 })
477 }
478}
479
480impl<G: Group> Ke1MessageIter<G>
481where
482 NonceLen: Add<G::PkLen>,
483 Ke1MessageIterLen<G>: ArrayLength,
484{
485 pub(crate) fn serialize(&self) -> GenericArray<u8, Ke1MessageIterLen<G>> {
486 self.client_nonce.cat(self.client_e_pk.clone())
487 }
488}