Skip to content

Commit eb782ec

Browse files
committed
Fixups to keep in sync with draft-10
1 parent df29524 commit eb782ec

12 files changed

+453
-1504
lines changed

CHANGELOG.md

+2-23
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,7 @@
11
# Changelog
22

3-
## 3.0.0-pre.5 (September 18, 2024)
4-
* Increased MSRV to 1.74
5-
* Updated voprf dependency
6-
7-
## 3.0.0-pre.4 (July 25, 2023)
8-
* Updated voprf and curve25519-dalek dependencies
9-
10-
## 3.0.0-pre.3 (June 7, 2023)
11-
* Adjusted curve25519 support logic
12-
* Adjusted key generation logic to be in line with commit 727b9ac of
13-
https://github.com/cfrg/draft-irtf-cfrg-opaque
14-
* Updated voprf dependency
15-
16-
## 3.0.0-pre.2 (March 14, 2023)
17-
* Updated VOPRF to draft 19
18-
* Added P384 testing support
19-
* Increased MSRV to 1.65
20-
* Updating dependencies
21-
22-
## 3.0.0-pre.1 (February 8, 2023)
23-
* Renaming of X25519 to Curve25519
24-
* Increased MSRV to 1.60
25-
* Updating dependencies
3+
## 2.1.0-pre.1 (October 10, 2024)
4+
* Updated dependencies: voprf v0.4.1, curve25519-dalek v4
265

276
## 2.0.0 (September 21, 2022)
287
* Synced implementation with draft-irtf-cfrg-opaque-10

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ name = "opaque-ke"
99
readme = "README.md"
1010
repository = "https://github.com/facebook/opaque-ke"
1111
rust-version = "1.74"
12-
version = "3.0.0-pre.5"
12+
version = "2.1.0-pre.1"
1313

1414
[features]
1515
argon2 = ["dep:argon2"]
@@ -39,7 +39,7 @@ serde = { version = "1", default-features = false, features = [
3939
"derive",
4040
], optional = true }
4141
subtle = { version = "2.3", default-features = false }
42-
voprf = { version = "0.5", default-features = false, features = ["danger"] }
42+
voprf = { version = "0.4.1", default-features = false, features = ["danger"] }
4343
zeroize = { version = "1.5", features = ["zeroize_derive"] }
4444

4545
[target.'cfg(target_arch = "wasm32")'.dependencies]

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Installation
2222
Add the following line to the dependencies of your `Cargo.toml`:
2323

2424
```
25-
opaque-ke = "3.0.0-pre.5"
25+
opaque-ke = "2.1.0-pre.1"
2626
```
2727

2828
### Minimum Supported Rust Version
@@ -40,7 +40,7 @@ Resources
4040
---------
4141

