Skip to content

Commit 28da100

Browse files
Merge pull request #252 from klever-io/fix/rand-unsound-with-a-custom-logger
Fix/rand unsound with a custom logger
2 parents 5695931 + 9a86101 commit 28da100

10 files changed

Lines changed: 947 additions & 605 deletions

File tree

Cargo.lock

Lines changed: 878 additions & 540 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,52 +46,46 @@ edition = "2021"
4646
homepage = "https://klever.org/"
4747
license = "Apache-2.0"
4848
repository = "https://github.com/klever-io/kos-rs"
49-
rust-version = "1.74.0"
49+
rust-version = "1.85.0"
5050
version = "0.3.0"
5151

5252
[workspace.dependencies]
5353
bech32 = "0.9.1"
54-
sha2 = { version = "0.10" }
55-
sha3 = { version = "0.10" }
54+
sha2 = { version = "0.11.0" }
55+
sha3 = { version = "0.11.0" }
5656
hmac = { version = "0.12" }
5757
secp256k1 = { version = "0.27", features = [
5858
"recovery",
5959
"rand",
6060
"bitcoin_hashes",
6161
] }
6262
ed25519-dalek = { version = "2" }
63-
coins-bip32 = { version = "0.8", default-features = false }
64-
coins-bip39 = { version = "0.8", default-features = false, features = [
63+
coins-bip32 = { version = "0.13.0", default-features = false }
64+
coins-bip39 = { version = "0.13.0", default-features = false, features = [
6565
"english",
6666
] }
6767
bytes = { version = "1.11.1", default-features = false }
6868
hex = { version = "0.4.3", default-features = false }
69-
rand = "0.8"
70-
69+
rand = "0.10.1"
7170
prost = "0.11"
7271
prost-types = "0.11"
73-
74-
7572
lazy_static = "1.4.0"
76-
77-
7873
reqwest = { version = "0.12", default-features = false, features = [
7974
"rustls-tls",
8075
"blocking",
8176
"json",
8277
] }
8378
wasm-bindgen = "0.2"
84-
uniffi = { version = "0.28.1" }
79+
uniffi = { version = "0.31.1" }
8580
enum_delegate = "0.2"
8681
serde = { version = "1.0", default-features = false }
8782
serde_json = "1.0"
88-
thiserror = "1.0"
83+
thiserror = "2.0.18"
8984
kos-mobile = { version = "0.3.0", path = "./packages/kos-mobile", default-features = false }
9085
ecies = { version = "0.2.7", default-features = false, features = ["pure"] }
9186
kos = { version = "0.3.0", path = "./packages/kos", default-features = false, features = ["not-ksafe"] }
9287

9388
# lightning
94-
lwk_common = "0.9.0"
95-
lwk_signer = "0.9.0"
96-
# lwk_wollet = { git = "https://github.com/breez/lwk", branch = "breez-sdk-liquid-0.6.3" }
97-
lwk_wollet = "0.9.0"
89+
lwk_common = "0.16.0"
90+
lwk_signer = "0.16.0"
91+
lwk_wollet = "0.16.0"

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
UNAME := $(shell uname)
44

55
all: fmt
6-
cargo build
6+
cargo build -j4
77

88
fmt:
99
cargo fmt --all -- --check
@@ -35,7 +35,7 @@ clean-mobile-build:
3535
cd packages/kos-mobile && ./build_clean.sh
3636

3737
build-ksafe:
38-
cargo build --package kos-hardware --target thumbv7em-none-eabihf --profile hardware
38+
cargo build -j4 --package kos-hardware --target thumbv7em-none-eabihf --profile hardware
3939

4040
build-android:
4141
cd packages/kos-mobile && ./build_android.sh

packages/kos-codec/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ crate-type = ["lib"]
1717
kos = { workspace = true }
1818
ciborium = "0.2.2"
1919
hex = { version = "0.4.3", features = ["alloc"], default-features = false }
20-
xrpl-rust = { version = "0.5.0", default-features = false, features = ["core", "utils"] }
20+
xrpl-rust = { version = "1.1.0", default-features = false, features = ["core", "utils"] }
2121
cosmrs = { version = "0.21.1", default-features = false }
2222
bitcoin = { version = "0.32.5", default-features = false }
2323
byteorder = { version = "1.5.0" }
@@ -34,7 +34,7 @@ bs58 = { version = "0.5", default-features = false, features = ["alloc"] }
3434
sha3 = { version = "0.10.8", default-features = false }
3535
regex = { version = "1.11.1", default-features = false, features = ["perf"] }
3636
base64 = "0.22.1"
37-
sha2 = "0.10.9"
37+
sha2 = {workspace = true }
3838
base32 = "0.5.1"
3939
leb128 = "0.2.5"
4040
serde_cbor = "0.11.2"

packages/kos-web/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ serde = { workspace = true, features = ["derive"], optional = true }
2020
serde_json = { workspace = true }
2121
serde-wasm-bindgen = "0.5"
2222
qrcode-generator = "4.1"
23+
getrandom = { version = "0.4", features = ["wasm_js"] }
2324
enum_delegate = { workspace = true }
2425
hex = { workspace = true }
2526
enum_dispatch = "0.3"

packages/kos/Cargo.toml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ blake2b-ref = { version = "0.3.1", default-features = false }
2323
rlp = { version = "0.5.0", default-features = false }
2424
hex = { version = "0.4.3", features = ["alloc"], default-features = false }
2525
ripemd = { version = "0.1.3", default-features = false }
26-
schnorrkel = { version = "0.11.4", default-features = false }
26+
schnorrkel = { version = "0.11.5", default-features = false }
2727
ed25519-dalek = { version = "2.1.0", features = ["hazmat"], default-features = false }
2828
pbkdf2 = { version = "0.12.2", features = ["sha2", "hmac", "password-hash"], default-features = false }
2929
bip39-dict = { version = "0.1.0", features = ["english"], default-features = false }
3030
libsecp256k1 = { version = "0.7.1", features = ["hmac", "static-context"], default-features = false }
31-
rand_core = { version = "0.6.4", default-features = false }
32-
33-
rand = { workspace = true, optional = true }
31+
rand_core = { version = "0.10.1", default-features = false }
32+
rand = { version = "0.10.1", optional = true, features = ["sys_rng"] }
3433
coins-bip32 = { workspace = true, optional = true }
3534
coins-bip39 = { workspace = true, optional = true }
3635
getrandom = { version = "0.2", features = ["js"], optional = true }
@@ -44,16 +43,13 @@ cbc = { version = "0.1", features = ["block-padding", "std"], optional = true }
4443
serde = { version = "1.0.215", features = ["derive"], optional = true }
4544
base32 = { version = "0.5.1" }
4645

47-
48-
49-
5046
[features]
5147
default = ["not-ksafe"]
5248
not-ksafe = [
49+
"schnorrkel/std",
5350
"rand",
5451
"coins-bip32",
5552
"coins-bip39",
56-
"rand_core/getrandom",
5753
"getrandom",
5854
"alloy-dyn-abi",
5955
"serde_json",

packages/kos/src/crypto/cipher.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -353,11 +353,8 @@ pub fn create_encrypted_certificate_pem(
353353
}
354354

355355
pub fn create_checksum(password: &str) -> String {
356-
let rng = rand::thread_rng();
357-
let salt = SaltString::generate(rng);
358-
let password_hash = pbkdf2::Pbkdf2
359-
.hash_password(password.as_bytes(), &salt)
360-
.unwrap();
356+
let salt = SaltString::generate(&mut OsRng);
357+
let password_hash = Pbkdf2.hash_password(password.as_bytes(), &salt).unwrap();
361358

362359
password_hash.to_string()
363360
}
@@ -397,7 +394,8 @@ pub fn decrypt(data: &[u8], password: &str, iterations: u32) -> Result<Vec<u8>,
397394

398395
pub fn gcm_encrypt(data: &[u8], password: &str, iterations: u32) -> Result<Vec<u8>, ChainError> {
399396
// Derive key from password
400-
let salt: [u8; 32] = rand::thread_rng().gen();
397+
let mut salt = [0u8; 32];
398+
rand::rng().fill_bytes(&mut salt);
401399
let derived_key = derive_key(&salt, password, iterations);
402400
let key = Key::<Aes256Gcm>::from_slice(&derived_key);
403401
let cipher = Aes256Gcm::new(key);
@@ -441,7 +439,8 @@ pub fn gcm_decrypt(
441439
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
442440
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
443441
pub fn cbc_encrypt(data: &[u8], password: &str, iterations: u32) -> Result<Vec<u8>, ChainError> {
444-
let iv: [u8; IV_SIZE] = rand::thread_rng().gen();
442+
let mut iv = [0u8; IV_SIZE];
443+
rand::rng().fill_bytes(&mut iv);
445444
let derived_key = derive_key(&iv, password, iterations); // KeySize [u8; 32]
446445
let key = GenericArray::from_slice(&derived_key);
447446

@@ -460,7 +459,8 @@ pub fn cbc_encrypt(data: &[u8], password: &str, iterations: u32) -> Result<Vec<u
460459
hmac_input.extend_from_slice(&iv);
461460
hmac_input.extend_from_slice(ct);
462461

463-
let hmac_salt: [u8; 32] = rand::thread_rng().gen();
462+
let mut hmac_salt = [0u8; 32];
463+
rand::rng().fill_bytes(&mut hmac_salt);
464464
let hmac_key = derive_key(&hmac_salt, password, iterations);
465465

466466
let mut mac: Hmac<Sha256> = <Hmac<Sha256> as KeyInit>::new_from_slice(&hmac_key)
@@ -523,7 +523,8 @@ pub fn cbc_decrypt(
523523
type Aes256CfbEnc = cfb_mode::Encryptor<aes::Aes256>;
524524
type Aes256CfbDec = cfb_mode::Decryptor<aes::Aes256>;
525525
pub fn cfb_encrypt(data: &[u8], password: &str, iterations: u32) -> Result<Vec<u8>, ChainError> {
526-
let iv: [u8; IV_SIZE] = rand::thread_rng().gen();
526+
let mut iv = [0u8; IV_SIZE];
527+
rand::rng().fill_bytes(&mut iv);
527528
let derived_key = derive_key(&iv, password, iterations); // KeySize [u8; 32]
528529
let key = GenericArray::from_slice(&derived_key);
529530

packages/kos/src/crypto/mnemonic.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
use crate::chains::ChainError;
2+
use alloc::vec;
23
use alloc::vec::Vec;
3-
4+
use coins_bip39::mnemonic::Entropy;
45
use coins_bip39::{English, Mnemonic};
6+
use rand::Rng;
7+
8+
fn word_count_to_entropy_bytes(count: usize) -> Result<usize, ChainError> {
9+
match count {
10+
12 => Ok(16),
11+
15 => Ok(20),
12+
18 => Ok(24),
13+
21 => Ok(28),
14+
24 => Ok(32),
15+
_ => Err(ChainError::InvalidMnemonic),
16+
}
17+
}
518

19+
#[cfg(feature = "not-ksafe")]
620
pub fn generate_mnemonic(count: usize) -> Result<Mnemonic<English>, ChainError> {
7-
// create rng
8-
let mut rng = rand::thread_rng();
9-
// generate mnemonic phrase
10-
Ok(Mnemonic::<English>::new_with_count(&mut rng, count)?)
21+
let entropy_bytes = word_count_to_entropy_bytes(count)?;
22+
let mut buf = vec![0u8; entropy_bytes];
23+
rand::rng().fill_bytes(&mut buf[..]);
24+
let entropy = Entropy::from_slice(&buf)?;
25+
Ok(Mnemonic::<English>::new_from_entropy(entropy))
1126
}
1227

1328
pub fn validate_mnemonic(phrase: &str) -> Result<(), ChainError> {
14-
// validate mnemonic phrase
1529
let _mnemonic: Mnemonic<English> = phrase.parse()?;
1630
Ok(())
1731
}

packages/kos/src/crypto/rng.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
#[allow(unused_imports)]
2-
use rand_core::{CryptoRng, Error, RngCore};
1+
use rand_core::CryptoRng;
2+
3+
#[cfg(feature = "ksafe")]
4+
use rand_core::{TryCryptoRng, TryRng};
35

46
#[cfg(feature = "ksafe")]
57
extern "C" {
@@ -12,44 +14,44 @@ extern "C" {
1214
struct MyRng;
1315

1416
#[cfg(feature = "ksafe")]
15-
impl RngCore for MyRng {
16-
fn next_u32(&mut self) -> u32 {
17+
impl TryRng for MyRng {
18+
type Error = core::convert::Infallible;
19+
20+
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
1721
let mut buf = [0u8; 4];
1822
unsafe {
1923
random_buffer(buf.as_mut_ptr(), buf.len() as u32);
2024
}
21-
u32::from_ne_bytes(buf)
25+
Ok(u32::from_ne_bytes(buf))
2226
}
2327

24-
fn next_u64(&mut self) -> u64 {
28+
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
2529
let mut buf = [0u8; 8];
2630
unsafe {
2731
random_buffer(buf.as_mut_ptr(), buf.len() as u32);
2832
}
29-
u64::from_ne_bytes(buf)
33+
Ok(u64::from_ne_bytes(buf))
3034
}
3135

32-
fn fill_bytes(&mut self, dest: &mut [u8]) {
36+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
3337
unsafe {
3438
random_buffer(dest.as_mut_ptr(), dest.len() as u32);
3539
}
36-
}
37-
38-
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
39-
self.fill_bytes(dest);
4040
Ok(())
4141
}
4242
}
4343

4444
#[cfg(feature = "ksafe")]
45-
impl CryptoRng for MyRng {}
45+
impl TryCryptoRng for MyRng {}
4646

4747
#[cfg(not(feature = "ksafe"))]
48-
pub fn getrandom_or_panic() -> impl RngCore + CryptoRng {
49-
rand_core::OsRng
48+
#[allow(dead_code)]
49+
pub fn getrandom_or_panic() -> impl CryptoRng {
50+
rand_core::UnwrapErr(rand::rngs::SysRng)
5051
}
5152

5253
#[cfg(feature = "ksafe")]
53-
pub fn getrandom_or_panic() -> impl RngCore + CryptoRng {
54+
#[allow(dead_code)]
55+
pub fn getrandom_or_panic() -> impl CryptoRng {
5456
MyRng
5557
}

packages/kos/src/crypto/sr25519.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use alloc::vec::Vec;
22
use core::fmt::{Display, Formatter};
3-
use schnorrkel::context::SigningContext;
43
use schnorrkel::derive::ChainCode;
54

65
const SUBSTRATE_CTX: &[u8; 9] = b"substrate";
@@ -40,11 +39,8 @@ impl Sr25519Trait for Sr25519 {
4039

4140
fn sign(msg: &[u8], pvk: &[u8; 64]) -> Result<Vec<u8>, Sr25519Error> {
4241
let pvk = schnorrkel::SecretKey::from_bytes(pvk).map_err(|_| Sr25519Error::ErrDerive)?;
43-
4442
let pbk = pvk.to_public();
45-
let ctx = SigningContext::new(SUBSTRATE_CTX).bytes(msg);
46-
let ctx = schnorrkel::context::attach_rng(ctx, crate::crypto::rng::getrandom_or_panic());
47-
let sig = pvk.sign(ctx, &pbk);
43+
let sig = pvk.sign_simple(SUBSTRATE_CTX, msg, &pbk);
4844
Ok(sig.to_bytes().as_slice().to_vec())
4945
}
5046

0 commit comments

Comments
 (0)