Skip to content

Commit

Permalink
osnma-longan-nano: add Merkle tree root
Browse files Browse the repository at this point in the history
  • Loading branch information
daniestevez committed Jan 17, 2024
1 parent 8b7fe7a commit bf7a85c
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 11 deletions.
1 change: 1 addition & 0 deletions osnma-longan-nano/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ panic-halt = "0.2"
riscv-rt = "0.11"

[build-dependencies]
hex = "0.4"
ecdsa = { version = "0.16", features = ["pkcs8"] }
p256 = { version = "0.13", features = ["ecdsa"] }
spki = { version = "0.7", features = ["pem"] }
Expand Down
22 changes: 13 additions & 9 deletions osnma-longan-nano/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ The instructions to set up the Rust riscv32imac toolchain can be found in the
documentation for the [longan-nano](https://github.com/riscv-rust/longan-nano)
crate.

The OSNMA ECDSA P-256 public key is embedded in the binary during the build
process. The public key is taken from the `pubkey.pem` file found in the root
folder of this crate. A "fake" public key is provided so that osnma-longan-demo
can be built without access to the authentic public key. A binary built with
this fake public key will not work with the Galileo signal in space, since
it will not be able to validate the TESLA root key.
The OSNMA ECDSA P-256 public key and the Merkle tree root are embedded in the
binary during the build process. The public key is taken from the `pubkey.pem`
file found in the root folder of this crate, and its Public Key ID is taken from
the `pubkey_id.txt` file. The Merkle tree root is taken from the
`merkle_tree_root.txt` file. "Fake" files are provided so that osnma-longan-demo
can be built without access to the real cryptographic material. A binary built with
this fake cryptographic material will not work with the Galileo signal-in-space.

The fake `pubkey.pem` needs to be replaced with the authentic key, using the
same PEM file format. Instructions about how to obtain the authentic public key
can be found in the
[galileo-osnma README](https://github.com/daniestevez/galileo-osnma#quick-start-using-galmon).
can be found in the [galileo-osnma
README](https://github.com/daniestevez/galileo-osnma#quick-start-using-galmon).
Likewise the Public Key ID in `pubkey_id.txt` needs to be replaced by the
correct one, and the Merkle tree root needs to be written to the file
`merkle_tree_root.txt`.

Once the file `pubkey.pem` contains the authentic public key, the crate can be
Once these files contain the real cryptographic material, the firmware can be
built using
```
cargo build --release
Expand Down
22 changes: 22 additions & 0 deletions osnma-longan-nano/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ fn pubkey_to_rust(pubkey: &[u8], pubkey_id: u8, path: &Path) -> std::io::Result<
)
}

fn read_merkle_tree_root(path: &str) -> std::io::Result<[u8; 32]> {
let hex = fs::read_to_string(path)?;
Ok(hex::decode(hex.trim())
.expect("invalid Merkle tree root")
.try_into()
.unwrap())
}

fn merkle_tree_to_rust(merkle_tree_root: &[u8; 32], path: &Path) -> std::io::Result<()> {
fs::write(
path,
format!(
"const OSNMA_MERKLE_TREE_ROOT: [u8; 32] = {:?};\n",
merkle_tree_root,
),
)
}

fn main() {
// Put the memory definition somewhere the linker can find it
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
Expand All @@ -37,4 +55,8 @@ fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=pubkey.pem");
println!("cargo:rerun-if-changed=pubkey_id.txt");

let merkle_tree_root = read_merkle_tree_root("merkle_tree_root.txt").unwrap();
merkle_tree_to_rust(&merkle_tree_root, &out_dir.join("osnma_merkle_tree.rs")).unwrap();
println!("cargo:rerun-if-changed=merkle_tree_root.txt");
}
1 change: 1 addition & 0 deletions osnma-longan-nano/merkle_tree_root.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000000000000000000000000000000000000000000000000000000000000000
9 changes: 7 additions & 2 deletions osnma-longan-nano/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ use panic_halt as _;
use riscv_rt::entry;

// The OSNMA public key, as a [u8; N] constant, is generated from pubkey.pem in
// the build script and included here.
// the build script and included here, together with is public key ID (generated
// from pubkey_id.txt).
include!(concat!(env!("OUT_DIR"), "/osnma_pubkey.rs"));
// The OSNMA Merkle tree root, as a [u8; 32] constant, is generated from
// merkle_tree_root.txt in the build script and included here.
include!(concat!(env!("OUT_DIR"), "/osnma_merkle_tree.rs"));

struct Board {
tx: serial::Tx<USART0>,
Expand Down Expand Up @@ -78,7 +82,8 @@ impl OsnmaInterface {
fn new(board: Board) -> OsnmaInterface {
let pubkey = VerifyingKey::from_sec1_bytes(&OSNMA_PUBKEY).unwrap();
let pubkey = PublicKey::from_p256(pubkey, OSNMA_PUBKEY_ID).force_valid();
let osnma = Osnma::<SmallStorage>::from_pubkey(pubkey, false);
let osnma =
Osnma::<SmallStorage>::from_merkle_tree(OSNMA_MERKLE_TREE_ROOT, Some(pubkey), false);
OsnmaInterface { osnma, board }
}

Expand Down

0 comments on commit bf7a85c

Please sign in to comment.