Identity derivation SVG diagram for fips-intro.md

Add fips-identity-derivation.svg showing the derivation chain from
pubkey to npub, node_addr, and IPv6 address with protocol roles and
privacy boundary. Replaces the code block in Node Address Derivation
with the diagram and consolidated descriptive text.
This commit is contained in:
Johnathan Corgan
2026-02-13 15:18:48 +00:00
parent 73129f16d7
commit fbdfe4e4f6
2 changed files with 136 additions and 21 deletions
+126
View File
@@ -0,0 +1,126 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 370" font-family="monospace" font-size="12">
<style>
rect.box { rx: 4; stroke-width: 1.5; }
rect.source { fill: #1a3a2a; stroke: #40a060; }
rect.derived { fill: #1a2a3a; stroke: #4080c0; }
rect.encoding { fill: #2a2040; stroke: #8060c0; }
rect.compat { fill: #2a1a1a; stroke: #c06040; }
rect.use { fill: #151520; stroke: #404060; stroke-width: 1; rx: 3; }
text { fill: #e0e0e0; }
text.title { font-size: 16px; font-weight: bold; }
text.subtitle { font-size: 12px; fill: #909090; }
text.label { font-size: 13px; font-weight: bold; }
text.detail { font-size: 10px; fill: #909090; }
text.op { font-size: 10px; fill: #b0b0b0; font-style: italic; }
text.use-text { font-size: 10px; fill: #c0c0c0; }
line.arrow { stroke: #606080; stroke-width: 1.5; }
line.derive { stroke: #4080c0; stroke-width: 1.5; }
line.encode { stroke: #8060c0; stroke-width: 1.5; stroke-dasharray: 4,3; }
polygon.head { fill: #606080; }
polygon.dhead { fill: #4080c0; }
polygon.ehead { fill: #8060c0; }
</style>
<!-- Background -->
<rect width="800" height="370" fill="#0d1117" rx="8"/>
<!-- Title -->
<text x="400" y="28" text-anchor="middle" class="title">FIPS Identity Derivation</text>
<text x="400" y="46" text-anchor="middle" class="subtitle">From Nostr keypair to protocol identifiers</text>
<!-- ═══ Source: pubkey ═══ -->
<rect x="280" y="70" width="240" height="50" class="box source"/>
<text x="400" y="92" text-anchor="middle" class="label">pubkey</text>
<text x="400" y="106" text-anchor="middle" class="detail">Nostr cryptographic identity</text>
<!-- ═══ Left branch: bech32 encoding → npub ═══ -->
<!-- Arrow: pubkey left edge → npub -->
<line x1="280" y1="95" x2="180" y2="95" class="encode"/>
<polygon points="182,91 172,95 182,99" class="ehead"/>
<text x="226" y="87" text-anchor="middle" class="op">encode</text>
<!-- npub box -->
<rect x="30" y="70" width="150" height="50" class="box encoding"/>
<text x="105" y="92" text-anchor="middle" class="label">npub</text>
<text x="105" y="106" text-anchor="middle" class="detail">human-readable form</text>
<!-- npub use box -->
<rect x="20" y="144" width="170" height="50" class="use"/>
<text x="105" y="162" text-anchor="middle" class="use-text">Display &amp; configuration</text>
<text x="105" y="176" text-anchor="middle" class="use-text">User-facing identifiers</text>
<text x="105" y="190" text-anchor="middle" class="use-text">DNS queries (npub.fips)</text>
<!-- npub → use arrow -->
<line x1="105" y1="120" x2="105" y2="144" class="arrow"/>
<polygon points="101,142 105,150 109,142" class="head"/>
<!-- ═══ Right branch: pubkey → session encryption ═══ -->
<!-- Arrow: pubkey right edge → use -->
<line x1="520" y1="95" x2="620" y2="95" class="arrow"/>
<polygon points="618,91 628,95 618,99" class="head"/>
<!-- pubkey use box -->
<rect x="620" y="70" width="160" height="50" class="use"/>
<text x="700" y="88" text-anchor="middle" class="use-text">Endpoint authentication</text>
<text x="700" y="102" text-anchor="middle" class="use-text">Link &amp; session encryption</text>
<text x="700" y="116" text-anchor="middle" class="use-text">Known only to endpoints</text>
<!-- ═══ Down: pubkey → one-way hash → node_addr ═══ -->
<!-- Arrow down from pubkey -->
<line x1="400" y1="120" x2="400" y2="170" class="derive"/>
<polygon points="396,168 400,176 404,168" class="dhead"/>
<text x="416" y="148" class="op">one-way hash</text>
<!-- node_addr box -->
<rect x="280" y="176" width="240" height="50" class="box derived"/>
<text x="400" y="198" text-anchor="middle" class="label">node_addr</text>
<text x="400" y="212" text-anchor="middle" class="detail">opaque routing identifier</text>
<!-- ═══ Right branch: node_addr → routing use ═══ -->
<!-- Arrow: node_addr right edge → use -->
<line x1="520" y1="201" x2="620" y2="201" class="arrow"/>
<polygon points="618,197 628,201 618,205" class="head"/>
<!-- node_addr use box -->
<rect x="620" y="176" width="160" height="50" class="use"/>
<text x="700" y="194" text-anchor="middle" class="use-text">Packet header addressing</text>
<text x="700" y="208" text-anchor="middle" class="use-text">Spanning tree coordinates</text>
<text x="700" y="222" text-anchor="middle" class="use-text">Bloom filter entries</text>
<!-- ═══ Down: node_addr → derive → IPv6 address ═══ -->
<!-- Arrow down from node_addr -->
<line x1="400" y1="226" x2="400" y2="276" class="derive"/>
<polygon points="396,274 400,282 404,274" class="dhead"/>
<text x="416" y="254" class="op">add fd::/8 prefix</text>
<!-- IPv6 address box -->
<rect x="280" y="282" width="240" height="50" class="box compat"/>
<text x="400" y="304" text-anchor="middle" class="label">IPv6 address</text>
<text x="400" y="318" text-anchor="middle" class="detail">overlay address for IP compatibility</text>
<!-- ═══ Right branch: IPv6 → application use ═══ -->
<!-- Arrow: IPv6 right edge → use -->
<line x1="520" y1="307" x2="620" y2="307" class="arrow"/>
<polygon points="618,303 628,307 618,311" class="head"/>
<!-- IPv6 use box -->
<rect x="620" y="282" width="160" height="50" class="use"/>
<text x="700" y="298" text-anchor="middle" class="use-text">TUN interface (fips0)</text>
<text x="700" y="312" text-anchor="middle" class="use-text">Unmodified IP applications</text>
<text x="700" y="326" text-anchor="middle" class="use-text">ping, curl, ssh, etc.</text>
<!-- ═══ Left: privacy annotation ═══ -->
<rect x="20" y="218" width="230" height="62" fill="#151520" stroke="#505040" stroke-width="1" rx="3"/>
<text x="135" y="236" text-anchor="middle" font-size="11" font-weight="bold" fill="#d0a040">Privacy boundary</text>
<text x="135" y="252" text-anchor="middle" class="use-text">Intermediate routers see only</text>
<text x="135" y="266" text-anchor="middle" class="use-text">node_addr — never the pubkey.</text>
<!-- Dashed line connecting privacy box to node_addr -->
<line x1="250" y1="249" x2="280" y2="201" stroke="#505040" stroke-width="1" stroke-dasharray="3,3"/>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

+10 -21
View File
@@ -131,28 +131,17 @@ from npub to this address for traditional software.
### Node Address Derivation
The pubkey is hashed to derive a node_addr used for routing:
![Identity Derivation](fips-identity-derivation.svg)
```
pubkey (secp256k1 x-only, 32 bytes)
→ SHA-256
→ node_addr (16 bytes)
→ truncate with prefix
→ IPv6 address (128 bits, fd::/8)
```
**Separation of concerns**: The keypair handles cryptographic operations (signing,
encryption). The node_addr derived from the pubkey handles routing. This keeps
cryptographic material out of routing tables and packet headers—the node_addr is
the only identifier used at the protocol level, and the pubkey cannot be derived
from it.
The one-way hash also provides privacy from intermediate routing nodes. Routers
see only node_addrs in packet headers—they can route traffic without learning
the Nostr identities of the endpoints. An observer can verify "does this
node_addr belong to pubkey X?" but cannot enumerate which pubkeys are
communicating by inspecting traffic. Only the endpoints, which complete the
Noise IK handshake, learn each other's pubkeys.
The pubkey is the node's cryptographic identity, used in Noise IK handshakes for
both link and session encryption. It is never exposed beyond the endpoints of an
encrypted channel. The node_addr, a one-way SHA-256 hash truncated to 16 bytes,
serves as the routing identifier in packet headers and bloom filters. Intermediate
routers see only node_addrs — they can forward traffic without learning the Nostr
identities of the endpoints. An observer can verify "does this node_addr belong
to pubkey X?" but cannot enumerate which pubkeys are communicating by inspecting
traffic. The IPv6 address prepends `fd` to the first 15 bytes of the node_addr,
providing an overlay address for unmodified IP applications via the TUN interface.
### Address Format