diff --git a/coldcard-cli/src/fw_upgrade.rs b/coldcard-cli/src/fw_upgrade.rs index 550d806..e984146 100644 --- a/coldcard-cli/src/fw_upgrade.rs +++ b/coldcard-cli/src/fw_upgrade.rs @@ -1,3 +1,4 @@ +use coldcard::util; use regex::Regex; use semver::Version; @@ -39,6 +40,7 @@ impl Release { .collect(); found.sort_by(|a, b| a.version.cmp(&b.version)); + found.dedup_by(|a, b| a.name == b.name); found.reverse(); Ok(found) @@ -74,6 +76,22 @@ impl Release { Ok(bytes) } + + /// Verifies the checksum of the downloaded firmware release. + pub fn verify(&self, dl: &[u8]) -> Result<(), &'static str> { + let sigs = fetch_signatures().map_err(|_| "Cannot fetch signature file")?; + let line = sigs + .lines() + .find(|l| l.ends_with(&self.name)) + .ok_or("Cannot find checksum")?; + let checksum = &line[0..64]; + + if checksum == hex::encode(util::sha256(dl)) { + Ok(()) + } else { + Err("Checksum mismatch") + } + } } pub fn best_match<'a>(releases: &'a [Release], our_model: Option<&str>) -> Option<&'a Release> { @@ -94,6 +112,12 @@ fn fetch_download_page() -> Result { .map(|r| r.into_string().expect("bad utf-8")) } +fn fetch_signatures() -> Result { + ureq::get("https://raw.githubusercontent.com/Coldcard/firmware/master/releases/signatures.txt") + .call() + .map(|r| r.into_string().expect("bad utf-8")) +} + fn firmware_regex() -> Regex { Regex::new(r"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]+-(v[0-9]+\.[0-9]+\.[0-9]+X?)(-mk.)?-coldcard.dfu") .unwrap() diff --git a/coldcard-cli/src/main.rs b/coldcard-cli/src/main.rs index 4e64f29..9d94667 100644 --- a/coldcard-cli/src/main.rs +++ b/coldcard-cli/src/main.rs @@ -558,8 +558,8 @@ fn handle(cli: Cli) -> Result<(), Error> { Command::Upgrade { path: Some(path) } => { let firmware = firmware::Firmware::load_dfu(&path)?; - ProgressBar::new(1, 2, "📁 Load firmware").finish(); - complete_upgrade(cc, firmware)?; + ProgressBar::new(1, 2, "📁 Read ").finish(); + complete_upgrade(cc, firmware, 2)?; } Command::Upgrade { path: None } => { @@ -681,9 +681,19 @@ fn handle(cli: Cli) -> Result<(), Error> { pb.finish(); + let pb = ProgressBar::new(2, 3, "🔒 Verify "); + + match release.verify(&fw_bytes) { + Ok(_) => pb.finish(), + Err(err) => { + pb.finish_with_err(err); + return Ok(()); + } + } + let firmware = firmware::Firmware::parse_dfu(&mut std::io::Cursor::new(fw_bytes))?; - complete_upgrade(cc, firmware)?; + complete_upgrade(cc, firmware, 3)?; } Command::User { @@ -807,8 +817,12 @@ fn load_psbt(path: &PathBuf) -> Result, Error> { } } -fn complete_upgrade(mut cc: coldcard::Coldcard, firmware: firmware::Firmware) -> Result<(), Error> { - let pb = ProgressBar::new(2, 2, "💾 Flashing"); +fn complete_upgrade( + mut cc: coldcard::Coldcard, + firmware: firmware::Firmware, + step: u16, +) -> Result<(), Error> { + let pb = ProgressBar::new(step, step, "💾 Flash "); let size = firmware.bytes().len(); cc.upgrade(firmware, |uploaded, _| { @@ -828,8 +842,8 @@ fn print_waiting() { } fn warn(text: &str) { - let warn = console::Style::new().color256(202).bold(); - eprintln!("{} {}", warn.apply_to("WARNING:"), text); + let prefix = console::Style::new().color256(202).bold(); + eprintln!("{} {}", prefix.apply_to("WARNING:"), text); } #[derive(Debug)] @@ -938,6 +952,14 @@ impl ProgressBar { .set_style(indicatif::ProgressStyle::with_template(FIN_TEMPLATE).unwrap()); self.pb.tick(); } + + pub fn finish_with_err(self, err: &str) { + let err = format!("{{prefix:.bold}}: {} ❌", err); + + self.pb + .set_style(indicatif::ProgressStyle::with_template(&err).unwrap()); + self.pb.tick(); + } } fn b64_encode(data: &[u8]) -> String {