pask-wire 0.1.0

SCITT-native COSE receipt library for Physical-Site Engagement Receipts; implements the wilder.pser/0.5 and 0.6 profiles
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 Wilder Management Inc. (d/b/a Wilder Robotics) <rob@wilder-robotics.com>
// pask-wire is licensed Apache-2.0. No commercial agreement is required to use,
// modify or redistribute it; see LICENSING.md in the workspace root.

//! Byte-exact tests for the #67 candidate-entry derivation.
//!
//! These test vectors are synthetic derivation fixtures, not verified proofs
//! or registration alignments. They are independently generated using the
//! `scripts/gen_test_vectors_67.py` script (Python 3.8+, cbor2 >= 6.0) and hardcoded
//! here. The Rust implementation must reproduce the exact expected bytes.
//!
//! See `scripts/gen_test_vectors_67.py` for the generator
//! and `pask_-04_design_67_v3_consolidated.md` for the design.

#![cfg(test)]

use pask_wire::derive_candidate_entry;

fn hex_decode(s: &str) -> Vec<u8> {
    let bytes = (0..s.len())
        .step_by(2)
        .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap());
    bytes.collect()
}

// ===========================================================================
// Independently generated test vectors
// ===========================================================================

// TV1: Standard untagged COSE_Sign1 (full wilder.pser/0.5 Transparent Statement)
const TV1_COSE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV1_CANDIDATE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV1_LEAF: &str = "1dd2694297e6c577726b9819fdd54a23f867fec86a382584e6bd9659e87537d2";

// TV2: Tag-18 wrapped COSE_Sign1 (same content as TV1)
const TV2_COSE: &str = "d2845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

// TV3: Transparent Statement with one receipt (acceptance invariant: same as TV1)
const TV3_COSE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a107815850bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb59017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

// TV4: Transparent Statement with two receipts (acceptance invariant: same as TV1)
const TV4_COSE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a107825850cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc5850dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd59017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

// TV5: Tampered payload (different candidate entry and leaf hash)
const TV5_COSE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3162222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV5_LEAF: &str = "1ddfecc2f78a43378889ed7e7b5adcf7f15ac3ba6e4ef502ccf261f1b7db1135";

// --- Outer-encoding test vectors ---

// TV6: Short protected header (3 bytes, 1-byte bstr header 0x43)
const TV6_COSE: &str = "8443a10127a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV6_CANDIDATE: &str = "8443a10127a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV6_LEAF: &str = "3a15ec6e616e7dcbe3d17d23d93dc63454336cba29d997fc69470f1f635ad740";

// TV7: Protected header length 10 (1-byte bstr header 0x4a)
const TV7_COSE: &str = "844aa20127036573686f7274a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV7_CANDIDATE: &str = "844aa20127036573686f7274a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV7_LEAF: &str = "c49287d94904cc371c5e82798e93a185f8058c0fb4627d5f895dfc7acabebf2f";

// TV8: Protected header length 25 (2-byte bstr header 0x58 0x19)
const TV8_COSE: &str = "845819a2012703743031323334353637383961626364656630313233a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV8_CANDIDATE: &str = "845819a2012703743031323334353637383961626364656630313233a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV8_LEAF: &str = "3eac21bee018737c1649079b100dc67accd4fb2065d39de934f59666f8c2c21e";

// TV9: Empty payload (length 0, 1-byte bstr header 0x40)
const TV9_COSE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a0405840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV9_CANDIDATE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a0405840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TV9_LEAF: &str = "2a9f50255c2e672daf302ea1539b6eef9bddd8f436bac9c054012be0bf68e362";

// TV10: Short signature (8 bytes, 1-byte bstr header 0x48)
const TV10_COSE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d484242424242424242";
const TV10_CANDIDATE: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d484242424242424242";
const TV10_LEAF: &str = "85136b2253756719693a7823e93d8445bfc38d2e780faaf5f6fe9316dde64e3b";

// --- Negative test vectors ---

