feat(versioning): snapshot corpus per spec version
A frozen spec is only meaningful if it's paired with the corpus that was current at release time — otherwise a v0.1.0 reader follows "see corpus/payloads/ack/" and lands on a file whose test vectors may have changed since v0.1.0 was cut. Three changes: 1. `cut-version` now copies BOTH src/ AND corpus/ into the snapshot directory. A release is the pair, not just the text. 2. The corpus URL rewriting in cut-version now retargets /tree/main/corpus -> /tree/main/versions/<v>/corpus (and the blob variant). This lets the link resolve on GitHub from main alone — no git tag required, since the frozen copy is a real path under main. Downgrades the "tag is REQUIRED" message from last commit back to "optionally tag the commit". 3. The frozen corpus's spec_ref fields get rewritten too: from `src/04-payload-ack.md` to `versions/<v>/04-payload-ack.md`, so a reader following a test vector lands on the spec text the vector was paired with at release time. versions/v0.1.0/ regenerated from scratch via `just cut-version v0.1.0` to pick up the corpus snapshot + the new sed patterns. The release is now self-consistent: v0.1.0 spec links go to v0.1.0 corpus, which links back to v0.1.0 spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
71 files changed
+5924
-28
No files matched your search
@@ -65,7 +65,12 @@ cut-version VERSION:
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p versions
|
||||
# Snapshot both the spec text AND its paired test corpus, so the
|
||||
# release is a self-contained pair. The site infrastructure
|
||||
# (book.toml, theme, scripts, tools) stays unversioned at the
|
||||
# repo root and applies to every build.
|
||||
cp -R src "versions/{{VERSION}}"
|
||||
cp -R corpus "versions/{{VERSION}}/corpus"
|
||||
# Freeze the version marker in the snapshot. src/ keeps the literal
|
||||
# `latest (main)` so the rolling /latest/ build stays accurate.
|
||||
sed -i 's|`latest (main)`|`{{VERSION}}`|' "versions/{{VERSION}}/00-overview.md"
|
||||
@@ -74,16 +79,23 @@ cut-version VERSION:
|
||||
echo "snapshot still says 'latest (main)'. Check that src/00-overview.md" >&2
|
||||
echo "contains the marker line ('**Spec version:** \`latest (main)\`')." >&2
|
||||
fi
|
||||
# Repoint corpus links to this version's GitHub tree. The spec keeps
|
||||
# CHANGELOG links pointing at main (more useful — full history) by
|
||||
# only matching .../tree/main/corpus and .../blob/main/corpus.
|
||||
find "versions/{{VERSION}}" -name '*.md' -exec sed -i \
|
||||
-e 's|/tree/main/corpus|/tree/{{VERSION}}/corpus|g' \
|
||||
-e 's|/blob/main/corpus|/blob/{{VERSION}}/corpus|g' \
|
||||
# Repoint corpus URLs in the snapshot's spec text from main/corpus
|
||||
# to main/versions/<v>/corpus — the frozen copy lives in the repo
|
||||
# at a stable path on main, so the GitHub links resolve without
|
||||
# needing a git tag. -maxdepth 1 keeps the substitution off the
|
||||
# corpus *.json files (they get their own pass below).
|
||||
find "versions/{{VERSION}}" -maxdepth 1 -name '*.md' -exec sed -i \
|
||||
-e 's|/tree/main/corpus|/tree/main/versions/{{VERSION}}/corpus|g' \
|
||||
-e 's|/blob/main/corpus|/blob/main/versions/{{VERSION}}/corpus|g' \
|
||||
{} +
|
||||
echo "Snapshotted src/ -> versions/{{VERSION}}"
|
||||
# Repoint spec_ref inside the frozen corpus to this version's
|
||||
# frozen spec, so a reader following a test vector lands on the
|
||||
# spec text the vector was paired with at release time.
|
||||
find "versions/{{VERSION}}/corpus" -name '*.json' -exec sed -i \
|
||||
's|"spec_ref": "src/|"spec_ref": "versions/{{VERSION}}/|g' \
|
||||
{} +
|
||||
echo "Snapshotted: src/ + corpus/ -> versions/{{VERSION}}/"
|
||||
echo
|
||||
echo "Next steps:"
|
||||
echo " - git add versions/{{VERSION}} && commit (e.g. \"release {{VERSION}}\")"
|
||||
echo " - git tag {{VERSION}} # REQUIRED so the corpus links in /{{VERSION}}/"
|
||||
echo " # resolve on GitHub (they point at tree/{{VERSION}}/...)"
|
||||
echo " - optionally tag the commit: git tag {{VERSION}}"
|
||||
@@ -31,7 +31,7 @@ DongLoRa firmware and apps against live MeshCore deployments. Where DongLoRa
|
||||
behavior diverges from upstream, this spec follows upstream and treats the
|
||||
DongLoRa side as a follow-up bug, not a spec change.
|
||||
|
||||
Conformance against the [test corpus](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus)
|
||||
Conformance against the [test corpus](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus)
|
||||
is a useful signal but **not a substitute** for cross-checking against
|
||||
upstream firmware. If you find a divergence from upstream, please
|
||||
[file an issue](https://github.com/swaits/meshcore-spec/issues) — that is
|
||||
|
||||
@@ -131,7 +131,7 @@ A conforming decoder MUST reject packets with any of the following:
|
||||
|
||||
- [Section 2: Header](02-header.md) — Header byte encoding details
|
||||
- [Section 3: Path](03-path.md) — Path length encoding and path field
|
||||
- Test vectors: [`corpus/wire-format/framing/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/wire-format/framing/), [`corpus/wire-format/invalid/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/wire-format/invalid/)
|
||||
- Test vectors: [`corpus/wire-format/framing/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/wire-format/framing/), [`corpus/wire-format/invalid/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/wire-format/invalid/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ is an in-memory sentinel only and MUST NOT appear on the wire. A header of
|
||||
|
||||
- [Section 1: Wire Format](01-wire-format.md) — Overall packet structure
|
||||
- [Section 3: Path](03-path.md) — Path encoding
|
||||
- Test vectors: [`corpus/wire-format/header/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/wire-format/header/)
|
||||
- Test vectors: [`corpus/wire-format/header/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/wire-format/header/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ this class of bug.
|
||||
- [Section 1: Wire Format](01-wire-format.md) — Overall packet structure
|
||||
- [Section 2: Header](02-header.md) — Header encoding
|
||||
- [Section 15: Identity](15-identity.md) — Hash derivation from public keys
|
||||
- Test vectors: [`corpus/wire-format/path/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/wire-format/path/)
|
||||
- Test vectors: [`corpus/wire-format/path/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/wire-format/path/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ sender attribute each received ACK to a specific transmission attempt. See
|
||||
|
||||
- [Section 1: Wire Format](01-wire-format.md) — Packet framing
|
||||
- [Section 11: Multipart](11-payload-multipart.md) — Multipart ACK encoding
|
||||
- Test vectors: [`corpus/payloads/ack/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/ack/)
|
||||
- Test vectors: [`corpus/payloads/ack/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/ack/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ preceding optional fields are present.
|
||||
|
||||
- [Section 15: Identity](15-identity.md) — Ed25519 key management
|
||||
- [Section 14: Cryptography](14-crypto.md) — Signature operations
|
||||
- Test vectors: [`corpus/payloads/advert/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/advert/)
|
||||
- Test vectors: [`corpus/payloads/advert/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/advert/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ patterns include:
|
||||
|
||||
- [Section 14: Cryptography](14-crypto.md) — Encrypt-then-MAC details
|
||||
- [Section 15: Identity](15-identity.md) — ECDH shared secret computation
|
||||
- Test vectors: [`corpus/payloads/encrypted/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/encrypted/)
|
||||
- Test vectors: [`corpus/payloads/encrypted/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/encrypted/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ Header payload type field: `0x07` (ANON_REQ)
|
||||
- [Section 6: Encrypted Payloads](06-payload-encrypted.md) — Standard encrypted format
|
||||
- [Section 14: Cryptography](14-crypto.md) — Encrypt-then-MAC
|
||||
- [Section 15: Identity](15-identity.md) — ECDH key exchange
|
||||
- Test vectors: [`corpus/payloads/anon-req/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/anon-req/)
|
||||
- Test vectors: [`corpus/payloads/anon-req/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/anon-req/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ secret. Only a successful MAC verification confirms the correct channel.
|
||||
### Cross-References
|
||||
|
||||
- [Section 14: Cryptography](14-crypto.md) — Encrypt-then-MAC
|
||||
- Test vectors: [`corpus/payloads/group/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/group/)
|
||||
- Test vectors: [`corpus/payloads/group/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/group/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ hash is unique.
|
||||
- [Section 3: Path](03-path.md) — Path length encoding
|
||||
- [Section 6: Encrypted Payloads](06-payload-encrypted.md) — Outer envelope format
|
||||
- [Section 14: Cryptography](14-crypto.md) — Encrypt-then-MAC
|
||||
- Test vectors: [`corpus/payloads/path-return/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/path-return/)
|
||||
- Test vectors: [`corpus/payloads/path-return/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/path-return/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ At that point, `onTraceRecv` is called with:
|
||||
### Cross-References
|
||||
|
||||
- [Section 16: Packet Hash](16-packet-hash.md) — TRACE packets include path_len in hash
|
||||
- Test vectors: [`corpus/payloads/trace/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/trace/)
|
||||
- Test vectors: [`corpus/payloads/trace/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/trace/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ intends to emit, not a guarantee that they will arrive.
|
||||
### Cross-References
|
||||
|
||||
- [Section 4: ACK](04-payload-ack.md) — ACK CRC format
|
||||
- Test vectors: [`corpus/payloads/multipart/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/multipart/)
|
||||
- Test vectors: [`corpus/payloads/multipart/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/multipart/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ formats are application-defined, but typically include:
|
||||
### Cross-References
|
||||
|
||||
- [Section 17: Routing](17-routing.md) — Zero-hop delivery
|
||||
- Test vectors: [`corpus/payloads/control/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/control/)
|
||||
- Test vectors: [`corpus/payloads/control/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/control/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ when received via direct routing. They are NOT flood-routed.
|
||||
|
||||
### Cross-References
|
||||
|
||||
- Test vectors: [`corpus/payloads/raw-custom/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/payloads/raw-custom/)
|
||||
- Test vectors: [`corpus/payloads/raw-custom/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/payloads/raw-custom/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ Output format:
|
||||
|
||||
- [Section 6: Encrypted Payloads](06-payload-encrypted.md) — Usage in packet payloads
|
||||
- [Section 15: Identity](15-identity.md) — Shared secret computation via ECDH
|
||||
- Test vectors: [`corpus/crypto/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/crypto/)
|
||||
- Test vectors: [`corpus/crypto/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/crypto/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ Hex representation uses uppercase characters with 2 hex digits per byte.
|
||||
- [Section 3: Path](03-path.md) — Hash size in path encoding
|
||||
- [Section 5: Advertisement](05-payload-advert.md) — Signature usage
|
||||
- [Section 14: Cryptography](14-crypto.md) — Shared secret usage
|
||||
- Test vectors: [`corpus/crypto/ed25519/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/crypto/ed25519/), [`corpus/crypto/ecdh/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/crypto/ecdh/)
|
||||
- Test vectors: [`corpus/crypto/ed25519/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/crypto/ed25519/), [`corpus/crypto/ecdh/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/crypto/ecdh/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ function calculatePacketHash(packet):
|
||||
### Cross-References
|
||||
|
||||
- [Section 10: Trace](10-payload-trace.md) — TRACE packet special handling
|
||||
- Test vectors: [`corpus/crypto/sha256/packet-hash.json`](https://github.com/swaits/meshcore-spec/blob/v0.1.0/corpus/crypto/sha256/packet-hash.json)
|
||||
- Test vectors: [`corpus/crypto/sha256/packet-hash.json`](https://github.com/swaits/meshcore-spec/blob/main/versions/v0.1.0/corpus/crypto/sha256/packet-hash.json)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ implementations SHOULD request a larger MTU (up to 512 bytes).
|
||||
### Cross-References
|
||||
|
||||
- [Section 1: Wire Format](01-wire-format.md) — Underlying packet format
|
||||
- Test vectors: [`corpus/companion/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/companion/)
|
||||
- Test vectors: [`corpus/companion/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/companion/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ Response codes: `response = command | 0x80`
|
||||
|
||||
- [Section 1: Wire Format](01-wire-format.md) — Packet format within data frames
|
||||
- [Section 14: Cryptography](14-crypto.md) — Crypto operations via SetHardware
|
||||
- Test vectors: [`corpus/kiss/`](https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/kiss/)
|
||||
- Test vectors: [`corpus/kiss/`](https://github.com/swaits/meshcore-spec/tree/main/versions/v0.1.0/corpus/kiss/)
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"$schema": "../schema/test-vector.schema.json",
|
||||
"name": "Bridge Protocol - Framing",
|
||||
"spec_ref": "versions/v0.1.0/20-bridge-protocol.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests bridge protocol framing: magic(2) + payload + fletcher16_checksum(2). RS232 variant adds length(2) between magic and payload.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "bridge-001",
|
||||
"description": "Basic bridge frame: magic + ACK payload + checksum",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C03E 0D0001000000 0E52"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C03E 0D0001000000 0E52",
|
||||
"notes": "Magic=0xC03E. Payload: ACK flood packet (0D 00 01000000). Fletcher-16 checksum=0x520E stored as bytes 0E 52 (low byte first)."
|
||||
},
|
||||
{
|
||||
"id": "bridge-002",
|
||||
"description": "RS232 bridge frame: magic + length + payload + checksum",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C03E 0600 0D0001000000 0E52"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C03E 0600 0D0001000000 0E52",
|
||||
"notes": "RS232 variant: magic(C03E) + length(6=0x0006 LE=0600) + 6-byte payload + checksum."
|
||||
},
|
||||
{
|
||||
"id": "bridge-003",
|
||||
"description": "Bridge frame with minimal 3-byte inner packet",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C03E 3D00FF 9C3D"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C03E 3D00FF 9C3D",
|
||||
"notes": "Bridge frame wrapping the smallest valid FLOOD packet (3D 00 FF). Fletcher-16 of [3D, 00, FF]: sum1=(3D+00+FF)%255=0x3C=60, but computed stepwise: s1=61,s2=61 -> s1=61,s2=122 -> s1=60,s2=182 -> checksum low=0x3C high=0xB6 -> stored as 3C B6. Recalculated: s1=(0+0x3D)%255=61, s2=(0+61)%255=61, s1=(61+0x00)%255=61, s2=(61+61)%255=122, s1=(61+0xFF)%255=(316)%255=61, s2=(122+61)%255=183. Checksum = (183<<8)|61 = 0xB73D, stored as 3D B7. Corrected: payload data is C03E 3D00FF 9C3D with checksum bytes verified against reference."
|
||||
},
|
||||
{
|
||||
"id": "bridge-004",
|
||||
"description": "Bridge frame with wrong magic bytes",
|
||||
"type": "invalid",
|
||||
"binary": "3D 00 DEAD 0D0001000000 0E52",
|
||||
"expected_error": "invalid_magic",
|
||||
"notes": "Magic bytes are 0xDEAD instead of 0xC03E. Bridge receiver must reject frames with wrong magic."
|
||||
},
|
||||
{
|
||||
"id": "bridge-005",
|
||||
"description": "Bridge frame with corrupted checksum",
|
||||
"type": "invalid",
|
||||
"binary": "3D 00 C03E 0D0001000000 0000",
|
||||
"expected_error": "checksum_invalid",
|
||||
"notes": "Correct payload but checksum zeroed. Fletcher-16 verification must fail."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"$schema": "../schema/test-vector.schema.json",
|
||||
"name": "Companion Protocol - Commands",
|
||||
"spec_ref": "versions/v0.1.0/18-companion-protocol.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests companion protocol command type encoding for various command types.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "cmd-001",
|
||||
"description": "CMD_SEND_CHANNEL_MESSAGE (0x04) with channel index and text",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "04 00 48656C6C6F"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 04 00 48656C6C6F",
|
||||
"notes": "CMD=0x04, channel_idx=0, text='Hello' (48656C6C6F)."
|
||||
},
|
||||
{
|
||||
"id": "cmd-002",
|
||||
"description": "CMD_GET_CONTACTS (0x06)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "06"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 06"
|
||||
},
|
||||
{
|
||||
"id": "cmd-003",
|
||||
"description": "CMD_GET_CHANNELS (0x07)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "07"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 07"
|
||||
},
|
||||
{
|
||||
"id": "cmd-004",
|
||||
"description": "CMD_REBOOT (0x12)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "12"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 12"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"$schema": "../schema/test-vector.schema.json",
|
||||
"name": "Companion Protocol - Framing",
|
||||
"spec_ref": "versions/v0.1.0/18-companion-protocol.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests companion protocol frame encoding: type byte followed by variable-length data.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "comp-001",
|
||||
"description": "CMD_APP_START (0x01) - no data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "01"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 01",
|
||||
"notes": "Companion command type 0x01 = CMD_APP_START. No payload data."
|
||||
},
|
||||
{
|
||||
"id": "comp-002",
|
||||
"description": "CMD_DEVICE_QUERY (0x02) - no data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "02"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 02"
|
||||
},
|
||||
{
|
||||
"id": "comp-003",
|
||||
"description": "CMD_SET_TIME (0x08) with timestamp",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "08 00F15365"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 08 00F15365",
|
||||
"notes": "CMD_SET_TIME with timestamp 1700000000 = 0x6553F100 LE = 00F15365."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"$schema": "../schema/test-vector.schema.json",
|
||||
"name": "Companion Protocol - Responses",
|
||||
"spec_ref": "versions/v0.1.0/18-companion-protocol.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests companion protocol response type encoding.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "resp-001",
|
||||
"description": "PACKET_CONTACT_MSG_RECV (0x02) with message data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "02 00 00F15365 48656C6C6F"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 02 00 00F15365 48656C6C6F",
|
||||
"notes": "Type=0x02 (contact msg recv), contact_idx=0, timestamp=1700000000 LE, text='Hello'."
|
||||
},
|
||||
{
|
||||
"id": "resp-002",
|
||||
"description": "PACKET_ADV_RECV (0x03) with advert data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "03 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 01"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 03 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 01",
|
||||
"notes": "Type=0x03 (advert recv). pub_key=AA×32, flags=0x01 (Chat)."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "AES-128-ECB Block Padding",
|
||||
"spec_ref": "versions/v0.1.0/14-crypto.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests AES-128-ECB zero-padding for partial blocks. MeshCore pads the final block with zeros before encryption.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "aes-pad-001",
|
||||
"description": "5-byte plaintext 'Hello' padded to 16 bytes with zeros",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "Padded input: 48656C6C6F 0000000000000000000000. Output: 1 block."
|
||||
},
|
||||
{
|
||||
"id": "aes-pad-002",
|
||||
"description": "15-byte plaintext padded with 1 zero byte",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "BF70C038D511F2A8B528E06691C6D42D"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 BF70C038D511F2A8B528E06691C6D42D",
|
||||
"crypto_context": {
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E"
|
||||
},
|
||||
"notes": "15 bytes padded with 1 zero: 000102...0E00. Output: 1 block."
|
||||
},
|
||||
{
|
||||
"id": "aes-pad-003",
|
||||
"description": "16-byte plaintext: no padding needed",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "0A940BB5416EF045F1C39458C653EA5A"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 0A940BB5416EF045F1C39458C653EA5A",
|
||||
"crypto_context": {
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E0F"
|
||||
},
|
||||
"notes": "Exact block boundary: no padding. Output: 1 block."
|
||||
},
|
||||
{
|
||||
"id": "aes-pad-004",
|
||||
"description": "17-byte plaintext: 2 blocks, second padded with 15 zeros",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "0A940BB5416EF045F1C39458C653EA5A299F7C29A3E13AE7F64ECBA062FC7560"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 0A940BB5416EF045F1C39458C653EA5A299F7C29A3E13AE7F64ECBA062FC7560",
|
||||
"crypto_context": {
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E0F10"
|
||||
},
|
||||
"notes": "Block 1: 000102...0F (no pad). Block 2: 10 + 15 zeros. Output: 2 blocks = 32 bytes."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "AES-128-ECB Encryption",
|
||||
"spec_ref": "versions/v0.1.0/14-crypto.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Standalone AES-128-ECB encryption test vectors. Key is the first 16 bytes of the shared secret. These vectors test the raw encryption operation without MAC.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "aes-001",
|
||||
"description": "AES-128-ECB: key=00..0F, plaintext=all zeros",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C6A13B37878F5B826F4F8162A1C8D879"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C6A13B37878F5B826F4F8162A1C8D879",
|
||||
"crypto_context": {
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "00000000000000000000000000000000"
|
||||
},
|
||||
"notes": "AES-128-ECB(key=00..0F, pt=00×16) = C6A13B37878F5B826F4F8162A1C8D879. Wrapped in RAW_CUSTOM for schema compliance."
|
||||
},
|
||||
{
|
||||
"id": "aes-002",
|
||||
"description": "AES-128-ECB: key=00..0F, plaintext=all FF",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "3C441F32CE07822364D7A2990E50BB13"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 3C441F32CE07822364D7A2990E50BB13",
|
||||
"crypto_context": {
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "aes-003",
|
||||
"description": "AES-128-ECB: key=00..0F, plaintext=00..0F (sequential)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "0A940BB5416EF045F1C39458C653EA5A"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 0A940BB5416EF045F1C39458C653EA5A",
|
||||
"crypto_context": {
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E0F"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "aes-004",
|
||||
"description": "AES-128-ECB: key=all FF, plaintext=all zeros",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "A1F6258C877D5FCD8964484538BFC92C"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 A1F6258C877D5FCD8964484538BFC92C",
|
||||
"crypto_context": {
|
||||
"encryption_key": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
|
||||
"plaintext": "00000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "ECDH Shared Secret (X25519)",
|
||||
"spec_ref": "versions/v0.1.0/15-identity.md",
|
||||
"protocol_version": 0,
|
||||
"description": "X25519 ECDH key exchange test vectors. MeshCore uses Ed25519 keys transposed to X25519 for key exchange. The resulting 32-byte shared secret is used as: AES key (bytes 0-15) and HMAC key (bytes 0-31).",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ecdh-001",
|
||||
"description": "RFC 7748 Test Vector: X25519 key exchange (Alice's perspective)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742",
|
||||
"crypto_context": {
|
||||
"sender_private_key": "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
|
||||
"recipient_public_key": "DE9EDB7D7B7DC1B4D35B61C2ECE435373F8343C85B78674DADFC7E146F882B4F"
|
||||
},
|
||||
"notes": "RFC 7748 Section 6.1. Alice's private key and Bob's public key produce the shared secret. Note: MeshCore transposes Ed25519 keys to X25519, so actual ECDH inputs differ from raw Ed25519 keys."
|
||||
},
|
||||
{
|
||||
"id": "ecdh-002",
|
||||
"description": "RFC 7748 Test Vector: X25519 key exchange (Bob's perspective)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742",
|
||||
"crypto_context": {
|
||||
"sender_private_key": "5DAB087E624A8A4B79E17F8B83800EE66F3BB1292618B6FD1C2F8B27FF88E0EB",
|
||||
"recipient_public_key": "8520F0098930A754748B7DDCB43EF75A0DBF3A0D26381AF4EBA4A98EAA9B4E6A"
|
||||
},
|
||||
"notes": "RFC 7748 Section 6.1. Bob's private key and Alice's public key produce the same shared secret as ecdh-001, verifying the commutative property of ECDH."
|
||||
},
|
||||
{
|
||||
"id": "ecdh-003",
|
||||
"description": "X25519 with all-zeros private key scalar (edge case)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"crypto_context": {
|
||||
"sender_private_key": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"recipient_public_key": "DE9EDB7D7B7DC1B4D35B61C2ECE435373F8343C85B78674DADFC7E146F882B4F"
|
||||
},
|
||||
"notes": "Edge case: all-zero private key after clamping becomes a low-order scalar. X25519 with a clamped all-zero key (which becomes 0x40...00) against any public key. The all-zero shared secret indicates this is a degenerate key and implementations SHOULD reject it."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Ed25519 Sign/Verify",
|
||||
"spec_ref": "versions/v0.1.0/15-identity.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Ed25519 signature test vectors. MeshCore uses Ed25519 for advertisement signing. These vectors use RFC 8032 test keypairs for cross-validation.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ed-001",
|
||||
"description": "RFC 8032 Test Vector 1: empty message",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100B"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100B",
|
||||
"crypto_context": {
|
||||
"sender_private_key": "9D61B19DEFFD5A60BA844AF492EC2CC44449C5697B326919703BAC031CAE7F60",
|
||||
"sender_public_key": "D75A980182B10AB7D54BFED3C964073A0EE172F3DAA3F4A18446B0B8D183F8E3",
|
||||
"plaintext": ""
|
||||
},
|
||||
"notes": "RFC 8032 Section 7.1 Test Vector 1. Private key (seed) is 32 bytes; the 64-byte format used by MeshCore includes the expanded key. Signature over empty message."
|
||||
},
|
||||
{
|
||||
"id": "ed-002",
|
||||
"description": "RFC 8032 Test Vector 2: single-byte message 0x72",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "92A009A9F0D4CAB8720E820B5F642540A2B27B5416503F8FB3762223EBDB69DA085AC1E43E159C7E94B2BA35F6F6A1918E25682F9B0ADC5C33C2BF3C39F04E50"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 92A009A9F0D4CAB8720E820B5F642540A2B27B5416503F8FB3762223EBDB69DA085AC1E43E159C7E94B2BA35F6F6A1918E25682F9B0ADC5C33C2BF3C39F04E50",
|
||||
"crypto_context": {
|
||||
"sender_private_key": "4CCD089B28FF96DA9DB6C346EC114E0F5B8A319F35ABA624DA8CF6ED4FB8A6FB",
|
||||
"sender_public_key": "3D4017C3E843895A92B70AA74D1B7EBC9C982CCF2EC4968CC0CD55F12AF4660C",
|
||||
"plaintext": "72"
|
||||
},
|
||||
"notes": "RFC 8032 Section 7.1 Test Vector 2. Signature over the single byte 0x72."
|
||||
},
|
||||
{
|
||||
"id": "ed-003",
|
||||
"description": "RFC 8032 Test Vector 3: two-byte message",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "6291D657DEEC24024827E69C3ABE01A30CE548A284743A445E3680D7DB5AC3AC18FF9B538D16F290AE67F760984DC6594A7C15E9716ED28DC027BECEEA1EC40A"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 6291D657DEEC24024827E69C3ABE01A30CE548A284743A445E3680D7DB5AC3AC18FF9B538D16F290AE67F760984DC6594A7C15E9716ED28DC027BECEEA1EC40A",
|
||||
"crypto_context": {
|
||||
"sender_private_key": "C5AA8DF43F9F837BEDB7442F31DCB7B166D38535076F094B85CE3A2E0B4458F7",
|
||||
"sender_public_key": "FC51CD8E6218A1A38DA47ED00230F0580816ED13BA3303AC5DEB911548908025",
|
||||
"plaintext": "AF82"
|
||||
},
|
||||
"notes": "RFC 8032 Section 7.1 Test Vector 3. Signature over the two-byte message 0xAF82."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Encrypt-then-MAC Roundtrip",
|
||||
"spec_ref": "versions/v0.1.0/14-crypto.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Full encrypt-then-MAC pipeline test vectors. Each vector provides the shared secret, plaintext, and expected MAC+ciphertext output. Implementations should verify: (1) encrypt produces the ciphertext, (2) HMAC produces the MAC, (3) MACThenDecrypt recovers the plaintext.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "etm-001",
|
||||
"description": "Encrypt-then-MAC: 'Hello' with key 00..1F",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "EAB5 D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 EAB5D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "AES key = secret[0..15], HMAC key = secret[0..31]. Output = MAC(2) || Ciphertext(16) = 18 bytes."
|
||||
},
|
||||
{
|
||||
"id": "etm-002",
|
||||
"description": "Encrypt-then-MAC: 'Pipeline test!' with key 00..1F",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FB32 9EFCA5121B41B626F93A7CBEE440CACE"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 FB329EFCA5121B41B626F93A7CBEE440CACE",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "506970656C696E65207465737421"
|
||||
},
|
||||
"notes": "Plaintext 'Pipeline test!' (14 bytes). Zero-padded to 16 bytes before AES encryption."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "HMAC-SHA256 Truncated to 2 Bytes",
|
||||
"spec_ref": "versions/v0.1.0/14-crypto.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests HMAC-SHA256 computation with 32-byte key, truncated to CIPHER_MAC_SIZE (2 bytes). The HMAC key is the full 32-byte shared secret (PUB_KEY_SIZE), not just the 16-byte AES key.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "hmac-001",
|
||||
"description": "HMAC-SHA256(key=00..1F, msg='test message') truncated to 2 bytes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "90B8"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 90B8",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "74657374206D657373616765"
|
||||
},
|
||||
"notes": "HMAC-SHA256(key=00..1F, 'test message') = 90B89BC3C5CA2C29... Truncated to 2 bytes = 90B8."
|
||||
},
|
||||
{
|
||||
"id": "hmac-002",
|
||||
"description": "HMAC over AES ciphertext (encrypt-then-MAC pipeline)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "EAB5"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 EAB5",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "D9FD218D50A4409143A7243D6D913502"
|
||||
},
|
||||
"notes": "HMAC key = full 32-byte secret. Message = the AES ciphertext of 'Hello'. Result truncated to 2 bytes = EAB5."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "SHA-256 ACK CRC Computation",
|
||||
"spec_ref": "versions/v0.1.0/04-payload-ack.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests MeshCore ACK CRC computation: SHA-256(timestamp_le || txt_type_attempt || text || sender_pub_key)[0..3]. Each vector presents the full ACK packet that would be returned, along with the crypto_context showing the plaintext fed to SHA-256. Vectors ack-crc-001..004 share timestamp/text/pub_key and differ only in the attempt nibble of txt_type_attempt, demonstrating that each retry produces a distinct ACK CRC.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ack-crc-001",
|
||||
"description": "ACK CRC for TXT_MSG 'hello', ts=0x12345678, attempt=0, zero pubkey",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "B2EC0C9E"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 9E0CECB2",
|
||||
"crypto_context": {
|
||||
"plaintext": "78563412 00 68656C6C6F",
|
||||
"sender_public_key": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
"notes": "SHA-256(timestamp(4) || txt_type_attempt(1) || text || sender_pub_key(32))[0..3] = 9E0CECB2 (LE). Displayed ack_crc field is the uint32 value 0xB2EC0C9E."
|
||||
},
|
||||
{
|
||||
"id": "ack-crc-002",
|
||||
"description": "Same message as ack-crc-001 but attempt=1 — distinct CRC",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "13F08151"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 5181F013",
|
||||
"crypto_context": {
|
||||
"plaintext": "78563412 01 68656C6C6F",
|
||||
"sender_public_key": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
"notes": "txt_type_attempt=0x01 (TXT_TYPE_PLAIN<<2 | attempt=1). Confirms attempt increment changes ACK CRC."
|
||||
},
|
||||
{
|
||||
"id": "ack-crc-003",
|
||||
"description": "Same message as ack-crc-001 but attempt=2 — distinct CRC",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "4AD71DDF"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 DF1DD74A",
|
||||
"crypto_context": {
|
||||
"plaintext": "78563412 02 68656C6C6F",
|
||||
"sender_public_key": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ack-crc-004",
|
||||
"description": "Same message as ack-crc-001 but attempt=3 — distinct CRC (max attempt)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "1A537B8F"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 8F7B531A",
|
||||
"crypto_context": {
|
||||
"plaintext": "78563412 03 68656C6C6F",
|
||||
"sender_public_key": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
"notes": "attempt=3 is the maximum value representable in the 2-bit attempt field."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "SHA-256 Basic",
|
||||
"spec_ref": "versions/v0.1.0/14-crypto.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Basic SHA-256 hash computation test vectors. MeshCore uses SHA-256 for packet hashing, channel hash derivation, and HMAC.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "sha-001",
|
||||
"description": "SHA-256 of empty string",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",
|
||||
"crypto_context": {
|
||||
"plaintext": ""
|
||||
},
|
||||
"notes": "SHA-256('') = E3B0C442...B855. Standard NIST test vector."
|
||||
},
|
||||
{
|
||||
"id": "sha-002",
|
||||
"description": "SHA-256 of 'abc'",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD",
|
||||
"crypto_context": {
|
||||
"plaintext": "616263"
|
||||
},
|
||||
"notes": "SHA-256('abc') = BA7816BF...15AD. Standard NIST test vector."
|
||||
},
|
||||
{
|
||||
"id": "sha-003",
|
||||
"description": "SHA-256 of 'MeshCore'",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "F4F17278283E082A4743CCA308285D601E0F9943E6D6F9271B5B8C7DFED09DC0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 F4F17278283E082A4743CCA308285D601E0F9943E6D6F9271B5B8C7DFED09DC0",
|
||||
"crypto_context": {
|
||||
"plaintext": "4D657368436F7265"
|
||||
},
|
||||
"notes": "SHA-256('MeshCore')."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "SHA-256 Packet Hash",
|
||||
"spec_ref": "versions/v0.1.0/16-packet-hash.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests MeshCore packet hash computation: SHA-256(payload_type || payload)[0..7]. TRACE packets additionally include path_len in the hash input.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "phash-001",
|
||||
"description": "Packet hash for ACK with CRC 0xDEADBEEF",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "1BEE08540E8F7E5B"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 1BEE08540E8F7E5B",
|
||||
"crypto_context": {
|
||||
"plaintext": "03 EFBEADDE"
|
||||
},
|
||||
"notes": "SHA-256(0x03 || EFBEADDE)[0..7] = 1BEE08540E8F7E5B. Input: type byte 0x03 (ACK) + CRC 0xDEADBEEF as LE."
|
||||
},
|
||||
{
|
||||
"id": "phash-002",
|
||||
"description": "Packet hash for TRACE (path_len=0): includes path_len in hash",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C105C34E45E60009"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C105C34E45E60009",
|
||||
"crypto_context": {
|
||||
"plaintext": "09 00 01000000 02000000 00"
|
||||
},
|
||||
"notes": "SHA-256(0x09 || 0x00 || tag=1_LE || auth=2_LE || flags=0)[0..7]. TRACE special case: path_len byte (0x00) included after type byte."
|
||||
},
|
||||
{
|
||||
"id": "phash-003",
|
||||
"description": "Packet hash for TRACE (path_len=3): different hash due to path_len",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "B83FB2E0EE276404"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 B83FB2E0EE276404",
|
||||
"crypto_context": {
|
||||
"plaintext": "09 03 01000000 02000000 00"
|
||||
},
|
||||
"notes": "Same TRACE payload but path_len=3 instead of 0. Different hash proves path_len is included in computation."
|
||||
},
|
||||
{
|
||||
"id": "phash-004",
|
||||
"description": "Packet hash for ADVERT (all-AA pubkey, ts=0, all-BB sig)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "F73157720FB1B5E1"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 F73157720FB1B5E1",
|
||||
"crypto_context": {
|
||||
"plaintext": "04 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
},
|
||||
"notes": "SHA-256(0x04 || advert_payload)[0..7]. Type 0x04 = ADVERT. Not a TRACE, so path_len is NOT included."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"$schema": "../schema/test-vector.schema.json",
|
||||
"name": "KISS Protocol - Framing",
|
||||
"spec_ref": "versions/v0.1.0/19-kiss-protocol.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests KISS frame encoding including FEND delimiters, byte stuffing (FESC sequences), and type byte encoding.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "kiss-001",
|
||||
"description": "Simple data frame: FEND + type(0x00) + data + FEND",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 00 DEADBEEF C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 00 DEADBEEF C0",
|
||||
"notes": "KISS frame: FEND(C0) + DataFrame(00) + 4 bytes data + FEND(C0). Data contains no special bytes so no escaping needed."
|
||||
},
|
||||
{
|
||||
"id": "kiss-002",
|
||||
"description": "Data frame with FEND in data (byte stuffing required)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 00 AA DBDC BB C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 00 AA DBDC BB C0",
|
||||
"notes": "Data byte 0xC0 escaped as DB DC (FESC TFEND). Original data: AA C0 BB."
|
||||
},
|
||||
{
|
||||
"id": "kiss-003",
|
||||
"description": "Data frame with FESC in data (byte stuffing required)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 00 AA DBDD BB C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 00 AA DBDD BB C0",
|
||||
"notes": "Data byte 0xDB escaped as DB DD (FESC TFESC). Original data: AA DB BB."
|
||||
},
|
||||
{
|
||||
"id": "kiss-004",
|
||||
"description": "Data frame with both FEND and FESC in data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 00 DBDC DBDD C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 00 DBDC DBDD C0",
|
||||
"notes": "Original data: C0 DB. Escaped: DBDC DBDD."
|
||||
},
|
||||
{
|
||||
"id": "kiss-005",
|
||||
"description": "SetHardware frame: GetIdentity (sub-cmd 0x01)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 01 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 01 C0",
|
||||
"notes": "KISS frame: FEND + SetHardware(06) + GetIdentity(01) + FEND."
|
||||
},
|
||||
{
|
||||
"id": "kiss-006",
|
||||
"description": "SetHardware response: Identity (sub-cmd 0x81) with pubkey",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 81 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 81 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA C0",
|
||||
"notes": "KISS response: FEND + SetHardware(06) + Identity(81) + 32-byte pubkey + FEND."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"$schema": "../schema/test-vector.schema.json",
|
||||
"name": "KISS Protocol - SetHardware Extensions",
|
||||
"spec_ref": "versions/v0.1.0/19-kiss-protocol.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests MeshCore-specific SetHardware (0x06) sub-commands and responses.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "kiss-hw-001",
|
||||
"description": "GetVersion request",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 11 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 11 C0",
|
||||
"notes": "SetHardware(06) + GetVersion(11)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-hw-002",
|
||||
"description": "Version response: version=1, reserved=0",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 91 01 00 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 91 01 00 C0",
|
||||
"notes": "SetHardware(06) + Version response(91) + version=1 + reserved=0."
|
||||
},
|
||||
{
|
||||
"id": "kiss-hw-003",
|
||||
"description": "SetRadio: freq=869618000Hz, bw=62500Hz, sf=12, cr=5",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 09 D0C5D733 A4F40000 0C 05 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 09 D0C5D733 A4F40000 0C 05 C0",
|
||||
"notes": "SetRadio(09). freq=869618000=0x33D7C5D0 LE=D0C5D733. bw=62500=0x0000F4A4 LE=A4F40000. sf=12, cr=5."
|
||||
},
|
||||
{
|
||||
"id": "kiss-hw-004",
|
||||
"description": "Ping request and Pong response",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 17 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 17 C0",
|
||||
"notes": "SetHardware(06) + Ping(17)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-hw-005",
|
||||
"description": "Error response: UnknownCmd (0x05)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 F1 05 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 F1 05 C0",
|
||||
"notes": "SetHardware(06) + Error(F1) + UnknownCmd(05)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-hw-006",
|
||||
"description": "RxMeta unsolicited event: SNR=-2.5dB, RSSI=-100dBm",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 F9 F6 9C C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 F9 F6 9C C0",
|
||||
"notes": "RxMeta(F9). SNR: -2.5dB × 4 = -10 = 0xF6 (signed byte). RSSI: -100dBm = 0x9C (signed byte)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-hw-007",
|
||||
"description": "TxDone success",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 F8 01 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 F8 01 C0",
|
||||
"notes": "TxDone(F8) + success(01)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-hw-008",
|
||||
"description": "Stats response: rx=100, tx=50, errors=2",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 06 92 64000000 32000000 02000000 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 06 92 64000000 32000000 02000000 C0",
|
||||
"notes": "Stats(92). rx=100=0x64 LE=64000000, tx=50=0x32 LE=32000000, errors=2 LE=02000000."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"$schema": "../schema/test-vector.schema.json",
|
||||
"name": "KISS Protocol - Standard Commands",
|
||||
"spec_ref": "versions/v0.1.0/19-kiss-protocol.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests standard KISS commands (TXDELAY, Persistence, SlotTime, etc.).",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "kiss-std-001",
|
||||
"description": "TXDELAY command: 500ms (50 × 10ms)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 01 32 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 01 32 C0",
|
||||
"notes": "Type=0x01 (TXDELAY), data=0x32 (50 decimal = 500ms)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-std-002",
|
||||
"description": "Persistence command: P=63",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 02 3F C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 02 3F C0",
|
||||
"notes": "Type=0x02 (Persistence), data=0x3F (63)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-std-003",
|
||||
"description": "SlotTime command: 100ms (10 × 10ms)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 03 0A C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 03 0A C0",
|
||||
"notes": "Type=0x03 (SlotTime), data=0x0A (10 = 100ms)."
|
||||
},
|
||||
{
|
||||
"id": "kiss-std-004",
|
||||
"description": "FullDuplex command: half duplex (0)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "C0 05 00 C0"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 C0 05 00 C0",
|
||||
"notes": "Type=0x05 (FullDuplex), data=0x00 (half duplex)."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "ACK Payload - Basic",
|
||||
"spec_ref": "versions/v0.1.0/04-payload-ack.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Basic ACK payload encoding/decoding tests with various CRC values.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ack-001",
|
||||
"description": "ACK with CRC 0xDEADBEEF",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "DEADBEEF"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 EFBEADDE",
|
||||
"notes": "CRC 0xDEADBEEF stored as little-endian: EF BE AD DE."
|
||||
},
|
||||
{
|
||||
"id": "ack-002",
|
||||
"description": "ACK with CRC 0x00000001",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 01000000"
|
||||
},
|
||||
{
|
||||
"id": "ack-003",
|
||||
"description": "ACK with CRC 0x12345678",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "12345678"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 78563412"
|
||||
},
|
||||
{
|
||||
"id": "ack-004",
|
||||
"description": "ACK via direct routing with path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 2,
|
||||
"hashes": [
|
||||
"AA",
|
||||
"BB"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "CAFEBABE"
|
||||
}
|
||||
},
|
||||
"binary": "0E 02 AABB BEBAFECA",
|
||||
"notes": "Direct routed ACK. CRC 0xCAFEBABE as LE = BEBAFECA."
|
||||
},
|
||||
{
|
||||
"id": "ack-005",
|
||||
"description": "ACK with 2-byte hash path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 1,
|
||||
"hashes": [
|
||||
"AABB"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000000"
|
||||
}
|
||||
},
|
||||
"binary": "0E 41 AABB 00000000",
|
||||
"notes": "path_len=0x41: hash_size=2, count=1."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "ACK Payload - Boundary Values",
|
||||
"spec_ref": "versions/v0.1.0/04-payload-ack.md",
|
||||
"protocol_version": 0,
|
||||
"description": "ACK CRC boundary value tests including all-zeros, all-ones, and single-bit patterns.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ack-bnd-001",
|
||||
"description": "ACK CRC all zeros",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000000"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 00000000"
|
||||
},
|
||||
{
|
||||
"id": "ack-bnd-002",
|
||||
"description": "ACK CRC all ones",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "FFFFFFFF"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 FFFFFFFF"
|
||||
},
|
||||
{
|
||||
"id": "ack-bnd-003",
|
||||
"description": "ACK CRC 0x80000000 (high bit set)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "80000000"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 00000080"
|
||||
},
|
||||
{
|
||||
"id": "ack-bnd-004",
|
||||
"description": "ACK CRC 0x00000080 (byte boundary)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000080"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 80000000"
|
||||
},
|
||||
{
|
||||
"id": "ack-bnd-005",
|
||||
"description": "ACK CRC 0x01000000",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "01000000"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 00000001",
|
||||
"notes": "Tests that 0x01000000 LE = 00 00 00 01, not confused with 0x00000001."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Advertisement App Data - Combined Optional Fields",
|
||||
"spec_ref": "versions/v0.1.0/05-payload-advert.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests app_data with multiple optional fields present simultaneously. Fields appear in order: flags, location (lat+lon), feat1, feat2, name.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "adc-001",
|
||||
"description": "Chat node with location + feat1 + name",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 177,
|
||||
"latitude": 37774900,
|
||||
"longitude": -122419400,
|
||||
"feat1": 256,
|
||||
"name": "Node1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB B1 34664002 3807B4F8 0001 4E6F646531",
|
||||
"notes": "flags=0xB1=10110001: type=Chat(1), has_location(bit4), has_feat1(bit5), has_name(bit7). No feat2 (bit6=0). feat1=256=0x0100 LE=0001. Name 'Node1' = 4E6F646531."
|
||||
},
|
||||
{
|
||||
"id": "adc-002",
|
||||
"description": "Repeater with all optional fields",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 242,
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"feat1": 1,
|
||||
"feat2": 2,
|
||||
"name": "RPT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB F2 00000000 00000000 0100 0200 525054",
|
||||
"notes": "flags=0xF2=11110010: type=Repeater(2), all optional fields present. feat1=1 LE=0100, feat2=2 LE=0200, name='RPT'=525054."
|
||||
},
|
||||
{
|
||||
"id": "adc-003",
|
||||
"description": "Node with name only (no location or features)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 129,
|
||||
"name": "Hi"
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 81 4869",
|
||||
"notes": "flags=0x81=10000001: type=Chat(1), has_name(bit7) only. name='Hi'=4869."
|
||||
},
|
||||
{
|
||||
"id": "adc-004",
|
||||
"description": "Node with feat2 only (no location, feat1, or name)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 65,
|
||||
"feat2": 65535
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 41 FFFF",
|
||||
"notes": "flags=0x41=01000001: type=Chat(1), has_feat2(bit6). feat2=65535=0xFFFF."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Advertisement App Data - Location",
|
||||
"spec_ref": "versions/v0.1.0/05-payload-advert.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests app_data with location fields (latitude and longitude as int32_le × 1,000,000).",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "adl-001",
|
||||
"description": "Chat node with location: San Francisco (37.7749, -122.4194)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 17,
|
||||
"latitude": 37774900,
|
||||
"longitude": -122419400
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 11 34664002 3807B4F8",
|
||||
"notes": "flags=0x11: type=Chat(1), has_location(bit 4). Lat 37774900=0x02406634 LE=34664002. Lon -122419400=0xF8B40738 LE=3807B4F8."
|
||||
},
|
||||
{
|
||||
"id": "adl-002",
|
||||
"description": "Repeater with location: equator/prime meridian (0, 0)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 18,
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 12 00000000 00000000",
|
||||
"notes": "flags=0x12: type=Repeater(2), has_location. Both coords zero."
|
||||
},
|
||||
{
|
||||
"id": "adl-003",
|
||||
"description": "Sensor with negative latitude: south pole (-90, 0)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 20,
|
||||
"latitude": -90000000,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 14 80B5A2FA 00000000",
|
||||
"notes": "flags=0x14: type=Sensor(4), has_location. Lat -90000000=0xFAA2B580 LE=80B5A2FA. Lon 0."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Advertisement App Data - Node Types",
|
||||
"spec_ref": "versions/v0.1.0/05-payload-advert.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests all defined node types in the app_data flags field (bits 0-3). Uses a common pub_key, zero timestamp, and zero signature for structural testing.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "adt-001",
|
||||
"description": "App data type: None (0)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 00",
|
||||
"notes": "flags=0x00: type=None(0), no optional fields."
|
||||
},
|
||||
{
|
||||
"id": "adt-002",
|
||||
"description": "App data type: Chat (1)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 01"
|
||||
},
|
||||
{
|
||||
"id": "adt-003",
|
||||
"description": "App data type: Repeater (2)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 02"
|
||||
},
|
||||
{
|
||||
"id": "adt-004",
|
||||
"description": "App data type: Room (3)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 03"
|
||||
},
|
||||
{
|
||||
"id": "adt-005",
|
||||
"description": "App data type: Sensor (4)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"timestamp": 0,
|
||||
"signature": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"app_data": {
|
||||
"flags": 4
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 00000000 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 04"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Advertisement Payload - Basic Structure",
|
||||
"spec_ref": "versions/v0.1.0/05-payload-advert.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests the basic structure of advertisement payloads: pub_key(32) + timestamp(4) + signature(64) + optional app_data. Uses synthetic signature bytes since signature verification requires real Ed25519 operations (see signature-verify.json for verifiable vectors).",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "adv-001",
|
||||
"description": "Minimal advertisement: pub_key + timestamp + signature, no app_data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20",
|
||||
"timestamp": 1700000000,
|
||||
"signature": "A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0"
|
||||
}
|
||||
},
|
||||
"binary": "11 00 0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 00F15365 A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0",
|
||||
"notes": "Header: 0x11 = ADVERT+FLOOD. Timestamp 1700000000 = 0x6553F100 LE = 00F15365. Payload: 32+4+64 = 100 bytes."
|
||||
},
|
||||
{
|
||||
"id": "adv-002",
|
||||
"description": "Advertisement with 1-byte app_data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20",
|
||||
"timestamp": 1700000000,
|
||||
"signature": "A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0",
|
||||
"app_data": {
|
||||
"flags": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary": "11 00 0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 00F15365 A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0 01",
|
||||
"notes": "flags=0x01: type=Chat(1), no optional fields. Payload: 101 bytes."
|
||||
},
|
||||
{
|
||||
"id": "adv-003",
|
||||
"description": "Advertisement with path (3 hops, direct routing)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 3,
|
||||
"hashes": [
|
||||
"AA",
|
||||
"BB",
|
||||
"CC"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"pub_key": "0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20",
|
||||
"timestamp": 0,
|
||||
"signature": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"binary": "11 03 AABBCC 0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20 00000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"notes": "Timestamp=0 and zero signature for structural testing."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Anonymous Request Payload",
|
||||
"spec_ref": "versions/v0.1.0/07-payload-anon-req.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests anonymous request payload: dest_hash(1) + sender_pub_key(32) + cipher_mac(2) + ciphertext.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "anon-001",
|
||||
"description": "Anonymous request with known keys",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "anon_req",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"sender_pub_key": "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F",
|
||||
"cipher_mac": "F4B4",
|
||||
"ciphertext": "908A4FC137435774D0BB603F55A06069"
|
||||
}
|
||||
},
|
||||
"binary": "1D 00 AB 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F F4B4 908A4FC137435774D0BB603F55A06069",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "416E6F6E526571"
|
||||
},
|
||||
"notes": "Header 0x1D = ANON_REQ(7)+FLOOD(1). Payload: dest_hash(1) + pub_key(32) + mac(2) + ct(16) = 51 bytes. Plaintext 'AnonReq' (7 bytes)."
|
||||
},
|
||||
{
|
||||
"id": "anon-002",
|
||||
"description": "Anonymous request with tampered MAC",
|
||||
"type": "invalid",
|
||||
"binary": "1D 00 AB 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F 0000 908A4FC137435774D0BB603F55A06069",
|
||||
"expected_error": "mac_invalid",
|
||||
"notes": "MAC zeroed. Verification must fail."
|
||||
},
|
||||
{
|
||||
"id": "anon-003",
|
||||
"description": "Anonymous request via DIRECT routing with 2-hop path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "anon_req",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 2,
|
||||
"hashes": ["AA", "BB"]
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "CC",
|
||||
"sender_pub_key": "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F",
|
||||
"cipher_mac": "F4B4",
|
||||
"ciphertext": "908A4FC137435774D0BB603F55A06069"
|
||||
}
|
||||
},
|
||||
"binary": "1E 02 AABB CC 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F F4B4 908A4FC137435774D0BB603F55A06069",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "416E6F6E526571"
|
||||
},
|
||||
"notes": "Header 0x1E = ANON_REQ(7)+DIRECT(2). Direct-routed anonymous request with 2 path hashes."
|
||||
},
|
||||
{
|
||||
"id": "anon-004",
|
||||
"description": "Anonymous request truncated before ciphertext",
|
||||
"type": "invalid",
|
||||
"binary": "1D 00 AB 404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F F4B4",
|
||||
"expected_error": "too_short",
|
||||
"notes": "Payload is only 35 bytes (dest_hash + pub_key + mac). Missing ciphertext. Minimum is 51 bytes."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Control Payload - Basic",
|
||||
"spec_ref": "versions/v0.1.0/12-payload-control.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests control payload encoding. Control packets carry raw bytes where the first byte's bit 7 indicates zero-hop-only restriction.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ctl-001",
|
||||
"description": "Control packet with multi-hop allowed (bit 7 clear)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "control",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "01 AABBCCDD"
|
||||
}
|
||||
},
|
||||
"binary": "2D 00 01 AABBCCDD",
|
||||
"notes": "control_byte=0x01: bit 7 clear, multi-hop allowed."
|
||||
},
|
||||
{
|
||||
"id": "ctl-002",
|
||||
"description": "Control packet with zero-hop only (bit 7 set)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "control",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "80 AABBCCDD"
|
||||
}
|
||||
},
|
||||
"binary": "2E 00 80 AABBCCDD",
|
||||
"notes": "control_byte=0x80: bit 7 set, zero-hop only. Direct route with path_count=0 = zero-hop."
|
||||
},
|
||||
{
|
||||
"id": "ctl-003",
|
||||
"description": "Minimal control packet (1 byte)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "control",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "00"
|
||||
}
|
||||
},
|
||||
"binary": "2D 00 00"
|
||||
},
|
||||
{
|
||||
"id": "ctl-004",
|
||||
"description": "Control packet with zero-hop restriction and data via direct routing",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "control",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF 0102030405"
|
||||
}
|
||||
},
|
||||
"binary": "2E 00 FF 0102030405",
|
||||
"notes": "control_byte=0xFF: bit 7 set (zero-hop), all other bits set."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Encrypted Payload - Basic",
|
||||
"spec_ref": "versions/v0.1.0/06-payload-encrypted.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests encrypted payload encoding (REQ/RESPONSE/TXT_MSG) with known cryptographic material. Uses shared_secret = 0x000102...1F, AES key = first 16 bytes, HMAC key = full 32 bytes.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "enc-001",
|
||||
"description": "Encrypted TXT_MSG with 5-byte plaintext 'Hello'",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "plaintext 'Hello' = 48656C6C6F (5 bytes), zero-padded to 16 for AES-128-ECB. MAC is HMAC-SHA256(shared_secret, ciphertext) truncated to 2 bytes."
|
||||
},
|
||||
{
|
||||
"id": "enc-002",
|
||||
"description": "Encrypted REQUEST with 16-byte plaintext (exact AES block)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "request",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "631C",
|
||||
"ciphertext": "0A940BB5416EF045F1C39458C653EA5A"
|
||||
}
|
||||
},
|
||||
"binary": "01 00 ABCD 631C 0A940BB5416EF045F1C39458C653EA5A",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E0F"
|
||||
},
|
||||
"notes": "16-byte plaintext fills exactly one AES block. No padding needed."
|
||||
},
|
||||
{
|
||||
"id": "enc-003",
|
||||
"description": "Encrypted RESPONSE with 17-byte plaintext (spans 2 AES blocks)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "response",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "9822",
|
||||
"ciphertext": "0A940BB5416EF045F1C39458C653EA5A299F7C29A3E13AE7F64ECBA062FC7560"
|
||||
}
|
||||
},
|
||||
"binary": "05 00 ABCD 9822 0A940BB5416EF045F1C39458C653EA5A299F7C29A3E13AE7F64ECBA062FC7560",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E0F10"
|
||||
},
|
||||
"notes": "17-byte plaintext: first 16 bytes in block 1, byte 17 zero-padded in block 2. 32 bytes ciphertext total."
|
||||
},
|
||||
{
|
||||
"id": "enc-004",
|
||||
"description": "Encrypted TXT_MSG with 1-byte plaintext",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "83FB",
|
||||
"ciphertext": "2D4135E80761429386BFE3833C535AA8"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD 83FB 2D4135E80761429386BFE3833C535AA8",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "42"
|
||||
},
|
||||
"notes": "1-byte plaintext 0x42, zero-padded to 16 bytes."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Encrypted Payload - MAC Verification",
|
||||
"spec_ref": "versions/v0.1.0/06-payload-encrypted.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests MAC verification: valid MACs must pass, tampered MACs or ciphertext must fail.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "mac-001",
|
||||
"description": "Valid MAC with known ciphertext",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "48656C6C6F"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mac-002",
|
||||
"description": "Tampered MAC (first byte changed)",
|
||||
"type": "invalid",
|
||||
"binary": "09 00 ABCD 00B5 D9FD218D50A4409143A7243D6D913502",
|
||||
"expected_error": "mac_invalid",
|
||||
"notes": "MAC changed from EAB5 to 00B5. MACThenDecrypt must return 0."
|
||||
},
|
||||
{
|
||||
"id": "mac-003",
|
||||
"description": "Tampered MAC (second byte changed)",
|
||||
"type": "invalid",
|
||||
"binary": "09 00 ABCD EA00 D9FD218D50A4409143A7243D6D913502",
|
||||
"expected_error": "mac_invalid",
|
||||
"notes": "MAC changed from EAB5 to EA00."
|
||||
},
|
||||
{
|
||||
"id": "mac-004",
|
||||
"description": "Tampered ciphertext (first byte changed)",
|
||||
"type": "invalid",
|
||||
"binary": "09 00 ABCD EAB5 00FD218D50A4409143A7243D6D913502",
|
||||
"expected_error": "mac_invalid",
|
||||
"notes": "Ciphertext first byte changed from D9 to 00. HMAC over modified ciphertext won't match."
|
||||
},
|
||||
{
|
||||
"id": "mac-005",
|
||||
"description": "Swapped MAC bytes",
|
||||
"type": "invalid",
|
||||
"binary": "09 00 ABCD B5EA D9FD218D50A4409143A7243D6D913502",
|
||||
"expected_error": "mac_invalid",
|
||||
"notes": "MAC bytes reversed: B5EA instead of EAB5."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Encrypted Payload - AES Block Padding",
|
||||
"spec_ref": "versions/v0.1.0/06-payload-encrypted.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests AES-128-ECB zero-padding behavior at block boundaries. Plaintexts of 1, 15, 16, 17, and 32 bytes exercise all padding edge cases.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "pad-001",
|
||||
"description": "1-byte plaintext: padded with 15 zero bytes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "83FB",
|
||||
"ciphertext": "2D4135E80761429386BFE3833C535AA8"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD 83FB 2D4135E80761429386BFE3833C535AA8",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "42"
|
||||
},
|
||||
"notes": "1 byte padded to 16: 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00. 1 AES block output."
|
||||
},
|
||||
{
|
||||
"id": "pad-002",
|
||||
"description": "16-byte plaintext: no padding needed (exact block boundary)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "631C",
|
||||
"ciphertext": "0A940BB5416EF045F1C39458C653EA5A"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD 631C 0A940BB5416EF045F1C39458C653EA5A",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E0F"
|
||||
},
|
||||
"notes": "Exactly 16 bytes: no padding. 1 AES block output."
|
||||
},
|
||||
{
|
||||
"id": "pad-003",
|
||||
"description": "17-byte plaintext: 1 byte in second block, padded with 15 zeros",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "9822",
|
||||
"ciphertext": "0A940BB5416EF045F1C39458C653EA5A299F7C29A3E13AE7F64ECBA062FC7560"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD 9822 0A940BB5416EF045F1C39458C653EA5A299F7C29A3E13AE7F64ECBA062FC7560",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "000102030405060708090A0B0C0D0E0F10"
|
||||
},
|
||||
"notes": "17 bytes spans 2 blocks. Block 2 = 10 00 00 ... 00. 32 bytes ciphertext."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Encrypted Payload - Roundtrip Verification",
|
||||
"spec_ref": "versions/v0.1.0/06-payload-encrypted.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Roundtrip encrypt-then-MAC verification vectors. Each vector provides full key material enabling implementations to verify: (1) encryption produces the given ciphertext, (2) MAC computation produces the given MAC, (3) MAC verification succeeds, (4) decryption recovers the plaintext.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "rt-enc-001",
|
||||
"description": "Roundtrip: encrypt 'Hello', verify MAC, decrypt back",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"encryption_key": "000102030405060708090A0B0C0D0E0F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "Verification steps: (1) AES-128-ECB encrypt(key=00..0F, pad('Hello')) -> D9FD...3502. (2) HMAC-SHA256(key=00..1F, ct)[:2] -> EAB5. (3) HMAC verify: recompute and compare. (4) AES-128-ECB decrypt -> 48656C6C6F + 11 zero bytes."
|
||||
},
|
||||
{
|
||||
"id": "rt-enc-002",
|
||||
"description": "Roundtrip with wrong MAC must fail verification",
|
||||
"type": "invalid",
|
||||
"binary": "09 00 ABCD FFFF D9FD218D50A4409143A7243D6D913502",
|
||||
"expected_error": "mac_invalid",
|
||||
"notes": "Same ciphertext as rt-enc-001 but MAC replaced with FFFF. MACThenDecrypt must return 0 (failure)."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Encrypted Payload - Extra Edge Cases",
|
||||
"spec_ref": "versions/v0.1.0/06-payload-encrypted.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Additional encrypted payload tests: non-FLOOD routes, hash_size>1 paths, 32-byte plaintext, and payload-level invalid cases.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "enc-extra-001",
|
||||
"description": "Encrypted TXT_MSG via DIRECT routing with 2-byte hash path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": { "version": 0, "payload_type": "txt_msg", "route_type": "direct" },
|
||||
"path": { "hash_size": 2, "hash_count": 1, "hashes": ["AABB"] },
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "0A 41 AABB ABCD EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "Header 0x0A = TXT_MSG(2)+DIRECT(2). path_len=0x41 (hash_size=2, count=1). Tests encrypted payload with non-FLOOD routing and multi-byte hash."
|
||||
},
|
||||
{
|
||||
"id": "enc-extra-002",
|
||||
"description": "Encrypted TXT_MSG via TRANSPORT_FLOOD with transport codes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": { "version": 0, "payload_type": "txt_msg", "route_type": "transport_flood" },
|
||||
"transport_codes": [1000, 0],
|
||||
"path": { "hash_size": 1, "hash_count": 0, "hashes": [] },
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "08 E803 0000 00 ABCD EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "Header 0x08 = TXT_MSG(2)+TRANSPORT_FLOOD(0). Tests encrypted payload with transport codes."
|
||||
},
|
||||
{
|
||||
"id": "enc-extra-003",
|
||||
"description": "ACK payload too short (3 bytes instead of 4)",
|
||||
"type": "invalid",
|
||||
"binary": "0D 00 EFBEAD",
|
||||
"expected_error": "incomplete_payload",
|
||||
"notes": "ACK requires exactly 4 bytes. Only 3 provided."
|
||||
},
|
||||
{
|
||||
"id": "enc-extra-004",
|
||||
"description": "Payload exceeds MAX_PACKET_PAYLOAD (185 bytes)",
|
||||
"type": "invalid",
|
||||
"binary": "3D 00 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8",
|
||||
"expected_error": "payload_too_large",
|
||||
"notes": "Payload is 185 bytes (0x00..0xB8), exceeding MAX_PACKET_PAYLOAD of 184. readFrom MUST reject."
|
||||
},
|
||||
{
|
||||
"id": "enc-extra-005",
|
||||
"description": "Header 0xFF sentinel on wire (must reject)",
|
||||
"type": "invalid",
|
||||
"binary": "FF 00 DEADBEEF",
|
||||
"expected_error": "sentinel_header",
|
||||
"notes": "Header 0xFF is used internally as do-not-retransmit marker. MUST NOT appear on the wire."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Group Data Payload",
|
||||
"spec_ref": "versions/v0.1.0/08-payload-group.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests group datagram payload. Same wire format as GRP_TXT but payload type = 0x06.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "grp-data-001",
|
||||
"description": "Group datagram with known channel secret",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "grp_data",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"channel_hash": "72",
|
||||
"cipher_mac": "D184",
|
||||
"ciphertext": "948C819389BA4ED7B1194A0FF8E62073"
|
||||
}
|
||||
},
|
||||
"binary": "19 00 72 D184 948C819389BA4ED7B1194A0FF8E62073",
|
||||
"crypto_context": {
|
||||
"shared_secret": "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F",
|
||||
"encryption_key": "202122232425262728292A2B2C2D2E2F",
|
||||
"plaintext": "47726F75704D736721"
|
||||
},
|
||||
"notes": "Header 0x19 = GRP_DATA(6)+FLOOD(1). Same ciphertext as GRP_TXT since same key and plaintext."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Group Text Payload",
|
||||
"spec_ref": "versions/v0.1.0/08-payload-group.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests group text message payload: channel_hash(1) + cipher_mac(2) + ciphertext.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "grp-txt-001",
|
||||
"description": "Group text message with known channel secret",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "grp_txt",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"channel_hash": "72",
|
||||
"cipher_mac": "D184",
|
||||
"ciphertext": "948C819389BA4ED7B1194A0FF8E62073"
|
||||
}
|
||||
},
|
||||
"binary": "15 00 72 D184 948C819389BA4ED7B1194A0FF8E62073",
|
||||
"crypto_context": {
|
||||
"shared_secret": "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F",
|
||||
"encryption_key": "202122232425262728292A2B2C2D2E2F",
|
||||
"plaintext": "47726F75704D736721"
|
||||
},
|
||||
"notes": "Channel hash = SHA256(channel_secret)[0] = 0x72. Plaintext 'GroupMsg!' (9 bytes). AES key = first 16 bytes of channel secret."
|
||||
},
|
||||
{
|
||||
"id": "grp-txt-002",
|
||||
"description": "Group text with tampered MAC must fail",
|
||||
"type": "invalid",
|
||||
"binary": "15 00 72 0000 948C819389BA4ED7B1194A0FF8E62073",
|
||||
"expected_error": "mac_invalid",
|
||||
"notes": "Same ciphertext as grp-txt-001 but MAC zeroed. Verification must fail."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Multipart Payload - Basic",
|
||||
"spec_ref": "versions/v0.1.0/11-payload-multipart.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests multipart payload encoding: first byte = (remaining << 4) | sub_type. Currently only multipart ACK (sub_type=3) is defined.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "mp-001",
|
||||
"description": "Multipart ACK with remaining=1, CRC 0xDEADBEEF",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "multipart",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"remaining": 1,
|
||||
"sub_type": 3,
|
||||
"sub_payload": "EFBEADDE"
|
||||
}
|
||||
},
|
||||
"binary": "29 00 13 EFBEADDE",
|
||||
"notes": "First byte: (1<<4)|3 = 0x13. Sub-payload: ACK CRC 0xDEADBEEF as LE."
|
||||
},
|
||||
{
|
||||
"id": "mp-002",
|
||||
"description": "Multipart ACK with remaining=0 (last in sequence)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "multipart",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"remaining": 0,
|
||||
"sub_type": 3,
|
||||
"sub_payload": "01000000"
|
||||
}
|
||||
},
|
||||
"binary": "29 00 03 01000000",
|
||||
"notes": "First byte: (0<<4)|3 = 0x03."
|
||||
},
|
||||
{
|
||||
"id": "mp-003",
|
||||
"description": "Multipart ACK with remaining=15 (maximum)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "multipart",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"remaining": 15,
|
||||
"sub_type": 3,
|
||||
"sub_payload": "FFFFFFFF"
|
||||
}
|
||||
},
|
||||
"binary": "29 00 F3 FFFFFFFF",
|
||||
"notes": "First byte: (15<<4)|3 = 0xF3."
|
||||
},
|
||||
{
|
||||
"id": "mp-004",
|
||||
"description": "Multipart ACK via direct routing",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "multipart",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 2,
|
||||
"hashes": [
|
||||
"AA",
|
||||
"BB"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"remaining": 2,
|
||||
"sub_type": 3,
|
||||
"sub_payload": "78563412"
|
||||
}
|
||||
},
|
||||
"binary": "2A 02 AABB 23 78563412",
|
||||
"notes": "Header 0x2A = MULTIPART(10)+DIRECT(2). First byte: (2<<4)|3 = 0x23."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Path Return Payload - Basic",
|
||||
"spec_ref": "versions/v0.1.0/09-payload-path.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests path return payload outer structure: dest_hash(1) + src_hash(1) + encrypt-then-MAC. The encrypted inner format contains path_len + path + extra_type + extra_data.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "path-001",
|
||||
"description": "Path return with encrypted inner data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "path",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "21 00 ABCD EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "Header 0x21 = PATH(8)+FLOOD(1). Same ciphertext as enc-001 since same key/plaintext. The decrypted inner format would be parsed as: path_len=0x48, hash_size=2, hash_count=8 etc. - this is a structural test only."
|
||||
},
|
||||
{
|
||||
"id": "path-002",
|
||||
"description": "Path return via DIRECT routing with 1-hop path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "path",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 1,
|
||||
"hashes": ["AB"]
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "CD",
|
||||
"src_hash": "EF",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "22 01 AB CDEF EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "Header 0x22 = PATH(8)+DIRECT(2). Direct-routed path return with 1 hop."
|
||||
},
|
||||
{
|
||||
"id": "path-003",
|
||||
"description": "Path return via TRANSPORT_FLOOD with transport codes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "path",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [1, 2],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "EAB5",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "20 0100 0200 00 ABCD EAB5 D9FD218D50A4409143A7243D6D913502",
|
||||
"crypto_context": {
|
||||
"shared_secret": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||
"plaintext": "48656C6C6F"
|
||||
},
|
||||
"notes": "Header 0x20 = PATH(8)+TRANSPORT_FLOOD(0). Transport codes [1, 2] as LE uint16."
|
||||
},
|
||||
{
|
||||
"id": "path-004",
|
||||
"description": "Path return with 2-block ciphertext (32 bytes plaintext)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "path",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "1234",
|
||||
"ciphertext": "D9FD218D50A4409143A7243D6D913502D9FD218D50A4409143A7243D6D913502"
|
||||
}
|
||||
},
|
||||
"binary": "21 00 ABCD 1234 D9FD218D50A4409143A7243D6D913502D9FD218D50A4409143A7243D6D913502",
|
||||
"notes": "Path return with 2 AES blocks of ciphertext (32 bytes). Tests multi-block encrypted path data."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Raw Custom Payload - Basic",
|
||||
"spec_ref": "versions/v0.1.0/13-payload-raw-custom.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests raw custom payload encoding. RAW_CUSTOM carries application-defined raw bytes.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "raw-001",
|
||||
"description": "Minimal raw custom payload (1 byte)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "42"
|
||||
}
|
||||
},
|
||||
"binary": "3E 00 42",
|
||||
"notes": "Header 0x3E = RAW_CUSTOM(15)+DIRECT(2). Single byte payload."
|
||||
},
|
||||
{
|
||||
"id": "raw-002",
|
||||
"description": "Raw custom with binary data",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 1,
|
||||
"hashes": [
|
||||
"AA"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"data": "DEADBEEFCAFEBABE"
|
||||
}
|
||||
},
|
||||
"binary": "3E 01 AA DEADBEEFCAFEBABE",
|
||||
"notes": "Direct route with 1-hop path."
|
||||
},
|
||||
{
|
||||
"id": "raw-003",
|
||||
"description": "Raw custom with all-zero payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "0000000000000000"
|
||||
}
|
||||
},
|
||||
"binary": "3E 00 0000000000000000"
|
||||
},
|
||||
{
|
||||
"id": "raw-004",
|
||||
"description": "Raw custom with all-FF payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FFFFFFFFFFFFFFFF"
|
||||
}
|
||||
},
|
||||
"binary": "3E 00 FFFFFFFFFFFFFFFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Trace Payload - Basic",
|
||||
"spec_ref": "versions/v0.1.0/10-payload-trace.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests the basic 9-byte TRACE payload structure: tag(4) + auth_code(4) + flags(1).",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "trc-001",
|
||||
"description": "Basic trace with tag=1, auth_code=2, flags=0",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "trace",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"tag": 1,
|
||||
"auth_code": 2,
|
||||
"flags": 0
|
||||
}
|
||||
},
|
||||
"binary": "25 00 01000000 02000000 00",
|
||||
"notes": "tag=1 LE=01000000, auth_code=2 LE=02000000, flags=0."
|
||||
},
|
||||
{
|
||||
"id": "trc-002",
|
||||
"description": "Trace with large tag and auth_code values",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "trace",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"tag": 4294967295,
|
||||
"auth_code": 305419896,
|
||||
"flags": 0
|
||||
}
|
||||
},
|
||||
"binary": "25 00 FFFFFFFF 78563412 00",
|
||||
"notes": "tag=0xFFFFFFFF, auth_code=0x12345678 LE=78563412."
|
||||
},
|
||||
{
|
||||
"id": "trc-003",
|
||||
"description": "Trace with flags indicating 1-byte path hash size (flags & 0x03 = 0)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "trace",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"tag": 100,
|
||||
"auth_code": 200,
|
||||
"flags": 0
|
||||
}
|
||||
},
|
||||
"binary": "25 00 64000000 C8000000 00",
|
||||
"notes": "flags=0: path_hash_size = 1 << (0 & 3) = 1 byte."
|
||||
},
|
||||
{
|
||||
"id": "trc-004",
|
||||
"description": "Trace with flags indicating 2-byte path hash size (flags & 0x03 = 1)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "trace",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"tag": 100,
|
||||
"auth_code": 200,
|
||||
"flags": 1
|
||||
}
|
||||
},
|
||||
"binary": "25 00 64000000 C8000000 01",
|
||||
"notes": "flags=1: path_hash_size = 1 << (1 & 3) = 2 bytes."
|
||||
},
|
||||
{
|
||||
"id": "trc-005",
|
||||
"description": "Trace sent via direct routing with path hashes appended to payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "trace",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "01000000 02000000 00 AABBCC"
|
||||
}
|
||||
},
|
||||
"binary": "26 00 01000000 02000000 00 AABBCC",
|
||||
"notes": "Header 0x26 = TRACE(9)+DIRECT(2). path_len=0 (path field used for SNR). Payload: 9-byte header + 3 path hash bytes. Using raw payload since the appended hashes are semantically part of the trace."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://github.com/meshcore-spec/corpus/schema/test-vector.schema.json",
|
||||
"title": "MeshCore Test Vector File",
|
||||
"description": "Schema for MeshCore protocol test vector files. Each file contains a named collection of test vectors for a specific aspect of the protocol.",
|
||||
"type": "object",
|
||||
"required": ["name", "protocol_version", "vectors"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "JSON Schema reference"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Human-readable name for this test vector collection"
|
||||
},
|
||||
"spec_ref": {
|
||||
"type": "string",
|
||||
"description": "Path to the relevant spec section (e.g., 'src/01-wire-format.md')"
|
||||
},
|
||||
"protocol_version": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3,
|
||||
"description": "MeshCore protocol version (0-3, encoded in header bits 6-7)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Optional longer description of what this file tests"
|
||||
},
|
||||
"vectors": {
|
||||
"type": "array",
|
||||
"description": "Array of test vectors",
|
||||
"items": {
|
||||
"$ref": "#/$defs/test_vector"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"hex_string": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9A-F\\s]*$",
|
||||
"description": "Hex-encoded binary data. MUST be uppercase. Whitespace allowed and stripped on parse."
|
||||
},
|
||||
"header": {
|
||||
"type": "object",
|
||||
"required": ["version", "payload_type", "route_type"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3,
|
||||
"description": "Protocol version (header bits 6-7)"
|
||||
},
|
||||
"payload_type": {
|
||||
"enum": [
|
||||
"request",
|
||||
"response",
|
||||
"txt_msg",
|
||||
"ack",
|
||||
"advert",
|
||||
"grp_txt",
|
||||
"grp_data",
|
||||
"anon_req",
|
||||
"path",
|
||||
"trace",
|
||||
"multipart",
|
||||
"control",
|
||||
"raw_custom"
|
||||
],
|
||||
"description": "Payload type (header bits 2-5). Values 0x0C-0x0E are reserved."
|
||||
},
|
||||
"route_type": {
|
||||
"enum": [
|
||||
"transport_flood",
|
||||
"flood",
|
||||
"direct",
|
||||
"transport_direct"
|
||||
],
|
||||
"description": "Route type (header bits 0-1). 0=transport_flood, 1=flood, 2=direct, 3=transport_direct"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_info": {
|
||||
"type": "object",
|
||||
"required": ["hash_size", "hash_count", "hashes"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"hash_size": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 3,
|
||||
"description": "Size of each hash in bytes (1, 2, or 3). Encoded as (hash_size - 1) in bits 6-7 of path_len byte."
|
||||
},
|
||||
"hash_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 63,
|
||||
"description": "Number of hashes in path (bits 0-5 of path_len byte)"
|
||||
},
|
||||
"hashes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/hex_string"
|
||||
},
|
||||
"description": "Array of hex-encoded path hashes. Length MUST equal hash_count. Each hash MUST be hash_size bytes."
|
||||
}
|
||||
}
|
||||
},
|
||||
"transport_codes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 65535
|
||||
},
|
||||
"minItems": 2,
|
||||
"maxItems": 2,
|
||||
"description": "Two 16-bit transport codes [code1, code2], encoded as little-endian uint16"
|
||||
},
|
||||
"payload_ack": {
|
||||
"type": "object",
|
||||
"required": ["ack_crc"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"ack_crc": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "4-byte ACK CRC as hex (big-endian representation of the value, wire encoding is little-endian)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_advert": {
|
||||
"type": "object",
|
||||
"required": ["pub_key", "timestamp", "signature"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"pub_key": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "32-byte Ed25519 public key"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "4-byte Unix timestamp"
|
||||
},
|
||||
"signature": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "64-byte Ed25519 signature over (pub_key + timestamp_le + app_data)"
|
||||
},
|
||||
"app_data": {
|
||||
"$ref": "#/$defs/advert_app_data",
|
||||
"description": "Optional advertisement application data"
|
||||
}
|
||||
}
|
||||
},
|
||||
"advert_app_data": {
|
||||
"type": "object",
|
||||
"required": ["flags"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"flags": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"description": "8-bit flags: bits 0-3 = type (0=None,1=Chat,2=Repeater,3=Room,4=Sensor), bit 4 = has_location, bit 5 = has_feat1, bit 6 = has_feat2, bit 7 = has_name"
|
||||
},
|
||||
"latitude": {
|
||||
"type": "integer",
|
||||
"description": "Latitude * 1,000,000 as int32 (present when flags bit 4 set)"
|
||||
},
|
||||
"longitude": {
|
||||
"type": "integer",
|
||||
"description": "Longitude * 1,000,000 as int32 (present when flags bit 4 set)"
|
||||
},
|
||||
"feat1": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"description": "Feature field 1 as uint16 (present when flags bit 5 set)"
|
||||
},
|
||||
"feat2": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"description": "Feature field 2 as uint16 (present when flags bit 6 set)"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Node name as UTF-8 string (present when flags bit 7 set)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_encrypted": {
|
||||
"type": "object",
|
||||
"required": ["dest_hash", "src_hash", "cipher_mac", "ciphertext"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"dest_hash": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Destination hash (PATH_HASH_SIZE bytes, default 1)"
|
||||
},
|
||||
"src_hash": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Source hash (PATH_HASH_SIZE bytes, default 1)"
|
||||
},
|
||||
"cipher_mac": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "2-byte HMAC-SHA256 truncated MAC"
|
||||
},
|
||||
"ciphertext": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "AES-128-ECB encrypted data (zero-padded to block boundary)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_anon_req": {
|
||||
"type": "object",
|
||||
"required": ["dest_hash", "sender_pub_key", "cipher_mac", "ciphertext"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"dest_hash": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Destination hash (PATH_HASH_SIZE bytes)"
|
||||
},
|
||||
"sender_pub_key": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "32-byte Ed25519 public key of sender"
|
||||
},
|
||||
"cipher_mac": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "2-byte cipher MAC"
|
||||
},
|
||||
"ciphertext": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Encrypted data"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_group": {
|
||||
"type": "object",
|
||||
"required": ["channel_hash", "cipher_mac", "ciphertext"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"channel_hash": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "1-byte channel hash (first byte of SHA-256 of channel shared key)"
|
||||
},
|
||||
"cipher_mac": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "2-byte cipher MAC"
|
||||
},
|
||||
"ciphertext": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Encrypted data"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_trace": {
|
||||
"type": "object",
|
||||
"required": ["tag", "auth_code", "flags"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "4-byte random tag set by initiator"
|
||||
},
|
||||
"auth_code": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "4-byte authentication code"
|
||||
},
|
||||
"flags": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"description": "1-byte flags (bits 0-1 encode path hash size)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_multipart": {
|
||||
"type": "object",
|
||||
"required": ["remaining", "sub_type", "sub_payload"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"remaining": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 15,
|
||||
"description": "Remaining parts count (upper 4 bits of first byte)"
|
||||
},
|
||||
"sub_type": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 15,
|
||||
"description": "Sub-payload type (lower 4 bits of first byte)"
|
||||
},
|
||||
"sub_payload": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Sub-payload data"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_control": {
|
||||
"type": "object",
|
||||
"required": ["data"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Raw control data bytes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"payload_raw": {
|
||||
"type": "object",
|
||||
"required": ["data"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Raw custom payload bytes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"crypto_context": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"sender_private_key": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "64-byte Ed25519 private key of sender"
|
||||
},
|
||||
"sender_public_key": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "32-byte Ed25519 public key of sender"
|
||||
},
|
||||
"recipient_private_key": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "64-byte Ed25519 private key of recipient"
|
||||
},
|
||||
"recipient_public_key": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "32-byte Ed25519 public key of recipient"
|
||||
},
|
||||
"shared_secret": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "32-byte ECDH shared secret"
|
||||
},
|
||||
"encryption_key": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "16-byte AES-128 key (first 16 bytes of shared secret)"
|
||||
},
|
||||
"plaintext": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Plaintext before encryption"
|
||||
}
|
||||
}
|
||||
},
|
||||
"structured_packet": {
|
||||
"type": "object",
|
||||
"required": ["header", "path"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"header": {
|
||||
"$ref": "#/$defs/header"
|
||||
},
|
||||
"transport_codes": {
|
||||
"$ref": "#/$defs/transport_codes"
|
||||
},
|
||||
"path": {
|
||||
"$ref": "#/$defs/path_info"
|
||||
},
|
||||
"payload": {
|
||||
"description": "Payload structure — the schema depends on the payload_type in header",
|
||||
"anyOf": [
|
||||
{ "$ref": "#/$defs/payload_ack" },
|
||||
{ "$ref": "#/$defs/payload_advert" },
|
||||
{ "$ref": "#/$defs/payload_encrypted" },
|
||||
{ "$ref": "#/$defs/payload_anon_req" },
|
||||
{ "$ref": "#/$defs/payload_group" },
|
||||
{ "$ref": "#/$defs/payload_trace" },
|
||||
{ "$ref": "#/$defs/payload_multipart" },
|
||||
{ "$ref": "#/$defs/payload_control" },
|
||||
{ "$ref": "#/$defs/payload_raw" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"test_vector": {
|
||||
"type": "object",
|
||||
"required": ["id", "description", "type"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9_-]*$",
|
||||
"description": "Unique identifier within this file (e.g., 'ack-001')"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Human-readable description of what this vector tests"
|
||||
},
|
||||
"type": {
|
||||
"enum": ["encode_decode", "decode_only", "invalid"],
|
||||
"description": "Test mode: encode_decode (bidirectional), decode_only (binary->structured), invalid (must reject)"
|
||||
},
|
||||
"structured": {
|
||||
"$ref": "#/$defs/structured_packet",
|
||||
"description": "Structured packet representation (required for encode_decode and decode_only)"
|
||||
},
|
||||
"binary": {
|
||||
"$ref": "#/$defs/hex_string",
|
||||
"description": "Wire-format binary encoding as hex (required for all types)"
|
||||
},
|
||||
"crypto_context": {
|
||||
"$ref": "#/$defs/crypto_context",
|
||||
"description": "Cryptographic key material for encrypted payload vectors"
|
||||
},
|
||||
"expected_error": {
|
||||
"type": "string",
|
||||
"description": "Expected error category for invalid vectors"
|
||||
},
|
||||
"notes": {
|
||||
"type": "string",
|
||||
"description": "Optional notes explaining the test vector"
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": { "type": { "const": "encode_decode" } }
|
||||
},
|
||||
"then": {
|
||||
"required": ["structured", "binary"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": { "type": { "const": "decode_only" } }
|
||||
},
|
||||
"then": {
|
||||
"required": ["structured", "binary"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": { "type": { "const": "invalid" } }
|
||||
},
|
||||
"then": {
|
||||
"required": ["binary"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"$schema": "../../../schema/test-vector.schema.json",
|
||||
"name": "Framing - Decode Only",
|
||||
"spec_ref": "versions/v0.1.0/01-wire-format.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests one-way decode cases where the binary is valid to decode but may not be the canonical encoding a conformant encoder would produce.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "dec-001",
|
||||
"description": "Decode-only: ACK FLOOD with trailing bytes beyond the 4-byte CRC payload",
|
||||
"type": "decode_only",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "DEADBEEF"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 DEADBEEF 00",
|
||||
"notes": "Binary has 1 trailing byte after the 4-byte ACK CRC. Decoders MUST accept this (payload_len includes the extra byte). Encoders would not produce trailing bytes."
|
||||
},
|
||||
{
|
||||
"id": "dec-002",
|
||||
"description": "Decode-only: raw_custom FLOOD with path hash_count=0 but hash_size=2",
|
||||
"type": "decode_only",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "AA"
|
||||
}
|
||||
},
|
||||
"binary": "3D 40 AA",
|
||||
"notes": "Path_len byte 0x40 encodes hash_size=2 (bits 6-7 = 01) with hash_count=0. Decoders must handle non-default hash_size even when hash_count is zero. Encoders typically default hash_size=1 when hash_count=0."
|
||||
},
|
||||
{
|
||||
"id": "dec-003",
|
||||
"description": "Decode-only: raw_custom FLOOD with path hash_size=3 and hash_count=0",
|
||||
"type": "decode_only",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 3,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "BB"
|
||||
}
|
||||
},
|
||||
"binary": "3D 80 BB",
|
||||
"notes": "Path_len byte 0x80 encodes hash_size=3 (bits 6-7 = 10) with hash_count=0. Same as dec-002 but with hash_size=3."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Framing - Empty Payload Rejection",
|
||||
"spec_ref": "versions/v0.1.0/01-wire-format.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Verifies that packets with zero-length payload are rejected. The reference implementation (Packet::readFrom) checks 'if (i >= len) return false' after reading header+transport+path_len+path, requiring at least 1 byte of payload.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ep-001",
|
||||
"description": "FLOOD packet with no payload bytes (just header + path_len)",
|
||||
"type": "invalid",
|
||||
"binary": "0D 00",
|
||||
"expected_error": "empty_payload",
|
||||
"notes": "2 bytes total: header 0x0D + path_len 0x00. No payload bytes remain. readFrom returns false because i(2) >= len(2)."
|
||||
},
|
||||
{
|
||||
"id": "ep-002",
|
||||
"description": "TRANSPORT_FLOOD with no payload (header + transport + path_len only)",
|
||||
"type": "invalid",
|
||||
"binary": "0C 0000 0000 00",
|
||||
"expected_error": "empty_payload",
|
||||
"notes": "6 bytes: header(1) + transport(4) + path_len(1). i=6, len=6, so i>=len = true."
|
||||
},
|
||||
{
|
||||
"id": "ep-003",
|
||||
"description": "FLOOD packet with path but no payload",
|
||||
"type": "invalid",
|
||||
"binary": "0D 02 AABB",
|
||||
"expected_error": "empty_payload",
|
||||
"notes": "4 bytes: header(1) + path_len(1) + path(2). i=4, len=4."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Framing - Maximum Size Packets",
|
||||
"spec_ref": "versions/v0.1.0/01-wire-format.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests packets at or near the maximum wire size of 255 bytes (MAX_TRANS_UNIT). Verifies size calculation: total = 1 (header) + transport(0|4) + 1 (path_len) + path_bytes + payload_len.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "max-001",
|
||||
"description": "Maximum FLOOD packet: 255 bytes with empty path and 253-byte payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFC"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFC",
|
||||
"notes": "Total: 1 + 0 + 1 + 0 + 253 = 255 bytes. However payload exceeds MAX_PACKET_PAYLOAD (184). This is a wire-level max size test only; payload-level validation would reject this."
|
||||
},
|
||||
{
|
||||
"id": "max-002",
|
||||
"description": "Maximum valid FLOOD packet respecting MAX_PACKET_PAYLOAD: 1 + 1 + 0 + 184 = 186 bytes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7",
|
||||
"notes": "Total: 1 + 1 + 184 = 186 bytes. Payload is exactly MAX_PACKET_PAYLOAD (184 bytes = 0x00..0xB7)."
|
||||
},
|
||||
{
|
||||
"id": "max-003",
|
||||
"description": "Large FLOOD packet with maximum path (63 bytes) and payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 63,
|
||||
"hashes": [
|
||||
"01",
|
||||
"02",
|
||||
"03",
|
||||
"04",
|
||||
"05",
|
||||
"06",
|
||||
"07",
|
||||
"08",
|
||||
"09",
|
||||
"0A",
|
||||
"0B",
|
||||
"0C",
|
||||
"0D",
|
||||
"0E",
|
||||
"0F",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"1A",
|
||||
"1B",
|
||||
"1C",
|
||||
"1D",
|
||||
"1E",
|
||||
"1F",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27",
|
||||
"28",
|
||||
"29",
|
||||
"2A",
|
||||
"2B",
|
||||
"2C",
|
||||
"2D",
|
||||
"2E",
|
||||
"2F",
|
||||
"30",
|
||||
"31",
|
||||
"32",
|
||||
"33",
|
||||
"34",
|
||||
"35",
|
||||
"36",
|
||||
"37",
|
||||
"38",
|
||||
"39",
|
||||
"3A",
|
||||
"3B",
|
||||
"3C",
|
||||
"3D",
|
||||
"3E",
|
||||
"3F"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"data": "DEADBEEF"
|
||||
}
|
||||
},
|
||||
"binary": "3D 3F 0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F DEADBEEF",
|
||||
"notes": "Total: 1 + 1 + 63 + 4 = 69 bytes. Max path with small payload."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Framing - Minimal Valid Packets",
|
||||
"spec_ref": "versions/v0.1.0/01-wire-format.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests the smallest valid packets for each route type. A valid packet must have at least header(1) + path_len(1) + payload(1+) bytes, plus 4 bytes for transport codes if applicable.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "min-001",
|
||||
"description": "Minimal FLOOD packet: 3 bytes (header + path_len + 1-byte payload)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 FF",
|
||||
"notes": "Smallest valid FLOOD packet: 3 bytes total."
|
||||
},
|
||||
{
|
||||
"id": "min-002",
|
||||
"description": "Minimal DIRECT packet: 3 bytes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "3E 00 FF",
|
||||
"notes": "Smallest valid DIRECT packet: 3 bytes total."
|
||||
},
|
||||
{
|
||||
"id": "min-003",
|
||||
"description": "Minimal TRANSPORT_FLOOD packet: 7 bytes (header + 4 transport + path_len + 1-byte payload)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "3C 0000 0000 00 FF",
|
||||
"notes": "Smallest valid TRANSPORT_FLOOD packet: 7 bytes total."
|
||||
},
|
||||
{
|
||||
"id": "min-004",
|
||||
"description": "Minimal TRANSPORT_DIRECT packet: 7 bytes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "transport_direct"
|
||||
},
|
||||
"transport_codes": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "3F 0000 0000 00 FF",
|
||||
"notes": "Smallest valid TRANSPORT_DIRECT packet: 7 bytes total."
|
||||
},
|
||||
{
|
||||
"id": "min-005",
|
||||
"description": "Minimal ACK via FLOOD: 6 bytes (header + path_len + 4-byte CRC)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000000"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 00000000",
|
||||
"notes": "ACK with zero CRC. 6 bytes total."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Header - Combined Field Encoding",
|
||||
"spec_ref": "versions/v0.1.0/02-header.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Combinatorial tests of header byte encoding, exercising various combinations of version, payload type, and route type to verify correct bit packing.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "hdr-000",
|
||||
"description": "All-zero header: version=0, request=0, transport_flood=0",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "request",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "00",
|
||||
"src_hash": "00",
|
||||
"cipher_mac": "0000",
|
||||
"ciphertext": "00000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"binary": "00 0000 0000 00 0000 0000 00000000000000000000000000000000",
|
||||
"notes": "Header: (0<<6)|(0<<2)|0 = 0x00."
|
||||
},
|
||||
{
|
||||
"id": "hdr-001",
|
||||
"description": "Version=0, ADVERT, DIRECT route",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 2,
|
||||
"hashes": [
|
||||
"AA",
|
||||
"BB"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "12 02 AABB FF",
|
||||
"notes": "Header: (0<<6)|(4<<2)|2 = 0x12. path_len=0x02 (hash_size=1, count=2). Path: AA BB."
|
||||
},
|
||||
{
|
||||
"id": "hdr-002",
|
||||
"description": "Version=1, CONTROL, FLOOD route",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 1,
|
||||
"payload_type": "control",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "6D 00 FF",
|
||||
"notes": "Header: (1<<6)|(11<<2)|1 = 0x40+0x2C+0x01 = 0x6D."
|
||||
},
|
||||
{
|
||||
"id": "hdr-003",
|
||||
"description": "Version=0, RAW_CUSTOM, TRANSPORT_DIRECT with non-trivial transport codes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "transport_direct"
|
||||
},
|
||||
"transport_codes": [
|
||||
256,
|
||||
512
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 1,
|
||||
"hashes": [
|
||||
"AA"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"data": "CAFEBABE"
|
||||
}
|
||||
},
|
||||
"binary": "3F 0001 0002 01 AA CAFEBABE",
|
||||
"notes": "Header: (0<<6)|(15<<2)|3 = 0x3F. Transport codes: 256=0x0100 LE=0001, 512=0x0200 LE=0002."
|
||||
},
|
||||
{
|
||||
"id": "hdr-004",
|
||||
"description": "Version=0, TRACE, FLOOD with path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "trace",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 3,
|
||||
"hashes": [
|
||||
"AA",
|
||||
"BB",
|
||||
"CC"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"data": "0102030405060708 09"
|
||||
}
|
||||
},
|
||||
"binary": "25 03 AABBCC 010203040506070809",
|
||||
"notes": "Header: (0<<6)|(9<<2)|1 = 0x25. path_len=0x03 (1-byte hashes, 3 hops)."
|
||||
},
|
||||
{
|
||||
"id": "hdr-005",
|
||||
"description": "Maximum header value that isn't 0xFF sentinel: version=2, RAW_CUSTOM, TRANSPORT_DIRECT",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 2,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "transport_direct"
|
||||
},
|
||||
"transport_codes": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "AA"
|
||||
}
|
||||
},
|
||||
"binary": "BF 0000 0000 00 AA",
|
||||
"notes": "Header: (2<<6)|(15<<2)|3 = 0x80+0x3C+0x03 = 0xBF."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Header - Payload Types",
|
||||
"spec_ref": "versions/v0.1.0/02-header.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests encoding of all defined payload types in the header byte. Uses FLOOD routing and minimal payloads to isolate header encoding.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "pt-000",
|
||||
"description": "Payload type REQUEST (0x00) - header byte encodes type in bits 2-5",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "request",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "1122",
|
||||
"ciphertext": "DEADBEEFCAFEBABE0102030405060708"
|
||||
}
|
||||
},
|
||||
"binary": "01 00 ABCD 1122 DEADBEEFCAFEBABE0102030405060708",
|
||||
"notes": "Header: (0<<6)|(0<<2)|1 = 0x01. Payload: dest_hash(1) + src_hash(1) + MAC(2) + 16 bytes ciphertext."
|
||||
},
|
||||
{
|
||||
"id": "pt-001",
|
||||
"description": "Payload type RESPONSE (0x01)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "response",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "1122",
|
||||
"ciphertext": "DEADBEEFCAFEBABE0102030405060708"
|
||||
}
|
||||
},
|
||||
"binary": "05 00 ABCD 1122 DEADBEEFCAFEBABE0102030405060708",
|
||||
"notes": "Header: (0<<6)|(1<<2)|1 = 0x05."
|
||||
},
|
||||
{
|
||||
"id": "pt-002",
|
||||
"description": "Payload type TXT_MSG (0x02)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "txt_msg",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"dest_hash": "AB",
|
||||
"src_hash": "CD",
|
||||
"cipher_mac": "1122",
|
||||
"ciphertext": "DEADBEEFCAFEBABE0102030405060708"
|
||||
}
|
||||
},
|
||||
"binary": "09 00 ABCD 1122 DEADBEEFCAFEBABE0102030405060708",
|
||||
"notes": "Header: (0<<6)|(2<<2)|1 = 0x09."
|
||||
},
|
||||
{
|
||||
"id": "pt-003",
|
||||
"description": "Payload type ACK (0x03)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "DEADBEEF"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 EFBEADDE",
|
||||
"notes": "Header: (0<<6)|(3<<2)|1 = 0x0D. CRC 0xDEADBEEF as LE = EFBEADDE."
|
||||
},
|
||||
{
|
||||
"id": "pt-004",
|
||||
"description": "Payload type ADVERT (0x04) - minimal advert with 1 byte payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "advert",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "11 00 FF",
|
||||
"notes": "Header: (0<<6)|(4<<2)|1 = 0x11. Using raw payload to test header encoding in isolation."
|
||||
},
|
||||
{
|
||||
"id": "pt-005",
|
||||
"description": "Payload type GRP_TXT (0x05)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "grp_txt",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"channel_hash": "AB",
|
||||
"cipher_mac": "1122",
|
||||
"ciphertext": "DEADBEEFCAFEBABE0102030405060708"
|
||||
}
|
||||
},
|
||||
"binary": "15 00 AB 1122 DEADBEEFCAFEBABE0102030405060708",
|
||||
"notes": "Header: (0<<6)|(5<<2)|1 = 0x15."
|
||||
},
|
||||
{
|
||||
"id": "pt-006",
|
||||
"description": "Payload type GRP_DATA (0x06)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "grp_data",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"channel_hash": "AB",
|
||||
"cipher_mac": "1122",
|
||||
"ciphertext": "DEADBEEFCAFEBABE0102030405060708"
|
||||
}
|
||||
},
|
||||
"binary": "19 00 AB 1122 DEADBEEFCAFEBABE0102030405060708",
|
||||
"notes": "Header: (0<<6)|(6<<2)|1 = 0x19."
|
||||
},
|
||||
{
|
||||
"id": "pt-007",
|
||||
"description": "Payload type ANON_REQ (0x07)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "anon_req",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "1D 00 FF",
|
||||
"notes": "Header: (0<<6)|(7<<2)|1 = 0x1D. Minimal payload to test header."
|
||||
},
|
||||
{
|
||||
"id": "pt-008",
|
||||
"description": "Payload type PATH (0x08)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "path",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "21 00 FF",
|
||||
"notes": "Header: (0<<6)|(8<<2)|1 = 0x21."
|
||||
},
|
||||
{
|
||||
"id": "pt-009",
|
||||
"description": "Payload type TRACE (0x09)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "trace",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "25 00 FF",
|
||||
"notes": "Header: (0<<6)|(9<<2)|1 = 0x25."
|
||||
},
|
||||
{
|
||||
"id": "pt-010",
|
||||
"description": "Payload type MULTIPART (0x0A)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "multipart",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "29 00 FF",
|
||||
"notes": "Header: (0<<6)|(10<<2)|1 = 0x29."
|
||||
},
|
||||
{
|
||||
"id": "pt-011",
|
||||
"description": "Payload type CONTROL (0x0B)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "control",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "FF"
|
||||
}
|
||||
},
|
||||
"binary": "2D 00 FF",
|
||||
"notes": "Header: (0<<6)|(11<<2)|1 = 0x2D."
|
||||
},
|
||||
{
|
||||
"id": "pt-012",
|
||||
"description": "Payload type RAW_CUSTOM (0x0F)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "raw_custom",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"data": "CAFEBABE"
|
||||
}
|
||||
},
|
||||
"binary": "3D 00 CAFEBABE",
|
||||
"notes": "Header: (0<<6)|(15<<2)|1 = 0x3D."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Header - Route Types",
|
||||
"spec_ref": "versions/v0.1.0/02-header.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests encoding and decoding of all 4 route types in the header byte, using ACK (payload_type=3) as a simple payload.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "rt-000",
|
||||
"description": "Route type TRANSPORT_FLOOD (0x00) with ACK payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "01020304"
|
||||
}
|
||||
},
|
||||
"binary": "0C 0000 0000 00 04030201",
|
||||
"notes": "Header: version=0, payload_type=3 (ACK), route_type=0 (TRANSPORT_FLOOD). 0x0C = (0<<6)|(3<<2)|0. Transport codes both zero. path_len=0x00 (hash_size=1, count=0). ACK CRC 0x01020304 as LE = 04030201."
|
||||
},
|
||||
{
|
||||
"id": "rt-001",
|
||||
"description": "Route type FLOOD (0x01) with ACK payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "01020304"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 04030201",
|
||||
"notes": "Header: 0x0D = (0<<6)|(3<<2)|1. No transport codes. path_len=0x00. ACK CRC as LE."
|
||||
},
|
||||
{
|
||||
"id": "rt-002",
|
||||
"description": "Route type DIRECT (0x02) with ACK payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "01020304"
|
||||
}
|
||||
},
|
||||
"binary": "0E 00 04030201",
|
||||
"notes": "Header: 0x0E = (0<<6)|(3<<2)|2. No transport codes."
|
||||
},
|
||||
{
|
||||
"id": "rt-003",
|
||||
"description": "Route type TRANSPORT_DIRECT (0x03) with ACK payload",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_direct"
|
||||
},
|
||||
"transport_codes": [
|
||||
4660,
|
||||
22136
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "01020304"
|
||||
}
|
||||
},
|
||||
"binary": "0F 3412 7856 00 04030201",
|
||||
"notes": "Header: 0x0F = (0<<6)|(3<<2)|3. Transport codes: 4660=0x1234 as LE=3412, 22136=0x5678 as LE=7856."
|
||||
},
|
||||
{
|
||||
"id": "rt-004",
|
||||
"description": "TRANSPORT_FLOOD with non-zero transport codes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [
|
||||
65535,
|
||||
1
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "FFFFFFFF"
|
||||
}
|
||||
},
|
||||
"binary": "0C FFFF 0100 00 FFFFFFFF",
|
||||
"notes": "Transport code 1: 0xFFFF as LE = FFFF. Transport code 2: 1 as LE = 0100. CRC 0xFFFFFFFF as LE."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Header - Protocol Versions",
|
||||
"spec_ref": "versions/v0.1.0/02-header.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests encoding of all 4 protocol version values in header bits 6-7. Uses ACK (type=3) with FLOOD routing as a simple baseline.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "ver-000",
|
||||
"description": "Version 0 (V1, current) - bits 6-7 = 00",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 01000000",
|
||||
"notes": "Header: (0<<6)|(3<<2)|1 = 0x0D. Version 0 = V1."
|
||||
},
|
||||
{
|
||||
"id": "ver-001",
|
||||
"description": "Version 1 (V2, reserved) - bits 6-7 = 01",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 1,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "4D 00 01000000",
|
||||
"notes": "Header: (1<<6)|(3<<2)|1 = 0x4D. Version 1 = V2 (reserved)."
|
||||
},
|
||||
{
|
||||
"id": "ver-002",
|
||||
"description": "Version 2 (V3, reserved) - bits 6-7 = 10",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 2,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "8D 00 01000000",
|
||||
"notes": "Header: (2<<6)|(3<<2)|1 = 0x8D. Version 2 = V3 (reserved)."
|
||||
},
|
||||
{
|
||||
"id": "ver-003",
|
||||
"description": "Version 3 (V4, reserved) - bits 6-7 = 11",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 3,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "CD 00 01000000",
|
||||
"notes": "Header: (3<<6)|(3<<2)|1 = 0xCD. Version 3 = V4 (reserved)."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Invalid - Bad Path Length Encoding",
|
||||
"spec_ref": "versions/v0.1.0/03-path.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests invalid path_len values including the reserved hash_size=4 and path overflow conditions.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "bpl-001",
|
||||
"description": "Reserved hash size code 3 (hash_size would be 4): path_len=0xC0",
|
||||
"type": "invalid",
|
||||
"binary": "0D C0 01000000",
|
||||
"expected_error": "reserved_hash_size",
|
||||
"notes": "path_len=0xC0: bits 6-7=11 (hash size code 3). isValidPathLen returns false because hash_size==4."
|
||||
},
|
||||
{
|
||||
"id": "bpl-002",
|
||||
"description": "Reserved hash size code 3 with non-zero count: path_len=0xC1",
|
||||
"type": "invalid",
|
||||
"binary": "0D C1 AA 01000000",
|
||||
"expected_error": "reserved_hash_size",
|
||||
"notes": "path_len=0xC1: hash size code 3 (reserved), count=1. Invalid regardless of count."
|
||||
},
|
||||
{
|
||||
"id": "bpl-003",
|
||||
"description": "Reserved hash size code 3, max count: path_len=0xFF",
|
||||
"type": "invalid",
|
||||
"binary": "0D FF 0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F 01000000",
|
||||
"expected_error": "reserved_hash_size",
|
||||
"notes": "path_len=0xFF: hash size code 3 (reserved), count=63. Even if hash_size=4 were valid, 63*4=252 > MAX_PATH_SIZE."
|
||||
},
|
||||
{
|
||||
"id": "bpl-004",
|
||||
"description": "Path overflow: hash_size=2, count=33 (66 bytes > MAX_PATH_SIZE)",
|
||||
"type": "invalid",
|
||||
"binary": "0D 61 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536373839 3A3B3C3D3E3F4041 01000000",
|
||||
"expected_error": "path_overflow",
|
||||
"notes": "path_len=0x61: hash_size=2, count=33. 33*2=66 > MAX_PATH_SIZE (64). isValidPathLen returns false."
|
||||
},
|
||||
{
|
||||
"id": "bpl-005",
|
||||
"description": "Path overflow: hash_size=3, count=22 (66 bytes > MAX_PATH_SIZE)",
|
||||
"type": "invalid",
|
||||
"binary": "0D 96 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F4041 01000000",
|
||||
"expected_error": "path_overflow",
|
||||
"notes": "path_len=0x96: hash_size=3, count=22. 22*3=66 > MAX_PATH_SIZE (64)."
|
||||
},
|
||||
{
|
||||
"id": "bpl-006",
|
||||
"description": "Path overflow edge case: hash_size=1, count=63 is valid (63<=64), but with hash_size=2 and count=33 it overflows",
|
||||
"type": "invalid",
|
||||
"binary": "0D 61 0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F3031323334353637383940 4142 DEADBEEF",
|
||||
"expected_error": "path_overflow",
|
||||
"notes": "path_len=0x61: size code 1 (hash_size=2), count 33. 33*2=66 > 64."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Invalid - Too Short Packets",
|
||||
"spec_ref": "versions/v0.1.0/01-wire-format.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests packets that are shorter than the minimum required size for their route type.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "short-001",
|
||||
"description": "1-byte packet (FLOOD): only header, missing path_len and payload",
|
||||
"type": "invalid",
|
||||
"binary": "0D",
|
||||
"expected_error": "too_short",
|
||||
"notes": "Minimum FLOOD packet is 3 bytes. This is 1."
|
||||
},
|
||||
{
|
||||
"id": "short-002",
|
||||
"description": "2-byte packet (FLOOD): header + path_len, missing payload",
|
||||
"type": "invalid",
|
||||
"binary": "0D 00",
|
||||
"expected_error": "empty_payload",
|
||||
"notes": "Header + path_len present but no payload. i=2, len=2, so i>=len."
|
||||
},
|
||||
{
|
||||
"id": "short-003",
|
||||
"description": "5-byte TRANSPORT_FLOOD: header + transport codes, missing path_len",
|
||||
"type": "invalid",
|
||||
"binary": "0C 00000000",
|
||||
"expected_error": "too_short",
|
||||
"notes": "TRANSPORT_FLOOD needs at least 7 bytes (1+4+1+1). This has 5."
|
||||
},
|
||||
{
|
||||
"id": "short-004",
|
||||
"description": "6-byte TRANSPORT_FLOOD: header + transport + path_len, missing payload",
|
||||
"type": "invalid",
|
||||
"binary": "0C 00000000 00",
|
||||
"expected_error": "empty_payload",
|
||||
"notes": "All framing present but zero payload."
|
||||
},
|
||||
{
|
||||
"id": "short-005",
|
||||
"description": "FLOOD with path consuming all remaining bytes",
|
||||
"type": "invalid",
|
||||
"binary": "0D 03 AABBCC",
|
||||
"expected_error": "empty_payload",
|
||||
"notes": "header(1) + path_len(1) + path(3) = 5 bytes = total. i=5, len=5."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Invalid - Truncated Packets",
|
||||
"spec_ref": "versions/v0.1.0/01-wire-format.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests packets that are truncated at various points, verifying that decoders reject them gracefully.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "trunc-001",
|
||||
"description": "Empty packet (0 bytes)",
|
||||
"type": "invalid",
|
||||
"binary": "",
|
||||
"expected_error": "too_short",
|
||||
"notes": "Zero-length input. Cannot read header."
|
||||
},
|
||||
{
|
||||
"id": "trunc-002",
|
||||
"description": "Header only (1 byte, FLOOD route)",
|
||||
"type": "invalid",
|
||||
"binary": "0D",
|
||||
"expected_error": "too_short",
|
||||
"notes": "Only header byte. Cannot read path_len."
|
||||
},
|
||||
{
|
||||
"id": "trunc-003",
|
||||
"description": "Header only (1 byte, TRANSPORT_FLOOD route) - transport codes truncated",
|
||||
"type": "invalid",
|
||||
"binary": "0C",
|
||||
"expected_error": "too_short",
|
||||
"notes": "Route type 0x00 requires 4 transport code bytes, but only header is present."
|
||||
},
|
||||
{
|
||||
"id": "trunc-004",
|
||||
"description": "TRANSPORT_FLOOD with partial transport codes (2 of 4 bytes)",
|
||||
"type": "invalid",
|
||||
"binary": "0C 1234",
|
||||
"expected_error": "too_short",
|
||||
"notes": "Only 2 of 4 transport code bytes present."
|
||||
},
|
||||
{
|
||||
"id": "trunc-005",
|
||||
"description": "TRANSPORT_FLOOD with transport codes but no path_len",
|
||||
"type": "invalid",
|
||||
"binary": "0C 0000 0000",
|
||||
"expected_error": "too_short",
|
||||
"notes": "5 bytes: header + 4 transport code bytes. Missing path_len."
|
||||
},
|
||||
{
|
||||
"id": "trunc-006",
|
||||
"description": "FLOOD with path_len indicating 3 hops but only 1 path byte",
|
||||
"type": "invalid",
|
||||
"binary": "0D 03 AA FF",
|
||||
"expected_error": "truncated_path",
|
||||
"notes": "path_len=0x03 means 3 bytes of path expected but only 1 byte follows before the alleged payload. The decoder would read 3 path bytes (AA FF ??) and run out."
|
||||
},
|
||||
{
|
||||
"id": "trunc-007",
|
||||
"description": "FLOOD with 2-byte hash path_len=0x42 (4 path bytes needed) but only 2 available",
|
||||
"type": "invalid",
|
||||
"binary": "0D 42 AABB FF",
|
||||
"expected_error": "truncated_path",
|
||||
"notes": "path_len=0x42: hash_size=2, count=2, need 4 path bytes. Only 2 bytes before payload marker."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Path - Boundary Conditions",
|
||||
"spec_ref": "versions/v0.1.0/03-path.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests boundary conditions for path encoding including maximum valid values and values just below overflow thresholds.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "pb-001",
|
||||
"description": "hash_size=2, count=31 (62 bytes, 2 below max)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 31,
|
||||
"hashes": [
|
||||
"0001",
|
||||
"0203",
|
||||
"0405",
|
||||
"0607",
|
||||
"0809",
|
||||
"0A0B",
|
||||
"0C0D",
|
||||
"0E0F",
|
||||
"1011",
|
||||
"1213",
|
||||
"1415",
|
||||
"1617",
|
||||
"1819",
|
||||
"1A1B",
|
||||
"1C1D",
|
||||
"1E1F",
|
||||
"2021",
|
||||
"2223",
|
||||
"2425",
|
||||
"2627",
|
||||
"2829",
|
||||
"2A2B",
|
||||
"2C2D",
|
||||
"2E2F",
|
||||
"3031",
|
||||
"3233",
|
||||
"3435",
|
||||
"3637",
|
||||
"3839",
|
||||
"3A3B",
|
||||
"3C3D"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 5F 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D 01000000",
|
||||
"notes": "path_len=0x5F: hash_size=2, count=31. Path: 62 bytes. Just below max."
|
||||
},
|
||||
{
|
||||
"id": "pb-002",
|
||||
"description": "hash_size=3, count=20 (60 bytes, 3 below max for this hash_size)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 3,
|
||||
"hash_count": 20,
|
||||
"hashes": [
|
||||
"000102",
|
||||
"030405",
|
||||
"060708",
|
||||
"090A0B",
|
||||
"0C0D0E",
|
||||
"0F1011",
|
||||
"121314",
|
||||
"151617",
|
||||
"18191A",
|
||||
"1B1C1D",
|
||||
"1E1F20",
|
||||
"212223",
|
||||
"242526",
|
||||
"272829",
|
||||
"2A2B2C",
|
||||
"2D2E2F",
|
||||
"303132",
|
||||
"333435",
|
||||
"363738",
|
||||
"393A3B"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 94 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536373839 3A3B 01000000",
|
||||
"notes": "path_len=0x94: hash_size=3, count=20. Path: 60 bytes."
|
||||
},
|
||||
{
|
||||
"id": "pb-003",
|
||||
"description": "Single-byte path: hash_size=1, count=1",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 1,
|
||||
"hashes": [
|
||||
"00"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 01 00 01000000",
|
||||
"notes": "Minimal non-empty path. Hash value 0x00 is valid."
|
||||
},
|
||||
{
|
||||
"id": "pb-004",
|
||||
"description": "Path with all-FF hashes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 3,
|
||||
"hashes": [
|
||||
"FF",
|
||||
"FF",
|
||||
"FF"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 03 FFFFFF 01000000",
|
||||
"notes": "All hashes are 0xFF. Valid encoding."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Path - Hash Counts",
|
||||
"spec_ref": "versions/v0.1.0/03-path.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests various hash count values including zero, small, and maximum values for each hash size.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "hc-000",
|
||||
"description": "Zero hops (hash_count=0, hash_size=1) - empty path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 01000000",
|
||||
"notes": "path_len=0x00. Zero-length path."
|
||||
},
|
||||
{
|
||||
"id": "hc-001",
|
||||
"description": "Zero hops with hash_size=2 - still empty path",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 40 01000000",
|
||||
"notes": "path_len=0x40: hash_size=2, count=0. Path is still 0 bytes even though hash_size>1."
|
||||
},
|
||||
{
|
||||
"id": "hc-002",
|
||||
"description": "Zero hops with hash_size=3",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 3,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 80 01000000",
|
||||
"notes": "path_len=0x80: hash_size=3, count=0. Path is 0 bytes."
|
||||
},
|
||||
{
|
||||
"id": "hc-010",
|
||||
"description": "Maximum count for hash_size=1: 63 hops, 63 bytes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 63,
|
||||
"hashes": [
|
||||
"01",
|
||||
"02",
|
||||
"03",
|
||||
"04",
|
||||
"05",
|
||||
"06",
|
||||
"07",
|
||||
"08",
|
||||
"09",
|
||||
"0A",
|
||||
"0B",
|
||||
"0C",
|
||||
"0D",
|
||||
"0E",
|
||||
"0F",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"1A",
|
||||
"1B",
|
||||
"1C",
|
||||
"1D",
|
||||
"1E",
|
||||
"1F",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27",
|
||||
"28",
|
||||
"29",
|
||||
"2A",
|
||||
"2B",
|
||||
"2C",
|
||||
"2D",
|
||||
"2E",
|
||||
"2F",
|
||||
"30",
|
||||
"31",
|
||||
"32",
|
||||
"33",
|
||||
"34",
|
||||
"35",
|
||||
"36",
|
||||
"37",
|
||||
"38",
|
||||
"39",
|
||||
"3A",
|
||||
"3B",
|
||||
"3C",
|
||||
"3D",
|
||||
"3E",
|
||||
"3F"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 3F 0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F 01000000",
|
||||
"notes": "path_len=0x3F: hash_size=1, count=63. Path: 63 bytes. Maximum valid path for 1-byte hashes."
|
||||
},
|
||||
{
|
||||
"id": "hc-020",
|
||||
"description": "Maximum count for hash_size=2: 32 hops, 64 bytes (exactly MAX_PATH_SIZE)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 32,
|
||||
"hashes": [
|
||||
"0001",
|
||||
"0203",
|
||||
"0405",
|
||||
"0607",
|
||||
"0809",
|
||||
"0A0B",
|
||||
"0C0D",
|
||||
"0E0F",
|
||||
"1011",
|
||||
"1213",
|
||||
"1415",
|
||||
"1617",
|
||||
"1819",
|
||||
"1A1B",
|
||||
"1C1D",
|
||||
"1E1F",
|
||||
"2021",
|
||||
"2223",
|
||||
"2425",
|
||||
"2627",
|
||||
"2829",
|
||||
"2A2B",
|
||||
"2C2D",
|
||||
"2E2F",
|
||||
"3031",
|
||||
"3233",
|
||||
"3435",
|
||||
"3637",
|
||||
"3839",
|
||||
"3A3B",
|
||||
"3C3D",
|
||||
"3E3F"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 60 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F 01000000",
|
||||
"notes": "path_len=0x60: bits 6-7=01 (hash_size=2), bits 0-5=32. Path: 64 bytes = MAX_PATH_SIZE. Maximum valid for 2-byte hashes."
|
||||
},
|
||||
{
|
||||
"id": "hc-030",
|
||||
"description": "Maximum count for hash_size=3: 21 hops, 63 bytes",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 3,
|
||||
"hash_count": 21,
|
||||
"hashes": [
|
||||
"000102",
|
||||
"030405",
|
||||
"060708",
|
||||
"090A0B",
|
||||
"0C0D0E",
|
||||
"0F1011",
|
||||
"121314",
|
||||
"151617",
|
||||
"18191A",
|
||||
"1B1C1D",
|
||||
"1E1F20",
|
||||
"212223",
|
||||
"242526",
|
||||
"272829",
|
||||
"2A2B2C",
|
||||
"2D2E2F",
|
||||
"303132",
|
||||
"333435",
|
||||
"363738",
|
||||
"393A3B",
|
||||
"3C3D3E"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 95 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E 01000000",
|
||||
"notes": "path_len=0x95: bits 6-7=10 (hash_size=3), bits 0-5=21. Path: 63 bytes. Maximum valid for 3-byte hashes (22*3=66 would overflow)."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Path - Hash Sizes",
|
||||
"spec_ref": "versions/v0.1.0/03-path.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests all valid hash sizes (1, 2, 3 bytes) in the path length encoding. These vectors are critical for catching implementations that treat path_len as a raw byte count.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "hs-001",
|
||||
"description": "Hash size 1 (code 0), 1 hop",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 1,
|
||||
"hashes": [
|
||||
"AA"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 01 AA 01000000",
|
||||
"notes": "path_len=0x01: bits 6-7=00 (size code 0, hash_size=1), bits 0-5=1 (count=1). Path: 1 byte."
|
||||
},
|
||||
{
|
||||
"id": "hs-002",
|
||||
"description": "Hash size 1 (code 0), 5 hops",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 5,
|
||||
"hashes": [
|
||||
"AA",
|
||||
"BB",
|
||||
"CC",
|
||||
"DD",
|
||||
"EE"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 05 AABBCCDDEE 01000000",
|
||||
"notes": "path_len=0x05: hash_size=1, count=5. Path: 5 bytes."
|
||||
},
|
||||
{
|
||||
"id": "hs-010",
|
||||
"description": "Hash size 2 (code 1), 1 hop",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 1,
|
||||
"hashes": [
|
||||
"AABB"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 41 AABB 01000000",
|
||||
"notes": "path_len=0x41: bits 6-7=01 (size code 1, hash_size=2), bits 0-5=1 (count=1). Path: 2 bytes. An implementation treating 0x41=65 as raw byte count would fail."
|
||||
},
|
||||
{
|
||||
"id": "hs-011",
|
||||
"description": "Hash size 2 (code 1), 2 hops",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 2,
|
||||
"hashes": [
|
||||
"AABB",
|
||||
"CCDD"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 42 AABBCCDD 01000000",
|
||||
"notes": "path_len=0x42: hash_size=2, count=2. Path: 4 bytes. This is the example that catches the meshcore-rs bug (0x42=66 raw vs 4 actual bytes)."
|
||||
},
|
||||
{
|
||||
"id": "hs-012",
|
||||
"description": "Hash size 2 (code 1), 5 hops",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 2,
|
||||
"hash_count": 5,
|
||||
"hashes": [
|
||||
"1122",
|
||||
"3344",
|
||||
"5566",
|
||||
"7788",
|
||||
"99AA"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 45 112233445566778899AA 01000000",
|
||||
"notes": "path_len=0x45: hash_size=2, count=5. Path: 10 bytes."
|
||||
},
|
||||
{
|
||||
"id": "hs-020",
|
||||
"description": "Hash size 3 (code 2), 1 hop",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 3,
|
||||
"hash_count": 1,
|
||||
"hashes": [
|
||||
"AABBCC"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 81 AABBCC 01000000",
|
||||
"notes": "path_len=0x81: bits 6-7=10 (size code 2, hash_size=3), bits 0-5=1. Path: 3 bytes."
|
||||
},
|
||||
{
|
||||
"id": "hs-021",
|
||||
"description": "Hash size 3 (code 2), 3 hops",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 3,
|
||||
"hash_count": 3,
|
||||
"hashes": [
|
||||
"AABBCC",
|
||||
"DDEEFF",
|
||||
"112233"
|
||||
]
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 83 AABBCC DDEEFF 112233 01000000",
|
||||
"notes": "path_len=0x83: hash_size=3, count=3. Path: 9 bytes."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
{
|
||||
"$schema": "../../schema/test-vector.schema.json",
|
||||
"name": "Transport Codes - Basic",
|
||||
"spec_ref": "versions/v0.1.0/01-wire-format.md",
|
||||
"protocol_version": 0,
|
||||
"description": "Tests transport code encoding with various values and route types that include/exclude transport codes.",
|
||||
"vectors": [
|
||||
{
|
||||
"id": "tc-001",
|
||||
"description": "Transport codes with TRANSPORT_FLOOD, both zero",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0C 0000 0000 00 01000000",
|
||||
"notes": "Both transport codes zero."
|
||||
},
|
||||
{
|
||||
"id": "tc-002",
|
||||
"description": "Transport codes with TRANSPORT_DIRECT, typical values",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_direct"
|
||||
},
|
||||
"transport_codes": [
|
||||
1000,
|
||||
2000
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0F E803 D007 00 01000000",
|
||||
"notes": "TC1: 1000=0x03E8 LE=E803. TC2: 2000=0x07D0 LE=D007."
|
||||
},
|
||||
{
|
||||
"id": "tc-003",
|
||||
"description": "Transport codes at maximum uint16 values",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [
|
||||
65535,
|
||||
65535
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0C FFFF FFFF 00 01000000",
|
||||
"notes": "Both transport codes at maximum: 0xFFFF."
|
||||
},
|
||||
{
|
||||
"id": "tc-004",
|
||||
"description": "Transport codes: code1=1, code2=0",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_flood"
|
||||
},
|
||||
"transport_codes": [
|
||||
1,
|
||||
0
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0C 0100 0000 00 01000000",
|
||||
"notes": "TC1=1 LE=0100, TC2=0."
|
||||
},
|
||||
{
|
||||
"id": "tc-005",
|
||||
"description": "Transport codes: code1=0x8000, code2=0x0001 (verify high bit handling)",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "transport_direct"
|
||||
},
|
||||
"transport_codes": [
|
||||
32768,
|
||||
1
|
||||
],
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0F 0080 0100 00 01000000",
|
||||
"notes": "TC1: 0x8000 LE=0080. TC2: 1 LE=0100."
|
||||
},
|
||||
{
|
||||
"id": "tc-006",
|
||||
"description": "FLOOD route (no transport codes) - verify they are absent from wire",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "flood"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0D 00 01000000",
|
||||
"notes": "FLOOD route: no transport codes on wire. Total: 1 (header) + 1 (path_len) + 4 (ACK) = 6 bytes."
|
||||
},
|
||||
{
|
||||
"id": "tc-007",
|
||||
"description": "DIRECT route (no transport codes) - verify they are absent from wire",
|
||||
"type": "encode_decode",
|
||||
"structured": {
|
||||
"header": {
|
||||
"version": 0,
|
||||
"payload_type": "ack",
|
||||
"route_type": "direct"
|
||||
},
|
||||
"path": {
|
||||
"hash_size": 1,
|
||||
"hash_count": 0,
|
||||
"hashes": []
|
||||
},
|
||||
"payload": {
|
||||
"ack_crc": "00000001"
|
||||
}
|
||||
},
|
||||
"binary": "0E 00 01000000",
|
||||
"notes": "DIRECT route: no transport codes on wire."
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user