diff options
| author | David Lönnhager <david.l@mullvad.net> | 2025-02-26 14:58:34 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2025-03-06 00:09:19 +0100 |
| commit | 583e28c03f5e8374e15df9df9a07fee25c4f0e8b (patch) | |
| tree | 61fe48bc80b3965d4ed46da7b4768baa59ab7e03 | |
| parent | bacc30a30be8bc3953f8d358d9545e302c5a0df6 (diff) | |
| download | mullvadvpn-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.rs | 42 | ||||
| -rw-r--r-- | mullvad-update/meta/src/main.rs | 23 |
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) |
