mirror of
https://github.com/luxfi/pqrns.git
synced 2026-10-30 15:21:45 +00:00
Decomplected, one-way-only surface:
pkg/identity hybrid identity wire layout — Ed25519 || X25519 ||
ML-DSA-65 || ML-KEM-768. PublicIdentitySize = 3200 B,
Fingerprint = first 16 B of SHA-256(identity blob)
matching native RNS shape. Single Generate / Sign /
Verify surface; both legs (Ed25519 AND ML-DSA-65)
must pass for a hybrid signature to verify.
Domain-separation context strings exported as
constants (PQRNS-LINK-v1, PQRNS-IDENTITY-SIG-v1,
PQRNS-ANNOUNCE-SIG-v1, PQRNS-LINK-REKEY-v1) — no two
layers share a transcript.
pkg/link 3-packet hybrid-link wire format surface (LinkRequest /
LinkAccept / LinkActivate / Session). Marshalling +
KEM/sig wiring lands in the next commit alongside KAT
vectors + round-trip tests. The surface is fixed now
so call sites can be wired ahead.
_examples/basic hybrid sign/verify demo: generates two identities,
prints their fingerprints, signs+verifies under the
PQRNS-IDENTITY-SIG-v1 context, demonstrates that a
different context refuses to verify (domain
separation enforced).
E2E identity tests:
TestGenerateAndSign hybrid sign + verify positive
TestGenerateAndSign wrong context refuses
TestGenerateAndSign tampered digest refuses
TestFingerprintStable deterministic from same key
TestFingerprintsDiffer two fresh identities don't collide
66 lines
2.2 KiB
Go
66 lines
2.2 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// Package link defines the PQ-RNS hybrid link establishment surface.
|
|
// The 3-packet handshake matches the wire layout in the paper:
|
|
//
|
|
// LinkRequest (initiator → responder): ephemeral X25519 || ephemeral
|
|
// ML-KEM-768 pub || encap to responder's static ML-KEM ||
|
|
// hybrid signature over the transcript.
|
|
// LinkAccept (responder → initiator): ephemeral X25519 || encap to
|
|
// initiator's ephemeral ML-KEM || HMAC ack || hybrid sig.
|
|
// LinkActivate (initiator → responder): HMAC activate (32 bytes).
|
|
//
|
|
// One way only. There is exactly one Establish() entry point on each
|
|
// side. There is no cipher negotiation: two endpoints either speak
|
|
// PQ-RNS-HYBRID-v1 or they don't link.
|
|
//
|
|
// This package currently exposes the surface only. The packet
|
|
// marshalling + KEM/sig wiring lands in a follow-up commit that
|
|
// includes the same KAT vectors and round-trip tests as pqsafe. The
|
|
// surface is fixed now so call sites can be wired ahead.
|
|
package link
|
|
|
|
import (
|
|
"github.com/luxfi/pqrns/pkg/identity"
|
|
)
|
|
|
|
// Constants from the wire format.
|
|
const (
|
|
X25519EphSize = 32
|
|
MLKEMCTSize = 1088
|
|
HMACAckSize = 32
|
|
SessionKeySize = 64 // 32 enc || 32 mac (matches native RNS Fernet token shape)
|
|
)
|
|
|
|
// LinkRequest is the initiator's first packet on the wire.
|
|
type LinkRequest struct {
|
|
InitiatorFingerprint [identity.FingerprintSize]byte
|
|
InitEphX25519 [X25519EphSize]byte
|
|
InitEphMLKEMPub []byte // initiator's ephemeral MLKEM pub (so responder can encap to it)
|
|
InitEphMLKEMCT [MLKEMCTSize]byte
|
|
HybridCapFlags uint16
|
|
Ed25519Sig []byte
|
|
MLDSA65Sig []byte
|
|
}
|
|
|
|
// LinkAccept is the responder's reply.
|
|
type LinkAccept struct {
|
|
RespEphX25519 [X25519EphSize]byte
|
|
RespEphMLKEMCT [MLKEMCTSize]byte
|
|
AckHMAC [HMACAckSize]byte
|
|
Ed25519Sig []byte
|
|
MLDSA65Sig []byte
|
|
}
|
|
|
|
// LinkActivate is the initiator's final ACK.
|
|
type LinkActivate struct {
|
|
AckHMAC [HMACAckSize]byte
|
|
}
|
|
|
|
// Session is the established link state.
|
|
type Session struct {
|
|
Key [SessionKeySize]byte
|
|
PeerFingerprint [identity.FingerprintSize]byte
|
|
}
|