diff --git a/Cargo.toml b/Cargo.toml index 56ef6e670..5aa61033c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,11 @@ experimental-session-config = [] # The low-level-api feature exposes extra APIs that are only useful in advanced # use cases and require extra care to use. low-level-api = [] +# The os-rng feature enables the use of the operating system's RNG instead of +# the default random number generator provided by the `rand` crate, which is not +# backtracking resistant. This is usually not necessary, but can be enabled +# for security-critical applications or compliance purposes. +os-rng = [] [dependencies] aes = { version = "0.9.1" } @@ -68,6 +73,7 @@ hpke = { version = "0.14.0", default-features = false, features = ["alloc", "cha matrix-pickle = { version = "0.2.3" } prost = "0.14.4" rand = "0.10.2" +rand_core = "0.10.1" serde = { version = "1.0.228", features = ["derive"] } serde_bytes = "0.11.19" serde_json = "1.0.150" diff --git a/src/ecies/mod.rs b/src/ecies/mod.rs index 4e028269f..e02692adb 100644 --- a/src/ecies/mod.rs +++ b/src/ecies/mod.rs @@ -82,15 +82,14 @@ use chacha20poly1305::{ChaCha20Poly1305, Key as Chacha20Key, KeyInit, Nonce, aead::Aead}; use hkdf::Hkdf; -use rand::rng; use sha2::Sha512; use thiserror::Error; use x25519_dalek::{EphemeralSecret, SharedSecret}; use zeroize::{Zeroize, ZeroizeOnDrop}; pub use self::messages::{InitialMessage, Message, MessageDecodeError}; -use crate::Curve25519PublicKey; pub use crate::hpke::{CheckCode, DigitMode}; +use crate::{Curve25519PublicKey, utilities::rng}; mod messages; diff --git a/src/lib.rs b/src/lib.rs index 60fa10f8c..803fdd98d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,19 @@ //! vodozemac = { version = "0.10.0", default-features = false, features = ["libolm-compat"] } //! ``` //! +//! ## OS RNG +//! +//! Feature: `os-rng` (default: off) +//! +//! Enabling this feature will use the operating system's RNG instead of the +//! default random number generator provided by the `rand` crate, which is not +//! backtracking resistant. This is usually not necessary, but can be enabled +//! for security-critical applications or compliance purposes. +//! +//! Please note that OS RNG is not guaranteed to be backtracking resistant either +//! since it will depend on the underlying OS implementation, but the RNG state +//! will be available in kernel space instead of user space so harder to extract. +//! //! # Pickling //! //! vodozemac supports serializing its entire internal state into a form diff --git a/src/megolm/ratchet.rs b/src/megolm/ratchet.rs index 2a4a6264d..0ed8c5b06 100644 --- a/src/megolm/ratchet.rs +++ b/src/megolm/ratchet.rs @@ -14,13 +14,15 @@ // limitations under the License. use hmac::{Hmac, KeyInit, Mac as _}; -use rand::{Rng, rng}; +use rand::Rng; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use sha2::{Sha256, digest::CtOutput}; use subtle::{Choice, ConstantTimeEq}; use thiserror::Error; use zeroize::{Zeroize, ZeroizeOnDrop}; +use crate::utilities::rng; + const ADVANCEMENT_SEEDS: [&[u8; 1]; Ratchet::RATCHET_PART_COUNT] = [b"\x00", b"\x01", b"\x02", b"\x03"]; diff --git a/src/sas.rs b/src/sas.rs index 284268f27..501f3c935 100644 --- a/src/sas.rs +++ b/src/sas.rs @@ -53,14 +53,13 @@ use hkdf::Hkdf; use hmac::{Hmac, KeyInit, Mac as _, digest::MacError}; -use rand::rng; use sha2::Sha256; use thiserror::Error; use x25519_dalek::{EphemeralSecret, SharedSecret}; use crate::{ Curve25519PublicKey, KeyError, - utilities::{base64_decode, base64_encode}, + utilities::{base64_decode, base64_encode, rng}, }; type HmacSha256Key = Box<[u8; 32]>; diff --git a/src/types/curve25519.rs b/src/types/curve25519.rs index a0c77430e..aef6ab5a4 100644 --- a/src/types/curve25519.rs +++ b/src/types/curve25519.rs @@ -16,13 +16,12 @@ use std::fmt::Display; use base64::decoded_len_estimate; use matrix_pickle::{Decode, DecodeError}; -use rand::rng; use serde::{Deserialize, Serialize}; use x25519_dalek::{EphemeralSecret, PublicKey, ReusableSecret, SharedSecret, StaticSecret}; use zeroize::Zeroize; use super::KeyError; -use crate::utilities::{base64_decode, base64_encode}; +use crate::utilities::{base64_decode, base64_encode, rng}; /// Struct representing a Curve25519 secret key. #[derive(Clone, Deserialize, Serialize)] diff --git a/src/types/ed25519.rs b/src/types/ed25519.rs index 2885c668b..46cc03c9c 100644 --- a/src/types/ed25519.rs +++ b/src/types/ed25519.rs @@ -20,14 +20,13 @@ use curve25519_dalek::EdwardsPoint; use ed25519_dalek::{ PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, Signature, Signer, SigningKey, VerifyingKey, }; -use rand::rng; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes}; use sha2::Sha512; use thiserror::Error; use zeroize::Zeroize; -use crate::utilities::{base64_decode, base64_encode}; +use crate::utilities::{base64_decode, base64_encode, rng}; /// Error type describing signature verification failures. #[derive(Debug, Error)] diff --git a/src/utilities/mod.rs b/src/utilities/mod.rs index 067628d93..a75643ae2 100644 --- a/src/utilities/mod.rs +++ b/src/utilities/mod.rs @@ -19,9 +19,14 @@ use base64::{ DecodeError, Engine, alphabet, engine::{GeneralPurpose, general_purpose}, }; +#[cfg(feature = "os-rng")] +use getrandom::SysRng; pub(crate) use libolm_compat::get_version as get_pickle_version; #[cfg(feature = "libolm-compat")] pub(crate) use libolm_compat::{LibolmEd25519Keypair, pickle_libolm, unpickle_libolm}; +use rand::CryptoRng; +#[cfg(feature = "os-rng")] +use rand_core::UnwrapErr; const STANDARD_NO_PAD: GeneralPurpose = GeneralPurpose::new( &alphabet::STANDARD, @@ -153,6 +158,16 @@ impl VarInt for u64 { } } +#[cfg(feature = "os-rng")] +pub(crate) fn rng() -> impl CryptoRng { + UnwrapErr(SysRng) +} + +#[cfg(not(feature = "os-rng"))] +pub(crate) fn rng() -> impl CryptoRng { + rand::rng() +} + #[cfg(test)] mod test { use ntest::timeout;