Skip to content

Commit

Permalink
Merge pull request #29 from Foundation-Devices/jeandudey/sft-3538-add…
Browse files Browse the repository at this point in the history
…-foundation-firmware-crate-to-parse-firmware-images

SFT-3538: Add firmware image parser.
  • Loading branch information
jeandudey committed May 15, 2024
2 parents ee69479 + 84faf84 commit 808e571
Show file tree
Hide file tree
Showing 10 changed files with 920 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ jobs:
#
# This is not needed for stable versions.
- run: cargo update -p proc-macro2 --precise 1.0.66 -Z minimal-versions
# hex-conservative uses wrong minimal versions.
- run: cargo update -p arrayvec --precise 0.7.4 -Z minimal-versions
- run: cargo check -Z minimal-versions
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ target/

# CMake
**/cmake-build-debug

# Firmware files.
*.bin
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"arena",
"codecs",
"ffi",
"firmware",
"test-vectors",
"ur",
"urtypes",
Expand All @@ -17,11 +18,12 @@ members = [
homepage = "https://github.com/Foundation-Devices/foundation-rs"

[workspace.dependencies]
anyhow = "1.0.83"
arbitrary = { version = "1", features = ["derive"] }
bech32 = { version = "0.9", default-features = false }
bip39 = { version = "2", default-features = false }
bitcoin = { version = "0.31", default-features = false }
bitcoin_hashes = { version = "0.13", default-features = false }
bitcoin_hashes = { version = "0.14", default-features = false }
bs58 = "0.5"
crc = "3"
criterion = { version = "0.4" }
Expand All @@ -30,8 +32,10 @@ hex = { version = "0.4.2", default-features = false }
itertools = { version = "0.10", default-features = false }
libfuzzer-sys = "0.4"
minicbor = { version = "0.20", features = ["derive"] }
nom = { version = "7", default-features = false }
phf = { version = "0.11", features = ["macros"], default-features = false }
rand_xoshiro = "0.6"
secp256k1 = { version = "0.29", default-features = false }
serde = { version = "1.0.156", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", default-features = false }
Expand Down
232 changes: 232 additions & 0 deletions LICENSES/GPL-3.0-only.txt

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions ffi/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ set(CMAKE_CXX_STANDARD 14)

FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.3.5
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion
GIT_TAG v0.4.9
)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_REPOSITORY https://github.com/google/googletest
GIT_TAG v1.13.0
)

Expand Down
27 changes: 27 additions & 0 deletions firmware/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. <[email protected]>
# SPDX-License-Identifier: GPL-3.0-or-later

[package]
name = "foundation-firmware"
version = "0.1.0"
description = "Firmware image format"
homepage.workspace = true
edition = "2021"
license = "GPL-3.0-or-later AND GPL-3.0-only"

[[bin]]
name = "foundation-firmware"
required-features = ["binary"]

[features]
default = ["std", "binary"]
std = ["hex?/std", "nom/std", "secp256k1/std"]
binary = ["anyhow", "hex", "secp256k1/global-context", "std"]

[dependencies]
bitcoin_hashes = { workspace = true }
heapless = { workspace = true }
hex = { workspace = true, optional = true }
nom = { workspace = true }
secp256k1 = { workspace = true }
anyhow = { workspace = true, optional = true }
91 changes: 91 additions & 0 deletions firmware/src/bin/foundation-firmware.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later

use anyhow::{anyhow, bail, Context, Result};
use bitcoin_hashes::{sha256, sha256d, Hash, HashEngine};
use foundation_firmware::{header, verify_signature, Information, HEADER_LEN};
use nom::Finish;
use secp256k1::global::SECP256K1;
use std::fs;

fn main() -> Result<()> {
let file_name = std::env::args_os()
.nth(1)
.ok_or_else(|| anyhow!("provide a file name"))?;

let file_buf = fs::read(file_name).context("failed to read firmware")?;

let header_len = usize::try_from(HEADER_LEN).unwrap();
let header = match header(&file_buf[..header_len]).finish() {
Ok((_, hdr)) => hdr,
Err(_) => bail!("failed to parse firmware header"),
};

header.verify().context("header verification failed")?;

let file_hash = sha256::Hash::hash(&file_buf);
let build_hash = sha256::Hash::hash(&file_buf[header_len..]);

let mut engine = sha256d::Hash::engine();
engine.input(&header.information.serialize());
engine.input(&file_buf[header_len..]);
let validation_hash = sha256d::Hash::from_engine(engine);

// This one is just for debugging.
let mut engine = sha256::Hash::engine();
engine.input(&header.information.serialize());
engine.input(&file_buf[header_len..]);
let single_hash = sha256::Hash::from_engine(engine);

let firmware_length = file_buf.len() - header_len;
if firmware_length != usize::try_from(header.information.length).unwrap() {
bail!(
"invalid specified firmware length, on disk size is {}, specified one {}",
firmware_length,
header.information.length
);
}

let signature1 = header.signature.signature1.serialize_compact();
let signature2 = header.signature.signature2.serialize_compact();

println!("Firmware:");
println!(
"{:>17}: {:#08X} ({}) ",
"Magic",
header.information.magic,
if header.information.magic == Information::MAGIC_COLOR {
"color"
} else {
"mono"
},
);
println!("{:>17}: {}", "Timestamp", header.information.timestamp);
println!("{:>17}: {}", "Date", header.information.date);
println!("{:>17}: {}", "Version", header.information.version);
println!("{:>17}: {} bytes", "Length", header.information.length);
println!("{:>17}: {}", "Key", header.signature.public_key1);
println!("{:>17}: {}", "Signature", hex::encode(signature1));
println!("{:>17}: {}", "Key", header.signature.public_key2);
println!("{:>17}: {}", "Signature", hex::encode(signature2));
println!("{:>17}: {}", "File Hash", file_hash);
println!("{:>17}: {}", "Build Hash", build_hash);
println!(
"{:>17}: {}",
"Validation Hash",
hex::encode(validation_hash.to_byte_array())
);
println!(
"{:>17}: {}",
"Single Hash",
hex::encode(single_hash.to_byte_array())
);
println!();

verify_signature(&SECP256K1, &header, &validation_hash, None)
.context("firmware signature verification failed.")?;

println!("Firmware signature is valid!");

Ok(())
}
Loading

0 comments on commit 808e571

Please sign in to comment.