mirror of
https://github.com/luxfi/zwing.git
synced 2026-09-24 00:24:38 +00:00
The previous "full PQ" claims for Python and TS were tested only within
each language. This commit proves the wire bytes actually match Go.
What was wrong:
Both Python and TS were calling ml_dsa65.sign(secretKey, message)
WITHOUT the FIPS 204 ctx parameter, while Go was calling
DSAPriv.SignCtx(rand, digest, []byte("lux.zwing.v1")). The signed
messages diverged → handshake verify failed across language pairs.
Fix:
Python: ML_DSA_65.sign(sk, digest, ctx=ZWING_DOMAIN) and matching
verify with ctx=ZWING_DOMAIN.
TS: ml_dsa65.sign(digest, sk, { context: ZWING_DOMAIN }) and
matching verify { context: ZWING_DOMAIN }.
Real interop tests added:
cmd/zwing-test-server/main.go — minimal Go server: prints listen
addr + identity hex on stdout,
accepts ONE handshake, echoes one
payload, exits.
py/test_interop_go.py — Python client connects via TCP,
runs run_initiator + Channel echo
against the live Go server.
js/interop_go.test.ts — TS client over node:net.Socket
runs runInitiator + Channel echo
against the live Go server.
Both interop tests pass:
Python ↔ Go: Ran 1 test (and 43 within-Python tests)
TypeScript ↔ Go: 1 interop test passes alongside 32 within-TS tests
Wire-byte compatibility now PROVEN end-to-end for X-Wing KEM, hybrid
identity (Ed25519 + ML-DSA-65 with lux.zwing.v1 ctx), HKDF channel
keys, and ChaCha20-Poly1305 records across Go ↔ Python ↔ TypeScript.
61 lines
1.2 KiB
Go
61 lines
1.2 KiB
Go
// Tiny Z-Wing test server used for cross-language interop tests.
|
|
// Listens on the address given by the first arg (or 127.0.0.1:0),
|
|
// prints the listening address + the public-identity hex on stdout
|
|
// (one per line), then accepts ONE Z-Wing connection, echoes one
|
|
// payload, and exits.
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/luxfi/zwing"
|
|
)
|
|
|
|
func main() {
|
|
addr := flag.String("addr", "127.0.0.1:0", "listen addr")
|
|
flag.Parse()
|
|
|
|
id, err := zwing.GenerateIdentity()
|
|
if err != nil {
|
|
log.Fatalf("identity: %v", err)
|
|
}
|
|
|
|
ln, err := zwing.Listen(*addr, &zwing.Config{LocalIdentity: id})
|
|
if err != nil {
|
|
log.Fatalf("listen: %v", err)
|
|
}
|
|
defer ln.Close()
|
|
|
|
pubBytes := id.Public().MarshalBinary()
|
|
fmt.Println(ln.Addr().String())
|
|
fmt.Println(hex.EncodeToString(pubBytes))
|
|
|
|
// Force stdout flush so the harness can read both lines before we
|
|
// block on Accept.
|
|
os.Stdout.Sync()
|
|
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
log.Fatalf("accept: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
buf := make([]byte, 4096)
|
|
n, err := conn.Read(buf)
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return
|
|
}
|
|
log.Fatalf("read: %v", err)
|
|
}
|
|
if _, err := conn.Write(buf[:n]); err != nil {
|
|
log.Fatalf("write: %v", err)
|
|
}
|
|
}
|