summaryrefslogtreecommitdiffhomepage
path: root/mullvad-update/src/bin/mullvad-version-metadata.rs
blob: b65eee837f88c9c8b94f520bbc4a8910522c3f13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! See [Opt].

use anyhow::Context;
use clap::Parser;
use std::io::Read;
use tokio::{fs, io};

use mullvad_update::format::{self, key};

#[allow(dead_code)]
const DEFAULT_EXPIRY_MONTHS: u32 = 6;

/// A tool that generates signed Mullvad version metadata.
#[derive(Parser)]
pub enum Opt {
    /// Generate an ed25519 secret key
    GenerateKey,

    /// Sign a JSON payload using an ed25519 key and output the signed metadata
    /// This data is typically generated by 'generate-unsigned-metadata'
    Sign {
        /// File to sign. Use "-" to read from stdin.
        #[clap(short, long)]
        file: String,

        /// Secret ed25519 key used for signing, as hexadecimal string
        #[clap(short, long)]
        secret: key::SecretKey,
    },
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let opt = Opt::parse();

    match opt {
        Opt::GenerateKey => {
            println!("{}", key::SecretKey::generate());
            Ok(())
        }
        Opt::Sign { file, secret } => sign(file, secret).await,
    }
}

async fn sign(file: String, secret: key::SecretKey) -> anyhow::Result<()> {
    // Read unsigned JSON data
    let data = if file == "-" {
        get_stdin().await?
    } else {
        fs::read(file).await?
    };

    // Deserialize version data
    let response: format::Response =
        serde_json::from_slice(&data).context("Failed to deserialize version metadata")?;

    // Sign it
    let signed_response = format::SignedResponse::sign(secret, response)?;

    // Print it
    println!(
        "{}",
        serde_json::to_string_pretty(&signed_response)
            .context("Failed to serialize signed version")?
    );

    Ok(())
}

async fn get_stdin() -> io::Result<Vec<u8>> {
    tokio::task::spawn_blocking(|| {
        let mut buf = vec![];
        std::io::stdin().read_to_end(&mut buf)?;
        Ok(buf)
    })
    .await
    .unwrap()
}