Skip to content

[zk-sdk] Replace internal rand usage with getrandom - #580

Open
samkim-crypto wants to merge 1 commit into
solana-program:mainfrom
samkim-crypto:zk-sdk-remove-rand-v8
Open

samkim-crypto wants to merge 1 commit into
solana-program:mainfrom
samkim-crypto:zk-sdk-remove-rand-v8

Conversation

@samkim-crypto

@samkim-crypto samkim-crypto commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The zk-sdk currently uses rand to generate random scalars, keys, and nonces. Scalar generation also depends on the RNG traits expected by curve25519-dalek, which makes dependency upgrades more difficult as we prepare to replace it with solana-ed25519 in #360.

We only need cryptographically secure random bytes here. getrandom already backs the OsRng calls being replaced, so using it directly preserves the randomness source while avoiding coupling scalar generation to the curve library's rand_core version.

Summary of Changes

I replaced direct rand usage with getrandom. This keeps randomness generation independent of the curve library's RNG interface. Together with disabling the RNG features in solana-ed25519, this will let us make the switch without requiring matching RNG trait versions across the cryptography, zk-elgamal-proof, and agave repos.

To do this, I added private helpers in random.rs that obtain random bytes directly through getrandom. Scalar generation uses the same 64-byte reduction as Scalar::random, and the temporary scalar sampling buffer is zeroized.

Public APIs and proof formats are preserved. Dalek's existing rand_core feature stays enabled for downstream compatibility, making this suitable for v8.1.0 ahead of #360, which targets v9. The retained feature can be dropped as part of that migration.

In the long term, it would be useful to consider updating the API to accept caller-provided random bytes, as some crates in solana-sdk already do. This would allow the core SDK to evolve independently of dependencies that supply randomness, since getrandom upgrades can still require maintenance. I decided to use getrandom for now because zk-sdk already generates randomness internally and this preserves the existing APIs. Making callers responsible for proof randomness would also need careful API design to reduce the risk of weak or reused randomness.

@samkim-crypto
samkim-crypto force-pushed the zk-sdk-remove-rand-v8 branch 2 times, most recently from 82d5152 to 8343047 Compare October 1, 2026 01:55

@zz-sol zz-sol left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@joncinque joncinque left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me! Just some potential little optimizations using MaybeUninit since we don't need initialized types

Comment thread zk-sdk/src/random.rs
///
/// Panics if the operating system's entropy source fails.
pub(crate) fn fill_random_bytes(bytes: &mut [u8]) {
getrandom::getrandom(bytes).expect("secure randomness unavailable");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, we could keep using rand but just as an implementation detail.

On the flip-side, this refactoring shows that we don't really need it, which is great!

Comment thread zk-sdk/src/random.rs

/// Samples a scalar with the same 64-byte reduction as `Scalar::random`.
pub(crate) fn random_scalar() -> Scalar {
let mut bytes = Zeroizing::new([0u8; 64]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could change this to MaybeUninit since it'll get filled immediately anyway and use https://docs.rs/getrandom/latest/getrandom/fn.fill_uninit.html

Comment thread zk-sdk/src/random.rs
/// Fills a buffer with cryptographically secure randomness.
///
/// Panics if the operating system's entropy source fails.
pub(crate) fn fill_random_bytes(bytes: &mut [u8]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the uses of this function start by zero-initializing -- how about changing to MaybeUninit here too?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants