Updating to version 0.4.0 and simplifying transcript computation logic in TripleDH (#148)
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.4.0 (February 26, 2020)
|
||||||
|
|
||||||
|
* Adherence to protocol format described in
|
||||||
|
https://tools.ietf.org/html/draft-irtf-cfrg-opaque-03
|
||||||
|
* Renamed to_bytes() and try_from() to serialize() and deserialize() for
|
||||||
|
top-level structs
|
||||||
|
* Conformed all message type parameters to be parameterized in the
|
||||||
|
Ciphersuite object
|
||||||
|
|
||||||
## 0.3.1 (February 11, 2020)
|
## 0.3.1 (February 11, 2020)
|
||||||
|
|
||||||
* Re-exporting the rand library (and including it as a dependency instead of
|
* Re-exporting the rand library (and including it as a dependency instead of
|
||||||
|
|||||||
Generated
+1
-1
@@ -573,7 +573,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-ke"
|
name = "opaque-ke"
|
||||||
version = "0.4.0-pre.1"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "opaque-ke"
|
name = "opaque-ke"
|
||||||
version = "0.4.0-pre.1"
|
version = "0.4.0"
|
||||||
repository = "https://github.com/novifinancial/opaque-ke"
|
repository = "https://github.com/novifinancial/opaque-ke"
|
||||||
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
|
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
|
||||||
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
|
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ Installation
|
|||||||
Add the following line to the dependencies of your `Cargo.toml`:
|
Add the following line to the dependencies of your `Cargo.toml`:
|
||||||
|
|
||||||
```
|
```
|
||||||
opaque-ke = "0.3.1"
|
opaque-ke = "0.4.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Resources
|
Resources
|
||||||
---------
|
---------
|
||||||
|
|
||||||
- [OPAQUE academic publication](https://eprint.iacr.org/2018/163.pdf), including formal definitions and a proof of security
|
- [OPAQUE academic publication](https://eprint.iacr.org/2018/163.pdf), including formal definitions and a proof of security
|
||||||
- [draft-irtf-cfrg-opaque-02](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-02.html), containing a detailed (byte-level) specification for OPAQUE
|
- [draft-irtf-cfrg-opaque-03](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-03.html), containing a detailed (byte-level) specification for OPAQUE
|
||||||
- ["Let's talk about PAKE"](https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/), an introductory blog post written by Matthew Green that covers OPAQUE
|
- ["Let's talk about PAKE"](https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/), an introductory blog post written by Matthew Green that covers OPAQUE
|
||||||
|
|
||||||
Contributors
|
Contributors
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ static STR_3DH: &[u8] = b"3DH";
|
|||||||
static STR_CLIENT_MAC: &[u8] = b"client mac";
|
static STR_CLIENT_MAC: &[u8] = b"client mac";
|
||||||
static STR_HANDSHAKE_SECRET: &[u8] = b"handshake secret";
|
static STR_HANDSHAKE_SECRET: &[u8] = b"handshake secret";
|
||||||
static STR_SERVER_MAC: &[u8] = b"server mac";
|
static STR_SERVER_MAC: &[u8] = b"server mac";
|
||||||
static STR_SERVER_ENC: &[u8] = b"handshake enc";
|
static STR_HANDSHAKE_ENC: &[u8] = b"handshake enc";
|
||||||
static STR_ENCRYPTION_PAD: &[u8] = b"encryption pad";
|
static STR_ENCRYPTION_PAD: &[u8] = b"encryption pad";
|
||||||
static STR_SESSION_KEY: &[u8] = b"session secret";
|
static STR_SESSION_SECRET: &[u8] = b"session secret";
|
||||||
static STR_OPAQUE: &[u8] = b"OPAQUE ";
|
static STR_OPAQUE: &[u8] = b"OPAQUE ";
|
||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
@@ -87,21 +87,14 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
let server_e_kp = KeyPair::<G>::generate_random(rng);
|
let server_e_kp = KeyPair::<G>::generate_random(rng);
|
||||||
let server_nonce = generate_nonce::<R>(rng);
|
let server_nonce = generate_nonce::<R>(rng);
|
||||||
|
|
||||||
let server_transcript = [
|
let mut transcript_hasher = D::new()
|
||||||
&l2_bytes[..],
|
.chain(STR_3DH)
|
||||||
&server_nonce[..],
|
.chain(&serialize(&id_u, 2))
|
||||||
&server_e_kp.public().to_arr(),
|
.chain(&serialized_credential_request[..])
|
||||||
]
|
.chain(&serialize(&id_s, 2))
|
||||||
.concat();
|
.chain(&l2_bytes[..])
|
||||||
|
.chain(&server_nonce[..])
|
||||||
let derivation_transcript = [
|
.chain(&server_e_kp.public().to_arr());
|
||||||
STR_3DH,
|
|
||||||
&serialize(&id_u, 2),
|
|
||||||
&serialized_credential_request[..],
|
|
||||||
&serialize(&id_s, 2),
|
|
||||||
&server_transcript[..],
|
|
||||||
]
|
|
||||||
.concat();
|
|
||||||
|
|
||||||
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
|
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
|
||||||
TripleDHComponents {
|
TripleDHComponents {
|
||||||
@@ -112,7 +105,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
pk3: client_s_pk,
|
pk3: client_s_pk,
|
||||||
sk3: server_e_kp.private().clone(),
|
sk3: server_e_kp.private().clone(),
|
||||||
},
|
},
|
||||||
&derivation_transcript,
|
&transcript_hasher.clone().finalize(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Compute encryption of e_info
|
// Compute encryption of e_info
|
||||||
@@ -126,27 +119,20 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
.map(|(&x1, &x2)| x1 ^ x2)
|
.map(|(&x1, &x2)| x1 ^ x2)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let transcript2: Vec<u8> =
|
transcript_hasher.update(&serialize(&ciphertext, 2));
|
||||||
[&derivation_transcript[..], &serialize(&ciphertext, 2)].concat();
|
|
||||||
|
|
||||||
let mut hasher = D::new();
|
|
||||||
hasher.update(&transcript2);
|
|
||||||
let hashed_transcript_without_mac = hasher.finalize_reset();
|
|
||||||
|
|
||||||
let mut mac_hasher =
|
let mut mac_hasher =
|
||||||
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
||||||
mac_hasher.update(&hashed_transcript_without_mac);
|
mac_hasher.update(&transcript_hasher.clone().finalize());
|
||||||
let mac = mac_hasher.finalize().into_bytes();
|
let mac = mac_hasher.finalize().into_bytes();
|
||||||
|
|
||||||
hasher.update(&transcript2);
|
transcript_hasher.update(&mac);
|
||||||
hasher.update(&mac);
|
|
||||||
let hashed_transcript = hasher.finalize();
|
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
ke1_message.info,
|
ke1_message.info,
|
||||||
Ke2State {
|
Ke2State {
|
||||||
km3,
|
km3,
|
||||||
hashed_transcript,
|
hashed_transcript: transcript_hasher.finalize(),
|
||||||
session_key,
|
session_key,
|
||||||
},
|
},
|
||||||
Ke2Message {
|
Ke2Message {
|
||||||
@@ -169,20 +155,13 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
id_u: Vec<u8>,
|
id_u: Vec<u8>,
|
||||||
id_s: Vec<u8>,
|
id_s: Vec<u8>,
|
||||||
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError> {
|
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError> {
|
||||||
let server_transcript = [
|
let mut transcript_hasher = D::new()
|
||||||
&l2_component[..],
|
.chain(STR_3DH)
|
||||||
&ke2_message.to_bytes_without_info_or_mac(),
|
.chain(&serialize(&id_u, 2))
|
||||||
]
|
.chain(&serialized_credential_request)
|
||||||
.concat();
|
.chain(&serialize(&id_s, 2))
|
||||||
|
.chain(&l2_component[..])
|
||||||
let derivation_transcript = [
|
.chain(&ke2_message.to_bytes_without_info_or_mac());
|
||||||
STR_3DH,
|
|
||||||
&serialize(&id_u, 2),
|
|
||||||
&serialized_credential_request,
|
|
||||||
&serialize(&id_s, 2),
|
|
||||||
&server_transcript[..],
|
|
||||||
]
|
|
||||||
.concat();
|
|
||||||
|
|
||||||
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
|
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
|
||||||
TripleDHComponents {
|
TripleDHComponents {
|
||||||
@@ -193,22 +172,14 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
pk3: ke2_message.server_e_pk.clone(),
|
pk3: ke2_message.server_e_pk.clone(),
|
||||||
sk3: client_s_sk,
|
sk3: client_s_sk,
|
||||||
},
|
},
|
||||||
&derivation_transcript,
|
&transcript_hasher.clone().finalize(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let transcript: Vec<u8> = [
|
transcript_hasher.update(&serialize(&ke2_message.e_info[..], 2));
|
||||||
&derivation_transcript[..],
|
|
||||||
&serialize(&ke2_message.e_info[..], 2),
|
|
||||||
]
|
|
||||||
.concat();
|
|
||||||
|
|
||||||
let mut hasher = D::new();
|
|
||||||
hasher.update(&transcript);
|
|
||||||
let hashed_transcript_without_mac = hasher.finalize_reset();
|
|
||||||
|
|
||||||
let mut server_mac =
|
let mut server_mac =
|
||||||
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
||||||
server_mac.update(&hashed_transcript_without_mac);
|
server_mac.update(&transcript_hasher.clone().finalize());
|
||||||
|
|
||||||
if ke2_message.mac != server_mac.finalize().into_bytes() {
|
if ke2_message.mac != server_mac.finalize().into_bytes() {
|
||||||
return Err(ProtocolError::VerificationError(
|
return Err(ProtocolError::VerificationError(
|
||||||
@@ -216,13 +187,11 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
hasher.update(transcript);
|
transcript_hasher.update(ke2_message.mac.to_vec());
|
||||||
hasher.update(ke2_message.mac.to_vec());
|
|
||||||
let hashed_transcript = hasher.finalize();
|
|
||||||
|
|
||||||
let mut client_mac =
|
let mut client_mac =
|
||||||
Hmac::<D>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?;
|
Hmac::<D>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?;
|
||||||
client_mac.update(&hashed_transcript);
|
client_mac.update(&transcript_hasher.finalize());
|
||||||
|
|
||||||
// Compute decryption of e_info
|
// Compute decryption of e_info
|
||||||
let h = Hkdf::<D>::from_prk(&ke2).map_err(|_| InternalPakeError::HkdfError)?;
|
let h = Hkdf::<D>::from_prk(&ke2).map_err(|_| InternalPakeError::HkdfError)?;
|
||||||
@@ -467,7 +436,7 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for Ke3Message<HashLen> {
|
|||||||
// with some auxiliary metadata, to produce the session key and two MAC keys
|
// with some auxiliary metadata, to produce the session key and two MAC keys
|
||||||
fn derive_3dh_keys<D: Hash, G: Group>(
|
fn derive_3dh_keys<D: Hash, G: Group>(
|
||||||
dh: TripleDHComponents,
|
dh: TripleDHComponents,
|
||||||
derivation_transcript: &[u8],
|
hashed_derivation_transcript: &[u8],
|
||||||
) -> Result<TripleDHDerivationResult<D>, ProtocolError> {
|
) -> Result<TripleDHDerivationResult<D>, ProtocolError> {
|
||||||
let ikm: Vec<u8> = [
|
let ikm: Vec<u8> = [
|
||||||
&KeyPair::<G>::diffie_hellman(dh.pk1, dh.sk1)?[..],
|
&KeyPair::<G>::diffie_hellman(dh.pk1, dh.sk1)?[..],
|
||||||
@@ -480,10 +449,13 @@ fn derive_3dh_keys<D: Hash, G: Group>(
|
|||||||
let handshake_secret = derive_secrets::<D>(
|
let handshake_secret = derive_secrets::<D>(
|
||||||
&extracted_ikm,
|
&extracted_ikm,
|
||||||
&STR_HANDSHAKE_SECRET,
|
&STR_HANDSHAKE_SECRET,
|
||||||
&derivation_transcript,
|
&hashed_derivation_transcript,
|
||||||
|
)?;
|
||||||
|
let session_key = derive_secrets::<D>(
|
||||||
|
&extracted_ikm,
|
||||||
|
&STR_SESSION_SECRET,
|
||||||
|
&hashed_derivation_transcript,
|
||||||
)?;
|
)?;
|
||||||
let session_key =
|
|
||||||
derive_secrets::<D>(&extracted_ikm, &STR_SESSION_KEY, &derivation_transcript)?;
|
|
||||||
|
|
||||||
let km2 = hkdf_expand_label::<D>(
|
let km2 = hkdf_expand_label::<D>(
|
||||||
&handshake_secret,
|
&handshake_secret,
|
||||||
@@ -493,7 +465,7 @@ fn derive_3dh_keys<D: Hash, G: Group>(
|
|||||||
)?;
|
)?;
|
||||||
let ke2 = hkdf_expand_label::<D>(
|
let ke2 = hkdf_expand_label::<D>(
|
||||||
&handshake_secret,
|
&handshake_secret,
|
||||||
&STR_SERVER_ENC,
|
&STR_HANDSHAKE_ENC,
|
||||||
b"",
|
b"",
|
||||||
<D as Digest>::OutputSize::to_usize(),
|
<D as Digest>::OutputSize::to_usize(),
|
||||||
)?;
|
)?;
|
||||||
@@ -548,13 +520,12 @@ fn hkdf_expand_label_extracted<D: Hash>(
|
|||||||
fn derive_secrets<D: Hash>(
|
fn derive_secrets<D: Hash>(
|
||||||
hkdf: &Hkdf<D>,
|
hkdf: &Hkdf<D>,
|
||||||
label: &[u8],
|
label: &[u8],
|
||||||
transcript: &[u8],
|
hashed_derivation_transcript: &[u8],
|
||||||
) -> Result<Vec<u8>, ProtocolError> {
|
) -> Result<Vec<u8>, ProtocolError> {
|
||||||
let hashed_transcript = D::digest(transcript);
|
|
||||||
hkdf_expand_label_extracted::<D>(
|
hkdf_expand_label_extracted::<D>(
|
||||||
hkdf,
|
hkdf,
|
||||||
label,
|
label,
|
||||||
&hashed_transcript,
|
&hashed_derivation_transcript,
|
||||||
<D as Digest>::OutputSize::to_usize(),
|
<D as Digest>::OutputSize::to_usize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
//! An implementation of the OPAQUE asymmetric password authentication key exchange protocol
|
//! An implementation of the OPAQUE asymmetric password authentication key exchange protocol
|
||||||
//!
|
//!
|
||||||
//! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-02](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-02.html),
|
//! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-03](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-03.html),
|
||||||
//! but this specification is subject to change, until the final version published by the IETF.
|
//! but this specification is subject to change, until the final version published by the IETF.
|
||||||
//!
|
//!
|
||||||
//! # Overview
|
//! # Overview
|
||||||
|
|||||||
Reference in New Issue
Block a user