crates/monetize-estate-side/tests/one_order_check.rs
//! ★ **One order check** (TASKS-4-v2 T16, A28 M11): monetize-embed's `fleet::verify_order` (the
//! check monetize's own tests and trials run as "the box") and the shared pump's
//! `pump::check` (what Holger's boxes run) give the same verdict on the same orders. Both are
//! strict Ed25519, both check the signature first, both read `not_after` 0 as no window, and
//! both refuse an order dated more than five minutes ahead.
//!
//! Verdicts: embed `Ok` = pump `Fresh`; embed `Signature` = pump `Forged`; any other embed
//! refusal = pump `Refused`.
use ed25519_dalek::{Signer as _, SigningKey, VerifyingKey};
use monetize_embed::fleet::{
order_message, verify_order, OrderRefusal, SignedOrder, FUTURE_SKEW_MS,
};
use monetize_estate_side::pump::{check, Addressee, Checked};
const MONETIZE: [u8; 32] = [7u8; 32];
const NOW: u64 = 1_800_000_100_000;
fn order() -> SignedOrder {
SignedOrder {
seq: 5,
product: "gunnar".into(),
instance: "acme".into(),
role: "main".into(),
kind: "recharge_from_twin".into(),
body: r#"{"epoch":5}"#.into(),
issued_unix_ms: NOW - 1_000,
not_after_unix_ms: NOW + 900_000,
signature: Vec::new(),
}
}
fn sign(mut o: SignedOrder, seed: [u8; 32]) -> SignedOrder {
o.signature = SigningKey::from_bytes(&seed)
.sign(&order_message(&o))
.to_bytes()
.to_vec();
o
}
fn good() -> SignedOrder {
sign(order(), MONETIZE)
}
fn monetize() -> VerifyingKey {
SigningKey::from_bytes(&MONETIZE).verifying_key()
}
fn me() -> Addressee {
Addressee {
product: "gunnar".into(),
instance: "acme".into(),
role: "main".into(),
}
}
#[derive(Debug, PartialEq, Eq)]
enum Verdict {
Run,
Forged,
Refused,
}
fn embed(o: &SignedOrder, key: &VerifyingKey, last: u64, now: u64) -> Verdict {
match verify_order(o, key, ("gunnar", "acme", "main"), now, last) {
Ok(()) => Verdict::Run,
Err(OrderRefusal::Signature) => Verdict::Forged,
Err(_) => Verdict::Refused,
}
}
fn pump(o: &SignedOrder, key: &VerifyingKey, last: u64, now: u64) -> Verdict {
match check(o, &me(), key, last, now) {
Checked::Fresh => Verdict::Run,
Checked::Forged(_) => Verdict::Forged,
Checked::Refused(_) => Verdict::Refused,
}
}
/// Both checks, and the verdict they must agree on.
fn agree(what: &str, o: &SignedOrder, key: &VerifyingKey, last: u64, now: u64, want: Verdict) {
assert_eq!(embed(o, key, last, now), want, "embed: {what}");
assert_eq!(pump(o, key, last, now), want, "pump: {what}");
}
#[test]
fn a_good_order_runs_under_both_checks() {
agree("good", &good(), &monetize(), 4, NOW, Verdict::Run);
}
#[test]
fn a_tampered_order_is_forged_under_both_checks() {
type Tamper = fn(&mut SignedOrder);
let tampers: [(&str, Tamper); 12] = [
("seq", |o| o.seq += 1),
("product", |o| o.product = "holger".into()),
("instance", |o| o.instance = "other".into()),
("role", |o| o.role = "twin".into()),
("kind", |o| o.kind = "fence_primary".into()),
("body", |o| o.body = "{}".into()),
("issued", |o| o.issued_unix_ms += 1),
("not_after", |o| o.not_after_unix_ms += 1),
("a flipped bit", |o| o.signature[9] ^= 0x10),
("a short signature", |o| o.signature.truncate(32)),
("no signature", |o| o.signature.clear()),
("a long signature", |o| o.signature.push(0)),
];
for (what, t) in tampers {
let mut o = good();
t(&mut o);
agree(what, &o, &monetize(), 0, NOW, Verdict::Forged);
}
let other = SigningKey::from_bytes(&[9u8; 32]).verifying_key();
agree("another key", &good(), &other, 0, NOW, Verdict::Forged);
agree(
"signed by another key",
&sign(order(), [9u8; 32]),
&monetize(),
0,
NOW,
Verdict::Forged,
);
}
/// The all-zero signature (R = the identity, s = 0) under a small-order key (the identity point)
/// verifies EVERY message with plain Ed25519. Both checks are strict and refuse it.
#[test]
fn a_small_order_key_with_the_all_zero_signature_is_forged_under_both_checks() {
let mut identity = [0u8; 32];
identity[0] = 1;
let weak = VerifyingKey::from_bytes(&identity).expect("the identity point decompresses");
let mut o = order();
o.signature = vec![0u8; 64];
o.signature[0] = 1;
agree("the all-zero signature", &o, &weak, 0, NOW, Verdict::Forged);
}
#[test]
fn a_refused_order_is_refused_under_both_checks() {
agree("a replay", &good(), &monetize(), 5, NOW, Verdict::Refused);
agree(
"an older one",
&good(),
&monetize(),
9,
NOW,
Verdict::Refused,
);
agree(
"expired",
&good(),
&monetize(),
0,
NOW + 900_001,
Verdict::Refused,
);
let mut twin = order();
twin.role = "twin".into();
agree(
"the twin's order",
&sign(twin, MONETIZE),
&monetize(),
0,
NOW,
Verdict::Refused,
);
let mut theirs = order();
theirs.instance = "other".into();
agree(
"another estate's order",
&sign(theirs, MONETIZE),
&monetize(),
0,
NOW,
Verdict::Refused,
);
let mut ahead = order();
ahead.issued_unix_ms = NOW + FUTURE_SKEW_MS + 1;
agree(
"dated ahead",
&sign(ahead, MONETIZE),
&monetize(),
0,
NOW,
Verdict::Refused,
);
}
#[test]
fn skew_and_no_window_run_under_both_checks() {
let mut skew = order();
skew.issued_unix_ms = NOW + FUTURE_SKEW_MS;
agree(
"five minutes of skew",
&sign(skew, MONETIZE),
&monetize(),
0,
NOW,
Verdict::Run,
);
let mut open = order();
open.not_after_unix_ms = 0;
agree(
"no window",
&sign(open, MONETIZE),
&monetize(),
0,
NOW + 10 * 365 * 86_400_000,
Verdict::Run,
);
}
/// A forged order addressed to another box is FORGED under both, so it never moves a sequence.
#[test]
fn a_forged_order_for_another_box_is_forged_under_both_checks() {
let mut o = good();
o.role = "twin".into();
o.seq = 99;
agree(
"forged, for the twin",
&o,
&monetize(),
0,
NOW,
Verdict::Forged,
);
}
#[test]
fn both_checks_allow_the_same_skew() {
assert_eq!(
FUTURE_SKEW_MS,
monetize_estate_side::pump::puller::FUTURE_SKEW_MS
);
}