hash_based_signatures/
cli.rs1use 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}