Skip to content

Commit 19adaeb

Browse files
committed
Include encapsulation randomness in test vectors
It wasn't obvious what to use for EncapDerand of DHKEM. RFC 9180 uses rejection sampling for DeriveKeyPair, but includes a scalar in the test vectors (which is not really an EncapDerand input). draft-irtf-cfrg-hybrid-kems defines the EncapsDerand of a nominal group to be reduction based. Since DHKEM already has a DeriveKeyPair, I went for that. Fixes #29
1 parent 165799e commit 19adaeb

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

reference-implementation/src/kem.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub trait Kem {
99
const N_PK: usize;
1010
const N_SK: usize;
1111
const N_SEED: usize;
12+
const N_RANDOM: usize;
1213

1314
type EncapsulationKey;
1415
type DecapsulationKey;
@@ -23,21 +24,23 @@ pub trait Kem {
2324
fn deserialize_private_key(skXm: &[u8]) -> Self::DecapsulationKey;
2425

2526
fn encap(rng: &mut impl rand::CryptoRng, pkR: &Self::EncapsulationKey) -> (Vec<u8>, Vec<u8>);
27+
fn encap_derand(pkR: &Self::EncapsulationKey, randomness: &[u8]) -> (Vec<u8>, Vec<u8>);
2628
fn decap(enc: &[u8], skR: &Self::DecapsulationKey) -> Vec<u8>;
2729
}
2830

2931
pub struct KemWithId<K, const ID: u16>(core::marker::PhantomData<K>);
3032

3133
impl<K, const ID: u16> Kem for KemWithId<K, ID>
3234
where
33-
K: concrete_hybrid_kem::Kem,
35+
K: concrete_hybrid_kem::Kem + concrete_hybrid_kem::EncapsDerand,
3436
{
3537
const ID: [u8; 2] = ID.to_be_bytes();
3638
const N_SECRET: usize = K::SHARED_SECRET_LENGTH;
3739
const N_ENC: usize = K::CIPHERTEXT_LENGTH;
3840
const N_PK: usize = K::ENCAPSULATION_KEY_LENGTH;
3941
const N_SK: usize = K::DECAPSULATION_KEY_LENGTH;
4042
const N_SEED: usize = K::SEED_LENGTH;
43+
const N_RANDOM: usize = K::RANDOMNESS_LENGTH;
4144

4245
type EncapsulationKey = <K as concrete_hybrid_kem::Kem>::EncapsulationKey;
4346
type DecapsulationKey = <K as concrete_hybrid_kem::Kem>::DecapsulationKey;
@@ -78,6 +81,12 @@ where
7881
(ss.as_bytes().to_vec(), ct.as_bytes().to_vec())
7982
}
8083

84+
fn encap_derand(pkR: &Self::EncapsulationKey, randomness: &[u8]) -> (Vec<u8>, Vec<u8>) {
85+
use concrete_hybrid_kem::AsBytes;
86+
let (ct, ss) = <K as concrete_hybrid_kem::EncapsDerand>::encaps_derand(pkR, randomness).unwrap();
87+
(ss.as_bytes().to_vec(), ct.as_bytes().to_vec())
88+
}
89+
8190
fn decap(enc: &[u8], skR: &Self::DecapsulationKey) -> Vec<u8> {
8291
use concrete_hybrid_kem::AsBytes;
8392
let enc = K::Ciphertext::from(enc);
@@ -92,14 +101,15 @@ pub struct MlKemWithId<K, const ID: u16>(core::marker::PhantomData<K>);
92101

93102
impl<K, const ID: u16> Kem for MlKemWithId<K, ID>
94103
where
95-
K: concrete_hybrid_kem::Kem,
104+
K: concrete_hybrid_kem::Kem + concrete_hybrid_kem::EncapsDerand,
96105
{
97106
const ID: [u8; 2] = ID.to_be_bytes();
98107
const N_SECRET: usize = K::SHARED_SECRET_LENGTH;
99108
const N_ENC: usize = K::CIPHERTEXT_LENGTH;
100109
const N_PK: usize = K::ENCAPSULATION_KEY_LENGTH;
101110
const N_SK: usize = K::SEED_LENGTH; // Use seed length for ML-KEM per spec
102111
const N_SEED: usize = K::SEED_LENGTH;
112+
const N_RANDOM: usize = K::RANDOMNESS_LENGTH;
103113

104114
type EncapsulationKey = <K as concrete_hybrid_kem::Kem>::EncapsulationKey;
105115
type DecapsulationKey = <K as concrete_hybrid_kem::Kem>::DecapsulationKey;
@@ -140,6 +150,12 @@ where
140150
(ss.as_bytes().to_vec(), ct.as_bytes().to_vec())
141151
}
142152

153+
fn encap_derand(pkR: &Self::EncapsulationKey, randomness: &[u8]) -> (Vec<u8>, Vec<u8>) {
154+
use concrete_hybrid_kem::AsBytes;
155+
let (ct, ss) = <K as concrete_hybrid_kem::EncapsDerand>::encaps_derand(pkR, randomness).unwrap();
156+
(ss.as_bytes().to_vec(), ct.as_bytes().to_vec())
157+
}
158+
143159
fn decap(enc: &[u8], skR: &Self::DecapsulationKey) -> Vec<u8> {
144160
use concrete_hybrid_kem::AsBytes;
145161
let enc = K::Ciphertext::from(enc);
@@ -513,6 +529,7 @@ where
513529
const N_PK: usize = C::POINT_SIZE;
514530
const N_SK: usize = C::SCALAR_SIZE;
515531
const N_SEED: usize = C::SCALAR_SIZE;
532+
const N_RANDOM: usize = C::SCALAR_SIZE;
516533

517534
type EncapsulationKey = C::Point;
518535
type DecapsulationKey = C::Scalar;
@@ -557,6 +574,20 @@ where
557574
(shared_secret, enc)
558575
}
559576

577+
fn encap_derand(pkR: &Self::EncapsulationKey, randomness: &[u8]) -> (Vec<u8>, Vec<u8>) {
578+
use crate::concat;
579+
580+
let (skE, pkE) = Self::derive_key_pair(randomness);
581+
let dh = C::dh(&skE, pkR);
582+
let enc = Self::serialize_public_key(&pkE);
583+
584+
let pkRm = Self::serialize_public_key(pkR);
585+
let kem_context = concat(&[&enc, &pkRm]);
586+
587+
let shared_secret = K::extract_and_expand(C::SUITE_ID, &dh, &kem_context, Self::N_SECRET);
588+
(shared_secret, enc)
589+
}
590+
560591
fn decap(enc: &[u8], skR: &Self::DecapsulationKey) -> Vec<u8> {
561592
use crate::concat;
562593

reference-implementation/src/test_vectors.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub struct TestVector {
3535
pub aead_id: u16,
3636
#[serde(with = "hex::serde")]
3737
pub info: Vec<u8>,
38+
#[serde(with = "hex::serde")]
39+
pub encap_rand: Vec<u8>,
3840
#[serde(rename = "ikmR", with = "hex::serde")]
3941
pub ikm_r: Vec<u8>,
4042
#[serde(rename = "skRm", with = "hex::serde")]
@@ -46,6 +48,8 @@ pub struct TestVector {
4648
#[serde(with = "hex::serde")]
4749
pub shared_secret: Vec<u8>,
4850
#[serde(with = "hex::serde")]
51+
pub suite_id: Vec<u8>,
52+
#[serde(with = "hex::serde")]
4953
pub key: Vec<u8>,
5054
#[serde(with = "hex::serde")]
5155
pub base_nonce: Vec<u8>,
@@ -211,6 +215,8 @@ impl TestVector {
211215
H: Kdf,
212216
A: Aead,
213217
{
218+
use rand::RngCore;
219+
214220
// Fixed test values
215221
let info = b"4f6465206f6e2061204772656369616e2055726e";
216222
let pt = b"4265617574792069732074727574682c20747275746820626561757479";
@@ -220,7 +226,6 @@ impl TestVector {
220226
// Generate random seed for recipient key pair
221227
let mut rng = rand::rng();
222228
let ikm_r = {
223-
use rand::RngCore;
224229
// Use the KEM's seed size
225230
let mut seed = vec![0u8; K::N_SEED];
226231
rng.fill_bytes(&mut seed);
@@ -231,7 +236,9 @@ impl TestVector {
231236
let (sk_r, pk_r) = K::derive_key_pair(&ikm_r);
232237

233238
// Perform encapsulation
234-
let (shared_secret, enc) = K::encap(&mut rng, &pk_r);
239+
let mut encap_rand = vec![0u8; K::N_RANDOM];
240+
rng.fill_bytes(&mut encap_rand);
241+
let (shared_secret, enc) = K::encap_derand(&pk_r, &encap_rand);
235242

236243
// Create context
237244
let suite_id = Instance::<K, H, A>::suite_id();
@@ -278,11 +285,13 @@ impl TestVector {
278285
kdf_id: u16::from_be_bytes(H::ID),
279286
aead_id: u16::from_be_bytes(A::ID),
280287
info: info.to_vec(),
288+
encap_rand,
281289
ikm_r: ikm_r.clone(),
282290
sk_rm: K::serialize_private_key(&sk_r),
283291
pk_rm: K::serialize_public_key(&pk_r),
284292
enc,
285293
shared_secret,
294+
suite_id: suite_id.to_vec(),
286295
key,
287296
base_nonce,
288297
exporter_secret,

0 commit comments

Comments
 (0)