diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index dab106b..3ab08b5 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -9,6 +9,7 @@ use crate::{ hash::Hash, key_exchange::traits::{KeyExchange, ToBytes}, keypair::{KeyPair, SizedBytes}, + serialization::serialize, }; use digest::{Digest, FixedOutput}; use generic_array::{ @@ -27,6 +28,11 @@ pub(crate) type NonceLen = U32; const KE1_STATE_LEN: usize = KEY_LEN + KEY_LEN + NONCE_LEN; static STR_3DH: &[u8] = b"3DH keys"; +static STR_CLIENT_MAC: &[u8] = b"client mac"; +static STR_HANDSHAKE_SECRET: &[u8] = b"handshake secret"; +static STR_SERVER_MAC: &[u8] = b"server mac"; +static STR_SESSION_SECRET: &[u8] = b"session secret"; +static STR_OPAQUE: &[u8] = b"OPAQUE "; /// The Triple Diffie-Hellman key exchange implementation pub struct TripleDH; @@ -84,7 +90,7 @@ impl KeyExchange for TripleDH { server_nonce_bytes.into() }; - let (shared_secret, km2, km3) = derive_3dh_keys::( + let (session_secret, km2, km3) = derive_3dh_keys::( TripleDHComponents { pk1: ke1_message.client_e_pk.clone(), sk1: server_e_kp.private().clone(), @@ -122,7 +128,7 @@ impl KeyExchange for TripleDH { KE2State { km3, hashed_transcript, - shared_secret, + session_secret, }, KE2Message { server_nonce, @@ -139,7 +145,7 @@ impl KeyExchange for TripleDH { server_s_pk: KeyFormat::Repr, client_s_sk: KeyFormat::Repr, ) -> Result<(Vec, Self::KE3Message), ProtocolError> { - let (shared_secret, km2, km3) = derive_3dh_keys::( + let (session_secret, km2, km3) = derive_3dh_keys::( TripleDHComponents { pk1: ke2_message.server_e_pk.clone(), sk1: ke1_state.client_e_sk.clone(), @@ -181,7 +187,7 @@ impl KeyExchange for TripleDH { client_mac.update(&hashed_transcript); Ok(( - shared_secret.to_vec(), + session_secret.to_vec(), KE3Message { mac: client_mac.finalize().into_bytes(), }, @@ -202,7 +208,7 @@ impl KeyExchange for TripleDH { )); } - Ok(ke2_state.shared_secret.to_vec()) + Ok(ke2_state.session_secret.to_vec()) } fn ke1_state_size() -> usize { @@ -285,7 +291,7 @@ impl TryFrom<&[u8]> for KE1Message { pub struct KE2State> { km3: GenericArray, hashed_transcript: GenericArray, - shared_secret: GenericArray, + session_secret: GenericArray, } /// The second key exchange message @@ -300,7 +306,7 @@ impl> ToBytes for KE2State { let output: Vec = [ &self.km3[..], &self.hashed_transcript[..], - &self.shared_secret[..], + &self.session_secret[..], ] .concat(); output @@ -316,7 +322,7 @@ impl> TryFrom<&[u8]> for KE2State { Ok(Self { km3: GenericArray::clone_from_slice(&checked_bytes[..KEY_LEN]), hashed_transcript: GenericArray::clone_from_slice(&checked_bytes[KEY_LEN..2 * KEY_LEN]), - shared_secret: GenericArray::clone_from_slice(&checked_bytes[2 * KEY_LEN..]), + session_secret: GenericArray::clone_from_slice(&checked_bytes[2 * KEY_LEN..]), }) } } @@ -362,50 +368,13 @@ struct TripleDHComponents { sk3: KeyFormat::Repr, } -// Consists of a shared secret, followed by two mac keys +// Consists of a shared secret, followed by two mac keys: (session_secret, km2, km3) type TripleDHDerivationResult = ( GenericArray::OutputSize>, GenericArray::OutputSize>, GenericArray::OutputSize>, ); -// Internal function which takes the public and private components of the client and server keypairs, along -// with some auxiliary metadata, to produce the shared secret and two MAC keys -fn derive_3dh_keys( - dh: TripleDHComponents, - client_nonce: &GenericArray, - server_nonce: &GenericArray, - client_s_pk: KeyFormat::Repr, - server_s_pk: KeyFormat::Repr, -) -> Result, ProtocolError> { - let ikm: Vec = [ - &KeyFormat::diffie_hellman(dh.pk1, dh.sk1)[..], - &KeyFormat::diffie_hellman(dh.pk2, dh.sk2)[..], - &KeyFormat::diffie_hellman(dh.pk3, dh.sk3)[..], - ] - .concat(); - - let info: Vec = [ - STR_3DH, - &client_nonce, - &server_nonce, - &client_s_pk.to_arr(), - &server_s_pk.to_arr(), - ] - .concat(); - - const OUTPUT_SIZE: usize = 32; - let mut okm = [0u8; 3 * OUTPUT_SIZE]; - let h = Hkdf::::new(None, &ikm); - h.expand(&info, &mut okm) - .map_err(|_| InternalPakeError::HkdfError)?; - Ok(( - GenericArray::clone_from_slice(&okm[..OUTPUT_SIZE]), - GenericArray::clone_from_slice(&okm[OUTPUT_SIZE..2 * OUTPUT_SIZE]), - GenericArray::clone_from_slice(&okm[2 * OUTPUT_SIZE..]), - )) -} - /// The third key exchange message pub struct KE3Message> { mac: GenericArray, @@ -428,3 +397,100 @@ impl> TryFrom<&[u8]> for KE3Message { }) } } + +// Helper functions + +// Internal function which takes the public and private components of the client and server keypairs, along +// with some auxiliary metadata, to produce the shared secret and two MAC keys +fn derive_3dh_keys( + dh: TripleDHComponents, + client_nonce: &GenericArray, + server_nonce: &GenericArray, + client_s_pk: KeyFormat::Repr, + server_s_pk: KeyFormat::Repr, +) -> Result, ProtocolError> { + let ikm: Vec = [ + &KeyFormat::diffie_hellman(dh.pk1, dh.sk1)[..], + &KeyFormat::diffie_hellman(dh.pk2, dh.sk2)[..], + &KeyFormat::diffie_hellman(dh.pk3, dh.sk3)[..], + ] + .concat(); + + let info: Vec = [ + STR_3DH, + &serialize(&client_nonce, 2), + &serialize(&server_nonce, 2), + &serialize(&client_s_pk.to_arr(), 2), + &serialize(&server_s_pk.to_arr(), 2), + ] + .concat(); + + let extracted_ikm = Hkdf::::new(None, &ikm); + let handshake_secret = derive_secrets::(&extracted_ikm, &STR_HANDSHAKE_SECRET, &info)?; + let session_secret = derive_secrets::(&extracted_ikm, &STR_SESSION_SECRET, &info)?; + let km2 = hkdf_expand_label::( + &handshake_secret, + &STR_SERVER_MAC, + b"", + ::OutputSize::to_usize(), + )?; + let km3 = hkdf_expand_label::( + &handshake_secret, + &STR_CLIENT_MAC, + b"", + ::OutputSize::to_usize(), + )?; + + Ok(( + GenericArray::clone_from_slice(&session_secret), + GenericArray::clone_from_slice(&km2), + GenericArray::clone_from_slice(&km3), + )) +} + +fn hkdf_expand_label( + secret: &[u8], + label: &[u8], + context: &[u8], + length: usize, +) -> Result, ProtocolError> { + let h = Hkdf::::new(None, secret); + hkdf_expand_label_extracted(&h, label, context, length) +} + +fn hkdf_expand_label_extracted( + hkdf: &Hkdf, + label: &[u8], + context: &[u8], + length: usize, +) -> Result, ProtocolError> { + let mut okm = vec![0u8; length]; + + let mut hkdf_label: Vec = Vec::new(); + hkdf_label.extend_from_slice(&length.to_be_bytes()[6..]); + + let mut opaque_label: Vec = Vec::new(); + opaque_label.extend_from_slice(&STR_OPAQUE); + opaque_label.extend_from_slice(&label); + hkdf_label.extend_from_slice(&serialize(&opaque_label, 1)); + + hkdf_label.extend_from_slice(&serialize(&context, 1)); + + hkdf.expand(&hkdf_label, &mut okm) + .map_err(|_| InternalPakeError::HkdfError)?; + Ok(okm) +} + +fn derive_secrets( + hkdf: &Hkdf, + label: &[u8], + transcript: &[u8], +) -> Result, ProtocolError> { + let hashed_transcript = D::digest(transcript); + hkdf_expand_label_extracted::( + hkdf, + label, + &hashed_transcript, + ::OutputSize::to_usize(), + ) +} diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index 2c01aae..3275275 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -64,35 +64,35 @@ pub struct TestVectorParameters { static TEST_VECTOR: &str = r#" { - "client_s_pk": "6c3f4c134a1697cda1d494cef349e99117aaedecb6792c3aeab06fc1da5f463d", - "client_s_sk": "98f628dfaed00892809f1ff24f44eddb933ca593eeb88c532827249c3fdae56d", - "client_e_pk": "b20d78da45e7d91fb9185be968ba20c680bfb735e5dbdec36c2aefdb261d6903", - "client_e_sk": "88f058f4de21fc364603318edce30931a60fe78f7953722e3214203c02d52a79", - "server_s_pk": "511b18e0a82493d9b0d7988f5a7e2d05fe6886cb26e9813d476e3fe6d3ca1349", - "server_s_sk": "e00d089a96b19c74f77dcc6079277ba18b2dd1f603d0bc5c46664d4fe5c2867a", - "server_e_pk": "98e12670ffd22a9a827d74bfa5ac44c9f83cb52b477b1ab201a589a8fd643674", - "server_e_sk": "080f051060d51c9f1406e7cf5d9ac3212d8ba3829ca94fdd7c8f43e80fa8b278", + "client_s_pk": "8ede26558ada44c55f44a39b3e4811bb71533b52c8331799349f5bf579b0063b", + "client_s_sk": "e0937ce386f57ba61303f56851c6c1c338535ef30a046090c211fbae2b925575", + "client_e_pk": "b2c126715667420809d14b6056d9492051dc31c77cbb39cd61e9d76736118e46", + "client_e_sk": "481a41606adc0d8aa358cee44e36e5cb02f80d445e42551a90f15e1be59bf450", + "server_s_pk": "da106c3619e8c17c2356f4827b33f6f869ab6f99badc8002324925458fecbd0d", + "server_s_sk": "50cc8c336756b9c91badaa42df728c0a06033eca4770a7912f9f2924742f4356", + "server_e_pk": "1848442db55ef8301a28b078317be3c40590fc40b3a356f704ced8c552f59349", + "server_e_sk": "b8d7358f35d79cfa5e288e8ca8356bc806d4463710144767caf65eb9b4f77973", "password": "70617373776f7264", - "blinding_factor_raw": "3def40a264b30c15dfcafc8d52cbe309d2d08e015d516b515f17cf98443bb39d6d164e9bd391303d890697b38dc59ee7e9ec196682528a078d51983ab18c227b", - "blinding_factor": "d315d7e6c81d5398c015af3d55ded87f0979dfdb28b6f222ee8699905fd41e0a", + "blinding_factor_raw": "6e5823f7d820cf6996a2cac80e239f58a9d7e8fcbf9588c07dde69dff3c330785bb85a9b1269f079bbcf4b1f428fbc3a977c324120867b9e918c97dad44576f2", + "blinding_factor": "033a794ce47e3fb19206b8e1f2d74e54c874efcf5b5055d2a31d2164efd41a07", "pepper": "706570706572", - "oprf_key": "75e559c8cc7a81642e61b77651bc61b71d3eb7d8b6cd9ca13d8532cb684d5405", - "envelope_nonce": "3048ea382aa7357dbcd480047c884b717c4fea3ca627f2c498001b2a46e015da", - "client_nonce": "47b20166a022bb319ba9a09d5c328b6e2d6cc5813018f7dd5b53f380f914e79f", - "server_nonce": "1bf2cc47738524a83c995122a44c7832be18b48b606897d71fc942e8ec6d9b2e", - "r1": "01000024000000204df6277df3ee67e084af0d24e27a6567b75589dc887d5acbe5e5830dc8dc7eb0", - "r2": "0200002800208d04d78807ed18542f65707f964d96089a322a8bb06dad269cb7a4bee8c20b57000001010103", - "r3": "030000ae3048ea382aa7357dbcd480047c884b717c4fea3ca627f2c498001b2a46e015da0023469a6c15d59bc2b9cf07e4e37138292e58070a34eb76f8cb5e3015c542b8e96346482f0023030020511b18e0a82493d9b0d7988f5a7e2d05fe6886cb26e9813d476e3fe6d3ca13490020b3fc2eb3ffc0ddfa3b9d94a33874d297263cb768077f128e7ec798b03c5c7ba300206c3f4c134a1697cda1d494cef349e99117aaedecb6792c3aeab06fc1da5f463d", - "l1": "04000024000000204df6277df3ee67e084af0d24e27a6567b75589dc887d5acbe5e5830dc8dc7eb047b20166a022bb319ba9a09d5c328b6e2d6cc5813018f7dd5b53f380f914e79fb20d78da45e7d91fb9185be968ba20c680bfb735e5dbdec36c2aefdb261d6903", - "l2": "050000ae00208d04d78807ed18542f65707f964d96089a322a8bb06dad269cb7a4bee8c20b573048ea382aa7357dbcd480047c884b717c4fea3ca627f2c498001b2a46e015da0023469a6c15d59bc2b9cf07e4e37138292e58070a34eb76f8cb5e3015c542b8e96346482f0023030020511b18e0a82493d9b0d7988f5a7e2d05fe6886cb26e9813d476e3fe6d3ca13490020b3fc2eb3ffc0ddfa3b9d94a33874d297263cb768077f128e7ec798b03c5c7ba3080f051060d51c9f1406e7cf5d9ac3212d8ba3829ca94fdd7c8f43e80fa8b27898e12670ffd22a9a827d74bfa5ac44c9f83cb52b477b1ab201a589a8fd64367476b2f0458ade73668e3aaab6e1cd7d48dacc702b9a49147d8431a18e649f814e", - "l3": "ed9d2f5af8d30d7927458a6d10f309ae6f49792d5e86d12f5e163a980f49c7ff", - "client_registration_state": "00000000d315d7e6c81d5398c015af3d55ded87f0979dfdb28b6f222ee8699905fd41e0a70617373776f7264", - "client_login_state": "00000000d315d7e6c81d5398c015af3d55ded87f0979dfdb28b6f222ee8699905fd41e0a88f058f4de21fc364603318edce30931a60fe78f7953722e3214203c02d52a7947b20166a022bb319ba9a09d5c328b6e2d6cc5813018f7dd5b53f380f914e79f2e9c4b0c4bc9bdd4fca317dab50240fb32c31f9ec3742ec610a7e5016ad88de570617373776f7264", - "server_registration_state": "75e559c8cc7a81642e61b77651bc61b71d3eb7d8b6cd9ca13d8532cb684d5405", - "server_login_state": "54a3c257ad6e5406be86e9f983bcb25a50dcdaf8c8bd6928f17ea8b001db400c02b16899d3fe648ef86fb914dba9892d09df4a449f6f077e3ad903c01e309d6fd8e21fc482f3d3a396b8c1f5587e00806c860e5a2b82f0da989333ce870ee1ee", - "password_file": "75e559c8cc7a81642e61b77651bc61b71d3eb7d8b6cd9ca13d8532cb684d54056c3f4c134a1697cda1d494cef349e99117aaedecb6792c3aeab06fc1da5f463d3048ea382aa7357dbcd480047c884b717c4fea3ca627f2c498001b2a46e015da0023469a6c15d59bc2b9cf07e4e37138292e58070a34eb76f8cb5e3015c542b8e96346482f0023030020511b18e0a82493d9b0d7988f5a7e2d05fe6886cb26e9813d476e3fe6d3ca13490020b3fc2eb3ffc0ddfa3b9d94a33874d297263cb768077f128e7ec798b03c5c7ba3", - "export_key": "90f9b6ec1edd53a60a9be4f744950befecf5bbfbd973702082fa031aa192bb5a", - "shared_secret": "d8e21fc482f3d3a396b8c1f5587e00806c860e5a2b82f0da989333ce870ee1ee" + "oprf_key": "fd76f0e1cfa9f971bc5dada4caa87dcba1d69d7ace0064d56107ca1932d36300", + "envelope_nonce": "b25a32f7d33a1225675a8ea65dd4ca0b1a09845ce1f917f66ccc62a695c79f20", + "client_nonce": "e29650629c1463124cb2283068557ba10d35637876b131040b4f8702276e1900", + "server_nonce": "b32c16a32fe0cbb926a4926ff29bcfae2a96fe6e92b45f5ca27de3b6e413d413", + "r1": "01000024000000200e0a8b356d7c81a331dddd6987a888943892863b23e14202895eb27b69da691e", + "r2": "020000280020d63f9afe21e3246534cbfbd230b4497255e0a7bb68c3ad6e2dd9b3f4283c8382000001010103", + "r3": "030000aeb25a32f7d33a1225675a8ea65dd4ca0b1a09845ce1f917f66ccc62a695c79f200023b73a7e011120604ac7d44a5ae7f65102ee79a4f6cc4b544d64b5dc064318cfb35230a70023030020da106c3619e8c17c2356f4827b33f6f869ab6f99badc8002324925458fecbd0d002016ba53f6b3b80db59717025c3a9246d5256bef434ce5f7d2c53b961cb5afe44000208ede26558ada44c55f44a39b3e4811bb71533b52c8331799349f5bf579b0063b", + "l1": "04000024000000200e0a8b356d7c81a331dddd6987a888943892863b23e14202895eb27b69da691ee29650629c1463124cb2283068557ba10d35637876b131040b4f8702276e1900b2c126715667420809d14b6056d9492051dc31c77cbb39cd61e9d76736118e46", + "l2": "050000ae0020d63f9afe21e3246534cbfbd230b4497255e0a7bb68c3ad6e2dd9b3f4283c8382b25a32f7d33a1225675a8ea65dd4ca0b1a09845ce1f917f66ccc62a695c79f200023b73a7e011120604ac7d44a5ae7f65102ee79a4f6cc4b544d64b5dc064318cfb35230a70023030020da106c3619e8c17c2356f4827b33f6f869ab6f99badc8002324925458fecbd0d002016ba53f6b3b80db59717025c3a9246d5256bef434ce5f7d2c53b961cb5afe440b8d7358f35d79cfa5e288e8ca8356bc806d4463710144767caf65eb9b4f779731848442db55ef8301a28b078317be3c40590fc40b3a356f704ced8c552f59349732f8a81df2e21f59afd20155576e6191439e4fc4212b78a74a1198cfca92184", + "l3": "512fa64c0540a5fa5d8ee82910fc91bfff98d6272b9070e37e79832e55916ce5", + "client_registration_state": "00000000033a794ce47e3fb19206b8e1f2d74e54c874efcf5b5055d2a31d2164efd41a0770617373776f7264", + "client_login_state": "00000000033a794ce47e3fb19206b8e1f2d74e54c874efcf5b5055d2a31d2164efd41a07481a41606adc0d8aa358cee44e36e5cb02f80d445e42551a90f15e1be59bf450e29650629c1463124cb2283068557ba10d35637876b131040b4f8702276e190062259ed12ff91ba92d2cb43c7433e73e79c4d6d1536ae77cc9ca808bdffc190b70617373776f7264", + "server_registration_state": "fd76f0e1cfa9f971bc5dada4caa87dcba1d69d7ace0064d56107ca1932d36300", + "server_login_state": "7a78a3ce25b39e78d65c9e648f13f6dbe08b3f91b0ef16565b780d42ca61c6d51a66199867f777c671a8e5fa6e8faf1e1047a26f64adea97b16b7a831204d4bdbc293cebd5bc7f82054b142b10617ee8f2e30f2e39fbcacb3566167fc2021589", + "password_file": "fd76f0e1cfa9f971bc5dada4caa87dcba1d69d7ace0064d56107ca1932d363008ede26558ada44c55f44a39b3e4811bb71533b52c8331799349f5bf579b0063bb25a32f7d33a1225675a8ea65dd4ca0b1a09845ce1f917f66ccc62a695c79f200023b73a7e011120604ac7d44a5ae7f65102ee79a4f6cc4b544d64b5dc064318cfb35230a70023030020da106c3619e8c17c2356f4827b33f6f869ab6f99badc8002324925458fecbd0d002016ba53f6b3b80db59717025c3a9246d5256bef434ce5f7d2c53b961cb5afe440", + "export_key": "57da80dc58057781bf65a4f4b1ea0d77d7eb69fbb2786e26dfdfa0c440ea3611", + "shared_secret": "bc293cebd5bc7f82054b142b10617ee8f2e30f2e39fbcacb3566167fc2021589" } "#;