tsp

package module
v0.0.0-...-d32ca7c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 14 Imported by: 1

README

affinidi-tsp-go

A Go implementation of the Trust Spanning Protocol (TSP), Rev 3 — wire version YTSP-AAC (0.2) — from the Trust over IP Foundation.

go get github.com/affinidi/affinidi-tsp-go

Requires Go 1.27 (crypto/mldsa, crypto/hpke).

Status: pre-release (0.1.0), not independently security-audited. Checked against the specification's Appendix A vectors and, for interoperability, against the ToIP reference and other implementations by tsp-conformance.

What it implements

Area Coverage
Framing Binary-domain CESR: short and long count codes (-X## / --X#####), variable-length data with lead-byte variants (4B/5B/6B, 7AAB/8AAB/9AAB), long VIDs, fixed data, indexed Ed25519 (B#) and ML-DSA-65 (1AAQ) signatures
Payloads XSCS, XCTL, XPAD, XRFI (Reply_Path, Referral_Field), XRFA, XRFD, XHOP (nested and routed); Padding_Field; ESSR VID_sndr as the NULL VID 4BAA or present
Digests Self-addressing TSP digests: SHA2-256 (I) and Blake2b-256 (F), computed on pack and verified on open
Schemes HPKE-Base (DHKEM(X25519, HKDF-SHA256) / HKDF-SHA256 / ChaCha20Poly1305); HPKE-Base with MLKEM768-X25519 + ML-DSA-65; libsodium sealed box; signed-only
Relationships Endpoint: invite / accept / cancel / send / receive, RFI race tie-break by lower digest, application messages refused without a relationship

All ten Appendix A vectors open; the nine that publish their ephemeral key material are reproduced byte for byte by Pack (see vectors_test.go). The post-quantum vector publishes none — the hybrid KEM draws its own randomness — so it is opened and round-tripped instead.

API

// Stateless
func Pack(sender *PrivateIdentity, receiver *Identity, payload Payload, opts *PackOptions) (*Packed, error)
func Open(receiver *PrivateIdentity, sender *Identity, msg []byte) (*Message, error)
func Peek(msg []byte) (*Envelope, error)
func VerifyReferral(m *Message, introduced *Identity) error

// Payloads: *SCS, *CTL, *PAD, *RFI, *RFA, *RFD, *HOP

// Stateful
type VidResolver interface { Resolve(ctx context.Context, vid string) (*Identity, error) }
type Store interface { /* Relationship / SetRelationship / DeleteRelationship */ }
func NewEndpoint(resolver VidResolver, opts ...EndpointOption) *Endpoint
func (e *Endpoint) Invite / Accept / Cancel(ctx, from, to string) (*Packed, error)
func (e *Endpoint) Send(ctx, from, to string, data []byte) (*Packed, error)
func (e *Endpoint) Receive(ctx, msg []byte) (*Event, error)
func (e *Endpoint) State(ctx, local, remote string) (Relationship, error)

Every error is a *tsp.Error with a Kind (malformed, version, signature, decrypt, sender, receiver, digest, relationship, unsupported, invalid-input, internal); test with errors.Is(err, tsp.ErrSignature) or tsp.KindOf(err).

Sub-packages: cesr (the binary CESR primitives) and crypto (HPKE, sealed box, signatures, digests).

Design notes

  • HPKE-Base is implemented here (RFC 9180 key schedule over the standard library's HKDF, X25519 and x/crypto's ChaCha20Poly1305) so that PackOptions.Ephemeral can drive DeriveKeyPair(ikmE), which is how the specification publishes its vectors. It is checked against the RFC 9180 vectors shipped with Go and against crypto/hpke in both directions. The hybrid KEM uses crypto/hpke directly.
  • ML-DSA-65 verification uses crypto/mldsa. Signing accepts the 32-byte seed (crypto/mldsa) or the 4032-byte expanded key the specification's vectors publish, which the standard library cannot import; that path uses CIRCL.
  • Strict parsing. Canonical encodings only (shortest count form, zero lead bytes and pad bits); every count must match the bytes it frames; one signature filling its attachment; no trailing bytes; unknown MAJOR version refused, MINOR carried. Size limits: MaxMessageSize (64 MiB), MaxVIDSize, MaxHops. FuzzOpen and FuzzPeek exercise the decoder.
  • SAIDs are verified from the received bytes, not from re-encoded values, so verification is exactly the reverse of derivation.
  • PackOptions.Ephemeral exists only to reproduce test vectors. Reusing ephemeral key material destroys confidentiality.

Testing

go vet ./...
go test ./...
go test -run XXX -fuzz FuzzOpen -fuzztime 60s .

License

Apache-2.0

Documentation

Overview

Package tsp implements the Trust Spanning Protocol (TSP), Rev 3 (YTSP-AAC, version 0.2), as specified by the Trust over IP Foundation.

A TSP message is an envelope (version, sender VID, receiver VID), a payload that is either encrypted or carried in the clear, and a signature over both, all framed in binary-domain CESR. This package encodes and decodes every Rev 3 payload type:

  • SCS: upper-layer application data (XSCS)
  • CTL: generic control data for the layer above TSP (XCTL)
  • PAD: padding messages (XPAD)
  • RFI, RFA, RFD: the relationship forming protocol (XRFI / XRFA / XRFD), with self-addressing digests (SAIDs), reply paths and referrals
  • HOP: nested and routed messages (XHOP)

under four schemes: HPKE-Base (DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305), HPKE-Base with the MLKEM768-X25519 hybrid KEM and ML-DSA-65 signatures, the libsodium sealed box, and signed-only.

Stateless API

Pack builds a message, Open verifies and decrypts one, and Peek reads the envelope an intermediary sees:

packed, err := tsp.Pack(alice, bob.Public(), &tsp.SCS{Data: []byte("hi")}, nil)
msg, err := tsp.Open(bob, alice.Public(), packed.Message)

Keys are carried by Identity (public) and PrivateIdentity. Nothing is resolved by Pack or Open; the caller supplies both parties.

Endpoint

Endpoint adds VID resolution (VidResolver), relationship state (Store) and the relationship forming state machine: Invite, Accept, Cancel, Send and Receive. It performs no I/O, so any transport can be layered on top.

Parsing

Parsing is strict and bounded: primitives must be canonically encoded, counts must agree with the bytes they frame, trailing bytes are rejected, and an unknown MAJOR version is refused. Every error is an *Error whose Kind says why (see ErrorKind).

Example
package main

import (
	"fmt"

	tsp "github.com/affinidi/affinidi-tsp-go"
)

func main() {
	alice, _ := tsp.GenerateIdentity("did:example:alice", tsp.SigKeyEd25519, tsp.EncKeyX25519, nil)
	bob, _ := tsp.GenerateIdentity("did:example:bob", tsp.SigKeyEd25519, tsp.EncKeyX25519, nil)

	packed, err := tsp.Pack(alice, bob.Public(), &tsp.SCS{Data: []byte("hello world")}, nil)
	if err != nil {
		panic(err)
	}
	env, _ := tsp.Peek(packed.Message)
	fmt.Println(env.Sender, "->", env.Receiver, "confidential:", env.Confidential)

	msg, err := tsp.Open(bob, alice.Public(), packed.Message)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s %s\n", msg.Scheme, msg.Payload.(*tsp.SCS).Data)
}
Output:
did:example:alice -> did:example:bob confidential: true
hpke-base hello world

Index

Examples

Constants

View Source
const (
	// MaxMessageSize bounds the size of a whole encoded message.
	MaxMessageSize = 64 << 20
	// MaxHops bounds a hop list or reply path.
	MaxHops = 64
)

Limits applied to untrusted input.

View Source
const MaxVIDSize = 64 * 1024

MaxVIDSize bounds the length of any VID this package encodes or accepts. It is generous enough for long-form did:peer identifiers carrying post-quantum keys.

View Source
const NonceSize = 16

NonceSize is the length of a TSP nonce (128 bits).

Variables

View Source
var (
	ErrInternal     = &Error{Kind: KindInternal}
	ErrMalformed    = &Error{Kind: KindMalformed}
	ErrVersion      = &Error{Kind: KindVersion}
	ErrSignature    = &Error{Kind: KindSignature}
	ErrDecrypt      = &Error{Kind: KindDecrypt}
	ErrSender       = &Error{Kind: KindSender}
	ErrReceiver     = &Error{Kind: KindReceiver}
	ErrDigest       = &Error{Kind: KindDigest}
	ErrRelationship = &Error{Kind: KindRelationship}
	ErrUnsupported  = &Error{Kind: KindUnsupported}
	ErrInvalidInput = &Error{Kind: KindInvalidInput}
)

Sentinel errors, one per kind, for use with errors.Is.

View Source
var CurrentVersion = Version{Major: 0, Minor: 2}

CurrentVersion is the version this package emits: 0.2, encoded YTSP-AAC.

View Source
var ErrNotFound = errors.New("tsp: not found")

ErrNotFound is returned by resolvers and stores for an unknown VID or relationship.

Functions

func VerifyReferral

func VerifyReferral(m *Message, introduced *Identity) error

VerifyReferral checks the Signature_new of an invite that introduces a new VID (a Referral), against introduced, the resolved new VID.

Open does not do this: the key of the introduced VID is exactly what the invite exists to convey, so it has to be resolved first. Until a caller verifies it, a referral is only the sender's claim.

Types

type CTL

type CTL struct {
	Data []byte
}

CTL is a generic control payload (XCTL) for the layer above TSP. It has the same shape as SCS.

func (*CTL) Type

func (*CTL) Type() PayloadType

type Digest

type Digest struct {
	Alg   DigestAlg
	Value [32]byte
}

Digest is a TSP digest: a 256-bit hash and the algorithm that produced it.

func (Digest) Compare

func (d Digest) Compare(o Digest) int

Compare orders digests lexicographically by their binary-domain value, the order the RFI race tie-break uses.

func (Digest) Equal

func (d Digest) Equal(o Digest) bool

Equal reports whether two digests are identical, in constant time with respect to the value.

func (Digest) String

func (d Digest) String() string

type DigestAlg

type DigestAlg uint8

DigestAlg is the hash function of a TSP digest, identified on the wire by the digest's CESR code.

const (
	// DigestDefault selects the scheme's algorithm: Blake2b-256 under the
	// sealed box, SHA2-256 otherwise. Only meaningful as an input.
	DigestDefault    DigestAlg = iota
	DigestSHA2256              // CESR code I
	DigestBlake2b256           // CESR code F
)

Digest algorithms.

func (DigestAlg) String

func (a DigestAlg) String() string

type EncKeyType

type EncKeyType uint8

EncKeyType is the KEM a VID's encryption key belongs to.

const (
	EncKeyUnknown EncKeyType = iota
	EncKeyX25519
	EncKeyMLKEM768X25519
)

Encryption key types.

func (EncKeyType) String

func (t EncKeyType) String() string

type Endpoint

type Endpoint struct {
	// contains filtered or unexported fields
}

Endpoint is a TSP endpoint: a set of VIDs it controls, a resolver for the VIDs of others, and a relationship store. It implements the direct relationship forming protocol (RFI / RFA / RFD) and gates application messages on it. It does no I/O: every operation returns or consumes encoded messages, so any transport can be layered on top.

An Endpoint is safe for concurrent use.

Example
package main

import (
	"context"
	"fmt"

	tsp "github.com/affinidi/affinidi-tsp-go"
)

func main() {
	ctx := context.Background()
	alice, _ := tsp.GenerateIdentity("did:example:alice", tsp.SigKeyEd25519, tsp.EncKeyX25519, nil)
	bob, _ := tsp.GenerateIdentity("did:example:bob", tsp.SigKeyEd25519, tsp.EncKeyX25519, nil)

	knowsBob, _ := tsp.NewStaticResolver(bob.Public())
	knowsAlice, _ := tsp.NewStaticResolver(alice.Public())
	a, b := tsp.NewEndpoint(knowsBob), tsp.NewEndpoint(knowsAlice)
	_ = a.AddIdentity(alice)
	_ = b.AddIdentity(bob)

	invite, _ := a.Invite(ctx, alice.VID, bob.VID)
	ev, _ := b.Receive(ctx, invite.Message)
	fmt.Println(ev.Kind)

	accept, _ := b.Accept(ctx, bob.VID, alice.VID)
	ev, _ = a.Receive(ctx, accept.Message)
	fmt.Println(ev.Kind)

	hello, _ := a.Send(ctx, alice.VID, bob.VID, []byte("hi bob"))
	ev, _ = b.Receive(ctx, hello.Message)
	fmt.Println(ev.Kind, string(ev.Data))

	state, _ := b.State(ctx, bob.VID, alice.VID)
	fmt.Println(state.State)
}
Output:
invite
accept
message hi bob
bidirectional

func NewEndpoint

func NewEndpoint(resolver VidResolver, opts ...EndpointOption) *Endpoint

NewEndpoint creates an endpoint that resolves peers with resolver.

func (*Endpoint) Accept

func (e *Endpoint) Accept(ctx context.Context, from, to string) (*Packed, error)

Accept answers the invite received from remote VID to, from local VID from.

func (*Endpoint) AddIdentity

func (e *Endpoint) AddIdentity(id *PrivateIdentity) error

AddIdentity registers a VID this endpoint controls.

func (*Endpoint) Cancel

func (e *Endpoint) Cancel(ctx context.Context, from, to string) (*Packed, error)

Cancel declines a pending invite, withdraws one we sent, or cancels an established relationship, and forgets the relationship locally.

The RFD names the relationship by a digest the remote holds: for an established relationship, the digest this side received (the Reply_Digest for the inviter, the Digest for the accepter); for a pending invite, that invite's Digest.

func (*Endpoint) Invite

func (e *Endpoint) Invite(ctx context.Context, from, to string) (*Packed, error)

Invite sends a relationship forming invite from a local VID to a remote one. It is refused while an invite from the remote is pending (accept it instead) or once the relationship is established. Re-inviting while an invite is outstanding replaces it.

func (*Endpoint) Receive

func (e *Endpoint) Receive(ctx context.Context, msg []byte) (*Event, error)

Receive opens a message addressed to one of this endpoint's VIDs and applies it to the relationship state.

Relationship rules: an invite crossing our own outstanding invite for the same pair is kept only if its digest is lower (binary comparison), otherwise it is refused; an accept must echo the digest of our outstanding invite; a cancel must name a digest of an existing relationship; application data is accepted only over a bidirectional relationship. A refused message is an error of kind KindRelationship and leaves the state unchanged.

func (*Endpoint) Send

func (e *Endpoint) Send(ctx context.Context, from, to string, data []byte) (*Packed, error)

Send packs application data over an established relationship.

func (*Endpoint) State

func (e *Endpoint) State(ctx context.Context, local, remote string) (Relationship, error)

State returns the relationship between a local and a remote VID.

type EndpointOption

type EndpointOption func(*Endpoint)

EndpointOption configures an Endpoint.

func WithRand

func WithRand(r io.Reader) EndpointOption

WithRand sets the randomness source (default: crypto/rand).

func WithStore

func WithStore(s Store) EndpointOption

WithStore sets the relationship store (default: a new MemoryStore).

type Envelope

type Envelope struct {
	Version          Version
	Sender, Receiver string
	// Confidential reports whether the payload is encrypted.
	Confidential bool
	// SealedBox reports a sealed-box ciphertext (code C) rather than HPKE
	// (code F). Whether an HPKE message uses the post-quantum KEM is not
	// visible on the wire.
	SealedBox bool
}

Envelope is what Peek reveals without keys: what an intermediary sees.

func Peek

func Peek(msg []byte) (*Envelope, error)

Peek parses a message's framing and returns its envelope. It checks that the message is structurally well formed (envelope, body and signature attachment, with no trailing bytes) but verifies nothing cryptographic.

type Error

type Error struct {
	Kind ErrorKind
	Msg  string
	Err  error // underlying cause, if any
}

Error is the error type returned by this package.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

Is reports whether target is a sentinel of the same kind.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying cause.

type ErrorKind

type ErrorKind uint8

ErrorKind classifies why an operation failed. Every error returned by this package is an *Error carrying one, so a caller can branch on the kind with errors.Is against the sentinel values below, or with KindOf.

const (
	KindInternal     ErrorKind = iota // anything else
	KindMalformed                     // CESR framing cannot be parsed
	KindVersion                       // unknown or unsupported TSP version
	KindSignature                     // signature verification failed
	KindDecrypt                       // decryption or authentication failed
	KindSender                        // envelope/ESSR sender mismatch, wrong sender key, unknown sender
	KindReceiver                      // message not addressed to the receiver
	KindDigest                        // a SAID does not verify
	KindRelationship                  // relationship state forbids the message
	KindUnsupported                   // scheme, payload type or option not supported
	KindInvalidInput                  // the caller's request is invalid
)

Error kinds.

func KindOf

func KindOf(err error) ErrorKind

KindOf returns the kind of err, or KindInternal if err is not an *Error.

func (ErrorKind) String

func (k ErrorKind) String() string

String returns the kind's short name ("malformed", "signature", ...).

type Event

type Event struct {
	Kind EventKind
	// From is the remote (envelope sender) VID, To the local VID.
	From, To string
	// Data is the application data of an EventMessage or EventControl.
	Data []byte
	// Digest is the invite digest (invite, accept) or the named digest (cancel).
	Digest *Digest
	// ReplyDigest is the accept's own digest.
	ReplyDigest *Digest
	// Message is the opened message.
	Message *Message
}

Event is the outcome of receiving a message.

type EventKind

type EventKind uint8

EventKind classifies a received message.

const (
	EventInvite  EventKind = iota + 1 // an RFI
	EventAccept                       // an RFA answering our invite
	EventCancel                       // an RFD
	EventMessage                      // application data (XSCS)
	EventControl                      // generic control payload (XCTL)
	EventPadding                      // a padding message (XPAD)
	EventHop                          // a nested or routed message (XHOP), inner unopened
)

Event kinds.

func (EventKind) String

func (k EventKind) String() string

type HOP

type HOP struct {
	Hops  []string
	Inner []byte
}

HOP carries a complete inner TSP message (XHOP): nested when Hops is empty, routed otherwise.

func (*HOP) Type

func (*HOP) Type() PayloadType

type Identity

type Identity struct {
	// VID is the verifiable identifier, e.g. a DID or URN.
	VID string
	// SigKeyType and SigningKey are the VID's public signature key: 32 bytes
	// for Ed25519, 1952 for ML-DSA-65.
	SigKeyType SigKeyType
	SigningKey []byte
	// EncKeyType and EncryptionKey are the VID's public encryption key: 32
	// bytes for X25519, 1216 for MLKEM768-X25519. May be empty for a VID that
	// only signs.
	EncKeyType    EncKeyType
	EncryptionKey []byte
}

Identity is the public view of a VID: the identifier and the keys bound to it. It is what a VidResolver produces.

func (*Identity) Validate

func (id *Identity) Validate() error

Validate checks that the identifier is usable and the keys are well formed.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is an in-memory Store.

func NewMemoryStore

func NewMemoryStore() *MemoryStore

NewMemoryStore returns an empty MemoryStore.

func (*MemoryStore) DeleteRelationship

func (s *MemoryStore) DeleteRelationship(_ context.Context, local, remote string) error

DeleteRelationship implements Store.

func (*MemoryStore) Relationship

func (s *MemoryStore) Relationship(_ context.Context, local, remote string) (Relationship, error)

Relationship implements Store.

func (*MemoryStore) SetRelationship

func (s *MemoryStore) SetRelationship(_ context.Context, local, remote string, r Relationship) error

SetRelationship implements Store.

type Message

type Message struct {
	Version Version
	// Sender and Receiver are the envelope VIDs. Receiver is "" when the
	// envelope names the NULL VID.
	Sender, Receiver string
	// Scheme is how the payload was protected.
	Scheme Scheme
	// PayloadSender is the ESSR VID_sndr field inside the payload; "" for the
	// NULL VID. When present it has been checked against Sender.
	PayloadSender string
	// Padding is the content of the Padding_Field.
	Padding []byte
	// Payload is the decoded payload. A *HOP's Inner message is not opened.
	Payload Payload
}

Message is an opened TSP message.

func Open

func Open(receiver *PrivateIdentity, sender *Identity, msg []byte) (*Message, error)

Open verifies and decrypts a message addressed to receiver from sender.

It checks, in order: framing; that the envelope names receiver and sender; the signature, under sender's signing key; decryption, with the envelope as HPKE associated data; the payload framing; the ESSR sender field against the envelope; and any SAID the payload carries.

receiver needs its decryption private key unless the message is signed-only.

type PAD

type PAD struct {
	// Nonce is 16 bytes. On Pack a nil Nonce is filled from the random source.
	Nonce []byte
}

PAD is a padding message (XPAD). Pad the message with PackOptions.Padding.

func (*PAD) Type

func (*PAD) Type() PayloadType

type PackOptions

type PackOptions struct {
	// Scheme selects the payload protection. SchemeAuto picks HPKE by the
	// receiver's encryption key type.
	Scheme Scheme
	// Padding is the content of the payload's Padding_Field. The receiver
	// discards it; it exists to hide the payload's size.
	Padding []byte
	// PayloadSender selects the ESSR sender field.
	PayloadSender SenderField
	// DigestAlg is the algorithm of the SAID an RFI or RFA carries.
	// DigestDefault uses Blake2b-256 under the sealed box, SHA2-256 otherwise.
	DigestAlg DigestAlg
	// ReferralSigner is the introduced VID, used to produce Signature_new when
	// an RFI carries a Referral without a Signature.
	ReferralSigner *PrivateIdentity
	// Rand is the source of nonces, ephemeral keys and signature randomness.
	// Defaults to crypto/rand.
	Rand io.Reader
	// Ephemeral pins the encryption's ephemeral key material, making the
	// output reproducible: the HPKE DeriveKeyPair input ikmE under HPKE-Base,
	// the ephemeral X25519 secret under the sealed box. It exists to reproduce
	// published test vectors. Never set it otherwise: reusing ephemeral key
	// material destroys confidentiality. Not supported for HPKE-PQ.
	Ephemeral []byte
}

PackOptions tunes Pack. The zero value is valid.

type Packed

type Packed struct {
	// Message is the binary-domain (qb2) wire encoding.
	Message []byte
	// Digest is the SAID the message carries: an RFI's Digest or an RFA's
	// Reply_Digest. Nil for other payload types.
	Digest *Digest
}

Packed is an encoded TSP message.

func Pack

func Pack(sender *PrivateIdentity, receiver *Identity, payload Payload, opts *PackOptions) (*Packed, error)

Pack encodes, protects and signs a TSP message from sender to receiver.

sender must hold its signing private key. receiver supplies the encryption key for a confidential scheme; it may be nil for a signed-only message, in which case the envelope names the NULL VID as receiver.

type Payload

type Payload interface {
	Type() PayloadType
	// contains filtered or unexported methods
}

Payload is one of *SCS, *CTL, *PAD, *RFI, *RFA, *RFD or *HOP.

type PayloadType

type PayloadType uint8

PayloadType is a TSP payload type code.

const (
	PayloadSCS PayloadType = iota + 1 // XSCS, upper-layer application payload
	PayloadCTL                        // XCTL, generic control payload
	PayloadPAD                        // XPAD, padding message
	PayloadRFI                        // XRFI, relationship forming invite
	PayloadRFA                        // XRFA, relationship forming accept
	PayloadRFD                        // XRFD, relationship forming decline / cancel
	PayloadHOP                        // XHOP, nested or routed message
)

Payload types.

func (PayloadType) String

func (t PayloadType) String() string

type PrivateIdentity

type PrivateIdentity struct {
	Identity
	// SigningPrivateKey is the Ed25519 seed (32 bytes, or 64 bytes seed||pk),
	// or the ML-DSA-65 private key as a 32-byte seed or the 4032-byte expanded
	// encoding.
	SigningPrivateKey []byte
	// DecryptionPrivateKey is the raw X25519 private key (32 bytes) or the
	// MLKEM768-X25519 private key seed (32 bytes).
	DecryptionPrivateKey []byte
}

PrivateIdentity is a VID this endpoint controls: its public Identity plus the private keys. Either private key may be absent when the identity is only used for the operations that need the other.

func GenerateIdentity

func GenerateIdentity(vid string, sigType SigKeyType, encType EncKeyType, rng io.Reader) (*PrivateIdentity, error)

GenerateIdentity creates a fresh PrivateIdentity for vid with the given key types. rng defaults to crypto/rand.

func NewPrivateIdentity

func NewPrivateIdentity(vid string, sigType SigKeyType, signingPrivateKey []byte, encType EncKeyType, decryptionPrivateKey []byte) (*PrivateIdentity, error)

NewPrivateIdentity builds a PrivateIdentity from private keys, deriving the public keys. Either private key may be nil (with its type left unknown).

func (*PrivateIdentity) Public

func (p *PrivateIdentity) Public() *Identity

Public returns the public Identity (a copy).

func (*PrivateIdentity) Validate

func (p *PrivateIdentity) Validate() error

Validate checks the public identity and that each private key present corresponds to its public key.

type RFA

type RFA struct {
	// InviteDigest is the Digest of the invite being accepted, echoed verbatim.
	InviteDigest Digest
	// ReplyDigest is the accept's own SAID. Computed by Pack and verified by
	// Open; ignored on input.
	ReplyDigest Digest
}

RFA is a relationship forming accept (XRFA).

func (*RFA) Type

func (*RFA) Type() PayloadType

type RFD

type RFD struct {
	// Digest names the relationship: the invite's Digest or the accept's
	// Reply_Digest. A DigestDefault algorithm on input means SHA2-256.
	Digest Digest
}

RFD is a relationship forming decline or cancel (XRFD).

func (*RFD) Type

func (*RFD) Type() PayloadType

type RFI

type RFI struct {
	// Nonce is 16 bytes. On Pack a nil Nonce is filled from the random source.
	Nonce []byte
	// ReplyPath is the route for the accept; empty for a direct reply.
	ReplyPath []string
	// Referral, when non-nil, introduces a new VID (parallel relationship
	// forming).
	Referral *Referral
	// Digest is the invite's own SAID. Computed by Pack and verified by Open;
	// ignored on input.
	Digest Digest
}

RFI is a relationship forming invite (XRFI).

func (*RFI) Type

func (*RFI) Type() PayloadType

type Referral

type Referral struct {
	VID string
	// Signature is a 64-byte Ed25519 or 3309-byte ML-DSA-65 signature. On Pack,
	// a nil Signature is produced with PackOptions.ReferralSigner.
	Signature []byte
}

Referral is the Referral_Field of an invite: a new VID introduced over an existing relationship, with that VID's own signature over the invite.

type Relationship

type Relationship struct {
	State RelationshipState
	// Digest is the invite's Digest, which identifies the inviter's direction
	// of the relationship (and is the thread identifier).
	Digest *Digest
	// ReplyDigest is the accept's Reply_Digest, identifying the accepter's
	// direction. Set once bidirectional.
	ReplyDigest *Digest
	// Initiator reports whether the local VID sent the invite.
	Initiator bool
}

Relationship is the stored state of <local, remote>.

type RelationshipState

type RelationshipState uint8

RelationshipState is the state of the relationship between a local and a remote VID.

const (
	// StateNone: no relationship.
	StateNone RelationshipState = iota
	// StateInviteSent: we sent an RFI and await the RFA.
	StateInviteSent
	// StateInviteReceived: we received an RFI and have not answered it.
	StateInviteReceived
	// StateBidirectional: the RFI/RFA exchange completed.
	StateBidirectional
)

Relationship states.

func (RelationshipState) String

func (s RelationshipState) String() string

type SCS

type SCS struct {
	// Data is the application bytes, carried as exactly one CESR Bytes
	// primitive inside the -A## stream. Its format is the upper layer's; Open
	// rejects a stream holding anything else (spec issue #77).
	Data []byte
}

SCS is an upper-layer application payload (XSCS).

func (*SCS) Type

func (*SCS) Type() PayloadType

type Scheme

type Scheme uint8

Scheme is how a message's payload is protected.

const (
	// SchemeAuto selects HPKE by the receiver's encryption key type: HPKE-Base
	// for X25519, HPKE-PQ for MLKEM768-X25519. Only meaningful for Pack.
	SchemeAuto Scheme = iota
	// SchemeHPKEBase is HPKE-Base with DHKEM(X25519, HKDF-SHA256), HKDF-SHA256
	// and ChaCha20Poly1305 (ciphertext code F).
	SchemeHPKEBase
	// SchemeHPKEPQ is HPKE-Base with the MLKEM768-X25519 hybrid KEM (ciphertext
	// code F; distinguished only by the receiver's key type).
	SchemeHPKEPQ
	// SchemeSealedBox is the libsodium anonymous sealed box (ciphertext code C).
	SchemeSealedBox
	// SchemeSignedOnly carries the payload in the clear, signed.
	SchemeSignedOnly
)

Schemes.

func (Scheme) Confidential

func (s Scheme) Confidential() bool

Confidential reports whether the scheme encrypts the payload.

func (Scheme) String

func (s Scheme) String() string

type SenderField

type SenderField uint8

SenderField selects what a payload carries in its ESSR VID_sndr field.

const (
	// SenderFieldDefault is the scheme's default: the sender's VID under the
	// sealed box (which requires it), the NULL VID otherwise.
	SenderFieldDefault SenderField = iota
	// SenderFieldNull carries the NULL VID 4BAA. Not permitted under the sealed
	// box.
	SenderFieldNull
	// SenderFieldPresent carries the envelope sender's VID.
	SenderFieldPresent
)

ESSR sender field choices.

type SigKeyType

type SigKeyType uint8

SigKeyType is the signature scheme a VID's signing key belongs to.

const (
	SigKeyUnknown SigKeyType = iota
	SigKeyEd25519
	SigKeyMLDSA65
)

Signature key types.

func (SigKeyType) String

func (t SigKeyType) String() string

type StaticResolver

type StaticResolver struct {
	// contains filtered or unexported fields
}

StaticResolver resolves VIDs from an in-memory table. It suits tests, out-of-band introductions and self-certifying VIDs whose keys are known.

func NewStaticResolver

func NewStaticResolver(ids ...*Identity) (*StaticResolver, error)

NewStaticResolver returns a resolver holding the given identities.

func (*StaticResolver) Add

func (r *StaticResolver) Add(id *Identity) error

Add registers (or replaces) an identity.

func (*StaticResolver) Resolve

func (r *StaticResolver) Resolve(_ context.Context, vid string) (*Identity, error)

Resolve implements VidResolver.

type Store

type Store interface {
	// Relationship returns the relationship, or a zero Relationship (StateNone)
	// if there is none.
	Relationship(ctx context.Context, local, remote string) (Relationship, error)
	// SetRelationship stores the relationship.
	SetRelationship(ctx context.Context, local, remote string, r Relationship) error
	// DeleteRelationship removes the relationship if it exists.
	DeleteRelationship(ctx context.Context, local, remote string) error
}

Store persists relationships. Implementations must be safe for concurrent use; the Endpoint serialises its own read-modify-write sequences.

type Version

type Version struct {
	Major, Minor int
}

Version is a TSP protocol version, MAJOR.MINOR.

func (Version) String

func (v Version) String() string

type VidResolver

type VidResolver interface {
	Resolve(ctx context.Context, vid string) (*Identity, error)
}

VidResolver resolves a VID to its current public Identity. Implementations perform whatever verification the VID type requires (DID resolution, KERI key state, ...). A resolver must be safe for concurrent use.

Directories

Path Synopsis
Package cesr implements the binary-domain (qb2) CESR primitives that the Trust Spanning Protocol uses to frame its messages.
Package cesr implements the binary-domain (qb2) CESR primitives that the Trust Spanning Protocol uses to frame its messages.
Package crypto holds the cryptographic primitives TSP Rev 3 composes: HPKE-Base over DHKEM(X25519, HKDF-SHA256) with ChaCha20Poly1305, the same suite over the MLKEM768-X25519 hybrid KEM, the libsodium anonymous sealed box, Ed25519 and ML-DSA-65 signatures, and the SHA2-256 / Blake2b-256 digests.
Package crypto holds the cryptographic primitives TSP Rev 3 composes: HPKE-Base over DHKEM(X25519, HKDF-SHA256) with ChaCha20Poly1305, the same suite over the MLKEM768-X25519 hybrid KEM, the libsodium anonymous sealed box, Ed25519 and ML-DSA-65 signatures, and the SHA2-256 / Blake2b-256 digests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL