hash_based_signatures/
cli.rs

1use crate::io::hash_file;
2use crate::signature::stateless_merkle::StatelessMerkleSignatureScheme;
3use crate::signature::winternitz::d::D;
4use crate::signature::{HashType, SignatureScheme};
5use crate::utils::{slice_to_hash, string_to_hash};
6use anyhow::{bail, Context, Result};
7use data_encoding::HEXLOWER;
8use rand::RngCore;
9use std::fs;
10use std::path::PathBuf;
11use std::time::{Duration, Instant};
12
13fn timed<F, T>(f: F) -> (Duration, T)
14where
15    F: FnOnce() -> T,
16{
17    let start = Instant::now();
18    let result = f();
19    let elapsed_time = start.elapsed();
20    (elapsed_time, result)
21}
22
23pub fn keygen(width: usize, depth: usize, d: u64) -> Result<()> {
24    println!();
25    println!(" #######################");
26    println!("   Generating key");
27    println!(" #######################");
28    println!();
29
30    let mut seed = [0u8; 32];
31    let mut rng = rand::thread_rng();
32    rng.fill_bytes(&mut seed);
33
34    if width & (width - 1) != 0 {
35        bail!("Width {width} is not a power of 2!")
36    }
37
38    let d = D::try_from(d)?;
39    let (time, signature_scheme) =
40        timed(move || StatelessMerkleSignatureScheme::new(seed, width, depth, d));
41    println!("  (Key generation took: {:?})\n", time);
42
43    let private_key = signature_scheme.private_key();
44    let public_key = signature_scheme.public_key();
45
46    let private_key_json =
47        serde_json::to_string_pretty(&private_key).context("Error serializing private key.")?;
48    let output_path = ".private_key.json";
49    fs::write(output_path, private_key_json).context("Could not write private key.")?;
50
51    println!("Public key:       {}", HEXLOWER.encode(&public_key));
52    println!("Private key path: {}", output_path);
53
54    println!(
55        "\n\nRemember that you should generate a new key pair well before having \
56    signed sqrt(width^depth) messages, which in your case is about {:0.2e}.",
57        (width as f32).powf(depth as f32 / 2.0)
58    );
59
60    Ok(())
61}
62
63pub fn sign(path: PathBuf) -> Result<()> {
64    println!();
65    println!(" #######################");
66    println!("   Signing File");
67    println!(" #######################");
68    println!();
69
70    let private_key_json =
71        fs::read_to_string(".private_key.json").context("Error reading private key")?;
72    let private_key =
73        serde_json::from_str(&private_key_json).context("Error parsing private key")?;
74
75    let file_hash = hash_file(&path)?;
76    let mut signature_scheme = StatelessMerkleSignatureScheme::from_private_key(&private_key)
77        .context("Error instantiating signature scheme from private key in .private_key.json.")?;
78
79    if string_to_hash(&private_key.public_key) != signature_scheme.public_key() {
80        bail!(
81            "The public key referenced in .private_key.json cannot be derived from the private key. \
82                This is probably because of an incompatible implementation change. \
83                Re-run key generation or manually change the public key to {}",
84            HEXLOWER.encode(&signature_scheme.public_key())
85        )
86    }
87
88    let (time, signature) = timed(|| signature_scheme.sign(slice_to_hash(file_hash.as_ref())));
89    println!("  (Signing took: {:?})\n", time);
90
91    println!("File Path:      {}", path.display());
92    println!("Hash:           {}", HEXLOWER.encode(file_hash.as_ref()));
93    println!(
94        "Public key:     {}",
95        HEXLOWER.encode(&signature_scheme.public_key())
96    );
97
98    let output_path = format!("{}.signature", path.display());
99    println!("Signature path: {}", output_path);
100
101    let signature_bytes = rmp_serde::to_vec(&signature).context("Error serializing signature")?;
102    fs::write(&output_path, &signature_bytes)
103        .with_context(|| format!("Could not write signature to {:?}", output_path))?;
104
105    Ok(())
106}
107
108pub fn verify(file_path: PathBuf, signature_path: PathBuf, public_key: HashType) -> Result<bool> {
109    println!();
110    println!(" #######################");
111    println!("   Verifying file");
112    println!(" #######################");
113    println!();
114
115    let file_hash = hash_file(&file_path)?;
116
117    let signature_bytes = fs::read(&signature_path).with_context(|| {
118        format!(
119            "Cannot signature file at {:?}. Does it exist?",
120            &signature_path
121        )
122    })?;
123    let signature = rmp_serde::from_slice(&signature_bytes)
124        .with_context(|| format!("Signature at {:?} is malformed.", &signature_path))?;
125
126    let (time, verifies) = timed(|| {
127        StatelessMerkleSignatureScheme::verify(
128            public_key,
129            slice_to_hash(file_hash.as_ref()),
130            &signature,
131        )
132    });
133    println!("  (Verification took: {:?})\n", time);
134
135    println!("File Path:      {}", file_path.display());
136    println!("Signature Path: {}", signature_path.display());
137    println!("Valid:          {}", verifies);
138
139    Ok(verifies)
140}