Every "Test vectors: \`corpus/...\`" mention in the spec is now a markdown link pointing at the corresponding file or directory on GitHub. Each version build resolves its links to the correct git ref: - /latest/ -> https://github.com/swaits/meshcore-spec/tree/main/corpus/... - /v0.1.0/ -> https://github.com/swaits/meshcore-spec/tree/v0.1.0/corpus/... - future /vX.Y.Z/ -> /tree/vX.Y.Z/corpus/... Two changes: 1. src/*.md (18 files): the inline-code corpus paths in each "Cross-References" section are now markdown links. Directories point at /tree/main/corpus/..., individual .json files point at /blob/main/corpus/.... CHANGELOG and other non-corpus main links are intentionally left pointing at main — the canonical history lives there regardless of which version you're reading. 2. justfile: `cut-version` now also runs a sed over the snapshot's *.md files replacing /tree/main/corpus -> /tree/<VERSION>/corpus and /blob/main/corpus -> /blob/<VERSION>/corpus. So a freshly-cut version is self-consistent. versions/v0.1.0/* regenerated by re-running `just cut-version v0.1.0` after the new src/ links + recipe sed landed. Tagging the release commit (`git tag v0.1.0`) is now REQUIRED — not "optional" — because the rendered /v0.1.0/ pages reference /tree/v0.1.0/corpus/... on GitHub. Without the tag the links 404. cut-version's output now says so. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
3.5 KiB
Markdown
91 lines
3.5 KiB
Markdown
# MeshCore Protocol Specification
|
||
|
||
## Section 9: Payload — Path Return
|
||
|
||
### Overview
|
||
|
||
The PATH payload carries an encrypted return path from one node to another. It
|
||
uses the same dest_hash + src_hash + encrypt-then-MAC envelope as REQ/RESPONSE/
|
||
TXT_MSG, but the decrypted contents contain routing path data and optional extra
|
||
application data.
|
||
|
||
### Payload Type
|
||
|
||
Header payload type field: `0x08` (PATH)
|
||
|
||
### Wire Format (Outer)
|
||
|
||
```
|
||
0
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| dest_hash(1) | src_hash(1) | Cipher MAC (2 bytes) |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| |
|
||
| Ciphertext (N × 16 bytes, N ≥ 1) |
|
||
| |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
```
|
||
|
||
The outer format is identical to [Section 6](06-payload-encrypted.md).
|
||
|
||
### Decrypted Inner Format
|
||
|
||
```
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| path_len(1) | Path (hash_count × hash_size) |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
| extra_type(1) | Extra Data (variable) |
|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||
```
|
||
|
||
| Field | Size | Type | Description |
|
||
|-------|------|------|-------------|
|
||
| path_len | 1 byte | encoded | Same bitfield encoding as packet path_len (see [Section 3](03-path.md)) |
|
||
| path | hash_count × hash_size | raw | The return path to the sender |
|
||
| extra_type | 1 byte | uint8 | Type of extra data (lower 4 bits used; upper 4 reserved) |
|
||
| extra | remaining | raw | Extra data (may include trailing zero padding from AES) |
|
||
|
||
### Extra Type Values
|
||
|
||
| Value | Description |
|
||
|-------|-------------|
|
||
| 0x00 | No meaningful extra data (reciprocal path) |
|
||
| 0xFF | Dummy/padding (used when no real extra data; followed by 4 random bytes) |
|
||
| Other | Application-defined |
|
||
|
||
When creating a path return with no extra data, the reference implementation
|
||
appends `0xFF` as extra_type followed by 4 random bytes to ensure the packet
|
||
hash is unique.
|
||
|
||
### Encoding
|
||
|
||
1. Build the inner plaintext:
|
||
a. Write path_len byte (encoded hash_size and hash_count).
|
||
b. Write path bytes (hash_count × hash_size).
|
||
c. If extra data is provided: write extra_type (lower 4 bits), write extra.
|
||
d. If no extra data: write `0xFF`, write 4 random bytes.
|
||
2. Encrypt the inner plaintext using encrypt-then-MAC with the shared secret.
|
||
3. Prepend dest_hash and src_hash.
|
||
|
||
### Decoding
|
||
|
||
1. Decode the outer envelope (dest_hash, src_hash, MAC, ciphertext) as in
|
||
[Section 6](06-payload-encrypted.md).
|
||
2. After decryption:
|
||
a. Read 1 byte as path_len. Decode hash_size and hash_count.
|
||
b. Read hash_count × hash_size bytes as the path.
|
||
c. Read 1 byte as extra_type (use lower 4 bits only).
|
||
d. Remaining bytes are extra data (may include AES zero-padding).
|
||
|
||
### Cross-References
|
||
|
||
- [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/)
|
||
|
||
### Reference Implementation
|
||
|
||
- `Mesh::createPathReturn()` in `src/Mesh.cpp` — Encoding
|
||
- `Mesh::onRecvPacket()`, PATH case within REQ/RESPONSE/TXT_MSG handling — Decoding
|