From 2b79c770841368e1d09c37d6515ce7ef7fe4a409 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 9 Nov 2025 13:15:35 -0700 Subject: [PATCH] elliptic-curve: `getrandom` feature Adds support for generating `SecretKey` and `NonZeroScalar` using the system's cryptographically secure random number generator. Notably this renames the former `SecretKey::random` and `NonZeroScalar::random` methods to `SecretKey::generate` and `NonZeroScalar::generate`, which take no parameters and are infallible. This avoids the need for the user to import an `OsRng` type, or worry about the generation failing (which it won't on most notable modern OSes). If a user still wants to handle RNG errors, the `try_from_rng` method still exists, and they can pass `OsRng` if they'd like. --- Cargo.lock | 1 + elliptic-curve/Cargo.toml | 2 ++ elliptic-curve/src/ecdh.rs | 7 ++++--- elliptic-curve/src/scalar/nonzero.rs | 11 +++++++---- elliptic-curve/src/secret_key.rs | 11 ++++------- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6722f785..b92766b6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,7 @@ dependencies = [ "crypto-bigint", "digest", "ff", + "getrandom", "group", "hex-literal", "hkdf", diff --git a/elliptic-curve/Cargo.toml b/elliptic-curve/Cargo.toml index 43e3accbc..1a1cd8cbc 100644 --- a/elliptic-curve/Cargo.toml +++ b/elliptic-curve/Cargo.toml @@ -27,6 +27,7 @@ zeroize = { version = "1.7", default-features = false } # optional dependencies digest = { version = "0.11.0-rc.4", optional = true } ff = { version = "=0.14.0-pre.0", optional = true, default-features = false } +getrandom = { version = "0.3", optional = true } group = { version = "=0.14.0-pre.0", optional = true, default-features = false } hkdf = { version = "0.13.0-rc.3", optional = true, default-features = false } hex-literal = { version = "1", optional = true } @@ -63,6 +64,7 @@ critical-section = ["basepoint-table", "once_cell/critical-section"] bits = ["arithmetic", "ff/bits"] dev = ["arithmetic", "dep:hex-literal", "pem", "pkcs8"] ecdh = ["arithmetic", "digest", "dep:hkdf"] +getrandom = ["dep:getrandom", "arithmetic"] group = ["dep:group", "ff"] pkcs8 = ["dep:pkcs8", "sec1"] pem = ["dep:pem-rfc7468", "alloc", "arithmetic", "pkcs8/pem", "sec1/pem"] diff --git a/elliptic-curve/src/ecdh.rs b/elliptic-curve/src/ecdh.rs index b6eef36c5..d5ee96d6d 100644 --- a/elliptic-curve/src/ecdh.rs +++ b/elliptic-curve/src/ecdh.rs @@ -34,7 +34,7 @@ use crate::{ }; use core::{borrow::Borrow, fmt}; use hkdf::Hkdf; -use rand_core::{CryptoRng, TryCryptoRng}; +use rand_core::TryCryptoRng; use zeroize::{Zeroize, ZeroizeOnDrop}; /// Low-level Elliptic Curve Diffie-Hellman (ECDH) function. @@ -108,9 +108,10 @@ where C: CurveArithmetic, { /// Generate a cryptographically random [`EphemeralSecret`]. - pub fn random(rng: &mut R) -> Self { + #[cfg(feature = "getrandom")] + pub fn generate() -> Self { Self { - scalar: NonZeroScalar::random(rng), + scalar: NonZeroScalar::generate(), } } diff --git a/elliptic-curve/src/scalar/nonzero.rs b/elliptic-curve/src/scalar/nonzero.rs index cc2a75daf..966d06bbc 100644 --- a/elliptic-curve/src/scalar/nonzero.rs +++ b/elliptic-curve/src/scalar/nonzero.rs @@ -13,7 +13,7 @@ use core::{ str, }; use ff::{Field, PrimeField}; -use rand_core::{CryptoRng, TryCryptoRng}; +use rand_core::TryCryptoRng; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use zeroize::Zeroize; @@ -51,12 +51,15 @@ where C: CurveArithmetic, { /// Generate a random `NonZeroScalar`. - pub fn random(rng: &mut R) -> Self { - // Use rejection sampling to eliminate zero values. + #[cfg(feature = "getrandom")] + pub fn generate() -> Self { + // Use rejection sampling to eliminate invalid values // While this method isn't constant-time, the attacker shouldn't learn // anything about unrelated outputs so long as `rng` is a secure `CryptoRng`. loop { - if let Some(result) = Self::new(Field::random(rng)).into() { + let mut repr = FieldBytes::::default(); + getrandom::fill(&mut repr).expect("RNG failure"); + if let Some(result) = Self::from_repr(repr).into() { break result; } } diff --git a/elliptic-curve/src/secret_key.rs b/elliptic-curve/src/secret_key.rs index bc2a10a6f..b51b77dc5 100644 --- a/elliptic-curve/src/secret_key.rs +++ b/elliptic-curve/src/secret_key.rs @@ -15,10 +15,7 @@ use subtle::{Choice, ConstantTimeEq, CtOption}; use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; #[cfg(feature = "arithmetic")] -use crate::{ - CurveArithmetic, NonZeroScalar, PublicKey, - rand_core::{CryptoRng, TryCryptoRng}, -}; +use crate::{CurveArithmetic, NonZeroScalar, PublicKey, rand_core::TryCryptoRng}; #[cfg(feature = "pem")] use pem_rfc7468::{self as pem, PemLabel}; @@ -87,13 +84,13 @@ where const MIN_SIZE: usize = 24; /// Generate a random [`SecretKey`]. - #[cfg(feature = "arithmetic")] - pub fn random(rng: &mut R) -> Self + #[cfg(feature = "getrandom")] + pub fn generate() -> Self where C: CurveArithmetic, { Self { - inner: NonZeroScalar::::random(rng).into(), + inner: NonZeroScalar::::generate().into(), } }