1use std::borrow::Borrow;
4
5use openmls_traits::{random::OpenMlsRand, storage::StorageProvider as StorageProviderTrait};
6use serde::{Deserialize, Serialize};
7use tls_codec::{Serialize as TlsSerializeTrait, VLBytes};
8
9use super::*;
10#[cfg(feature = "extensions-draft")]
11use crate::component::ComponentId;
12use crate::{
13 group::{GroupEpoch, GroupId},
14 schedule::psk::store::ResumptionPskStore,
15 storage::{OpenMlsProvider, StorageProvider},
16};
17
18#[derive(
31 Clone,
32 Copy,
33 Debug,
34 PartialEq,
35 Eq,
36 PartialOrd,
37 Ord,
38 Hash,
39 Deserialize,
40 Serialize,
41 TlsDeserialize,
42 TlsDeserializeBytes,
43 TlsSerialize,
44 TlsSize,
45)]
46#[repr(u8)]
47pub enum ResumptionPskUsage {
48 Application = 1,
50 Reinit = 2,
54 Branch = 3,
58}
59
60#[derive(
62 Debug,
63 PartialEq,
64 Eq,
65 PartialOrd,
66 Ord,
67 Clone,
68 Hash,
69 Deserialize,
70 Serialize,
71 TlsDeserialize,
72 TlsDeserializeBytes,
73 TlsSerialize,
74 TlsSize,
75)]
76pub struct ExternalPsk {
77 psk_id: VLBytes,
78}
79
80impl ExternalPsk {
81 pub fn new(psk_id: Vec<u8>) -> Self {
83 Self {
84 psk_id: psk_id.into(),
85 }
86 }
87
88 pub fn psk_id(&self) -> &[u8] {
90 self.psk_id.as_slice()
91 }
92}
93
94#[derive(Serialize, Deserialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize)]
97pub(crate) struct PskBundle {
98 secret: Secret,
99}
100
101#[derive(
103 Clone,
104 Debug,
105 PartialEq,
106 Eq,
107 PartialOrd,
108 Ord,
109 Deserialize,
110 Serialize,
111 TlsDeserialize,
112 TlsDeserializeBytes,
113 TlsSerialize,
114 TlsSize,
115 Hash,
116)]
117pub struct ResumptionPsk {
118 pub(crate) usage: ResumptionPskUsage,
119 pub(crate) psk_group_id: GroupId,
120 pub(crate) psk_epoch: GroupEpoch,
121}
122
123impl ResumptionPsk {
124 pub fn new(usage: ResumptionPskUsage, psk_group_id: GroupId, psk_epoch: GroupEpoch) -> Self {
126 Self {
127 usage,
128 psk_group_id,
129 psk_epoch,
130 }
131 }
132
133 pub fn usage(&self) -> ResumptionPskUsage {
135 self.usage
136 }
137
138 pub fn psk_group_id(&self) -> &GroupId {
140 &self.psk_group_id
141 }
142
143 pub fn psk_epoch(&self) -> GroupEpoch {
145 self.psk_epoch
146 }
147}
148
149#[cfg(feature = "extensions-draft")]
153#[derive(
154 Clone,
155 Debug,
156 PartialEq,
157 Eq,
158 PartialOrd,
159 Ord,
160 Deserialize,
161 Serialize,
162 TlsDeserialize,
163 TlsDeserializeBytes,
164 TlsSerialize,
165 TlsSize,
166 Hash,
167)]
168pub struct ApplicationPsk {
169 pub(crate) component_id: ComponentId,
170 pub(crate) psk_id: VLBytes,
171}
172
173#[cfg(feature = "extensions-draft")]
174impl ApplicationPsk {
175 pub fn new(component_id: ComponentId, psk_id: VLBytes) -> Self {
177 Self {
178 component_id,
179 psk_id,
180 }
181 }
182
183 pub fn component_id(&self) -> ComponentId {
185 self.component_id
186 }
187
188 pub fn psk_id(&self) -> &[u8] {
190 self.psk_id.as_slice()
191 }
192}
193
194#[derive(
196 Clone,
197 Debug,
198 PartialEq,
199 Eq,
200 PartialOrd,
201 Ord,
202 Deserialize,
203 Serialize,
204 TlsDeserialize,
205 TlsDeserializeBytes,
206 TlsSerialize,
207 TlsSize,
208 Hash,
209)]
210#[repr(u8)]
211pub enum Psk {
212 #[tls_codec(discriminant = 1)]
214 External(ExternalPsk),
215 #[tls_codec(discriminant = 2)]
217 Resumption(ResumptionPsk),
218 #[cfg(feature = "extensions-draft")]
219 #[tls_codec(discriminant = 3)]
221 Application(ApplicationPsk),
222}
223
224#[derive(Clone, Copy, Debug, Eq, PartialEq)]
236#[repr(u8)]
237pub enum PskType {
238 External = 1,
240 Resumption = 2,
242 #[cfg(feature = "extensions-draft")]
243 Application = 3,
245}
246
247#[derive(
267 Clone,
268 Debug,
269 PartialEq,
270 Eq,
271 PartialOrd,
272 Ord,
273 Deserialize,
274 Serialize,
275 TlsDeserialize,
276 TlsDeserializeBytes,
277 TlsSerialize,
278 TlsSize,
279 Hash,
280)]
281pub struct PreSharedKeyId {
282 pub(crate) psk: Psk,
283 pub(crate) psk_nonce: VLBytes,
284}
285
286impl PreSharedKeyId {
287 pub fn new(
289 ciphersuite: Ciphersuite,
290 rand: &impl OpenMlsRand,
291 psk: Psk,
292 ) -> Result<Self, CryptoError> {
293 let psk_nonce = rand
294 .random_vec(ciphersuite.hash_length())
295 .map_err(|_| CryptoError::InsufficientRandomness)?
296 .into();
297
298 Ok(Self { psk, psk_nonce })
299 }
300
301 pub fn external(psk_id: Vec<u8>, psk_nonce: Vec<u8>) -> Self {
303 let psk = Psk::External(ExternalPsk::new(psk_id));
304
305 Self {
306 psk,
307 psk_nonce: psk_nonce.into(),
308 }
309 }
310
311 pub fn resumption(
313 usage: ResumptionPskUsage,
314 psk_group_id: GroupId,
315 psk_epoch: GroupEpoch,
316 psk_nonce: Vec<u8>,
317 ) -> Self {
318 let psk = Psk::Resumption(ResumptionPsk::new(usage, psk_group_id, psk_epoch));
319
320 Self {
321 psk,
322 psk_nonce: psk_nonce.into(),
323 }
324 }
325
326 #[cfg(feature = "extensions-draft")]
328 pub fn application(component_id: ComponentId, psk_id: Vec<u8>, psk_nonce: Vec<u8>) -> Self {
329 let psk = Psk::Application(ApplicationPsk::new(component_id, psk_id.into()));
330
331 Self {
332 psk,
333 psk_nonce: psk_nonce.into(),
334 }
335 }
336
337 pub fn psk(&self) -> &Psk {
339 &self.psk
340 }
341
342 pub fn psk_nonce(&self) -> &[u8] {
344 self.psk_nonce.as_slice()
345 }
346
347 pub fn store<Provider: OpenMlsProvider>(
353 &self,
354 provider: &Provider,
355 psk: &[u8],
356 ) -> Result<(), PskError> {
357 let psk_bundle = {
358 let secret = Secret::from_slice(psk);
359
360 PskBundle { secret }
361 };
362
363 provider
364 .storage()
365 .write_psk(&self.psk, &psk_bundle)
366 .map_err(|_| PskError::Storage)
367 }
368
369 pub(crate) fn validate_in_proposal(self, ciphersuite: Ciphersuite) -> Result<(), PskError> {
372 match self.psk() {
374 Psk::Resumption(resumption_psk) => {
375 if resumption_psk.usage != ResumptionPskUsage::Application {
378 return Err(PskError::UsageMismatch {
379 allowed: vec![ResumptionPskUsage::Application],
380 got: resumption_psk.usage,
381 });
382 }
383 }
384 Psk::External(_) => {}
385 #[cfg(feature = "extensions-draft")]
386 Psk::Application(_) => {}
387 };
388
389 {
392 let expected_nonce_length = ciphersuite.hash_length();
393 let got_nonce_length = self.psk_nonce().len();
394
395 if expected_nonce_length != got_nonce_length {
396 return Err(PskError::NonceLengthMismatch {
397 expected: expected_nonce_length,
398 got: got_nonce_length,
399 });
400 }
401 }
402
403 Ok(())
404 }
405
406 pub(crate) fn validate_in_welcome(
407 psk_ids: &[PreSharedKeyId],
408 ciphersuite: Ciphersuite,
409 ) -> Result<(), PskError> {
410 let mut contains_branch_psk = false;
411 let mut contains_reinit_psk = false;
412 for id in psk_ids {
413 match id.psk() {
415 Psk::Resumption(resumption_psk) => match resumption_psk.usage {
416 ResumptionPskUsage::Application => {
417 return Err(PskError::UsageMismatch {
418 allowed: vec![ResumptionPskUsage::Reinit, ResumptionPskUsage::Branch],
419 got: resumption_psk.usage,
420 });
421 }
422 ResumptionPskUsage::Reinit => {
423 if contains_reinit_psk {
424 return Err(PskError::UsageDuplicate {
425 usage: ResumptionPskUsage::Reinit,
426 });
427 }
428 if contains_branch_psk {
429 return Err(PskError::UsageConflict {
430 first: ResumptionPskUsage::Reinit,
431 second: ResumptionPskUsage::Branch,
432 });
433 }
434 contains_reinit_psk = true;
435 }
436 ResumptionPskUsage::Branch => {
437 if contains_branch_psk {
438 return Err(PskError::UsageDuplicate {
439 usage: ResumptionPskUsage::Branch,
440 });
441 }
442 if contains_reinit_psk {
443 return Err(PskError::UsageConflict {
444 first: ResumptionPskUsage::Branch,
445 second: ResumptionPskUsage::Reinit,
446 });
447 }
448 contains_branch_psk = true;
449 }
450 },
451 Psk::External(_) => {}
452 #[cfg(feature = "extensions-draft")]
453 Psk::Application(_) => {}
454 };
455
456 {
457 let expected_nonce_length = ciphersuite.hash_length();
458 let got_nonce_length = id.psk_nonce().len();
459
460 if expected_nonce_length != got_nonce_length {
461 return Err(PskError::NonceLengthMismatch {
462 expected: expected_nonce_length,
463 got: got_nonce_length,
464 });
465 }
466 }
467 }
468 Ok(())
469 }
470}
471
472#[cfg(test)]
473impl PreSharedKeyId {
474 pub(crate) fn new_with_nonce(psk: Psk, psk_nonce: Vec<u8>) -> Self {
475 Self {
476 psk,
477 psk_nonce: psk_nonce.into(),
478 }
479 }
480}
481
482#[derive(TlsSerialize, TlsSize)]
494pub(crate) struct PskLabel<'a> {
495 pub(crate) id: &'a PreSharedKeyId,
496 pub(crate) index: u16,
497 pub(crate) count: u16,
498}
499
500impl<'a> PskLabel<'a> {
501 fn new(id: &'a PreSharedKeyId, index: u16, count: u16) -> Self {
503 Self { id, index, count }
504 }
505}
506
507#[derive(Clone)]
510pub struct PskSecret {
511 secret: Secret,
512}
513
514impl PskSecret {
515 pub(crate) fn new(
526 crypto: &impl OpenMlsCrypto,
527 ciphersuite: Ciphersuite,
528 psks: Vec<(impl Borrow<PreSharedKeyId>, Secret)>,
529 ) -> Result<Self, PskError> {
530 let num_psks = u16::try_from(psks.len()).map_err(|_| PskError::TooManyKeys)?;
532
533 let mut psk_secret = Secret::zero(ciphersuite);
537
538 for (index, (psk_id, psk)) in psks.into_iter().enumerate() {
539 let psk_extracted = {
541 let zero_secret = Secret::zero(ciphersuite);
542 zero_secret
543 .hkdf_extract(crypto, ciphersuite, &psk)
544 .map_err(LibraryError::unexpected_crypto_error)?
545 };
546
547 let psk_input = {
549 let psk_label = PskLabel::new(psk_id.borrow(), index as u16, num_psks)
550 .tls_serialize_detached()
551 .map_err(LibraryError::missing_bound_check)?;
552
553 psk_extracted
554 .kdf_expand_label(
555 crypto,
556 ciphersuite,
557 "derived psk",
558 &psk_label,
559 ciphersuite.hash_length(),
560 )
561 .map_err(LibraryError::unexpected_crypto_error)?
562 };
563
564 psk_secret = psk_input
566 .hkdf_extract(crypto, ciphersuite, &psk_secret)
567 .map_err(LibraryError::unexpected_crypto_error)?;
568 }
569
570 Ok(Self { secret: psk_secret })
571 }
572
573 pub(crate) fn secret(&self) -> &Secret {
575 &self.secret
576 }
577
578 #[cfg(any(feature = "test-utils", feature = "crypto-debug", test))]
579 pub(crate) fn as_slice(&self) -> &[u8] {
580 self.secret.as_slice()
581 }
582}
583
584#[cfg(any(feature = "test-utils", test))]
585impl From<Secret> for PskSecret {
586 fn from(secret: Secret) -> Self {
587 Self { secret }
588 }
589}
590
591pub(crate) fn load_psks<'p, Storage: StorageProvider>(
592 storage: &Storage,
593 resumption_psk_store: &ResumptionPskStore,
594 psk_ids: &'p [PreSharedKeyId],
595) -> Result<Vec<(&'p PreSharedKeyId, Secret)>, PskError> {
596 let mut psk_bundles = Vec::new();
597
598 for psk_id in psk_ids.iter() {
599 log_crypto!(trace, "PSK store {:?}", resumption_psk_store);
600
601 match &psk_id.psk {
602 Psk::Resumption(resumption) => {
603 if let Some(psk_bundle) = resumption_psk_store.get(resumption.psk_epoch()) {
604 psk_bundles.push((psk_id, psk_bundle.secret.clone()));
605 } else {
606 return Err(PskError::KeyNotFound);
607 }
608 }
609 Psk::External(_) => {
610 let psk_bundle: Option<PskBundle> = storage
611 .psk(psk_id.psk())
612 .map_err(|_| PskError::KeyNotFound)?;
613 if let Some(psk_bundle) = psk_bundle {
614 psk_bundles.push((psk_id, psk_bundle.secret));
615 } else {
616 return Err(PskError::KeyNotFound);
617 }
618 }
619 #[cfg(feature = "extensions-draft")]
620 Psk::Application(_) => {
621 let psk_bundle: Option<PskBundle> = storage
622 .psk(psk_id.psk())
623 .map_err(|_| PskError::KeyNotFound)?;
624 if let Some(psk_bundle) = psk_bundle {
625 psk_bundles.push((psk_id, psk_bundle.secret));
626 } else {
627 return Err(PskError::KeyNotFound);
628 }
629 }
630 }
631 }
632
633 Ok(psk_bundles)
634}
635
636pub mod store {
638 use serde::{Deserialize, Serialize};
639
640 use crate::{group::GroupEpoch, schedule::ResumptionPskSecret};
641
642 #[derive(Debug, Serialize, Deserialize)]
646 #[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
647 pub(crate) struct ResumptionPskStore {
648 max_number_of_secrets: usize,
649 resumption_psk: Vec<(GroupEpoch, ResumptionPskSecret)>,
650 cursor: usize,
651 }
652
653 impl ResumptionPskStore {
654 pub(crate) fn new(max_number_of_secrets: usize) -> Self {
656 Self {
657 max_number_of_secrets,
658 resumption_psk: vec![],
659 cursor: 0,
660 }
661 }
662
663 pub(crate) fn add(&mut self, epoch: GroupEpoch, resumption_psk: ResumptionPskSecret) {
665 if self.max_number_of_secrets == 0 {
666 return;
667 }
668 let item = (epoch, resumption_psk);
669 if self.resumption_psk.len() < self.max_number_of_secrets {
670 self.resumption_psk.push(item);
671 self.cursor += 1;
672 } else {
673 self.cursor += 1;
674 self.cursor %= self.resumption_psk.len();
675 self.resumption_psk[self.cursor] = item;
676 }
677 }
678
679 pub(crate) fn get(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
682 self.resumption_psk
683 .iter()
684 .find(|&(e, _s)| e == &epoch)
685 .map(|(_e, s)| s)
686 }
687 }
688
689 #[cfg(test)]
690 impl ResumptionPskStore {
691 pub(crate) fn cursor(&self) -> usize {
692 self.cursor
693 }
694 }
695}