diff --git a/.trinity/seals/src_port::trios::crates::trios-chat::rings::CR-CHAT-03::src::commit_path_secret_aead_keying_mismatch.json b/.trinity/seals/src_port::trios::crates::trios-chat::rings::CR-CHAT-03::src::commit_path_secret_aead_keying_mismatch.json new file mode 100644 index 0000000000..2139cfa436 --- /dev/null +++ b/.trinity/seals/src_port::trios::crates::trios-chat::rings::CR-CHAT-03::src::commit_path_secret_aead_keying_mismatch.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+01d0a635f", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:1d337eddf19bbf82fe1725768eb70607c4e7c857f8c533c9f626a99e9aede549", + "gen_hash_rust": "sha256:a837ee5955fec2e54a8343f519365f13cb5eb9eafbc2fa9e8eacdda8ea1c9d92", + "gen_hash_verilog": "sha256:beac8ea2200f0c79539a3a5492f2193767aac41849c158669745fad5af232eb2", + "gen_hash_zig": "sha256:245c8c81ae09f93672bfe0934f77100f169f3005b5e46cb7a3629f03ef302cf8", + "module": "port::trios::crates::trios-chat::rings::CR-CHAT-03::src::commit_path_secret_aead_keying_mismatch", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-17T08:39:42Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:d98c635e1c59e51c2901de063b10f1f13d22cd2fd8c60134560aa621104e8cae", + "spec_path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/commit_path_secret_aead_keying_mismatch.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 13, + "total": 13, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/docs/reports/t27b_expectations.json b/docs/reports/t27b_expectations.json index cfbb74965e..20ea1b2842 100644 --- a/docs/reports/t27b_expectations.json +++ b/docs/reports/t27b_expectations.json @@ -857,6 +857,7 @@ {"path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-01/src/key_package_expiry_guard.t27", "t27b": "pass"}, {"path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-02/src/epoch_rollover_wraparound_guard.t27", "t27b": "pass"}, {"path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-02/src/skipped_message_key_gap_bound_guard.t27", "t27b": "pass"}, + {"path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/commit_path_secret_aead_keying_mismatch.t27", "t27b": "pass"}, {"path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/confirmation_tag_chain.t27", "t27b": "pass"}, {"path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/external_commit.t27", "t27b": "pass"}, {"path": "specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/external_commit_resumption_psk_misbinding.t27", "t27b": "pass"}, diff --git a/specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/commit_path_secret_aead_keying_mismatch.t27 b/specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/commit_path_secret_aead_keying_mismatch.t27 new file mode 100644 index 0000000000..53cc762c6f --- /dev/null +++ b/specs/port/trios/crates/trios-chat/rings/CR-CHAT-03/src/commit_path_secret_aead_keying_mismatch.t27 @@ -0,0 +1,372 @@ +module port::trios::crates::trios-chat::rings::CR-CHAT-03::src::commit_path_secret_aead_keying_mismatch; + +// Port of gHashTag/trios crates/trios-chat/rings/CR-CHAT-03/src/commit_path_secret_aead_keying_mismatch.rs +// (CR-CHAT-03 - Commit path-secret AEAD keying mismatch, +// Wave-36, L-CHAT-3-cpakm, R-CHAT-3, RFC 9420 7.7/8/12.4.3.2). +// +// A Commit that ships an UPDATE-PATH seals the new path +// secret once per leaf in each updated node's resolution, +// bound to the recipient's init key, to a nonce and to an +// AAD pinning the group, the epoch, the sender's leaf and +// the node. A mis-keyed slot - sealed to the wrong leaf, or +// with an AAD that lies about the node - poisons the whole +// epoch, and the receiver rejects on a single deny. The +// rules, in the original's fixed order: the canonical +// GroupID length (thirty-two), the epoch advancing by +// exactly one, the sender's leaf inside the tree, the +// receiver's resolution slot present, the ciphertext length +// canonical (forty-eight - the sealed secret plus the +// Poly1305 tag, rejected before any decap), the recipient +// init key equal to the local one, and the AAD context equal +// to the canonical encoding of group, epoch, sender leaf +// and node - with the slot's own node index answering the +// same rule. +// +// The original returns Result<(), PathSecretAeadKeyingError>. +// This port carries the verdict as a named struct (owner +// decision D5): Ok first, then the seven variants in the +// original's declaration order - NonCanonicalGroupIdLength, +// EpochMismatch, SenderLeafOutOfRange, +// ResolutionSlotOutOfRange, RecipientInitKeyMismatch, +// AadContextMismatch, NonCanonicalCiphertextLength. Every +// original variant carries no payload, so the code is the +// whole verdict. +// +// The original's commit and view structs become flat +// parameters (slices as parameters); the slot stays a +// record. The Vec fields become fixed capacities plus +// lengths where a rule reads the length: the ciphertext +// gains a sixty-four capacity with its length (the original's +// short and long fixtures live there), the GroupID arrives +// as a plain byte slice whose length rule one reads, and the +// init key and AAD context stay at their canonical +// capacities - every original fixture carries them +// canonical, only the content ever disagrees. The canonical +// AAD encoding is answered by a positional matcher - the +// first thirty-two bytes the GroupID, then the big-endian +// epoch, sender leaf and node index read from a table of +// powers of two hundred fifty-six (t27 has no shifts by a +// variable amount; the table walks are exact). The original's +// saturating epoch advance differs from the plain successor +// only at the top of u64, where both answer the same +// maximum - the port spells the edge explicitly. + +// Canonical MLS GroupID length (32 bytes). +pub const CPAKM_GROUP_ID_LEN: usize = 32; + +// Canonical HPKE init_key length (32 bytes). +pub const CPAKM_INIT_KEY_LEN: usize = 32; + +// Canonical AAD-context length (32 + 8 + 8 + 8 = 56 bytes). +pub const CPAKM_AAD_CONTEXT_LEN: usize = 56; + +// Canonical path-secret ciphertext length (32 + 16 = 48 bytes). +pub const CPAKM_PATH_SECRET_CIPHERTEXT_LEN: usize = 48; + +// The ciphertext record capacity: the original's longest +// fixture is sixty-four bytes. +pub const CPAKM_CIPHERTEXT_CAP: usize = 64; + +// The top of u64 - the saturating epoch edge. +pub const CPAKM_U64_TOP: u64 = 18446744073709551615; + +// The big-endian byte weights of a u64, as a table - byte k +// of the encoding is (x / P256[k]) mod 256. +pub const P256: [8]u64 = [8]u64{1, 256, 65536, 16777216, 4294967296, 1099511627776, 281474976710656, 72057594037927936,} + +// One HPKE encryption inside a Commit's UPDATE-PATH, +// addressed to a single leaf in a node's resolution. +pub struct UpdatePathSlot { + node_index: u64, + recipient_init_key: [32]u8, + aad_context: [56]u64, + ciphertext: [64]u8, + ciphertext_len: usize, +} + +// Verdict codes: Ok(()) and PathSecretAeadKeyingError's variants, in declaration order. +pub enum CpakmCode { + Ok, + NonCanonicalGroupIdLength, + EpochMismatch, + SenderLeafOutOfRange, + ResolutionSlotOutOfRange, + RecipientInitKeyMismatch, + AadContextMismatch, + NonCanonicalCiphertextLength, +} + +// The verdict of the path-secret validation - the original's +// variants carry no payload, so the code alone carries it. +pub struct CpakmVerdict { + code: CpakmCode, +} + +// Whole-key equality over the canonical init-key capacity - +// the original's Vec equality on keys that every fixture +// carries canonical. +pub fn init_keys_equal(a: [32]u8, b: [32]u8) -> bool { + for i in 0..32 { + if (a[i] != b[i]) { + return false; + } + } + return true; +} + +// Whether the AAD context equals the canonical encoding +// group_id then epoch then sender leaf then node index, all +// big-endian - the original's Vec extend walk answered +// positionally, the GroupID already proven canonical by rule +// one. +pub fn aad_context_matches(aad: [56]u64, group_id: []const u8, epoch: u64, sender_leaf: u64, node_index: u64) -> bool { + for i in 0..32 { + if (aad[i] != @as(u64, group_id[i])) { + return false; + } + } + for k in 0..8 { + if (aad[32 + k] != (epoch / P256[7 - k]) % 256) { + return false; + } + if (aad[40 + k] != (sender_leaf / P256[7 - k]) % 256) { + return false; + } + if (aad[48 + k] != (node_index / P256[7 - k]) % 256) { + return false; + } + } + return true; +} + +// [VERIFIED] Validate a single Commit's UPDATE-PATH slot +// targeted at the local receiver. Returns Ok only when every +// rule holds, checked in the original's fixed order: the +// GroupID length, the epoch advance, the sender's leaf, the +// slot's presence, the ciphertext length before any crypto +// binding, the recipient init key, then the AAD context with +// the slot's node index. +pub fn validate_commit_path_secret(group_id: []const u8, epoch: u64, sender_leaf: u64, slots: []const UpdatePathSlot, local_epoch: u64, tree_size: u64, local_init_key: [32]u8, local_resolution_index: usize, local_node_index: u64) -> CpakmVerdict { + if (group_id.len != CPAKM_GROUP_ID_LEN) { + return CpakmVerdict { code: CpakmCode.NonCanonicalGroupIdLength, }; + } + var expected_epoch: u64 = CPAKM_U64_TOP; + if (local_epoch < CPAKM_U64_TOP) { + expected_epoch = local_epoch + 1; + } + if (epoch != expected_epoch) { + return CpakmVerdict { code: CpakmCode.EpochMismatch, }; + } + if (sender_leaf >= tree_size) { + return CpakmVerdict { code: CpakmCode.SenderLeafOutOfRange, }; + } + if (local_resolution_index >= slots.len) { + return CpakmVerdict { code: CpakmCode.ResolutionSlotOutOfRange, }; + } + if (slots[local_resolution_index].ciphertext_len != CPAKM_PATH_SECRET_CIPHERTEXT_LEN) { + return CpakmVerdict { code: CpakmCode.NonCanonicalCiphertextLength, }; + } + if (!init_keys_equal(slots[local_resolution_index].recipient_init_key, local_init_key)) { + return CpakmVerdict { code: CpakmCode.RecipientInitKeyMismatch, }; + } + if (!aad_context_matches(slots[local_resolution_index].aad_context, group_id, epoch, sender_leaf, local_node_index)) { + return CpakmVerdict { code: CpakmCode.AadContextMismatch, }; + } + if (slots[local_resolution_index].node_index != local_node_index) { + return CpakmVerdict { code: CpakmCode.AadContextMismatch, }; + } + return CpakmVerdict { code: CpakmCode.Ok, }; +} + +// The fixture bytes: the GroupID of eleven-hex-eleven, the +// local init key of twenty-two-hex, the stranger's key of +// thirty-three-hex, the ciphertext of forty-four-hex over +// its full capacity, and the canonical AAD contexts of the +// fixtures. +pub const GOOD_GROUP_ID: [32]u8 = [32]u8{17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,} +pub const LOCAL_INIT_KEY: [32]u8 = [32]u8{34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,} +pub const OTHER_INIT_KEY: [32]u8 = [32]u8{51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,} +pub const GOOD_CIPHERTEXT: [64]u8 = [64]u8{68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,} +pub const OK_AAD: [56]u64 = [56]u8{17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 7,} + +// **CPAKM-01** - the short GroupID rejected: sixteen bytes of +// a canonical thirty-two. +test cpakm_01_short_group_id_rejected { + var short_gid: [16]u8; + for j in 0..16 { + short_gid[j] = 17; + } + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&short_gid, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.NonCanonicalGroupIdLength); +} + +// **CPAKM-02** - the epoch skip rejected: forty-four of a +// required forty-two over the local forty-one. +test cpakm_02_epoch_skip_rejected { + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 44, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.EpochMismatch); +} + +// **CPAKM-03** - the sender's leaf out of range rejected: +// sixteen of a tree of sixteen, the AAD adjusted to the bad +// sender exactly as the original's fixture does. +test cpakm_03_sender_leaf_out_of_range_rejected { + var sender16_aad: [56]u64 = OK_AAD; + for k in 0..8 { + sender16_aad[40 + k] = (16 / P256[7 - k]) % 256; + } + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: sender16_aad, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 16, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.SenderLeafOutOfRange); +} + +// **CPAKM-04** - the receiver's resolution slot out of range +// rejected: index one of a single slot. +test cpakm_04_resolution_slot_out_of_range_rejected { + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 1, 7); + assert(v.code == CpakmCode.ResolutionSlotOutOfRange); +} + +// **CPAKM-05** - the recipient init key mismatch rejected: +// the stranger's key in the slot. +test cpakm_05_recipient_init_key_mismatch_rejected { + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: OTHER_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.RecipientInitKeyMismatch); +} + +// **CPAKM-06** - the AAD context with the wrong epoch +// rejected: nine hundred ninety-nine of a canonical +// forty-two. +test cpakm_06_aad_context_epoch_mismatch_rejected { + var epoch999_aad: [56]u64 = OK_AAD; + for k in 0..8 { + epoch999_aad[32 + k] = (999 / P256[7 - k]) % 256; + } + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: epoch999_aad, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.AadContextMismatch); +} + +// **CPAKM-07** - the AAD context disagreeing on the node +// index rejected: the slot claims node nine of a local seven, +// the AAD encoding nine with it exactly as the original's +// fixture does. +test cpakm_07_aad_context_node_index_mismatch_rejected { + var node9_aad: [56]u64 = OK_AAD; + for k in 0..8 { + node9_aad[48 + k] = (9 / P256[7 - k]) % 256; + } + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 9, recipient_init_key: LOCAL_INIT_KEY, aad_context: node9_aad, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.AadContextMismatch); +} + +// **CPAKM-08** - the short ciphertext rejected: thirty-two +// bytes, the sealed secret without its tag. +test cpakm_08_short_ciphertext_rejected { + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 32, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.NonCanonicalCiphertextLength); +} + +// **CPAKM-09** - the long ciphertext rejected: sixty-four +// bytes of a canonical forty-eight. +test cpakm_09_long_ciphertext_rejected { + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 64, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.NonCanonicalCiphertextLength); +} + +// **CPAKM-10** - the canonical update-path slot accepted. +test cpakm_10_canonical_commit_accepted { + const slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v.code == CpakmCode.Ok); +} + +// The helpers have direct tests (coverage: Untested 0). +test init_keys_equal_answers { + assert(init_keys_equal(LOCAL_INIT_KEY, LOCAL_INIT_KEY)); + assert(!init_keys_equal(LOCAL_INIT_KEY, OTHER_INIT_KEY)); +} + +test aad_context_matches_answers { + assert(aad_context_matches(OK_AAD, &GOOD_GROUP_ID, 42, 3, 7)); + assert(!aad_context_matches(OK_AAD, &GOOD_GROUP_ID, 999, 3, 7)); + assert(!aad_context_matches(OK_AAD, &GOOD_GROUP_ID, 42, 3, 9)); +} + +// Rule order is the original's fixed ladder, each probe +// stacking the violation of the earlier rule on the later +// one: a short GroupID over an epoch skip, the epoch skip +// over a phantom sender, the phantom sender over a missing +// slot, the missing slot over a short ciphertext, the short +// ciphertext over a stranger's key (the pre-decap order), +// the stranger's key over a lying AAD - and the slot's node +// index alone answering the AAD rule. +test rule_order_matches_original { + var short_gid: [16]u8; + for j in 0..16 { + short_gid[j] = 17; + } + const ok_slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v1 = validate_commit_path_secret(&short_gid, 44, 16, &ok_slots, 41, 16, LOCAL_INIT_KEY, 1, 7); + assert(v1.code == CpakmCode.NonCanonicalGroupIdLength); + + const v2 = validate_commit_path_secret(&GOOD_GROUP_ID, 44, 16, &ok_slots, 41, 16, LOCAL_INIT_KEY, 1, 7); + assert(v2.code == CpakmCode.EpochMismatch); + + const v3 = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 16, &ok_slots, 41, 16, LOCAL_INIT_KEY, 1, 7); + assert(v3.code == CpakmCode.SenderLeafOutOfRange); + + const v4 = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &ok_slots, 41, 16, LOCAL_INIT_KEY, 1, 7); + assert(v4.code == CpakmCode.ResolutionSlotOutOfRange); + + const bad_ct_slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: OTHER_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 32, }, + }; + const v5 = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &bad_ct_slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v5.code == CpakmCode.NonCanonicalCiphertextLength); + + var lying_aad: [56]u64 = OK_AAD; + for k in 0..8 { + lying_aad[32 + k] = (999 / P256[7 - k]) % 256; + } + const wrong_key_slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 7, recipient_init_key: OTHER_INIT_KEY, aad_context: lying_aad, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v6 = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &wrong_key_slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v6.code == CpakmCode.RecipientInitKeyMismatch); + + const node9_slots: [1]UpdatePathSlot = [1]UpdatePathSlot{ + UpdatePathSlot { node_index: 9, recipient_init_key: LOCAL_INIT_KEY, aad_context: OK_AAD, ciphertext: GOOD_CIPHERTEXT, ciphertext_len: 48, }, + }; + const v7 = validate_commit_path_secret(&GOOD_GROUP_ID, 42, 3, &node9_slots, 41, 16, LOCAL_INIT_KEY, 0, 7); + assert(v7.code == CpakmCode.AadContextMismatch); +}