radicle, cli/auth: Let binary crate generate seed

In preparation of a rewrite of `radicle-crypto`, move the
responsibility for generating a cryptographically secure
seed from `radicle` (transitively via dependency on
`radicle-crypto` and `ec25519`) to the dependent of `radicle`.

Applications should decide how to obtain randomness, not `radicle` via
some far-removed dependency.

Within the workspace, the only dependent using `Profile::seed` is
`radicle-cli`, in particular for the `rad auth` subcommand. To obtain
random bytes, `radicle-cli` depends on `getrandom` and uses
`getrandom::fill`.
This commit is contained in:
Lorenz Leutgeb
2026-07-17 16:52:33 +01:00
committed by Fintan Halpenny
parent 8ceef3c405
commit 0fd0104e87
4 changed files with 47 additions and 193 deletions
Generated
+7 -176
View File
@@ -1134,12 +1134,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -1199,15 +1193,13 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"wasip2",
"wasip3",
]
[[package]]
@@ -1445,7 +1437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d33b455e07b3c16d3b2eeebc7b38d2dafcbf8a653de1138ef55d4c2a1fd0b08b"
dependencies = [
"gix-hash",
"hashbrown 0.16.1",
"hashbrown",
"parking_lot",
]
@@ -1805,15 +1797,6 @@ dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
@@ -1972,12 +1955,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
@@ -2006,7 +1983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"hashbrown",
"serde",
"serde_core",
]
@@ -2174,12 +2151,6 @@ dependencies = [
"spin",
]
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "lexopt"
version = "0.3.2"
@@ -2779,16 +2750,6 @@ dependencies = [
"yansi",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn 2.0.117",
]
[[package]]
name = "primeorder"
version = "0.13.6"
@@ -2953,6 +2914,7 @@ dependencies = [
"clap",
"clap_complete",
"dunce",
"getrandom 0.4.3",
"human-panic",
"humantime",
"itertools",
@@ -4234,7 +4196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.2",
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.61.2",
@@ -4651,12 +4613,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unit-prefix"
version = "0.5.2"
@@ -4703,7 +4659,7 @@ version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37"
dependencies = [
"getrandom 0.4.2",
"getrandom 0.4.3",
"js-sys",
"wasm-bindgen",
]
@@ -4813,15 +4769,6 @@ dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.114"
@@ -4867,40 +4814,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "web-time"
version = "1.1.0"
@@ -5320,88 +5233,6 @@ name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn 2.0.117",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn 2.0.117",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
+1
View File
@@ -24,6 +24,7 @@ chrono = { workspace = true, features = ["clock", "std"] }
clap = { version = "4.5.44", features = ["derive"] }
clap_complete = "4.5"
dunce = { workspace = true }
getrandom = "0.4.3"
human-panic.workspace = true
humantime.workspace = true
itertools.workspace = true
+12 -1
View File
@@ -56,7 +56,18 @@ pub fn init(args: Args) -> anyhow::Result<()> {
};
let passphrase = passphrase.filter(|passphrase| !passphrase.trim().is_empty());
let spinner = term::spinner("Creating your Ed25519 keypair…");
let profile = Profile::init(home, alias, passphrase.clone(), env::seed())?;
let profile = Profile::init(
home,
alias,
passphrase.clone(),
env::seed().unwrap_or_else(|| {
use radicle::crypto::Seed;
let mut seed = [0; Seed::BYTES];
getrandom::fill(&mut seed).expect("failed get random bytes from the operating system");
Seed::new(seed)
}),
)?;
let mut agent = true;
spinner.finish();
+27 -16
View File
@@ -39,6 +39,8 @@ use crate::{cob, git, node, storage};
pub mod env {
pub use std::env::*;
use crypto::Seed;
/// Path to the Radicle home folder.
pub const RAD_HOME: &str = "RAD_HOME";
/// Path to the Radicle node socket file.
@@ -129,22 +131,31 @@ pub mod env {
}
/// Return the seed stored in the [`RAD_KEYGEN_SEED`] environment variable,
/// or generate a random one.
pub fn seed() -> crypto::Seed {
if let Ok(seed) = var(RAD_KEYGEN_SEED) {
let Ok(seed) = (0..seed.len())
.step_by(2)
.map(|i| u8::from_str_radix(&seed[i..i + 2], 16))
.collect::<Result<Vec<u8>, _>>()
else {
panic!("env::seed: invalid hexadecimal value set in `{RAD_KEYGEN_SEED}`");
};
let Ok(seed): Result<[u8; 32], _> = seed.try_into() else {
panic!("env::seed: invalid seed length set in `{RAD_KEYGEN_SEED}`");
};
crypto::Seed::new(seed)
} else {
crypto::Seed::generate()
/// if set. Otherwise, return `None`.
///
/// # Panics
///
/// If the environment value [`RAD_KEYGEN_SEED`] is set but malformed,
/// i.e., not Unicode, not hexadecimal, or not 32 bytes long.
pub fn seed() -> Option<Seed> {
match var(RAD_KEYGEN_SEED) {
Err(VarError::NotPresent) => None,
Err(VarError::NotUnicode(_)) => {
panic!("env::seed: invalid Unicode value set in `{RAD_KEYGEN_SEED}`")
}
Ok(seed) => Some(Seed::new(
(0..seed.len())
.step_by(2)
.map(|i| u8::from_str_radix(&seed[i..i + 2], 16))
.collect::<Result<Vec<_>, _>>()
.unwrap_or_else(|_| {
panic!("env::seed: invalid hexadecimal value set in `{RAD_KEYGEN_SEED}`")
})
.try_into()
.unwrap_or_else(|_| {
panic!("env::seed: invalid seed length set in `{RAD_KEYGEN_SEED}`")
}),
)),
}
}