const NV_DETACHED: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a0f65840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_BAD_TAG: &str = "d863845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_TRAILING: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00";
const NV_UH_INT: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e350059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_UH_NULL: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35f659017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_UH_ARRAY: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35810159017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_UH_TEXT: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35696e6f742061206d617059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_UH_BYTES: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35496e6f742061206d617059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_PH_INT: &str = "8400a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_PL_INT: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a0182a5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const NV_SIG_INT: &str = "845834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d182a";
const NV_3_ELEM: &str = "835834a2012703782e6170706c69636174696f6e2f707365722b6a736f6e3b2070726f66696c653d77696c6465722e707365722f302e35a059017e7b2273706563223a2277696c6465722e707365722f302e35222c227473223a22323032352d30392d31325431343a30303a30305a222c226e6f744265666f7265223a22323032352d30392d31325431333a30303a30305a222c226e6f744166746572223a22323032352d30392d31325431353a30303a30305a222c227375626a656374223a7b22736974655f6964223a227265732d303031227d2c226174746573746174696f6e223a7b227769746e6573734b6579223a226b65793a7465653a7265732d3030312d7769746e6573732d3031222c2262696e64696e674d6f6465223a224449524543545f5749544e455353222c2265766964656e6365223a227265662d303031227d2c2270726f76656e616e6365223a7b2273636f7065223a22756e69742d3161222c22636861696e223a7b2268617368223a2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d7d";
const NV_NON_ARRAY: &str = "182a";

// ===========================================================================
// Tests
// ===========================================================================

