Documentation
¶
Overview ¶
Package crypto is the remote-control cryptographic foundation (ADR-007 D2/D3/D4; implementation-plan R-CRY.1-.16, R-PAIR.4). It holds the two pinned crypto protocols — Noise XX live transport and the async epoch-key envelope — plus device identity/keystore, sealed epoch grants, the pairing SAS, and device command signatures.
This file establishes the package so its _test.go files (the FROZEN contract, written test-first per GG-5) have a package to compile against. A separate implementer supplies every exported symbol the tests reference; until then the package has no implementation and `go test` fails to compile with "undefined" errors, which is the correct TDD RED.
Index ¶
- Constants
- Variables
- func KeyID(pub []byte) [8]byte
- func LivePrologue(machineRoutingID, deviceRoutingID []byte) []byte
- func OpenMailbox(k ContentKey, e *Envelope) ([]byte, error)
- func OpenWake(k WakeKey, e *Envelope) ([]byte, error)
- func PairPrologue(rendezvousID []byte) []byte
- func SAS(channelBinding []byte) ([6]string, error)
- func SealToRecipient(recipientPub, plaintext []byte) ([]byte, error)
- func VerifyCommandSig(commandSigningPub, msg, sig []byte) error
- type Command
- type ContentKey
- type Envelope
- type EnvelopeHeader
- type EpochGrant
- type EpochKeys
- type GrantReceiver
- type Identity
- type KeyMaterial
- type KeyStore
- type MailboxReceiver
- type MailboxResult
- type NoiseConfig
- type NoiseSession
- func (s *NoiseSession) ChannelBinding() []byte
- func (s *NoiseSession) Decrypt(ciphertext []byte) ([]byte, error)
- func (s *NoiseSession) Encrypt(plaintext []byte) ([]byte, error)
- func (s NoiseSession) Format(f fmt.State, _ rune)
- func (s NoiseSession) GoString() string
- func (s *NoiseSession) HandshakeComplete() bool
- func (s *NoiseSession) PeerStatic() []byte
- func (s *NoiseSession) ReadMessage(message []byte) ([]byte, error)
- func (s *NoiseSession) Rekey()
- func (s *NoiseSession) RekeyDue() bool
- func (NoiseSession) String() string
- func (s *NoiseSession) Suite() string
- func (s *NoiseSession) WriteMessage(payload []byte) ([]byte, error)
- type NoiseStatic
- type WakeKey
Constants ¶
const ( VersionV1 uint8 = 0x01 TypeMailbox uint8 = 0x01 // session content, under the content key TypePushWake uint8 = 0x02 // content-free wake, under the wake key )
Wire constants (R-CRY.9).
const ( RekeyAfterBytes = 1 << 30 RekeyAfterDuration = 15 * time.Minute )
Rekey thresholds (R-CRY.7): each direction rekeys after 1 GiB or 15 minutes.
const NoiseSuite = "Noise_XX_25519_ChaChaPoly_SHA256"
NoiseSuite is the exact live-transport suite. SHA-256 (not BLAKE2s) is mandatory for Swift/CryptoKit interop (R-CRY.4).
Variables ¶
var ( // ErrUnknownVersion rejects a future/unknown wire version. ErrUnknownVersion = errors.New("crypto: unknown envelope version") // ErrUnknownType rejects a type outside {mailbox, push-wake} at parse. ErrUnknownType = errors.New("crypto: unknown envelope type") // ErrTruncated rejects a buffer too short to hold a full header + tag. ErrTruncated = errors.New("crypto: truncated envelope") // ErrStaleSeq rejects a replayed or reordered mailbox sequence number. ErrStaleSeq = errors.New("crypto: stale or reordered sequence number") // ErrStaleAge rejects a mailbox event whose authenticated issued_at is // older than the receiver's max age (A5 bounded-age). ErrStaleAge = errors.New("crypto: mailbox event exceeds max age") // ErrWrongKeyType rejects opening an envelope whose type does not match the // typed key used (wake key vs content key — A15). ErrWrongKeyType = errors.New("crypto: envelope type does not match key type") )
var ( // ErrBadGrant is returned when an opened grant is not the expected size. ErrBadGrant = errors.New("crypto: malformed epoch grant") // ErrGrantSig is returned when a grant is not signed by the pinned machine key. ErrGrantSig = errors.New("crypto: epoch grant signature invalid") // ErrGrantCoord is returned when a grant's outer routing coordinates do not // match the coordinates authenticated inside the sealed plaintext. ErrGrantCoord = errors.New("crypto: epoch grant coordinates mismatch") // ErrGrantReplay is returned when a grant is a replay of, or older than, the // highest (epoch_id, grant_seq) this device has already accepted. ErrGrantReplay = errors.New("crypto: epoch grant replay or stale") )
Epoch-grant errors (F3).
var ( // ErrBadRecipient is returned for a malformed recipient public key. ErrBadRecipient = errors.New("crypto: recipient public key must be 32 bytes") // ErrSealedOpen is returned when a sealed box fails to open. ErrSealedOpen = errors.New("crypto: sealed box open failed") // ErrKeyAuthRequired is a RECOVERABLE custody refusal: the key is gated on user // authentication and the user has not authenticated. A prompt restores it. ErrKeyAuthRequired = errors.New("crypto: key custody requires user authentication") // ErrKeyInvalidated is a PERMANENT custody refusal: the key is gone for good // (Android invalidates an auth-bound key on biometric re-enrollment). No prompt // restores it and the device must re-pair. It is distinct from ErrKeyAuthRequired // because the two demand opposite handling, and a UI that cannot tell them apart // would prompt forever against a key no prompt can bring back (ADR-007 B14). ErrKeyInvalidated = errors.New("crypto: key custody permanently invalidated") )
var ( // ErrPeerStaticMismatch aborts a handshake whose peer static is not the // pinned value (R-CRY.6 — authenticated, not TOFU). ErrPeerStaticMismatch = errors.New("crypto: peer static key does not match pinned value") // ErrNoStatic is returned when a config omits the required static keypair. ErrNoStatic = errors.New("crypto: noise config requires a static keypair") // ErrNoPin is returned when a LIVE session is constructed without a valid // 32-byte peer-static pin. "Unpinned" is an explicit opt-in (AllowUnpinnedPeer) // used only by pairing, never by a live session (F1 — no fail-open). ErrNoPin = errors.New("crypto: live session requires a 32-byte peer-static pin") // ErrBadPSK is returned when a supplied PSK is not exactly 32 bytes. ErrBadPSK = errors.New("crypto: preshared key must be 32 bytes") // ErrHandshakeDone is returned by handshake calls after completion. ErrHandshakeDone = errors.New("crypto: handshake already complete") // ErrNotEstablished is returned by transport calls before completion. ErrNotEstablished = errors.New("crypto: transport not established") // ErrUnpinnedRequiresPSK forbids an unpinned session without a PSK: only // pairing (which always carries a 32-byte PSK) may run unpinned, so a live // session cannot opt out of static pinning (F1 — mechanically pairing-only). ErrUnpinnedRequiresPSK = errors.New("crypto: AllowUnpinnedPeer requires a 32-byte PSK (pairing only)") // ErrRekeyRequired is returned by Encrypt once a direction crosses its rekey // threshold; the transport MUST perform a coordinated Rekey() before more // traffic flows on the old key (R-CRY.7 / F7 — enforced, not advisory). ErrRekeyRequired = errors.New("crypto: rekey required before further traffic") )
var ErrBadCommand = errors.New("crypto: malformed command")
ErrBadCommand is returned when a command is missing a mandatory identifier, an oversized field, or a content hash that is neither absent nor exactly 32 bytes.
var ErrBadCommandKey = errors.New("crypto: command-signing public key must be 32 bytes")
ErrBadCommandKey is returned when a command-signing public key is malformed.
var ErrCommandSig = errors.New("crypto: command signature verification failed")
ErrCommandSig is returned when a command signature does not verify.
var ErrEmptyBinding = errors.New("crypto: empty channel binding")
ErrEmptyBinding is returned when no channel binding is supplied.
var ErrUnsafeKeyFile = errors.New("crypto: refusing to write key material through a symlink")
ErrUnsafeKeyFile is returned when a key path is a symlink (a symlink-swap attack could redirect the write) — Save refuses rather than write through it.
Functions ¶
func LivePrologue ¶
LivePrologue binds the live protocol tag and both routing ids (R-CRY.5). Each routing id is length-prefixed so ("a","bc") cannot collide with ("ab","c").
func OpenMailbox ¶
func OpenMailbox(k ContentKey, e *Envelope) ([]byte, error)
OpenMailbox opens type-0x01 session content under the content key; any other type is refused (a wake payload cannot be opened as content).
func OpenWake ¶
OpenWake opens a type-0x02 wake under the wake key; any other type is refused (mailbox content cannot be opened with the NSE-readable wake key).
func PairPrologue ¶
PairPrologue binds the pairing protocol tag and the length-prefixed rendezvous id.
func SAS ¶
SAS derives the 6-emoji short authentication string from a Noise channel binding: okm = HKDF-SHA256(salt=sasSalt, ikm=channelBinding); the first 5 bytes are read as a 40-bit big-endian integer, and six big-endian 6-bit indices (the top 36 bits; the low 4 bits are unused) select into the 64-entry table. A clean handshake yields the same SAS on both ends; a tampered transcript (divergent bindings) yields different SAS, which operators reject out-of-band.
ADR-007 amendment 2026-07-23 widened this from 24 bits (four emoji) to 36 bits (six emoji) to close the pairing grind attack (review finding MED-1): a leaked-QR attacker with a live man-in-the-middle position could grind ~2^24 candidate keypairs (seconds on commodity hardware) to force its channel binding to a SAS equal to the honest leg's; six emoji raise that to ~2^36, infeasible inside a rate-limited pairing window. This is a LENGTH EXTENSION ONLY — the salt, the 64-emoji wordlist, and the HKDF construction are unchanged.
func SealToRecipient ¶
SealToRecipient seals plaintext to a recipient X25519 public key using box.SealAnonymous (libsodium crypto_box_seal-compatible). The anonymous sender means no signature oracle; integrity/authenticity of the payload is the caller's concern (here, an authenticated Noise channel).
func VerifyCommandSig ¶
VerifyCommandSig verifies a detached command signature against the pinned device command-signing key. A malformed key is rejected without panic.
Types ¶
type Command ¶
type Command struct {
Action string
Machine string
Session string
OperationID string
ExpiresAt int64
ContentHash []byte
}
Command is the canonical tuple every remote mutating op is signed over (D4). The signature binds all fields including the optional content hash, so a captured command cannot be replayed under a new operation_id or a pushed-out expiry.
func (Command) Canonical ¶
Canonical is the unambiguous, length-prefixed signing input, or an error if the command is malformed (F13). Length prefixes make every field boundary explicit, so no two distinct well-formed commands share an encoding. A nil and an empty content hash canonicalize identically (both encode as length 0): "no content hash". The mandatory identifiers must be present and no field may exceed maxCommandField; the content hash is absent or exactly 32 bytes.
type ContentKey ¶
type ContentKey [32]byte
ContentKey decrypts only session content (type 0x01); it is biometric-gated, not NSE-readable, and not derivable from the wake key (A15 / F10).
type Envelope ¶
type Envelope struct {
Header EnvelopeHeader
Nonce [24]byte
Ciphertext []byte
}
Envelope is a sealed async message: header, 24-byte XChaCha nonce, ciphertext.
func ParseEnvelope ¶
ParseEnvelope parses the wire form. It rejects a short header, an unknown version, an unknown type, and a ciphertext too short to carry the AEAD tag. An empty-plaintext envelope (16-byte tag, no content) is valid (F12).
func SealMailbox ¶
func SealMailbox(k ContentKey, h EnvelopeHeader, plaintext []byte) (*Envelope, error)
SealMailbox seals session content (type 0x01) under the content key. The distinct ContentKey type makes it impossible to seal content under a wake key via this API (A15 / F10).
type EnvelopeHeader ¶
type EnvelopeHeader struct {
Version uint8
Type uint8
EpochID uint32
Seq uint64
RecipientKeyID [8]byte
SenderKeyID [8]byte
IssuedAt int64 // unix milliseconds; authenticated (AAD-covered)
}
EnvelopeHeader is the cleartext routing/authentication header. Every field except recipient_key_id is bound as AAD; recipient_key_id is routing-only so one ciphertext fans out byte-identically to every recipient (A5). issued_at is AAD-covered (F9), so it is authenticated and age-checkable, and is NOT part of the recipient_key_id exclusion.
type EpochGrant ¶
EpochGrant is the sealed, signed delivery of an epoch's keys (F3). Sealed is a crypto_box_seal artifact addressed to a device's recipient key that carries the epoch/grant coordinates AND both keys inside its authenticated plaintext; EpochID/GrantSeq are outer routing copies that must match the sealed inner values on open. Sig is the machine's Ed25519 signature so a relay cannot forge a grant (SealAnonymous alone gives no sender authentication).
func SealEpochGrant ¶
func SealEpochGrant(machinePriv ed25519.PrivateKey, recipientPub []byte, epochID uint32, grantSeq uint64, keys EpochKeys) (*EpochGrant, error)
SealEpochGrant seals the epoch keys and coordinates to a device's recipient X25519 key (A14) and signs the result with the machine's Ed25519 signing key (F3). The coordinates travel authenticated both inside the sealed plaintext and under the signature; SealAnonymous alone would let anyone holding the recipient public key forge a grant, so the signature is what pins the sender.
type EpochKeys ¶
type EpochKeys struct {
WakeKey WakeKey
ContentKey ContentKey
}
EpochKeys is the wake/content key split delivered per epoch (A15). The wake key is after-first-unlock / NSE-readable and opens only content-free push wakes; the content key is biometric-gated, not NSE-readable, and not derivable from the wake key.
func NewEpochKeys ¶
NewEpochKeys generates a fresh, independent wake key and content key with crypto/rand (A15/F10). The content key is never derived from the wake key, so an NSE holding only the wake key cannot recover session content.
func OpenEpochGrant ¶
func OpenEpochGrant(ks KeyStore, machinePub ed25519.PublicKey, g *EpochGrant) (uint32, uint64, EpochKeys, error)
OpenEpochGrant verifies a grant against the pinned machine signing key, opens it via the device KeyStore, and checks the outer routing coordinates against the values authenticated inside the sealed plaintext (F3). It does NOT track replay — use a GrantReceiver for that.
type GrantReceiver ¶
type GrantReceiver struct {
// contains filtered or unexported fields
}
GrantReceiver tracks the highest (epoch_id, grant_seq) a device has accepted, so a replayed or older grant is rejected (F3). It is safe for concurrent use.
func NewGrantReceiver ¶
func NewGrantReceiver() *GrantReceiver
NewGrantReceiver returns a receiver with no accepted grants yet.
func NewGrantReceiverAt ¶
func NewGrantReceiverAt(epochID uint32, grantSeq uint64) *GrantReceiver
NewGrantReceiverAt seeds the receiver with the highest (epoch_id, grant_seq) already accepted, persisted across restart (F3). Without this, a relay could replay an old correctly-signed grant after a phone/app restart and have it accepted as the first grant — the same restart-replay hole SeedHighWater closes for the mailbox. The gateway/phone MUST reseed from durable state.
func (*GrantReceiver) Accept ¶
func (r *GrantReceiver) Accept(ks KeyStore, machinePub ed25519.PublicKey, g *EpochGrant) (uint32, uint64, EpochKeys, error)
Accept verifies and opens a grant (OpenEpochGrant) and then enforces strict (epoch_id, grant_seq) monotonicity, rejecting replays and stale grants.
type Identity ¶
type Identity struct {
// contains filtered or unexported fields
}
Identity is a machine's long-term cryptographic identity: two distinct X25519 keys (A14) — a Noise-static key for the live handshake and a sealed-box recipient key for async epoch grants. The private scalars never appear in any formatted representation (R-CRY.1); see String/GoString.
func GenerateIdentity ¶
GenerateIdentity creates a fresh machine identity from crypto/rand.
func LoadIdentity ¶
LoadIdentity reads a persisted identity. A missing identity is an error, not a silently fabricated fresh key.
func NewIdentityFromMaterial ¶
NewIdentityFromMaterial builds an identity from explicit private scalars (deterministic construction for tests/KATs).
func (*Identity) NoiseStatic ¶
func (id *Identity) NoiseStatic() *NoiseStatic
NoiseStatic returns the opaque handshake handle for the Noise-static key. It carries the private scalar for the DH but exposes no accessor to it.
func (*Identity) NoiseStaticPublic ¶
NoiseStaticPublic returns a copy of the 32-byte Noise-static public key.
func (*Identity) RecipientPublic ¶
RecipientPublic returns a copy of the 32-byte sealed-box recipient public key.
type KeyMaterial ¶
type KeyMaterial struct {
NoiseStaticPriv [32]byte
RecipientPriv [32]byte
CommandSignSeed [32]byte
RelayAuthSeed [32]byte
}
KeyMaterial is the raw private material for a device KeyStore. The two X25519 keys (A14) plus the Ed25519 command-signing (D4) and relay-auth (R-CRY.3) seeds.
func (KeyMaterial) GoString ¶
func (m KeyMaterial) GoString() string
func (KeyMaterial) String ¶
func (KeyMaterial) String() string
String/GoString/Format redact KeyMaterial under every fmt verb — it is all private seeds, so there is nothing safe to print (F2). Value receiver so a KeyMaterial value or pointer, and any struct embedding it, are all covered.
type KeyStore ¶
type KeyStore interface {
NoiseStaticPublic() []byte
RecipientPublic() []byte
NoiseStatic() (*NoiseStatic, error)
OpenSealedBox(sealed []byte) ([]byte, error)
SignCommand(msg []byte) ([]byte, error)
CommandSigningPublic() []byte
SignRelayAuth(challenge []byte) ([]byte, error)
RelayAuthPublic() []byte
}
KeyStore is the device's key custody boundary. It exposes DH (via the opaque Noise-static handle), sealed-box open, and detached signatures — but NEVER the raw private scalar (R-CRY.2). A hardware-gated impl must drop in with bit-identical wire output (R-CRY.15).
Every operation that touches private material can FAIL (ADR-007 B14): a hardware-gated custody refuses when the user has not authenticated (ErrKeyAuthRequired) and permanently after a biometric re-enrollment (ErrKeyInvalidated). A failed operation returns NOTHING a caller could send. The public-key accessors stay errorless — public material is not gated.
func NewFileKeyStore ¶
NewFileKeyStore generates fresh material and persists it 0600 under dir.
func NewFileKeyStoreFromMaterial ¶
func NewFileKeyStoreFromMaterial(dir string, m KeyMaterial) (KeyStore, error)
NewFileKeyStoreFromMaterial persists explicit material 0600 and returns the store (deterministic construction for tests/KATs).
func NewKeyStoreFromMaterial ¶
func NewKeyStoreFromMaterial(m KeyMaterial) KeyStore
NewKeyStoreFromMaterial returns a KeyStore over explicit material with NO file custody at all (ADR-007 B18(b)). It is the construction path NewFileKeyStoreFromMaterial already used, with the write lifted out, so a caller that owns the device-key file itself — phonecore, which must SEAL it under a KEK this package never sees — can supply the material it unsealed. No semantics change and no new I/O: the derived keys and every wire byte are identical.
func OpenFileKeyStore ¶
OpenFileKeyStore loads persisted material; a missing file is an error.
type MailboxReceiver ¶
type MailboxReceiver struct {
// contains filtered or unexported fields
}
MailboxReceiver enforces per-epoch sequence integrity end to end: it tracks the highest seq per (sender_key_id, epoch_id), rejects replay/reorder, and surfaces gaps (R-CRY.12). When maxAge > 0 it also rejects events older than maxAge by their authenticated issued_at (A5). now is a test-only clock seam.
func NewMailboxReceiver ¶
func NewMailboxReceiver() *MailboxReceiver
NewMailboxReceiver returns an empty receiver.
func (*MailboxReceiver) Accept ¶
func (r *MailboxReceiver) Accept(key ContentKey, e *Envelope) (*MailboxResult, error)
Accept authenticates an envelope and advances the sequence tracker. A seq at or below the highest seen is ErrStaleSeq (replay/reorder); a valid seq beyond highest+1 sets Gap. An envelope that fails the AEAD (e.g. a relay forgery) returns the AEAD error and does not advance the tracker. When maxAge > 0 an authenticated-but-too-old issued_at is ErrStaleAge.
func (*MailboxReceiver) SeedHighWater ¶
func (r *MailboxReceiver) SeedHighWater(sender [8]byte, epoch uint32, seq uint64)
SeedHighWater seeds the high-water mark for a (sender, epoch) stream to a resume-snapshot cursor N, so the FIRST envelope after resume whose seq is not N+1 surfaces a gap. Without seeding, gap detection is blind on the first event of a fresh receiver (F4).
type MailboxResult ¶
MailboxResult is the outcome of accepting a mailbox envelope. Gap is set when a sequence number was skipped, so the client can resync-from-snapshot rather than silently lose events.
type NoiseConfig ¶
type NoiseConfig struct {
Initiator bool
Static *NoiseStatic
// PeerStatic is the pinned peer static. A live session (AllowUnpinnedPeer
// false) requires exactly 32 bytes; the handshake aborts on mismatch.
PeerStatic []byte
// AllowUnpinnedPeer opts into learning the peer static instead of pinning
// it. This is the ONLY path that may run without a pin and is used solely by
// pairing (the SAS + desktop confirm are the out-of-band gate). A live
// session never sets it.
AllowUnpinnedPeer bool
// PSK, when set, configures Noise XXpsk0 (PresharedKeyPlacement 0). This is
// the pairing seam (R-PAIR.3); a live session leaves it nil.
PSK []byte
Prologue []byte // binds protocol/role/route (R-CRY.5)
// contains filtered or unexported fields
}
NoiseConfig parameterises a single live/pairing XX session.
func (NoiseConfig) GoString ¶
func (c NoiseConfig) GoString() string
func (NoiseConfig) String ¶
func (NoiseConfig) String() string
type NoiseSession ¶
type NoiseSession struct {
// contains filtered or unexported fields
}
NoiseSession is one Noise_XX session: handshake first (WriteMessage/ ReadMessage), then transport (Encrypt/Decrypt). It retains no resumption state — the handshake state is dropped at completion (R-CRY.8).
func NewNoise ¶
func NewNoise(cfg NoiseConfig) (*NoiseSession, error)
NewNoise starts an XX session. The pinned PeerStatic is held here and checked against the value learned on the wire; flynn/noise itself is not given a pre-message static (XX transmits it, and a preset value would be rejected).
func (*NoiseSession) ChannelBinding ¶
func (s *NoiseSession) ChannelBinding() []byte
ChannelBinding returns the handshake hash, identical on both clean ends.
func (*NoiseSession) Decrypt ¶
func (s *NoiseSession) Decrypt(ciphertext []byte) ([]byte, error)
Decrypt opens a transport frame under the receiving cipher state. A replayed or reordered frame fails the AEAD (the per-state nonce already advanced).
func (*NoiseSession) Encrypt ¶
func (s *NoiseSession) Encrypt(plaintext []byte) ([]byte, error)
Encrypt seals a transport frame under the sending cipher state.
func (NoiseSession) GoString ¶
func (s NoiseSession) GoString() string
func (*NoiseSession) HandshakeComplete ¶
func (s *NoiseSession) HandshakeComplete() bool
HandshakeComplete reports whether transport mode has been reached.
func (*NoiseSession) PeerStatic ¶
func (s *NoiseSession) PeerStatic() []byte
PeerStatic returns the peer's authenticated static public key.
func (*NoiseSession) ReadMessage ¶
func (s *NoiseSession) ReadMessage(message []byte) ([]byte, error)
ReadMessage consumes the next handshake message. As soon as the peer static is learned it is compared to the pinned value; a mismatch aborts before the session reaches transport mode (R-CRY.6). The comparison is constant-time.
func (*NoiseSession) Rekey ¶
func (s *NoiseSession) Rekey()
Rekey rotates both directions' key streams and resets the rekey accounting; peers must rekey in coordination.
func (*NoiseSession) RekeyDue ¶
func (s *NoiseSession) RekeyDue() bool
RekeyDue reports whether either direction has crossed its byte budget or the session has crossed its time budget (R-CRY.7). The live transport MUST check this and drive a COORDINATED rekey (both ends call Rekey) before continuing; the crypto layer cannot rekey unilaterally without desyncing the peer.
func (NoiseSession) String ¶
func (NoiseSession) String() string
String/GoString/Format redact NoiseSession and NoiseConfig: both transitively hold private key material (flynn's HandshakeState and transport CipherStates, the static keypair, and the PSK), so no fmt verb (%v %+v %#v %s) may print their contents (F2). Value receivers so both a value and a pointer are covered.
func (*NoiseSession) Suite ¶
func (s *NoiseSession) Suite() string
Suite returns the pinned suite string.
func (*NoiseSession) WriteMessage ¶
func (s *NoiseSession) WriteMessage(payload []byte) ([]byte, error)
WriteMessage produces the next handshake message.
type NoiseStatic ¶
type NoiseStatic struct {
// contains filtered or unexported fields
}
NoiseStatic is an opaque handshake handle for an X25519 static keypair. It carries the private scalar for the DH but exposes no accessor that returns it, and redacts the private scalar under every fmt verb (F2 — see Format/String).
func (NoiseStatic) Format ¶
func (s NoiseStatic) Format(f fmt.State, _ rune)
Format routes EVERY fmt verb (%v %+v %#v %s %x %q) through the redacted String, so no verb can spill the DH private scalar.
func (NoiseStatic) GoString ¶
func (s NoiseStatic) GoString() string
GoString backs %#v so it cannot dump the private field.
func (NoiseStatic) String ¶
func (s NoiseStatic) String() string
String is a redacted representation: the public-key fingerprint only, never the private scalar. Value receiver so an embedded NoiseStatic (value or pointer) is covered too.