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<Self, PskError> {
372 match self.psk() {
374 Psk::Resumption(resumption_psk) => {
375 match resumption_psk.usage {
378 ResumptionPskUsage::Application => {}
379 ResumptionPskUsage::Reinit => {
380 return Err(PskError::UsageMismatch {
381 allowed: vec![ResumptionPskUsage::Application],
382 got: resumption_psk.usage,
383 });
384 }
385 ResumptionPskUsage::Branch => {
386 }
390 }
391 }
392 Psk::External(_) => {}
393 #[cfg(feature = "extensions-draft")]
394 Psk::Application(_) => {}
395 };
396
397 {
400 let expected_nonce_length = ciphersuite.hash_length();
401 let got_nonce_length = self.psk_nonce().len();
402
403 if expected_nonce_length != got_nonce_length {
404 return Err(PskError::NonceLengthMismatch {
405 expected: expected_nonce_length,
406 got: got_nonce_length,
407 });
408 }
409 }
410
411 Ok(self)
412 }
413
414 pub(crate) fn validate_in_welcome(
415 psk_ids: &[PreSharedKeyId],
416 ciphersuite: Ciphersuite,
417 ) -> Result<(), PskError> {
418 let mut contains_branch_psk = false;
419 let mut contains_reinit_psk = false;
420 for id in psk_ids {
421 match id.psk() {
423 Psk::Resumption(resumption_psk) => match resumption_psk.usage {
424 ResumptionPskUsage::Application => {
425 return Err(PskError::UsageMismatch {
426 allowed: vec![ResumptionPskUsage::Reinit, ResumptionPskUsage::Branch],
427 got: resumption_psk.usage,
428 });
429 }
430 ResumptionPskUsage::Reinit => {
431 if contains_reinit_psk {
432 return Err(PskError::UsageDuplicate {
433 usage: ResumptionPskUsage::Reinit,
434 });
435 }
436 if contains_branch_psk {
437 return Err(PskError::UsageConflict {
438 first: ResumptionPskUsage::Reinit,
439 second: ResumptionPskUsage::Branch,
440 });
441 }
442 contains_reinit_psk = true;
443 }
444 ResumptionPskUsage::Branch => {
445 if contains_branch_psk {
446 return Err(PskError::UsageDuplicate {
447 usage: ResumptionPskUsage::Branch,
448 });
449 }
450 if contains_reinit_psk {
451 return Err(PskError::UsageConflict {
452 first: ResumptionPskUsage::Branch,
453 second: ResumptionPskUsage::Reinit,
454 });
455 }
456 contains_branch_psk = true;
457 }
458 },
459 Psk::External(_) => {}
460 #[cfg(feature = "extensions-draft")]
461 Psk::Application(_) => {}
462 };
463
464 {
465 let expected_nonce_length = ciphersuite.hash_length();
466 let got_nonce_length = id.psk_nonce().len();
467
468 if expected_nonce_length != got_nonce_length {
469 return Err(PskError::NonceLengthMismatch {
470 expected: expected_nonce_length,
471 got: got_nonce_length,
472 });
473 }
474 }
475 }
476 Ok(())
477 }
478}
479
480#[cfg(test)]
481impl PreSharedKeyId {
482 pub(crate) fn new_with_nonce(psk: Psk, psk_nonce: Vec<u8>) -> Self {
483 Self {
484 psk,
485 psk_nonce: psk_nonce.into(),
486 }
487 }
488}
489
490#[derive(TlsSerialize, TlsSize)]
502pub(crate) struct PskLabel<'a> {
503 pub(crate) id: &'a PreSharedKeyId,
504 pub(crate) index: u16,
505 pub(crate) count: u16,
506}
507
508impl<'a> PskLabel<'a> {
509 fn new(id: &'a PreSharedKeyId, index: u16, count: u16) -> Self {
511 Self { id, index, count }
512 }
513}
514
515#[derive(Clone)]
518pub struct PskSecret {
519 secret: Secret,
520}
521
522impl PskSecret {
523 pub(crate) fn new(
534 crypto: &impl OpenMlsCrypto,
535 ciphersuite: Ciphersuite,
536 psks: Vec<(impl Borrow<PreSharedKeyId>, Secret)>,
537 ) -> Result<Self, PskError> {
538 let num_psks = u16::try_from(psks.len()).map_err(|_| PskError::TooManyKeys)?;
540
541 let mut psk_secret = Secret::zero(ciphersuite);
545
546 for (index, (psk_id, psk)) in psks.into_iter().enumerate() {
547 let psk_extracted = {
549 let zero_secret = Secret::zero(ciphersuite);
550 zero_secret
551 .hkdf_extract(crypto, ciphersuite, &psk)
552 .map_err(LibraryError::unexpected_crypto_error)?
553 };
554
555 let psk_input = {
557 let psk_label = PskLabel::new(psk_id.borrow(), index as u16, num_psks)
558 .tls_serialize_detached()
559 .map_err(LibraryError::missing_bound_check)?;
560
561 psk_extracted
562 .kdf_expand_label(
563 crypto,
564 ciphersuite,
565 "derived psk",
566 &psk_label,
567 ciphersuite.hash_length(),
568 )
569 .map_err(LibraryError::unexpected_crypto_error)?
570 };
571
572 psk_secret = psk_input
574 .hkdf_extract(crypto, ciphersuite, &psk_secret)
575 .map_err(LibraryError::unexpected_crypto_error)?;
576 }
577
578 Ok(Self { secret: psk_secret })
579 }
580
581 pub(crate) fn secret(&self) -> &Secret {
583 &self.secret
584 }
585
586 #[cfg(any(feature = "test-utils", feature = "crypto-debug", test))]
587 pub(crate) fn as_slice(&self) -> &[u8] {
588 self.secret.as_slice()
589 }
590}
591
592#[cfg(any(feature = "test-utils", test))]
593impl From<Secret> for PskSecret {
594 fn from(secret: Secret) -> Self {
595 Self { secret }
596 }
597}
598
599pub(crate) fn load_psks<'p, Storage: StorageProvider>(
601 storage: &Storage,
602 resumption_psk_store: &ResumptionPskStore,
603 psk_ids: &'p [PreSharedKeyId],
604) -> Result<Vec<(&'p PreSharedKeyId, Secret)>, PskError> {
605 let mut psk_bundles = Vec::new();
606
607 for psk_id in psk_ids.iter() {
608 log_crypto!(trace, "PSK store {:?}", resumption_psk_store);
609
610 match &psk_id.psk {
611 Psk::Resumption(resumption) => {
612 let psk_epoch = match resumption.usage() {
613 ResumptionPskUsage::Application | ResumptionPskUsage::Reinit => {
615 resumption.psk_epoch()
616 }
617 ResumptionPskUsage::Branch => 0.into(),
622 };
623 if let Some(psk_bundle) = resumption_psk_store.get(psk_epoch) {
624 psk_bundles.push((psk_id, psk_bundle.secret.clone()));
625 } else {
626 return Err(PskError::KeyNotFound);
627 }
628 }
629 Psk::External(_) => {
630 let psk_bundle: Option<PskBundle> = storage
631 .psk(psk_id.psk())
632 .map_err(|_| PskError::KeyNotFound)?;
633 if let Some(psk_bundle) = psk_bundle {
634 psk_bundles.push((psk_id, psk_bundle.secret));
635 } else {
636 return Err(PskError::KeyNotFound);
637 }
638 }
639 #[cfg(feature = "extensions-draft")]
640 Psk::Application(_) => {
641 let psk_bundle: Option<PskBundle> = storage
642 .psk(psk_id.psk())
643 .map_err(|_| PskError::KeyNotFound)?;
644 if let Some(psk_bundle) = psk_bundle {
645 psk_bundles.push((psk_id, psk_bundle.secret));
646 } else {
647 return Err(PskError::KeyNotFound);
648 }
649 }
650 }
651 }
652
653 Ok(psk_bundles)
654}
655
656pub mod store {
658 use serde::{Deserialize, Serialize};
659
660 use crate::{group::GroupEpoch, schedule::ResumptionPskSecret};
661
662 #[derive(Debug, Serialize, Deserialize)]
666 #[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
667 pub(crate) struct ResumptionPskStore {
668 max_number_of_secrets: usize,
669 resumption_psk: Vec<(GroupEpoch, ResumptionPskSecret)>,
670 cursor: usize,
671 }
672
673 impl ResumptionPskStore {
674 pub(crate) fn new(max_number_of_secrets: usize) -> Self {
676 Self {
677 max_number_of_secrets,
678 resumption_psk: vec![],
679 cursor: 0,
680 }
681 }
682
683 pub(crate) fn clear(&mut self) {
685 self.resumption_psk = vec![];
686 self.cursor = 0;
687 }
688
689 pub(crate) fn add(&mut self, epoch: GroupEpoch, resumption_psk: ResumptionPskSecret) {
691 if self.max_number_of_secrets == 0 {
692 return;
693 }
694 let item = (epoch, resumption_psk);
695 if self.resumption_psk.len() < self.max_number_of_secrets {
696 self.resumption_psk.push(item);
697 self.cursor += 1;
698 } else {
699 self.cursor += 1;
700 self.cursor %= self.resumption_psk.len();
701 self.resumption_psk[self.cursor] = item;
702 }
703 }
704
705 pub(crate) fn get(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
708 self.resumption_psk
709 .iter()
710 .find(|&(e, _s)| e == &epoch)
711 .map(|(_e, s)| s)
712 }
713 }
714
715 #[cfg(test)]
716 impl ResumptionPskStore {
717 pub(crate) fn cursor(&self) -> usize {
718 self.cursor
719 }
720 }
721}