GitHub Security Advisory: GHSA-w3g8-fp6j-wvqw
Package: sm2 (RUST)
Severity: HIGH (CVSS 0.0)
CWE: CWE-331: Insufficient Entropy
Vulnerable versions: >= 0.14.0-pre.0, <= 0.14.0-rc.4
Patched in: unpatched

Summary: SM2-PKE has 32-bit Biased Nonce Vulnerability

Details:
### Summary

A critical vulnerability exists in the SM2 Public Key Encryption (PKE) implementation where the ephemeral nonce `k` is generated with severely reduced entropy. A unit mismatch error causes the nonce generation function to request only 32 bits of randomness instead of the expected 256 bits. This reduces the security of the encryption from a 128-bit level to a trivial 16-bit level, allowing a practical attack to recover the nonce `k` and decrypt any ciphertext **given only the public key and ciphertext**.



### Affected Versions

- sm2 0.14.0-rc.0 (https://crates.io/crates/sm2/0.14.0-rc.0)
- sm2 0.14.0-pre.0 (https://crates.io/crates/sm2/0.14.0-pre.0)

This vulnerability is introduced in commit: [Commit 4781762](https://github.com/RustCrypto/elliptic-curves/commit/4781762f23ff22ab34763410f648128055c93731) on Sep 6, 2024, which is over a year ago.



### Details

The root cause of this vulnerability is a unit mismatch in the `encrypt` function located in `sm2/src/pke/encrypting.rs`.

1.  The code correctly calculates the byte-length of the curve order (256 bits / 8 = 32 bytes) and stores it in a constant `N_BYTES`.
    ```rust
    const N_BYTES: u32 = Sm2::ORDER.as_ref().bits().div_ceil(8); // Value is 32 (bytes)
    ```
2.  However, this `N_BYTES` value is then passed to the `next_k` helper function, which incorrectly interprets this value as a *bit length*.
    ```rust
    let k = Scalar::from_uint(&next_k(rng, N_BYTES)?).unwrap();
    ```
3.  Inside `next_k`, the `bit_length` parameter (which holds the value 32) is passed directly to `U256::try_random_bits`, a function that generates a random number with the specified number of bits.
    ```rust
    fn next_k<R: TryCryptoRng + ?Sized>(rng: &mut R, bit_length: u32) -> Result<U256> {
        let k = U256::try_random_bits(rng, bit_length).map_err(|_| Error)?;
        // ...
    }
    ```
    As a result, the ephemeral nonce `k` is generated with only 32 bits of entropy, with its upper 224 bits being zero. This catastrophic loss of randomness makes the encryption scheme insecure.
    
    

### PoC

A proof-of-concept demonstrating the feasibility of this attack is provided in `examples/bsgs_recover.rs`. The PoC performs the following steps:

1.  **Encrypt a Message**: It uses the vulnerable `EncryptingKey::encrypt` function to encrypt a sample message.
2.  **Extract Ephemeral Public Key**: It parses the ciphertext to extract `C1`, which is the ephemeral public key `[k]G`.
3.  **Recover Nonce `k`**: It runs a Baby-Step Giant-Step (BSGS) algorithm to search the reduced 2^32 search space for the nonce `k`. This attack is computationally feasible on modern hardware in seconds with time complexity `O(2^16)`.
4.  **Decrypt without Secret Key**: Once `k` is recovered, it computes the shared secret `[k]PB` (where `PB` is the recipient's public key) and successfully decrypts the ciphertext without access to the recipient's secret key.

`examples/bsgs_recover.rs`

``` rust
//! Example: Recover l