summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-02-26 14:58:34 +0100
committerDavid Lönnhager <david.l@mullvad.net>2025-03-06 00:09:19 +0100
commit583e28c03f5e8374e15df9df9a07fee25c4f0e8b (patch)
tree61fe48bc80b3965d4ed46da7b4768baa59ab7e03
parentbacc30a30be8bc3953f8d358d9545e302c5a0df6 (diff)
downloadmullvadvpn-583e28c03f5e8374e15df9df9a07fee25c4f0e8b.tar.xz
mullvadvpn-583e28c03f5e8374e15df9df9a07fee25c4f0e8b.zip
Allow ed25519 secret to be entered in stdin in meta tool
-rw-r--r--mullvad-update/meta/src/io_util.rs42
-rw-r--r--mullvad-update/meta/src/main.rs23
2 files changed, 52 insertions, 13 deletions
diff --git a/mullvad-update/meta/src/io_util.rs b/mullvad-update/meta/src/io_util.rs
index bcadc222c1..156af039aa 100644
--- a/mullvad-update/meta/src/io_util.rs
+++ b/mullvad-update/meta/src/io_util.rs
@@ -10,18 +10,21 @@ use tokio::fs;
pub async fn wait_for_confirm(prompt: &str) -> bool {
const DEFAULT: bool = true;
- print!("{prompt}");
- if DEFAULT {
- println!(" [Y/n]");
- } else {
- println!(" [y/N]");
- }
+ let prompt = prompt.to_owned();
- tokio::task::spawn_blocking(|| {
- let mut s = String::new();
+ tokio::task::spawn_blocking(move || {
let stdin = std::io::stdin();
loop {
+ let mut s = String::new();
+
+ print!("{prompt}");
+ if DEFAULT {
+ println!(" [Y/n]");
+ } else {
+ println!(" [y/N]");
+ }
+
stdin.read_line(&mut s).context("Failed to read line")?;
match s.trim().to_ascii_lowercase().as_str() {
@@ -37,6 +40,29 @@ pub async fn wait_for_confirm(prompt: &str) -> bool {
.unwrap_or(false)
}
+/// Wait for user to respond with any input, ignoring empty responses
+pub async fn wait_for_input(prompt: &str) -> anyhow::Result<String> {
+ let prompt = prompt.to_owned();
+ tokio::task::spawn_blocking(move || {
+ let stdin = std::io::stdin();
+
+ loop {
+ let mut s = String::new();
+
+ println!("{prompt}");
+
+ stdin.read_line(&mut s).context("Failed to read line")?;
+
+ match s.trim().to_ascii_lowercase().as_str() {
+ "" => continue,
+ input => break Ok(input.to_owned()),
+ }
+ }
+ })
+ .await
+ .unwrap()
+}
+
/// Recursively create directories and write to 'file'
pub async fn create_dir_and_write(
path: impl AsRef<Path>,
diff --git a/mullvad-update/meta/src/main.rs b/mullvad-update/meta/src/main.rs
index 58bb3c5ebd..1bc1dd4f9f 100644
--- a/mullvad-update/meta/src/main.rs
+++ b/mullvad-update/meta/src/main.rs
@@ -5,13 +5,14 @@
use anyhow::{bail, Context};
use clap::Parser;
+use std::str::FromStr;
+
use config::Config;
use io_util::create_dir_and_write;
+use platform::Platform;
use mullvad_update::format::{self, key, SignedResponse};
-use platform::Platform;
-
mod artifacts;
mod config;
mod github;
@@ -93,10 +94,12 @@ pub enum Opt {
/// Sign using an ed25519 key and output the signed metadata to `signed/`
Sign {
- /// Secret ed25519 key used for signing, as hexadecimal string
- secret: key::SecretKey,
/// Platforms to remove releases for. All if none are specified
platforms: Vec<Platform>,
+ /// Secret ed25519 key used for signing, as hexadecimal string
+ /// If not specified, this will be read from user
+ #[arg(long)]
+ secret: Option<key::SecretKey>,
/// When the metadata expires, in months from now
#[arg(long, default_value_t = DEFAULT_EXPIRY_MONTHS)]
expiry: usize,
@@ -145,11 +148,21 @@ async fn main() -> anyhow::Result<()> {
Ok(())
}
Opt::Sign {
- secret,
platforms,
+ secret,
expiry,
assume_yes,
} => {
+ let secret = match secret {
+ Some(secret) => secret,
+ None => {
+ let key_str = io_util::wait_for_input("Enter ed25519 secret: ")
+ .await
+ .context("Failed to read secret from stdin")?;
+ key::SecretKey::from_str(&key_str).context("Invalid secret")?
+ }
+ };
+
for platform in all_platforms_if_empty(platforms) {
platform
.sign(secret.clone(), expiry, assume_yes)