1
- use crate :: kem:: Kem as KemTrait ;
1
+ use crate :: { kem:: Kem as KemTrait , HpkeError } ;
2
2
3
3
/// Contains preshared key bytes and an identifier. This is intended to go inside an `OpModeR` or
4
4
/// `OpModeS` struct.
5
- ///
6
- /// Requirements
7
- /// ============
8
- /// `psk` MUST contain at least 32 bytes of entropy. Further, `psk.len()` SHOULD be at least as
9
- /// long as an extracted key from the KDF you use with `setup_sender`/`setup_receiver`, i.e., at
10
- /// least `Kdf::extracted_key_size()`.
11
5
#[ derive( Clone , Copy ) ]
12
6
pub struct PskBundle < ' a > {
13
7
/// The preshared key
14
- pub psk : & ' a [ u8 ] ,
8
+ psk : & ' a [ u8 ] ,
15
9
/// A bytestring that uniquely identifies this PSK
16
- pub psk_id : & ' a [ u8 ] ,
10
+ psk_id : & ' a [ u8 ] ,
11
+ }
12
+
13
+ impl < ' a > PskBundle < ' a > {
14
+ /// Creates a new preshared key bundle from the given preshared key and its ID
15
+ ///
16
+ /// Errors
17
+ /// ======
18
+ /// `psk` and `psk_id` must either both be empty or both be nonempty. If one is empty while
19
+ /// the other is not, then this returns [`HpkeError::InvalidPskBundle`].
20
+ ///
21
+ /// Other requirements
22
+ /// ==================
23
+ /// Other requirements from the HPKE spec: `psk` MUST contain at least 32 bytes of entropy.
24
+ /// Further, `psk.len()` SHOULD be at least as long as an extracted key from the KDF you use
25
+ /// with `setup_sender`/`setup_receiver`, i.e., at least `Kdf::extracted_key_size()`.
26
+ pub fn new ( psk : & ' a [ u8 ] , psk_id : & ' a [ u8 ] ) -> Result < Self , HpkeError > {
27
+ // RFC 9180 §5.1: The psk and psk_id fields MUST appear together or not at all
28
+ if ( psk. is_empty ( ) && psk_id. is_empty ( ) ) || ( !psk. is_empty ( ) && !psk_id. is_empty ( ) ) {
29
+ Ok ( PskBundle { psk, psk_id } )
30
+ } else {
31
+ Err ( HpkeError :: InvalidPskBundle )
32
+ }
33
+ }
17
34
}
18
35
19
36
/// The operation mode of the HPKE session (receiver's view). This is how the sender authenticates
@@ -23,7 +40,8 @@ pub struct PskBundle<'a> {
23
40
pub enum OpModeR < ' a , Kem : KemTrait > {
24
41
/// No extra information included
25
42
Base ,
26
- /// A preshared key known to the sender and receiver
43
+ /// A preshared key known to the sender and receiver. If the bundle contents is empty strings,
44
+ /// then this is equivalent to `Base`.
27
45
Psk ( PskBundle < ' a > ) ,
28
46
/// The identity public key of the sender
29
47
Auth ( Kem :: PublicKey ) ,
@@ -50,7 +68,8 @@ impl<Kem: KemTrait> OpModeR<'_, Kem> {
50
68
pub enum OpModeS < ' a , Kem : KemTrait > {
51
69
/// No extra information included
52
70
Base ,
53
- /// A preshared key known to the sender and receiver
71
+ /// A preshared key known to the sender and receiver. If the bundle contents is empty strings,
72
+ /// then this is equivalent to `Base`.
54
73
Psk ( PskBundle < ' a > ) ,
55
74
/// The identity keypair of the sender
56
75
Auth ( ( Kem :: PrivateKey , Kem :: PublicKey ) ) ,
@@ -148,3 +167,12 @@ impl<Kem: KemTrait> OpMode<Kem> for OpModeS<'_, Kem> {
148
167
}
149
168
}
150
169
}
170
+
171
+ // Test that you can only make a PskBundle if both fields are empty or both fields are nonempty
172
+ #[ test]
173
+ fn psk_bundle_validation ( ) {
174
+ assert ! ( PskBundle :: new( b"hello" , b"world" ) . is_ok( ) ) ;
175
+ assert ! ( PskBundle :: new( b"" , b"" ) . is_ok( ) ) ;
176
+ assert ! ( PskBundle :: new( b"hello" , b"" ) . is_err( ) ) ;
177
+ assert ! ( PskBundle :: new( b"" , b"world" ) . is_err( ) ) ;
178
+ }
0 commit comments