Phases 3-6 (SPEC T19-T23): type-1 identity carries ML-KEM-768 + ML-DSA-65 pubs, COM type-2 double-sign, HELLO capability bit, local.conf settings.pqcMode (off/hybrid/pqconly) with classic fallback. selftest KAT + interop matrix + fragment-loss benchmark green.
9.2 KiB
9.2 KiB
SPEC — BackOne
§G (goal)
Make BackOne quantum ready: phases 1-6 of backone-quantum-fork-research.md — liboqs + hybrid X25519+ML-KEM-768 / Ed25519+ML-DSA-65 handshake, identity/COM v2 double-sign, capability-bit negotiation. Phase 7 (AES-256-GCM) OUT.
§C (constraints)
- C++17. GNU make = real build. Top
CMakeLists.txt= stub, never product build. - Deps vendored in
ext/. No new dep for few-liners. - Tabs always,
.clang-formatLLVM/Stroustrup, indent 4. No reformat of untouched lines. - New
.cpp/.hpp/.hgets 11-line BSL header verbatim (node/Mutex.hpp:1-12). node/stays OS-independent. Sockets/files →osdep/. Orchestration →service/. Persistence →controller/.- No exceptions across C API. Fatal
ZT_ResultCode= 100–999. - One reactor thread (
Phy::poll). Cross-thread wake onlyPhy::whack(). - New compiled file → register in
objects.mkor no link. - Platforms: Linux (primary), macOS, BSD/NetBSD, Windows (msbuild).
- Header edits: no
-MMDdep tracking →make cleanafter touching.h. - PQC lock: liboqs static vendored
ext/; hybrid key = KDF(classical ‖ pq); presetshybrid(ML-KEM-768/ML-DSA-65) +pqconly(ML-KEM-1024/ML-DSA-87) - wire/identity/COM use versioned v2 + capability-bit fallback to classic
include/ZeroTierOne.hchanges additive-only (Java/libzt ripple)- OUT: AES-256-GCM swap, trusted-path skipCrypto, forward secrecy, protocol standardization, crypto audit, production release/signing
?address v2 derivation keeping 40-bit + PoW?VL1 fragmentation capacity for +4KB identity (checknode/Packet.*)?liboqs version/tag + build flags forext/?measured ML-KEM/ML-DSA latency (doc estimates unverified)?capability-bit placement in existing handshake
§I (public surfaces)
| id | surface | detail |
|---|---|---|
| I.capi | C API | include/ZeroTierOne.h, ZT_Node_* (lines 1796-2258), ZT_SDK_API. Ripples to Java/libzt/SDK |
| I.api | HTTP JSON API | 127.0.0.1:9993, /status /peer /network /member /controller /metrics /info, auth X-ZT1-Auth header or ?auth= from authtoken.secret |
| I.cli | CLI | backone-cli info|listpeers|listnetworks|join|leave (-j -p -D), backone-idtool generate|validate|getpublic|sign|verify|mkcom — symlink dispatch on argv[0] |
| I.cfg | config | home /var/lib/backone (macOS /Library/Application Support/BackOne), local.conf, identity.secret, networks.d/, controller.db, backone.port, backone.pid |
| I.build | build | make, make one, make selftest, make core, make debug, make install, make debian/redhat, make central-controller; env ZT_* knobs |
| I.ffi | Rust FFI | rustybits/zeroidc (zeroidc_new…), smeeclient — C header zeroidc.h, link on ZT_SSO_SUPPORTED=1/ZT_CONTROLLER=1 |
| I.jni | Java SDK | java/jni/…Node.cpp, ant targets build_java/build_android/build_jar |
| I.pqc.cfg | config | local.conf settings.pqcMode (off/hybrid/pqconly), kem, sig |
| I.pqc.wire | wire | static-static hybrid, no ephemeral exchange: identity v2 (type byte 1) carries ML-KEM-768 pub + ML-DSA-65 pub; agree = KDF(X25519-identity-agree ‖ ML-KEM-encaps(peer static pub)) (node/Peer.cpp:63); capability via HELLO protocol-version bytes (node/Packet.hpp:329-333); identity.secret/identity.public v2 on disk |
| I.pqc.com | credentials | COM double-sign/verify — controller + node/CertificateOfMembership.* |
| I.rules | rules compiler | node rule-compiler/cli.js <rules> → rules/tags JSON |
§V (invariants)
Derived from selftest.cpp + CI gates. ? = code-derived, no test yet.
| id | invariant | evidence |
|---|---|---|
| V1 | Crypto KATs pass: Salsa20 vectors, C25519 agreement, Ed25519 sign/verify, SHA512, Poly1305 | selftest.cpp:137 testCrypto |
| V2 | Identity known-good accepted, known-bad rejected | selftest.cpp:475 testIdentity |
| V3 | CertificateOfMembership signs and agreesWith roundtrip |
selftest.cpp:577 testCertificate |
| V4 | Packet encode/decode roundtrip exact | selftest.cpp:641 testPacket |
| V5 | Phy UDP+TCP loopback on 127.0.0.1 works, no root | selftest.cpp:1002 testPhy |
| V6 | Zero definite leaks under valgrind; test exit code 0 | .github/workflows/validate-report.sh |
| V7 | ? No exception crosses C API; ZT_ResultCode_isFatal only 100–999 |
include/ZeroTierOne.h:426 |
| V8 | ? node/ contains no OS calls (sockets/files/interfaces) |
node/README.md rule |
| V9 | hybrid↔vanilla negotiates classic, link stays up | interop matrix |
| V10 | identity v2 survives 1280B MTU tunnel intact; v2 = type byte 1 (wire Identity.hpp:219, string slot Identity.cpp:173); address derivation hashes X25519 material only → v1/v2 addresses bit-identical; v1 parser rejects type ≠ 0 cleanly |
interop matrix |
| V11 | ML-KEM/ML-DSA KATs + hybrid KDF vector + identity v2 roundtrip + COM double-sign verify pass | extended selftest.cpp |
| V12 | pqcMode negotiation honors local.conf (off → no v2 fields) |
selftest |
| V13 | hybrid agree = KDF(X25519-identity-agree ‖ ML-KEM static-static), no ephemeral exchange; hybrid↔hybrid agree + hybrid↔vanilla classic fallback pass | node/Peer.cpp:63, node/IncomingPacket.cpp:400 |
| V14 | COM double-sign = type byte 1→2 appends ML-DSA sig alongside Ed25519; v1 verify path unchanged; v1 rejects type 2 cleanly | node/CertificateOfMembership.hpp:222-296, node/CertificateOfMembership.cpp:97-162, selftest.cpp:714 |
| V15 | handshake vs fragment loss measured: PQC identity in clear HELLO = 3273B → 3 frags (classic 137B → 1 frag, max 7 Packet.hpp:235); Monte Carlo 20k trials obs≈analytic at p∈{.01,.05,.10,.30}; fragment loss delays HELLO (retransmit), does not break handshake |
selftest.cpp:1735 benchmark |
§T (tasks)
| id | status | task | cites |
|---|---|---|---|
| T1 | x | Fix CI build smoke: build.yml runs ./zerotier-selftest, build emits backone-selftest (also tar zerotier-one → backone) |
I.build,V6 |
| T2 | x | Fix validate-linux.sh binary names: invokes ./zerotier-one, ./zerotier-cli (lines 41-43,105,111,451), builds backone* |
I.cli,V6 |
| T3 | x | Wire ZT_COVERAGE into makefiles — consumed by none, gcovr likely reports zeros ? |
I.build |
| T4 | x | make install manpage rule: expects doc/backone.8, doc/backone-cli.1, doc/backone-idtool.1; doc/ only has zerotier-*, no rename rule |
I.build |
| T5 | x | make redhat: backone.spec %install copies missing debian/backone.service + ext/installfiles/linux/backone.init.rhel6 (actual files zerotier-*) |
I.build |
| T6 | x | Rebrand make-bsd.mk/make-netbsd.mk: still emit/install zerotier-one, /var/db/zerotier-one vs code writing backone.pid |
I.build |
| T7 | x | Dockerfile.ci: copies zerotier-one from build → fails; binary is backone |
I.build |
| T8 | . | pkg/snap/snapcraft.yaml: builds upstream github.com/zerotier/zerotierone.git, expects usr/sbin/zerotier-one |
I.build |
| T9 | . | Wire orphaned java/test/StringUtilsTest.java (JUnit4) into java/build.xml target or delete |
I.jni |
| T10 | . | Replace rule-compiler/package.json npm test failing placeholder with real test of rule-compiler.js |
I.rules |
| T11 | . | ? Add #[test] coverage for rustybits/zeroidc FFI parse/exchange paths |
I.ffi,V7 |
| T12 | x | Implement Redis config TODO (service/OneService.cpp:1467 // TODO: Redis config) |
I.cfg |
| T13 | . | Implement LFDB::eraseNetwork/eraseMember TODOs (controller/LFDB.cpp:383,388) |
I.cfg |
| T14 | . | make lint (tools/lint.sh, changed-lines clang-format) + .github/workflows/lint.yml |
I.build |
| T15 | . | Fix -Wsign-compare suppression FIXME (node/Bond.cpp:23, node/Node.cpp:40) |
I.build |
| T16 | x | Federation identity-collision payload FIXME (node/IncomingPacket.cpp:211) ? scope |
I.capi,V7 |
| T17 | x | Phase 1: vendor liboqs static into ext/, wire make-linux.mk + objects.mk, pin version/tag + flags |
I.build |
| T18 | x | Phase 2: node/PQHybrid.* hybrid agree = KDF(X25519-identity-agree ‖ ML-KEM-768 encaps over peer static pub); phase-2 ephemeral-field mockup dropped |
I.pqc.wire,V13 |
| T19 | x | Phase 3: identity v2 type byte 1, append ML-KEM+ML-DSA pubs, address hash X25519-only, v1 clean reject type ≠ 0 | I.pqc.wire,V10 |
| T20 | x | Phase 4: COM type byte 2 double-sign (Ed25519+ML-DSA-65), v1 rejects cleanly | I.pqc.com,V14 |
| T21 | x | Phase 5: local.conf settings.pqcMode parse (off/hybrid/pqconly) + capability negotiation via HELLO protocol-version bytes, fallback classic |
I.pqc.cfg,V12,V9 |
| T22 | x | Phase 6: selftest extension — ML-KEM/ML-DSA KATs, hybrid KDF vector, identity v2 roundtrip + address stability, COM double-sign, interop hybrid↔vanilla matrix | I.build,V11,V10,V14 |
| T23 | x | Phase 6: handshake-vs-fragment-loss benchmark for PQC HELLO; cap/segment if loss breaks handshake | V15 |
T1/T2 = prerequisites — CI must invoke backone-selftest before PQC gates mean anything. T1–T16 backlog (rebrand/CI/lint leftovers); T17–T23 = PQC phases 1–6.
Not tracked (low value ?): one.cpp:256 port/token cleanup, MacKextEthernetTap.cpp:480 iface status, VLAN TODOs in NetBSDEthernetTap.cpp:468/MacKextEthernetTap.cpp:685, Phy.hpp:325 unix sock type comment.
§B (bug log)
| id | date | cause | fix |
|---|