#[test]
fn tv1_standard_untagged() {
    let cose = hex_decode(TV1_COSE);
    let expected = hex_decode(TV1_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv2_tag_18_wrapped() {
    let cose = hex_decode(TV2_COSE);
    let expected = hex_decode(TV1_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv3_one_receipt_acceptance_invariant() {
    let cose = hex_decode(TV3_COSE);
    let expected = hex_decode(TV1_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv4_two_receipts_acceptance_invariant() {
    let cose = hex_decode(TV4_COSE);
    let expected = hex_decode(TV1_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv5_tampered_payload() {
    let cose = hex_decode(TV5_COSE);
    let result = derive_candidate_entry(&cose).unwrap();
    // Must differ from TV1 candidate entry
    assert_ne!(result, hex_decode(TV1_CANDIDATE));
}

#[test]
fn tv1_leaf_hash() {
    let cose = hex_decode(TV1_COSE);
    let candidate = derive_candidate_entry(&cose).unwrap();
    let leaf = pask_wire::candidate_leaf_hash(&candidate);
    assert_eq!(hex::encode(leaf), TV1_LEAF);
}

#[test]
fn tv5_leaf_hash() {
    let cose = hex_decode(TV5_COSE);
    let candidate = derive_candidate_entry(&cose).unwrap();
    let leaf = pask_wire::candidate_leaf_hash(&candidate);
    assert_eq!(hex::encode(leaf), TV5_LEAF);
}

#[test]
fn tv1_receipts_share_leaf_hash() {
    let cose1 = hex_decode(TV1_COSE);
    let cose3 = hex_decode(TV3_COSE);
    let cose4 = hex_decode(TV4_COSE);

    let candidate1 = derive_candidate_entry(&cose1).unwrap();
    let candidate3 = derive_candidate_entry(&cose3).unwrap();
    let candidate4 = derive_candidate_entry(&cose4).unwrap();

    assert_eq!(candidate1, candidate3);
    assert_eq!(candidate1, candidate4);
}

#[test]
fn tv5_differs_from_tv1() {
    let cose1 = hex_decode(TV1_COSE);
    let cose5 = hex_decode(TV5_COSE);
    let candidate1 = derive_candidate_entry(&cose1).unwrap();
    let candidate5 = derive_candidate_entry(&cose5).unwrap();
    assert_ne!(candidate1, candidate5);
}

// --- Outer-encoding tests ---

#[test]
fn tv6_short_protected_header_1byte_bstr() {
    // Protected header is 3 bytes, uses 1-byte bstr header (0x43)
    let cose = hex_decode(TV6_COSE);
    let expected = hex_decode(TV6_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv6_leaf_hash() {
    let cose = hex_decode(TV6_COSE);
    let candidate = derive_candidate_entry(&cose).unwrap();
    let leaf = pask_wire::candidate_leaf_hash(&candidate);
    assert_eq!(hex::encode(leaf), TV6_LEAF);
}

#[test]
fn tv7_protected_header_len_10_1byte_bstr() {
    // Protected header is 10 bytes, uses 1-byte bstr header (0x4a)
    let cose = hex_decode(TV7_COSE);
    let expected = hex_decode(TV7_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv7_leaf_hash() {
    let cose = hex_decode(TV7_COSE);
    let candidate = derive_candidate_entry(&cose).unwrap();
    let leaf = pask_wire::candidate_leaf_hash(&candidate);
    assert_eq!(hex::encode(leaf), TV7_LEAF);
}

#[test]
fn tv8_protected_header_len_25_2byte_bstr() {
    // Protected header is 25 bytes, uses 2-byte bstr header (0x58 0x19)
    let cose = hex_decode(TV8_COSE);
    let expected = hex_decode(TV8_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv8_leaf_hash() {
    let cose = hex_decode(TV8_COSE);
    let candidate = derive_candidate_entry(&cose).unwrap();
    let leaf = pask_wire::candidate_leaf_hash(&candidate);
    assert_eq!(hex::encode(leaf), TV8_LEAF);
}

#[test]
fn tv9_empty_payload_1byte_bstr() {
    // Payload is empty (0 bytes), uses 1-byte bstr header (0x40)
    let cose = hex_decode(TV9_COSE);
    let expected = hex_decode(TV9_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv9_leaf_hash() {
    let cose = hex_decode(TV9_COSE);
    let candidate = derive_candidate_entry(&cose).unwrap();
    let leaf = pask_wire::candidate_leaf_hash(&candidate);
    assert_eq!(hex::encode(leaf), TV9_LEAF);
}

#[test]
fn tv10_short_signature_1byte_bstr() {
    // Signature is 8 bytes, uses 1-byte bstr header (0x48)
    let cose = hex_decode(TV10_COSE);
    let expected = hex_decode(TV10_CANDIDATE);
    let result = derive_candidate_entry(&cose).unwrap();
    assert_eq!(result, expected);
}

#[test]
fn tv10_leaf_hash() {
    let cose = hex_decode(TV10_COSE);
    let candidate = derive_candidate_entry(&cose).unwrap();
    let leaf = pask_wire::candidate_leaf_hash(&candidate);
    assert_eq!(hex::encode(leaf), TV10_LEAF);
}

// --- Negative tests ---

#[test]
fn nv_detached_payload_rejected() {
    let cose = hex_decode(NV_DETACHED);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_unsupported_tag_rejected() {
    let cose = hex_decode(NV_BAD_TAG);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_trailing_bytes_rejected() {
    let cose = hex_decode(NV_TRAILING);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_unprotected_header_integer_rejected() {
    let cose = hex_decode(NV_UH_INT);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_unprotected_header_null_rejected() {
    let cose = hex_decode(NV_UH_NULL);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_unprotected_header_array_rejected() {
    let cose = hex_decode(NV_UH_ARRAY);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_unprotected_header_text_rejected() {
    let cose = hex_decode(NV_UH_TEXT);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_unprotected_header_bytes_rejected() {
    let cose = hex_decode(NV_UH_BYTES);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_protected_header_integer_rejected() {
    let cose = hex_decode(NV_PH_INT);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_payload_integer_rejected() {
    let cose = hex_decode(NV_PL_INT);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_signature_integer_rejected() {
    let cose = hex_decode(NV_SIG_INT);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_three_element_array_rejected() {
    let cose = hex_decode(NV_3_ELEM);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_non_array_rejected() {
    let cose = hex_decode(NV_NON_ARRAY);
    assert!(derive_candidate_entry(&cose).is_err());
}

#[test]
fn nv_empty_input_rejected() {
    assert!(derive_candidate_entry(&[]).is_err());
}

#[test]
fn nv_invalid_cbor_rejected() {
    let cose = hex_decode("ffffff");
    assert!(derive_candidate_entry(&cose).is_err());
}