Skip to content

Commit e4f7778

Browse files
authored
sm2: fix SM2PKE biased k generation [SECURITY] (#1600)
This fixes a critical security vulnerability originally reported as GHSA-w3g8-fp6j-wvqw by @XlabAITeam. Previously the module defined its own `next_k` function which invoked the `RandomBits::try_random_bits` API to generate a `k` value via rejection sampling, however it was passing previous calculation of the number of bytes to generate to an API expecting a number of bits. This bits/bytes confusion lead to highly biased values for `k` consisting of mostly zeros. Such a bias can be exploited for full plaintext recovery, and potentially key recovery. All ciphertexts ever encrypted using this API are vulnerable. This commit replaces the `next_k` function with the recently added `Generate` trait (#1586), using `NonZeroScalar::try_generate_from_rng` which is backed by the rejection sampling implemenatation from `crypto-bigint`.
1 parent f9b5009 commit e4f7778

File tree

1 file changed

+5
-14
lines changed

1 file changed

+5
-14
lines changed

sm2/src/pke/encrypting.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use core::fmt::Debug;
22

33
use crate::{
4-
AffinePoint, ProjectivePoint, PublicKey, Scalar, Sm2,
4+
AffinePoint, NonZeroScalar, ProjectivePoint, PublicKey, Scalar, Sm2,
55
arithmetic::field::FieldElement,
66
pke::{kdf, vec},
77
};
88

99
#[cfg(feature = "alloc")]
1010
use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
1111
use elliptic_curve::{
12-
Curve, Error, Group, Result,
13-
bigint::{RandomBits, U256, Uint},
12+
Curve, Error, Generate, Group, Result,
13+
bigint::{U256, Uint},
1414
ops::Reduce,
1515
pkcs8::der::Encode,
1616
rand_core::TryCryptoRng,
@@ -158,7 +158,7 @@ fn encrypt<R: TryCryptoRng + ?Sized>(
158158
let mut hpb: AffinePoint;
159159
loop {
160160
// A1: generate a random number 𝑘 ∈ [1, 𝑛 − 1] with the random number generator
161-
let k = Scalar::from_uint(&next_k(rng, N_BYTES)?).unwrap();
161+
let k = NonZeroScalar::try_generate_from_rng(rng).map_err(|_| Error)?;
162162

163163
// A2: compute point 𝐶1 = [𝑘]𝐺 = (𝑥1, 𝑦1)
164164
let kg = ProjectivePoint::mul_by_generator(&k).to_affine();
@@ -171,7 +171,7 @@ fn encrypt<R: TryCryptoRng + ?Sized>(
171171
}
172172

173173
// A4: compute point [𝑘]𝑃𝐵 = (𝑥2, 𝑦2)
174-
hpb = (s * k).to_affine();
174+
hpb = (s * *k).to_affine();
175175

176176
// A5: compute 𝑡 = 𝐾𝐷𝐹(𝑥2||𝑦2, 𝑘𝑙𝑒𝑛)
177177
// A6: compute 𝐶2 = 𝑀 ⊕ t
@@ -200,12 +200,3 @@ fn encrypt<R: TryCryptoRng + ?Sized>(
200200
Mode::C1C3C2 => [c1.as_slice(), &c3, &c2].concat(),
201201
})
202202
}
203-
204-
fn next_k<R: TryCryptoRng + ?Sized>(rng: &mut R, bit_length: u32) -> Result<U256> {
205-
loop {
206-
let k = U256::try_random_bits(rng, bit_length).map_err(|_| Error)?;
207-
if !bool::from(k.is_zero()) && k < *Sm2::ORDER {
208-
return Ok(k);
209-
}
210-
}
211-
}

0 commit comments

Comments
 (0)