@@ -9,6 +9,7 @@ use crate::{
9
9
hash:: Hash ,
10
10
key_exchange:: traits:: { KeyExchange , ToBytes } ,
11
11
keypair:: { KeyPair , SizedBytes } ,
12
+ serialization:: serialize,
12
13
} ;
13
14
use digest:: { Digest , FixedOutput } ;
14
15
use generic_array:: {
@@ -27,6 +28,11 @@ pub(crate) type NonceLen = U32;
27
28
const KE1_STATE_LEN : usize = KEY_LEN + KEY_LEN + NONCE_LEN ;
28
29
29
30
static STR_3DH : & [ u8 ] = b"3DH keys" ;
31
+ static STR_CLIENT_MAC : & [ u8 ] = b"client mac" ;
32
+ static STR_HANDSHAKE_SECRET : & [ u8 ] = b"handshake secret" ;
33
+ static STR_SERVER_MAC : & [ u8 ] = b"server mac" ;
34
+ static STR_SESSION_SECRET : & [ u8 ] = b"session secret" ;
35
+ static STR_OPAQUE : & [ u8 ] = b"OPAQUE " ;
30
36
31
37
/// The Triple Diffie-Hellman key exchange implementation
32
38
pub struct TripleDH ;
@@ -84,7 +90,7 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
84
90
server_nonce_bytes. into ( )
85
91
} ;
86
92
87
- let ( shared_secret , km2, km3) = derive_3dh_keys :: < KeyFormat , D > (
93
+ let ( session_secret , km2, km3) = derive_3dh_keys :: < KeyFormat , D > (
88
94
TripleDHComponents {
89
95
pk1 : ke1_message. client_e_pk . clone ( ) ,
90
96
sk1 : server_e_kp. private ( ) . clone ( ) ,
@@ -122,7 +128,7 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
122
128
KE2State {
123
129
km3,
124
130
hashed_transcript,
125
- shared_secret ,
131
+ session_secret ,
126
132
} ,
127
133
KE2Message {
128
134
server_nonce,
@@ -139,7 +145,7 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
139
145
server_s_pk : KeyFormat :: Repr ,
140
146
client_s_sk : KeyFormat :: Repr ,
141
147
) -> Result < ( Vec < u8 > , Self :: KE3Message ) , ProtocolError > {
142
- let ( shared_secret , km2, km3) = derive_3dh_keys :: < KeyFormat , D > (
148
+ let ( session_secret , km2, km3) = derive_3dh_keys :: < KeyFormat , D > (
143
149
TripleDHComponents {
144
150
pk1 : ke2_message. server_e_pk . clone ( ) ,
145
151
sk1 : ke1_state. client_e_sk . clone ( ) ,
@@ -181,7 +187,7 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
181
187
client_mac. update ( & hashed_transcript) ;
182
188
183
189
Ok ( (
184
- shared_secret . to_vec ( ) ,
190
+ session_secret . to_vec ( ) ,
185
191
KE3Message {
186
192
mac : client_mac. finalize ( ) . into_bytes ( ) ,
187
193
} ,
@@ -202,7 +208,7 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
202
208
) ) ;
203
209
}
204
210
205
- Ok ( ke2_state. shared_secret . to_vec ( ) )
211
+ Ok ( ke2_state. session_secret . to_vec ( ) )
206
212
}
207
213
208
214
fn ke1_state_size ( ) -> usize {
@@ -285,7 +291,7 @@ impl<KeyFormat: KeyPair> TryFrom<&[u8]> for KE1Message<KeyFormat> {
285
291
pub struct KE2State < HashLen : ArrayLength < u8 > > {
286
292
km3 : GenericArray < u8 , HashLen > ,
287
293
hashed_transcript : GenericArray < u8 , HashLen > ,
288
- shared_secret : GenericArray < u8 , HashLen > ,
294
+ session_secret : GenericArray < u8 , HashLen > ,
289
295
}
290
296
291
297
/// The second key exchange message
@@ -300,7 +306,7 @@ impl<HashLen: ArrayLength<u8>> ToBytes for KE2State<HashLen> {
300
306
let output: Vec < u8 > = [
301
307
& self . km3 [ ..] ,
302
308
& self . hashed_transcript [ ..] ,
303
- & self . shared_secret [ ..] ,
309
+ & self . session_secret [ ..] ,
304
310
]
305
311
. concat ( ) ;
306
312
output
@@ -316,7 +322,7 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2State<HashLen> {
316
322
Ok ( Self {
317
323
km3 : GenericArray :: clone_from_slice ( & checked_bytes[ ..KEY_LEN ] ) ,
318
324
hashed_transcript : GenericArray :: clone_from_slice ( & checked_bytes[ KEY_LEN ..2 * KEY_LEN ] ) ,
319
- shared_secret : GenericArray :: clone_from_slice ( & checked_bytes[ 2 * KEY_LEN ..] ) ,
325
+ session_secret : GenericArray :: clone_from_slice ( & checked_bytes[ 2 * KEY_LEN ..] ) ,
320
326
} )
321
327
}
322
328
}
@@ -362,13 +368,38 @@ struct TripleDHComponents<KeyFormat: KeyPair> {
362
368
sk3 : KeyFormat :: Repr ,
363
369
}
364
370
365
- // Consists of a shared secret, followed by two mac keys
371
+ // Consists of a shared secret, followed by two mac keys: (session_secret, km2, km3)
366
372
type TripleDHDerivationResult < D > = (
367
373
GenericArray < u8 , <D as FixedOutput >:: OutputSize > ,
368
374
GenericArray < u8 , <D as FixedOutput >:: OutputSize > ,
369
375
GenericArray < u8 , <D as FixedOutput >:: OutputSize > ,
370
376
) ;
371
377
378
+ /// The third key exchange message
379
+ pub struct KE3Message < HashLen : ArrayLength < u8 > > {
380
+ mac : GenericArray < u8 , HashLen > ,
381
+ }
382
+
383
+ impl < HashLen : ArrayLength < u8 > > ToBytes for KE3Message < HashLen > {
384
+ fn to_bytes ( & self ) -> Vec < u8 > {
385
+ self . mac . to_vec ( )
386
+ }
387
+ }
388
+
389
+ impl < HashLen : ArrayLength < u8 > > TryFrom < & [ u8 ] > for KE3Message < HashLen > {
390
+ type Error = InternalPakeError ;
391
+
392
+ fn try_from ( bytes : & [ u8 ] ) -> Result < Self , Self :: Error > {
393
+ let checked_bytes = check_slice_size ( bytes, KEY_LEN , "ke3_message" ) ?;
394
+
395
+ Ok ( Self {
396
+ mac : GenericArray :: clone_from_slice ( & checked_bytes) ,
397
+ } )
398
+ }
399
+ }
400
+
401
+ // Helper functions
402
+
372
403
// Internal function which takes the public and private components of the client and server keypairs, along
373
404
// with some auxiliary metadata, to produce the shared secret and two MAC keys
374
405
fn derive_3dh_keys < KeyFormat : KeyPair , D : Hash > (
@@ -387,44 +418,79 @@ fn derive_3dh_keys<KeyFormat: KeyPair, D: Hash>(
387
418
388
419
let info: Vec < u8 > = [
389
420
STR_3DH ,
390
- & client_nonce,
391
- & server_nonce,
392
- & client_s_pk. to_arr ( ) ,
393
- & server_s_pk. to_arr ( ) ,
421
+ & serialize ( & client_nonce, 2 ) ,
422
+ & serialize ( & server_nonce, 2 ) ,
423
+ & serialize ( & client_s_pk. to_arr ( ) , 2 ) ,
424
+ & serialize ( & server_s_pk. to_arr ( ) , 2 ) ,
394
425
]
395
426
. concat ( ) ;
396
427
397
- const OUTPUT_SIZE : usize = 32 ;
398
- let mut okm = [ 0u8 ; 3 * OUTPUT_SIZE ] ;
399
- let h = Hkdf :: < D > :: new ( None , & ikm) ;
400
- h. expand ( & info, & mut okm)
401
- . map_err ( |_| InternalPakeError :: HkdfError ) ?;
428
+ let extracted_ikm = Hkdf :: < D > :: new ( None , & ikm) ;
429
+ let handshake_secret = derive_secrets :: < D > ( & extracted_ikm, & STR_HANDSHAKE_SECRET , & info) ?;
430
+ let session_secret = derive_secrets :: < D > ( & extracted_ikm, & STR_SESSION_SECRET , & info) ?;
431
+ let km2 = hkdf_expand_label :: < D > (
432
+ & handshake_secret,
433
+ & STR_SERVER_MAC ,
434
+ b"" ,
435
+ <D as Digest >:: OutputSize :: to_usize ( ) ,
436
+ ) ?;
437
+ let km3 = hkdf_expand_label :: < D > (
438
+ & handshake_secret,
439
+ & STR_CLIENT_MAC ,
440
+ b"" ,
441
+ <D as Digest >:: OutputSize :: to_usize ( ) ,
442
+ ) ?;
443
+
402
444
Ok ( (
403
- GenericArray :: clone_from_slice ( & okm [ .. OUTPUT_SIZE ] ) ,
404
- GenericArray :: clone_from_slice ( & okm [ OUTPUT_SIZE .. 2 * OUTPUT_SIZE ] ) ,
405
- GenericArray :: clone_from_slice ( & okm [ 2 * OUTPUT_SIZE .. ] ) ,
445
+ GenericArray :: clone_from_slice ( & session_secret ) ,
446
+ GenericArray :: clone_from_slice ( & km2 ) ,
447
+ GenericArray :: clone_from_slice ( & km3 ) ,
406
448
) )
407
449
}
408
450
409
- /// The third key exchange message
410
- pub struct KE3Message < HashLen : ArrayLength < u8 > > {
411
- mac : GenericArray < u8 , HashLen > ,
451
+ fn hkdf_expand_label < D : Hash > (
452
+ secret : & [ u8 ] ,
453
+ label : & [ u8 ] ,
454
+ context : & [ u8 ] ,
455
+ length : usize ,
456
+ ) -> Result < Vec < u8 > , ProtocolError > {
457
+ let h = Hkdf :: < D > :: new ( None , secret) ;
458
+ hkdf_expand_label_extracted ( & h, label, context, length)
412
459
}
413
460
414
- impl < HashLen : ArrayLength < u8 > > ToBytes for KE3Message < HashLen > {
415
- fn to_bytes ( & self ) -> Vec < u8 > {
416
- self . mac . to_vec ( )
417
- }
418
- }
461
+ fn hkdf_expand_label_extracted < D : Hash > (
462
+ hkdf : & Hkdf < D > ,
463
+ label : & [ u8 ] ,
464
+ context : & [ u8 ] ,
465
+ length : usize ,
466
+ ) -> Result < Vec < u8 > , ProtocolError > {
467
+ let mut okm = vec ! [ 0u8 ; length] ;
419
468
420
- impl < HashLen : ArrayLength < u8 > > TryFrom < & [ u8 ] > for KE3Message < HashLen > {
421
- type Error = InternalPakeError ;
469
+ let mut hkdf_label : Vec < u8 > = Vec :: new ( ) ;
470
+ hkdf_label . extend_from_slice ( & length . to_be_bytes ( ) [ 6 .. ] ) ;
422
471
423
- fn try_from ( bytes : & [ u8 ] ) -> Result < Self , Self :: Error > {
424
- let checked_bytes = check_slice_size ( bytes, KEY_LEN , "ke3_message" ) ?;
472
+ let mut opaque_label: Vec < u8 > = Vec :: new ( ) ;
473
+ opaque_label. extend_from_slice ( & STR_OPAQUE ) ;
474
+ opaque_label. extend_from_slice ( & label) ;
475
+ hkdf_label. extend_from_slice ( & serialize ( & opaque_label, 1 ) ) ;
425
476
426
- Ok ( Self {
427
- mac : GenericArray :: clone_from_slice ( & checked_bytes) ,
428
- } )
429
- }
477
+ hkdf_label. extend_from_slice ( & serialize ( & context, 1 ) ) ;
478
+
479
+ hkdf. expand ( & hkdf_label, & mut okm)
480
+ . map_err ( |_| InternalPakeError :: HkdfError ) ?;
481
+ Ok ( okm)
482
+ }
483
+
484
+ fn derive_secrets < D : Hash > (
485
+ hkdf : & Hkdf < D > ,
486
+ label : & [ u8 ] ,
487
+ transcript : & [ u8 ] ,
488
+ ) -> Result < Vec < u8 > , ProtocolError > {
489
+ let hashed_transcript = D :: digest ( transcript) ;
490
+ hkdf_expand_label_extracted :: < D > (
491
+ hkdf,
492
+ label,
493
+ & hashed_transcript,
494
+ <D as Digest >:: OutputSize :: to_usize ( ) ,
495
+ )
430
496
}
0 commit comments