4242
- [OPAQUE academic publication](https://eprint.iacr.org/2018/163.pdf), including formal definitions and a proof of security
43-
- [draft-irtf-cfrg-opaque-16](https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque/16/), containing a detailed (byte-level) specification for OPAQUE
43+
- [draft-irtf-cfrg-opaque-10](https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque/10/), containing a detailed (byte-level) specification for OPAQUE
4444
- ["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
4545
- [@serenity-kit/opaque](https://github.com/serenity-kit/opaque), a WebAssembly package for this library
4646
- [opaque-wasm](https://github.com/marucjmar/opaque-wasm), a WebAssembly package for this library. A comparison between `@serenity-kit/opaque` and `opaque-wasm` can be found [here](https://opaque-documentation.netlify.app/docs/faq#how-does-it-compare-to-opaque-wasm)

src/key_exchange/group/curve25519.rs

+28-29
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
//! Key Exchange group implementation for Curve25519
1010
1111
use curve25519_dalek::montgomery::MontgomeryPoint;
12-
use curve25519_dalek::scalar::{self, Scalar};
12+
use curve25519_dalek::scalar::Scalar;
1313
use curve25519_dalek::traits::Identity;
1414
use digest::core_api::BlockSizeUser;
15-
use digest::{FixedOutput, HashMarker, OutputSizeUser};
16-
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32};
15+
use digest::{FixedOutput, HashMarker};
16+
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
17+
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
1718
use generic_array::GenericArray;
1819
use rand::{CryptoRng, RngCore};
1920
use subtle::ConstantTimeEq;
@@ -28,7 +29,7 @@ pub struct Curve25519;
2829
impl KeGroup for Curve25519 {
2930
type Pk = MontgomeryPoint;
3031
type PkLen = U32;
31-
type Sk = [u8; 32];
32+
type Sk = Scalar;
3233
type SkLen = U32;
3334

3435
fn serialize_pk(pk: Self::Pk) -> GenericArray<u8, Self::PkLen> {
@@ -46,61 +47,59 @@ impl KeGroup for Curve25519 {
4647

4748
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
4849
loop {
49-
// Sample 32 random bytes and then clamp, as described in https://cr.yp.to/ecdh.html
50-
let mut scalar_bytes = [0u8; 32];
50+
let mut scalar_bytes = [0u8; 64];
5151
rng.fill_bytes(&mut scalar_bytes);
52-
let scalar = scalar::clamp_integer(scalar_bytes);
52+
let scalar = Scalar::from_bytes_mod_order_wide(&scalar_bytes);
5353

54-
if scalar != Scalar::ZERO.to_bytes() {
54+
if scalar != Scalar::ZERO {
5555
break scalar;
5656
}
5757
}
5858
}
5959

60-
fn hash_to_scalar<'a, H>(_input: &[&[u8]], _dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
60+
// Implements the `HashToScalar()` function from
61+
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-09.html#section-4.1>
62+
fn hash_to_scalar<'a, H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Sk, InternalError>
6163
where
6264
H: BlockSizeUser + Default + FixedOutput + HashMarker,
6365
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
6466
{
65-
unimplemented!()
66-
}
67+
let mut uniform_bytes = GenericArray::<_, U64>::default();
68+
ExpandMsgXmd::<H>::expand_message(input, &[dst], 64)
69+
.map_err(|_| InternalError::HashToScalar)?
70+
.fill_bytes(&mut uniform_bytes);
6771

68-
fn derive_auth_keypair<CS: voprf::CipherSuite>(
69-
seed: GenericArray<u8, Self::SkLen>,
70-
_info: &[u8],
71-
) -> Result<Self::Sk, InternalError>
72-
where
73-
<CS::Hash as OutputSizeUser>::OutputSize:
74-
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
75-
{
76-
Ok(scalar::clamp_integer(seed.into()))
72+
let scalar = Scalar::from_bytes_mod_order_wide(&uniform_bytes.into());
73+
74+
if scalar == Scalar::ZERO {
75+
Err(InternalError::HashToScalar)
76+
} else {
77+
Ok(scalar)
78+
}
7779
}
7880

7981
fn is_zero_scalar(scalar: Self::Sk) -> subtle::Choice {
80-
scalar.ct_eq(&Scalar::ZERO.to_bytes())
82+
scalar.ct_eq(&Scalar::ZERO)
8183
}
8284

8385
fn public_key(sk: Self::Sk) -> Self::Pk {
84-
MontgomeryPoint::mul_base_clamped(sk)
86+
MontgomeryPoint::mul_base(&sk)
8587
}
8688

8789
fn diffie_hellman(pk: Self::Pk, sk: Self::Sk) -> GenericArray<u8, Self::PkLen> {
88-
Self::serialize_pk(pk.mul_clamped(sk))
90+
Self::serialize_pk(sk * pk)
8991
}
9092

9193
fn serialize_sk(sk: Self::Sk) -> GenericArray<u8, Self::SkLen> {
92-
sk.into()
94+
sk.to_bytes().into()
9395
}
9496

9597
fn deserialize_sk(bytes: &[u8]) -> Result<Self::Sk, InternalError> {
9698
bytes
9799
.try_into()
98100
.ok()
99-
.and_then(|bytes| {
100-
let scalar = scalar::clamp_integer(bytes);
101-
(scalar == bytes).then_some(scalar)
102-
})
103-
.filter(|scalar| scalar != &Scalar::ZERO.to_bytes())
101+
.and_then(|bytes| Scalar::from_canonical_bytes(bytes).into())
102+
.filter(|scalar| scalar != &Scalar::ZERO)
104103
.ok_or(InternalError::PointError)
105104
}
106105
}

src/key_exchange/group/elliptic_curve.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ where
5353

5454
// Implements the `HashToScalar()` function from
5555
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html#section-4>
56-
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
56+
fn hash_to_scalar<'a, H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Sk, InternalError>
5757
where
5858
H: BlockSizeUser + Default + FixedOutput + HashMarker,
5959
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
6060
{
61-
Self::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
61+
Self::hash_to_scalar::<ExpandMsgXmd<H>>(input, &[dst])
6262
.map_err(|_| InternalError::HashToScalar)
6363
.and_then(|scalar| {
6464
if bool::from(scalar.is_zero()) {

src/key_exchange/group/mod.rs

+28-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub mod ristretto255;
1717
use digest::core_api::BlockSizeUser;
1818
use digest::{FixedOutput, HashMarker, OutputSizeUser};
1919
use generic_array::sequence::Concat;
20-
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
20+
use generic_array::typenum::{IsLess, IsLessOrEqual, U11, U256};
2121
use generic_array::{ArrayLength, GenericArray};
2222
use rand::{CryptoRng, RngCore};
2323
use zeroize::Zeroize;
@@ -49,7 +49,7 @@ pub trait KeGroup {
4949
/// # Errors
5050
/// [`InternalError::HashToScalar`] if the `input` is empty or longer then
5151
/// [`u16::MAX`].
52-
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
52+
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Sk, InternalError>
5353
where
5454
H: BlockSizeUser + Default + FixedOutput + HashMarker,
5555
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
@@ -69,11 +69,8 @@ pub trait KeGroup {
6969
<CS::Hash as OutputSizeUser>::OutputSize:
7070
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
7171
{
72-
let dst_1 = GenericArray::from(STR_DERIVE_KEYPAIR)
73-
.concat(STR_OPRF.into())
74-
.concat([voprf::Mode::Oprf.to_u8()].into())
75-
.concat([b'-'].into());
76-
let dst_2 = CS::ID.as_bytes();
72+
let context_string = create_context_string::<CS>(voprf::Mode::Oprf);
73+
let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string);
7774

7875
let info_len = i2osp_2(info.len())
7976
.map_err(|_| InternalError::OprfError(voprf::Error::DeriveKeyPair))?;
@@ -84,7 +81,7 @@ pub trait KeGroup {
8481
// || contextString)
8582
let sk_s = Self::hash_to_scalar::<CS::Hash>(
8683
&[&seed, &info_len, info, &counter.to_be_bytes()],
87-
&[&dst_1, dst_2],
84+
&dst,
8885
)
8986
.map_err(|_| InternalError::OprfError(voprf::Error::DeriveKeyPair))?;
9087

@@ -115,9 +112,31 @@ pub trait KeGroup {
115112
// Helper functions used to compute DeriveAuthKeyPair() (taken from the voprf
116113
// crate)
117114

118-
const STR_OPRF: [u8; 7] = *b"OPRFV1-";
115+
const STR_VOPRF: [u8; 8] = *b"VOPRF10-";
119116
const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair";
120117

118+
/// Generates the contextString parameter as defined in
119+
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
120+
fn create_context_string<CS: voprf::CipherSuite>(mode: voprf::Mode) -> GenericArray<u8, U11>
121+
where
122+
<CS::Hash as OutputSizeUser>::OutputSize:
123+
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
124+
{
125+
// FIXME: this should be in voprf library
126+
let cs_id_u16: u16 = match CS::ID {
127+
"ristretto255-SHA512" => 0x0001,
128+
"decaf448-SHAKE256" => 0x0002,
129+
"P256-SHA256" => 0x0003,
130+
"P384-SHA384" => 0x0004,
131+
"P521-SHA512" => 0x0005,
132+
_ => panic!("Incompatible ciphersuite: {}", CS::ID),
133+
};
134+
135+
GenericArray::from(STR_VOPRF)
136+
.concat([mode.to_u8()].into())
137+
.concat(cs_id_u16.to_be_bytes().into())
138+
}
139+
121140
fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
122141
u16::try_from(input)
123142
.map(|input| input.to_be_bytes())

src/key_exchange/group/ristretto255.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ impl KeGroup for Ristretto255 {
7272

7373
// Implements the `HashToScalar()` function from
7474
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html#section-4>
75-
fn hash_to_scalar<'a, H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
75+
fn hash_to_scalar<'a, H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Sk, InternalError>
7676
where
7777
H: BlockSizeUser + Default + FixedOutput + HashMarker,
7878
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
7979
{
80-
<voprf::Ristretto255 as Group>::hash_to_scalar::<H>(input, dst)
80+
<voprf::Ristretto255 as Group>::hash_to_scalar::<H>(input, &[dst])
8181
.map_err(InternalError::OprfInternalError)
8282
}
8383

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//! An implementation of the OPAQUE asymmetric password authentication key
1010
//! exchange protocol
1111
//!
12-
//! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-16](https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque/16/),
12+
//! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-10](https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque/10/),
1313
//! but this specification is subject to change, until the final version
1414
//! published by the IETF.
1515
//!

0 commit comments

Comments
 (0)