nordisk/heimdall
crates/heimdall-core/tests/pinned_key.rs
// SPDX-License-Identifier: AGPL-3.0-only
// Copyright (C) 2026 Ignalina ApS and Vetra Automation AB
//! The pinned public key record a caller receives at boot. Heimdall is a
//! defensive sandbox.
mod common;
use common::*;
use heimdall_core::*;
#[test]
fn pinned_key_text_round_trips() {
let p = key_a().pinned();
assert_eq!(PinnedKey::from_text(&p.to_text()).unwrap(), p);
}
#[test]
fn pinned_key_text_has_the_documented_shape() {
let p = key_a().pinned();
let t = p.to_text();
assert!(t.ends_with('\n'));
let parts: Vec<&str> = t.trim_end().split(' ').collect();
assert_eq!(parts.len(), 5);
assert_eq!(parts[0], "heimdall-boot-key");
assert_eq!(parts[1], "v1");
assert_eq!(parts[2], p.key_id.to_hex());
assert_eq!(parts[3], p.boot_id.to_hex());
assert_eq!(parts[4], p.public_key.to_hex());
}
#[test]
fn pinned_key_without_trailing_newline_is_accepted() {
let p = key_a().pinned();
assert_eq!(PinnedKey::from_text(p.to_text().trim_end()).unwrap(), p);
}
#[test]
fn pinned_key_with_a_wrong_key_id_is_refused() {
let mut p = key_a().pinned();
p.key_id = key_b().key_id();
assert_eq!(
PinnedKey::from_text(&p.to_text()).unwrap_err(),
PinError::KeyIdMismatch
);
assert_eq!(Verifier::new(p).unwrap_err(), PinError::KeyIdMismatch);
}
#[test]
fn pinned_key_with_version_two_is_refused() {
let t = key_a().pinned().to_text().replace(" v1 ", " v2 ");
assert_eq!(PinnedKey::from_text(&t).unwrap_err(), PinError::BadFormat);
}
#[test]
fn pinned_key_with_a_wrong_label_is_refused() {
let t = key_a()
.pinned()
.to_text()
.replace("heimdall-boot-key", "other-key");
assert_eq!(PinnedKey::from_text(&t).unwrap_err(), PinError::BadFormat);
}
#[test]
fn pinned_key_with_short_hex_is_refused() {
let p = key_a().pinned();
let t = p.to_text();
let cut = t.trim_end();
assert_eq!(
PinnedKey::from_text(&cut[..cut.len() - 2]).unwrap_err(),
PinError::BadFormat
);
}
#[test]
fn pinned_key_with_an_extra_field_is_refused() {
let t = format!("{} extra\n", key_a().pinned().to_text().trim_end());
assert_eq!(PinnedKey::from_text(&t).unwrap_err(), PinError::BadFormat);
}
#[test]
fn pinned_key_with_two_lines_is_refused() {
let t = format!("{0}{0}", key_a().pinned().to_text());
assert_eq!(PinnedKey::from_text(&t).unwrap_err(), PinError::BadFormat);
}
#[test]
fn pinned_key_with_non_hex_is_refused() {
let t = key_a().pinned().to_text();
let bad = format!("{}g\n", &t.trim_end()[..t.trim_end().len() - 1]);
assert_eq!(PinnedKey::from_text(&bad).unwrap_err(), PinError::BadFormat);
}
#[test]
fn pinned_key_with_double_spaces_is_refused() {
let t = key_a().pinned().to_text().replacen(' ', " ", 1);
assert_eq!(PinnedKey::from_text(&t).unwrap_err(), PinError::BadFormat);
}
#[test]
fn pinned_identity_point_is_refused_as_weak_key() {
// The identity point (y = 1) is a valid encoding but a weak key: every
// signature check against it is meaningless.
let mut pk = [0u8; 32];
pk[0] = 1;
let p = PinnedKey {
key_id: key_id_of(&pk),
boot_id: BOOT_A,
public_key: Ed25519PublicKey(pk),
};
assert_eq!(Verifier::new(p).unwrap_err(), PinError::BadPublicKey);
assert_eq!(
PinnedKey::from_text(&p.to_text()).unwrap_err(),
PinError::BadPublicKey
);
}
#[test]
fn pinned_key_that_is_not_a_curve_point_is_refused() {
// y = 2 is not on the curve.
let mut pk = [0u8; 32];
pk[0] = 2;
let p = PinnedKey {
key_id: key_id_of(&pk),
boot_id: BOOT_A,
public_key: Ed25519PublicKey(pk),
};
assert_eq!(Verifier::new(p).unwrap_err(), PinError::BadPublicKey);
}
#[test]
fn key_id_is_domain_separated_sha256_prefix() {
use sha2::{Digest, Sha256};
let pk = key_a().pinned().public_key.0;
let mut h = Sha256::new();
h.update(KEY_ID_DOMAIN);
h.update(pk);
let full: [u8; 32] = h.finalize().into();
assert_eq!(key_id_of(&pk).0, full[..16]);
assert_ne!(key_id_of(&pk).0, Sha256::digest(pk)[..16]);
}
#[test]
fn key_id_differs_between_keys() {
assert_ne!(key_a().key_id(), key_b().key_id());
}
#[test]
fn pinned_key_json_round_trips() {
let p = key_a().pinned();
let j = serde_json::to_string(&p).unwrap();
assert_eq!(serde_json::from_str::<PinnedKey>(&j).unwrap(), p);
}