Documentation
¶
Overview ¶
Package dtls13 implements Datagram Transport Layer Security 1.3 as defined by RFC 9147. It provides authenticated, encrypted datagrams over UDP and supports the TLS 1.3 handshake inherited from RFC 8446. The package does not implement DTLS 1.2 or protocol-version fallback.
Datagram semantics ¶
DTLS preserves application message boundaries. A Conn is therefore not a net.Conn byte stream and intentionally implements neither net.Conn nor net.PacketConn. Applications use Conn.WriteDatagram and Conn.ReadDatagram instead.
Each call to WriteDatagram sends exactly one DTLS Application Data record. Application data is not fragmented, reordered, or retransmitted by this package. By default, a datagram that exceeds the current path MTU or the DTLS record limit is rejected with ErrDatagramTooLarge before any partial record is sent. Config.IgnorePathMTU skips the library's PMTU check for application data, but the record limit and transport errors still apply. Applications that need messages larger than the available payload must define their own fragmentation and recovery protocol.
Each successful call to ReadDatagram consumes exactly one authenticated Application Data record. If the destination is too small, the unread bytes are discarded and DatagramInfo.Truncated is true. DatagramInfo.FullLength reports the original length. Empty application datagrams are valid. Replay protection discards duplicate and stale records, but records may otherwise be delivered out of order because the underlying transport is unreliable.
Handshake and post-handshake messages have different reliability rules. The package fragments them to the configured MTU, acknowledges records, and retransmits unacknowledged flight data with bounded exponential backoff as required by RFC 9147. Those mechanisms do not make application data reliable.
Clients and servers ¶
Dial and DialWithDialer create a connected UDP client and complete its handshake before returning. Listen owns a UDP socket and returns a Listener, whose Listener.Accept method yields one Conn per DTLS association. NewListener places the same association demultiplexer over an existing net.PacketConn.
Client and Server wrap an existing connected datagram net.Conn, usually a net.UDPConn. The transport must preserve datagram boundaries: a stream transport such as TCP is not valid. These constructors defer the handshake until Conn.Handshake, Conn.ReadDatagram, or Conn.WriteDatagram is first called. Closing a Conn closes its underlying transport.
A Config may be shared by multiple connections, but it must not be modified after first use. Use Config.Clone to derive an independent configuration. Conn methods synchronize protocol state; a reader and a writer may operate concurrently. Multiple ReadDatagram calls are serialized, as are multiple writes.
Authentication ¶
Certificate and ALPN configuration follows the corresponding TLS 1.3 concepts in crypto/tls.Config. Clients normally set Config.RootCAs and Config.ServerName. Servers set Config.Certificates or Config.GetCertificate, and may configure Config.ClientAuth and Config.ClientCAs for client authentication.
Config.InsecureSkipVerify disables the built-in peer identity check and should not be enabled in production unless Config.VerifyPeerCertificate performs an equivalent check. Encryption without authentication does not prevent an active attacker from impersonating a peer.
Session resumption and 0-RTT ¶
Set Config.ClientSessionCache to retain NewSessionTicket state and enable client-side session resumption. The built-in cache returned by NewLRUClientSessionCache is bounded and safe for concurrent use. Servers can share Config.SessionTicketKey when tickets must remain valid across server instances.
Conn.WriteEarlyData attempts one client 0-RTT datagram using a cached session. The server must configure Config.MaxEarlyData and an appropriate replay policy. Config.EarlyDataReplayCache controls replay admission; a nil cache selects a bounded process-wide cache. On an untrusted UDP listener, leave Config.AllowEarlyDataWithoutCookie false; accepting early data before return-routability validation increases amplification exposure.
Successful replay-cache admission reduces accidental or malicious reuse of a ticket identity within one cache domain, but it cannot give 0-RTT the same replay guarantees as 1-RTT data. Early data can be replayed across failures, cache domains, or server deployments. It must contain only operations that are safe to repeat. Callers must handle ErrEarlyDataUnavailable and ErrEarlyDataRejected and decide whether to retry after the handshake.
Connection IDs and network paths ¶
The package implements DTLS Connection IDs from RFC 9146, including the RFC 9147 post-handshake update messages. Config.ConnectionID and Config.GetConnectionID configure local IDs; Conn.SendNewConnectionIDs, Conn.RequestConnectionIDs, and Conn.UseNextConnectionID manage updates. A Listener can use authenticated CIDs to route records whose source address has changed.
A CID authenticates a connection, not a network path. This package does not define or perform path validation and does not automatically change the address used for replies merely because a valid record arrived from a new address. Applications that support migration must provide a path-validation and rebinding policy appropriate to their protocol.
Post-handshake operations ¶
Conn.SendKeyUpdate performs the ACK-gated DTLS 1.3 KeyUpdate procedure; the sending epoch changes only after the update record is acknowledged. The package also initiates KeyUpdate automatically before an AEAD usage limit is reached.
A client sets Config.PostHandshakeAuth to advertise post-handshake client authentication. A server whose Config.ClientAuth requests a certificate can then call Conn.RequestClientCertificate. Exporters are available after a completed handshake through ConnectionState.ExportKeyingMaterial.
Resource limits and errors ¶
Config includes explicit bounds for handshake reassembly, queued application datagrams, Listener associations, per-association input, replay state, and Connection IDs. The defaults suit ordinary Internet-sized datagrams and certificate chains. Increase a limit only when the application also accepts the corresponding memory and denial-of-service cost.
Local configuration failures are reported as ConfigError, protocol and state-machine failures as ProtocolError, and fatal alerts received from a peer as AlertError. Datagram-size errors can be tested with errors.Is against ErrDatagramTooLarge. A valid peer close_notify causes ReadDatagram to return io.EOF. Network errors and deadlines follow the standard net package conventions. Applications should inspect errors with errors.Is and errors.As, not by matching error strings.
Index ¶
- Constants
- Variables
- type AlertError
- type ClientHelloInfo
- type ClientSessionCache
- type ClientSessionState
- type Config
- type ConfigError
- type Conn
- func (c *Conn) Close() error
- func (c *Conn) ConnectionState() ConnectionState
- func (c *Conn) Handshake() error
- func (c *Conn) HandshakeContext(ctx context.Context) error
- func (c *Conn) LocalAddr() net.Addr
- func (c *Conn) PathMTU() int
- func (c *Conn) ReadDatagram(p []byte) (int, DatagramInfo, error)
- func (c *Conn) RecordOverhead() int
- func (c *Conn) RemoteAddr() net.Addr
- func (c *Conn) RequestClientCertificate(ctx context.Context) error
- func (c *Conn) RequestConnectionIDs(count uint8) error
- func (c *Conn) SendKeyUpdate(requestPeer bool) error
- func (c *Conn) SendNewConnectionIDs(connectionIDs [][]byte, immediate bool) error
- func (c *Conn) SetDeadline(t time.Time) error
- func (c *Conn) SetReadDeadline(t time.Time) error
- func (c *Conn) SetWriteDeadline(t time.Time) error
- func (c *Conn) UseNextConnectionID() error
- func (c *Conn) WriteDatagram(p []byte) (int, error)
- func (c *Conn) WriteEarlyData(p []byte) (int, error)
- type ConnectionState
- type DatagramInfo
- type EarlyDataReplayCache
- type Listener
- type ProtocolError
Examples ¶
Constants ¶
const ( // TLS_AES_128_GCM_SHA256 identifies the TLS_AES_128_GCM_SHA256 TLS 1.3 // cipher suite. TLS_AES_128_GCM_SHA256 uint16 = 0x1301 // TLS_AES_256_GCM_SHA384 identifies the TLS_AES_256_GCM_SHA384 TLS 1.3 // cipher suite. TLS_AES_256_GCM_SHA384 uint16 = 0x1302 // TLS_CHACHA20_POLY1305_SHA256 identifies the // TLS_CHACHA20_POLY1305_SHA256 TLS 1.3 cipher suite. TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303 // TLS_AES_128_CCM_SHA256 identifies the TLS_AES_128_CCM_SHA256 TLS 1.3 // cipher suite. TLS_AES_128_CCM_SHA256 uint16 = 0x1304 // TLS_AES_128_CCM_8_SHA256 identifies TLS_AES_128_CCM_8_SHA256. The value // is exported for protocol identification, but the suite is not supported // by this package and is rejected in Config.CipherSuites. RFC 9147 requires // additional deployment-level safeguards for its shortened authentication // tag that a general-purpose library cannot enforce. TLS_AES_128_CCM_8_SHA256 uint16 = 0x1305 )
const VersionDTLS13 uint16 = 0xfefc
VersionDTLS13 is the DTLS 1.3 version value carried in supported_versions.
Variables ¶
var ( // ErrDatagramTooLarge indicates that an application datagram exceeds the // current path MTU or the DTLS record size limit. A transport can still // return it when [Config.IgnorePathMTU] is enabled. [Conn.WriteDatagram] and // [Conn.WriteEarlyData] may wrap it in a net.OpError; use [errors.Is] to test // for it. No partial application record is sent when this error is returned. ErrDatagramTooLarge = errors.New("dtls13: datagram too large") // early-data allowance was available, the connection is not a client, or // WriteEarlyData was already attempted. The handshake may still have // completed successfully and 1-RTT data may still be sent. ErrEarlyDataUnavailable = errors.New("dtls13: early data unavailable") // ErrEarlyDataRejected indicates that the peer completed the handshake but // did not accept the queued 0-RTT record, for example because of a // HelloRetryRequest, replay detection, or server policy. The caller decides // whether the operation is safe to retry as 1-RTT data. ErrEarlyDataRejected = errors.New("dtls13: early data rejected") )
Functions ¶
This section is empty.
Types ¶
type AlertError ¶
type AlertError uint8
AlertError reports a fatal TLS alert received from the peer. Its numeric value is the alert description assigned by TLS. Compare a known description with errors.Is, or use errors.As to obtain the value without depending on an error string.
func (AlertError) Error ¶
func (e AlertError) Error() string
type ClientHelloInfo ¶
type ClientHelloInfo struct {
// ServerName is the SNI name requested by the client, or the empty string
// when the client sent no server_name extension.
ServerName string
// SupportedProtos lists the ALPN protocols offered by the client, in client
// preference order. The callback must not retain or modify this slice.
SupportedProtos []string
// Conn is the server connection processing the ClientHello. Its handshake is
// not complete while GetCertificate is running.
Conn *Conn
}
ClientHelloInfo contains information from a ClientHello for a Config.GetCertificate callback. Its fields must not be modified.
type ClientSessionCache ¶
type ClientSessionCache interface {
// Get returns the session associated with sessionKey. The returned state is
// read-only and must not be modified.
Get(sessionKey string) (*ClientSessionState, bool)
// Put associates cs with sessionKey. A nil cs removes any existing entry.
// The package does not modify cs after the call returns.
Put(sessionKey string, cs *ClientSessionState)
}
ClientSessionCache stores opaque DTLS client sessions by a package-defined server cache key. Implementations must be safe for concurrent use. A client consumes a selected ticket when beginning a resumption attempt so that concurrent connections do not intentionally reuse the same identity.
func NewLRUClientSessionCache ¶
func NewLRUClientSessionCache(capacity int) ClientSessionCache
NewLRUClientSessionCache returns a concurrency-safe, fixed-capacity client session cache. The least recently used entry is removed when the capacity is exceeded. State is cloned on insertion and retrieval so callers cannot mutate cached ticket secrets.
If capacity is less than one, a default capacity of 64 is used.
type ClientSessionState ¶
type ClientSessionState struct {
// contains filtered or unexported fields
}
ClientSessionState is opaque state for one resumable DTLS session. Values are created and consumed by this package through ClientSessionCache and must not be modified by applications or cache implementations.
type Config ¶
type Config struct {
// Rand provides cryptographically secure randomness for key exchange,
// signatures, cookies, tickets, and protocol nonces. It must be safe for
// concurrent use. If nil, crypto/rand.Reader is used.
Rand io.Reader
// Time returns the current time for certificate validation, ticket expiry,
// replay policy, and protocol timers. It must be safe for concurrent use.
// If nil, time.Now is used.
Time func() time.Time
// Certificates contains certificate chains to present to the peer. A
// server uses the first certificate unless GetCertificate is set. A client
// considers the first certificate when the server requests client
// authentication and sends it when compatible. The leaf certificate must be
// followed by any intermediates, as in tls.Certificate.
Certificates []tls.Certificate
// GetCertificate selects a server certificate after the ClientHello has
// been parsed. It must return a non-nil certificate or an error. When set,
// it takes precedence over Certificates. It is not used by clients. The
// callback must be safe for concurrent use when Config is shared.
GetCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
// RootCAs defines the roots used by a client to verify the server
// certificate. If nil, the host's root CA set is used.
RootCAs *x509.CertPool
// ClientCAs defines the roots used by a server when ClientAuth requires
// client-certificate verification. If nil, the host's root CA set is used.
ClientCAs *x509.CertPool
// ServerName is sent by clients as SNI and is used to verify the server
// certificate hostname. Dial derives it from the target address when it is
// empty; Client does not.
ServerName string
// InsecureSkipVerify disables built-in certificate-chain and hostname
// verification. Certificate signatures within the DTLS handshake are still
// checked. This should be used only for tests or together with
// VerifyPeerCertificate that implements equivalent identity verification.
InsecureSkipVerify bool
// VerifyPeerCertificate, when non-nil, is called after normal certificate
// parsing and verification. rawCerts contains the peer-provided DER chain.
// verifiedChains contains the chains built by normal verification, or is nil
// when built-in verification was skipped. Returning an error aborts the
// handshake.
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
// ClientAuth controls a server's policy for client certificates during the
// handshake and for RequestClientCertificate. The zero value is
// tls.NoClientCert. It is ignored by clients.
ClientAuth tls.ClientAuthType
// PostHandshakeAuth makes a client advertise support for post-handshake
// client authentication and permits it to answer CertificateRequest using
// Certificates. It has no effect on server configurations.
PostHandshakeAuth bool
// NextProtos lists supported ALPN protocol names in preference order. The
// server's order controls selection. If either peer provides no list, no
// protocol is negotiated; otherwise the handshake fails when there is no
// common protocol.
NextProtos []string
// CipherSuites lists supported TLS 1.3 cipher suites in preference order.
// Empty selects AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305, then AES-128-CCM.
// TLS_AES_128_CCM_8_SHA256 is intentionally not supported.
CipherSuites []uint16
// CurvePreferences contains the elliptic curves used for ephemeral key
// exchange, in preference order. Empty selects X25519 followed by P-256;
// those are the only currently supported groups.
CurvePreferences []tls.CurveID
// MTU is the initial maximum UDP payload, including DTLS record framing,
// generated by Conn. Values below 256 are rejected. Zero selects 1200 bytes,
// which avoids IP fragmentation on the Internet paths discussed by RFC 9147
// section 4.1.1. The effective path MTU may decrease after write errors or
// repeated handshake timeouts; see Conn.PathMTU.
MTU int
// IgnorePathMTU skips the library's current PMTU payload limit for
// Application Data, including 0-RTT. The zero value is false. When enabled,
// each application datagram is still limited to one DTLS record with at
// most 2^14 bytes of content, but the complete record is handed directly to
// the transport. The network may fragment or drop it, or the transport may
// return [ErrDatagramTooLarge].
//
// This option does not change handshake or post-handshake flight
// fragmentation, retransmission, or PMTU backoff.
IgnorePathMTU bool
// FlightInterval is the initial handshake and post-handshake retransmission
// timeout. Zero selects one second.
FlightInterval time.Duration
// MaxFlightInterval caps exponential retransmission backoff. Zero selects
// 60 seconds.
MaxFlightInterval time.Duration
// HandshakeTimeout bounds the complete handshake. Zero selects 30 seconds.
// A shorter HandshakeContext deadline takes precedence.
HandshakeTimeout time.Duration
// ReplayWindow is the number of record sequence numbers tracked per epoch.
// It must be between 1 and 64. Zero selects 64. Larger values accept more
// reordering at the cost of retaining a wider anti-replay window.
ReplayWindow int
// MaxHandshakeMessage bounds memory allocated while reassembling one
// handshake message. Zero selects 1 MiB. RFC 9147 permits larger messages,
// so applications with unusually large certificate chains may raise it up
// to the protocol limit of 2^24-1 bytes.
MaxHandshakeMessage int
// MaxBufferedHandshakeMessages bounds the number of incomplete handshake
// messages retained across message sequence numbers. Zero selects 8.
MaxBufferedHandshakeMessages int
// MaxBufferedHandshakeBytes bounds the total bytes retained for incomplete
// handshake reassembly. Zero selects four times MaxHandshakeMessage. It must
// be at least MaxHandshakeMessage.
MaxBufferedHandshakeBytes int
// MaxBufferedApplicationData bounds decrypted application data waiting for
// ReadDatagram, in bytes. Zero selects 1 MiB. Exceeding the limit terminates
// the association instead of allowing unbounded memory growth.
MaxBufferedApplicationData int
// MaxBufferedApplicationDatagrams bounds complete application records
// waiting for ReadDatagram, including zero-length records. Zero selects 1024.
// Exceeding the limit terminates the association.
MaxBufferedApplicationDatagrams int
// MaxPendingConnections bounds active address-demultiplexed Listener
// sessions, including sessions returned by Accept. Zero selects 128. New
// unrecognized peers are ignored while this limit is full.
MaxPendingConnections int
// MaxSessionQueueDatagrams bounds unread UDP datagrams per Listener
// session. Zero selects 64; excess datagrams are dropped.
MaxSessionQueueDatagrams int
// ClientSessionCache enables client-side session resumption. A cache may be
// shared by concurrent clients and therefore must be safe for concurrent use.
ClientSessionCache ClientSessionCache
// SessionTicketsDisabled disables server NewSessionTicket messages and
// client ticket use. It also disables session resumption and 0-RTT.
SessionTicketsDisabled bool
// SessionTicketKey authenticates and encrypts server ticket state. Servers
// sharing resumable sessions must use the same key. A zero key is replaced
// with random key material when the configuration is first used.
SessionTicketKey [32]byte
// SessionTicketLifetime controls ticket validity. Zero selects 24 hours;
// values from one second through seven days are accepted, matching RFC 8446.
SessionTicketLifetime time.Duration
// MaxEarlyData advertises and permits at most this many 0-RTT application
// bytes per resumed connection. Zero disables early data. It does not by
// itself make early data replay-safe; see EarlyDataReplayCache.
MaxEarlyData uint32
// EarlyDataReplayCache is shared by server connections to prevent reuse of
// a PSK identity for 0-RTT. It must be safe for concurrent use. Nil uses a
// bounded process-wide cache. Deployments with more than one process must
// provide a cache whose replay domain covers every server that shares ticket
// keys and accepts 0-RTT.
EarlyDataReplayCache EarlyDataReplayCache
// AllowEarlyDataWithoutCookie permits a server to accept 0-RTT on the
// initial address before a cookie exchange. Keep it false on untrusted UDP
// listeners: accepting application bytes before return-routability validation
// increases amplification exposure. A false value causes early data to fall
// back to a 1-RTT handshake through HelloRetryRequest.
AllowEarlyDataWithoutCookie bool
// ConnectionID is the CID that the peer places in protected records sent
// to this endpoint. A non-nil empty slice negotiates support without
// requesting a CID in this direction. Clients with nil ConnectionID offer
// an empty CID by default, as recommended by RFC 9147 section 5.1.
ConnectionID []byte
// DisableConnectionID suppresses the client's recommended empty-CID offer
// when ConnectionID is nil. It has no effect when ConnectionID is non-nil.
DisableConnectionID bool
// GetConnectionID optionally creates a local CID for each accepted Listener
// session and in response to RequestConnectionId. It overrides ConnectionID
// for a newly accepted Listener session. Returned IDs must be at most 255
// bytes and must be unique and prefix-free among active sessions. The
// callback may be invoked concurrently for different connections.
GetConnectionID func() ([]byte, error)
// MaxConnectionIDs bounds the number of local or peer-provided CIDs kept
// for one connection. It must be between 1 and 255. Zero selects 8.
MaxConnectionIDs int
// contains filtered or unexported fields
}
Config configures a DTLS client or server. Fields that represent TLS 1.3 concepts follow crypto/tls.Config where practical.
A Config may be reused by multiple connections. It must not be modified after it has been passed to a DTLS function. Call Config.Clone before changing a configuration that is already in use.
func (*Config) Clone ¶
Clone returns a shallow clone of c whose slice-valued configuration fields have independent backing arrays. It is safe to clone a Config that is not being concurrently modified. Clone returns nil when c is nil.
Callback values, certificate internals, certificate pools, session caches, and the time and randomness sources are shared with c.
type ConfigError ¶
type ConfigError struct {
// Reason describes the rejected setting or limit.
Reason string
}
ConfigError reports an invalid local configuration or an operation that exceeds a configured resource limit. Use errors.As to inspect the Reason.
func (*ConfigError) Error ¶
func (e *ConfigError) Error() string
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn represents one DTLS association over a connected datagram transport. It exposes unreliable, message-oriented I/O and intentionally implements neither net.Conn nor net.PacketConn.
Unless Dial completed it already, the first call to Handshake, ReadDatagram, WriteDatagram, or another operation that requires traffic keys performs the handshake. Conn methods synchronize access to protocol state; reads and writes may run concurrently. Concurrent reads are serialized, as are concurrent writes.
func Client ¶
Client returns a client-side DTLS association over conn. The underlying connection must be a connected datagram transport that preserves one write as one datagram, normally a *net.UDPConn; a stream connection is invalid.
Client does not perform network I/O. The first operation that needs traffic keys performs the handshake. Close closes conn. A nil config selects default settings, but a client that verifies certificates usually needs RootCAs and ServerName.
func Dial ¶
Dial connects to address on a UDP network and performs a client handshake. Network must be "udp", "udp4", or "udp6". If Config.ServerName is empty, Dial derives it from the host portion of address. The returned Conn owns the underlying socket and must be closed by the caller.
Example ¶
package main
import (
"crypto/x509"
"log"
"os"
dtls13 "github.com/puernya/go-dtls"
)
func main() {
rootPEM, err := os.ReadFile("ca.pem")
if err != nil {
log.Fatal(err)
}
roots := x509.NewCertPool()
if ok := roots.AppendCertsFromPEM(rootPEM); !ok {
log.Fatal("invalid root certificate")
}
conn, err := dtls13.Dial("udp", "dtls.example:4433", &dtls13.Config{
RootCAs: roots,
ServerName: "dtls.example",
NextProtos: []string{"example/1"},
})
if err != nil {
log.Fatal(err)
}
defer conn.Close()
if _, err := conn.WriteDatagram([]byte("ping")); err != nil {
log.Fatal(err)
}
}
Output:
func DialWithDialer ¶
DialWithDialer is like Dial but uses dialer to create the connected UDP transport. A nil dialer is equivalent to a zero net.Dialer. A positive dialer Timeout also bounds the DTLS handshake; Config.HandshakeTimeout still applies when it is shorter.
func Server ¶
Server returns a server-side DTLS association over conn. The underlying connection must be a connected datagram transport that preserves one write as one datagram. Server defers the handshake until the first operation that needs traffic keys. Close closes conn.
func (*Conn) Close ¶
Close sends close_notify when application sending keys are available, clears retained traffic, resumption, and exporter secrets, stops background protocol work, and closes the underlying transport. It does not wait for the peer to acknowledge close_notify.
func (*Conn) ConnectionState ¶
func (c *Conn) ConnectionState() ConnectionState
ConnectionState returns a snapshot of the current negotiated state. Before handshake completion its fields have zero values. Post-handshake client authentication and Connection ID changes are reflected in later snapshots.
func (*Conn) Handshake ¶
Handshake performs the DTLS handshake if it has not already run. Subsequent calls return the result of the first handshake attempt.
func (*Conn) HandshakeContext ¶
HandshakeContext performs the DTLS handshake if it has not already run. The handshake is bounded by the earlier of ctx's deadline and Config.HandshakeTimeout. Canceling ctx interrupts transport I/O.
The first call controls the handshake and all later calls return its result; a canceled or failed handshake is not retried. The context must be non-nil.
func (*Conn) PathMTU ¶
PathMTU returns the current maximum transport datagram size used by the connection, including DTLS record framing. It starts at Config.MTU and can decrease in response to path-MTU write errors or repeated handshake timeouts. It returns zero before an effective configuration is available.
For an established connection, PathMTU()-RecordOverhead() is the current upper bound for an application datagram, subject also to the DTLS 2^14-byte record-content limit. The path can change after this check, so callers must still handle ErrDatagramTooLarge from Conn.WriteDatagram. Config.IgnorePathMTU makes application writes ignore this estimate; handshake traffic continues to use it.
func (*Conn) ReadDatagram ¶
func (c *Conn) ReadDatagram(p []byte) (int, DatagramInfo, error)
ReadDatagram reads and consumes one authenticated DTLS Application Data record. It performs the handshake first if necessary. The returned n is the number of plaintext bytes copied into p.
If p is too small, the unread remainder is discarded, DatagramInfo.FullLength reports the original length, and DatagramInfo.Truncated is true. A subsequent call reads the next record, not the remainder. Empty records are returned with n and FullLength both zero. A peer close_notify is returned as io.EOF after all earlier deliverable records have been consumed.
Example ¶
package main
import (
"fmt"
dtls13 "github.com/puernya/go-dtls"
)
func main() {
read := func(conn *dtls13.Conn) ([]byte, error) {
buffer := make([]byte, 1200)
n, info, err := conn.ReadDatagram(buffer)
if err != nil {
return nil, err
}
if info.Truncated {
// The rest of this datagram has already been discarded.
return nil, fmt.Errorf("datagram needs %d bytes", info.FullLength)
}
return append([]byte(nil), buffer[:n]...), nil
}
_ = read
}
Output:
func (*Conn) RecordOverhead ¶
RecordOverhead returns the number of bytes added around one application datagram in the current sending epoch, including record framing, the inner content type, and the AEAD tag. Before handshake keys exist it returns the plaintext record-header size and must not be used to size application data.
func (*Conn) RemoteAddr ¶
RemoteAddr returns the destination address of the connected underlying transport. It is not automatically rebound when a valid CID record arrives from a different source address.
func (*Conn) RequestClientCertificate ¶
RequestClientCertificate starts post-handshake client authentication and waits until the client's response has been received and verified. It is a server-only operation and performs the initial handshake first if necessary.
The client must have advertised Config.PostHandshakeAuth. The server's Config.ClientAuth must request or require a certificate, and ClientCAs and VerifyPeerCertificate are applied according to that policy. At most one post-handshake authentication exchange may be active on a connection.
Canceling ctx stops this call from waiting but does not retract a CertificateRequest that is already on the wire; the protocol exchange may continue in the background. A nil context is treated as context.Background.
func (*Conn) RequestConnectionIDs ¶
RequestConnectionIDs reliably asks the peer to advertise up to count spare Connection IDs. It is valid only when a non-empty CID is active for records sent to the peer. A new request cannot be sent until the preceding request has been acknowledged and fulfilled.
The peer controls how many IDs it returns, subject to its Config.MaxConnectionIDs and Config.GetConnectionID policy.
func (*Conn) SendKeyUpdate ¶
SendKeyUpdate reliably sends a post-handshake KeyUpdate. Application writes continue with the old sending epoch until the KeyUpdate record is acknowledged, as required by RFC 9147 section 8. If requestPeer is true, the peer is asked to update its sending keys as well.
Only one locally initiated KeyUpdate may await acknowledgement at a time. The package also initiates updates automatically before the negotiated AEAD usage limit is reached.
func (*Conn) SendNewConnectionIDs ¶
SendNewConnectionIDs reliably advertises new local Connection IDs to the peer. Each ID is a value the peer may place in records sent to this endpoint. IDs must be at most 255 bytes and, together with existing local IDs, unique and prefix-free. Their total number is bounded by Config.MaxConnectionIDs.
When immediate is true, connectionIDs must be non-empty and the peer is instructed to switch immediately to one of the supplied IDs. Otherwise the IDs become spares. Only one locally initiated NewConnectionId flight may await acknowledgement at a time. The method fails if non-empty Connection IDs and local CID updates were not negotiated.
func (*Conn) SetDeadline ¶
SetDeadline sets both read and write deadlines on the underlying transport. A zero value disables the deadlines. The deadline applies to currently blocked and future I/O, following net.Conn semantics. An initial handshake temporarily installs its own deadline and clears transport deadlines when it finishes.
func (*Conn) SetReadDeadline ¶
SetReadDeadline sets the deadline for future and currently blocked reads from the underlying transport. A zero value disables the deadline.
func (*Conn) SetWriteDeadline ¶
SetWriteDeadline sets the deadline for future and currently blocked writes to the underlying transport. A zero value disables the deadline.
func (*Conn) UseNextConnectionID ¶
UseNextConnectionID switches outgoing protected records to the next spare CID provided by the peer. It returns an error if no spare CID is available.
Switching a CID changes record routing but does not validate, select, or rebind a network path. Applications implementing migration need a separate path-validation policy.
func (*Conn) WriteDatagram ¶
WriteDatagram sends p as exactly one DTLS Application Data record to the association's authenticated peer. It performs the handshake first if necessary. Application data is not internally fragmented, retransmitted, or reordered. A nil or empty p sends a valid empty application datagram.
By default, if p exceeds the current path MTU or the DTLS record-content limit, WriteDatagram returns an error matching ErrDatagramTooLarge and n == 0, without a partial record on the wire. Config.IgnorePathMTU skips the library's PMTU check but not the record-content limit. The transport may still reject the complete record with ErrDatagramTooLarge. Otherwise n is len(p) if the complete record was handed to the underlying transport.
Example ¶
package main
import (
"errors"
"fmt"
dtls13 "github.com/puernya/go-dtls"
)
func main() {
send := func(conn *dtls13.Conn, payload []byte) error {
if err := conn.Handshake(); err != nil {
return err
}
maximum := conn.PathMTU() - conn.RecordOverhead()
if len(payload) > maximum {
return fmt.Errorf("payload is %d bytes; current maximum is %d", len(payload), maximum)
}
_, err := conn.WriteDatagram(payload)
if errors.Is(err, dtls13.ErrDatagramTooLarge) {
// The path MTU can decrease after the check above. Fragmentation and
// retry policy belong to the application protocol.
return fmt.Errorf("resize datagram: %w", err)
}
return err
}
_ = send
}
Output:
func (*Conn) WriteEarlyData ¶
WriteEarlyData attempts to send p as one client 0-RTT Application Data record and completes the handshake. It can be called at most once and only on a client created with a usable cached session whose ticket permits early data. A nil or empty p is a no-op.
The method returns ErrEarlyDataUnavailable when no eligible early-data session exists, and ErrEarlyDataRejected when the record was sent but the server completed the handshake without accepting it. In both cases the caller may use the established connection for 1-RTT data. Retrying p is an application decision because 0-RTT data is replayable. Oversized data returns ErrDatagramTooLarge without a partial record. Config.IgnorePathMTU skips the library's PMTU check but not the DTLS record-content or ticket limits; the transport may still reject the complete record as too large.
Example ¶
package main
import (
"errors"
dtls13 "github.com/puernya/go-dtls"
)
func main() {
sendReplaySafe := func(conn *dtls13.Conn, payload []byte) error {
_, err := conn.WriteEarlyData(payload)
switch {
case err == nil:
return nil
case errors.Is(err, dtls13.ErrEarlyDataUnavailable),
errors.Is(err, dtls13.ErrEarlyDataRejected):
// Retry only because this application operation is safe to repeat.
_, err = conn.WriteDatagram(payload)
return err
default:
return err
}
}
_ = sendReplaySafe
}
Output:
type ConnectionState ¶
type ConnectionState struct {
// Version is the negotiated DTLS version. It is VersionDTLS13 after a
// successful handshake.
Version uint16
// HandshakeComplete is true after the initial handshake has completed and
// installed application traffic keys.
HandshakeComplete bool
// DidResume is true when the connection used a PSK from a session ticket
// instead of performing a full certificate handshake.
DidResume bool
// CipherSuite is the negotiated TLS 1.3 cipher-suite identifier.
CipherSuite uint16
// NegotiatedProtocol is the ALPN protocol selected by the server, or the
// empty string when ALPN was not negotiated.
NegotiatedProtocol string
// ServerName is the name sent by a client and used for certificate hostname
// verification when built-in verification is enabled. It is empty in
// server-side state.
ServerName string
// PeerCertificates contains the certificate chain presented by the peer,
// with the leaf first. It can be empty for an authenticated resumed session
// or when a server did not request a client certificate.
PeerCertificates []*x509.Certificate
// VerifiedChains contains the chains built during certificate verification.
// It is nil when built-in verification was skipped or the peer did not send
// a certificate.
VerifiedChains [][]*x509.Certificate
// LocalConnectionID is the CID the peer currently uses when sending
// protected records to this endpoint. It is empty when no non-empty CID is
// active in that direction.
LocalConnectionID []byte
// PeerConnectionID is the CID this endpoint currently places in protected
// records sent to the peer. It is empty when no non-empty CID is active in
// that direction.
PeerConnectionID []byte
// contains filtered or unexported fields
}
ConnectionState records negotiated parameters and authenticated peer identity for a DTLS association. A zero value describes a connection whose handshake has not completed.
The certificate slices refer to immutable certificate objects owned by the connection and must not be modified. Connection ID slices returned by Conn.ConnectionState are copies.
func (ConnectionState) ExportKeyingMaterial ¶
func (s ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error)
ExportKeyingMaterial returns exporter output for the completed connection, following RFC 8446 section 7.5 with the DTLS 1.3 label prefix required by RFC 9147. A nil and an empty context are equivalent.
The label is application-defined and must fit the TLS HKDF label encoding. The maximum output is 255 times the negotiated hash length. The method returns an error before handshake completion, after the connection's secrets have been cleared, or for an invalid label or length.
type DatagramInfo ¶
type DatagramInfo struct {
// Source is the network address from which the authenticated record was
// received. It is informational: Connection IDs do not by themselves
// validate a changed network path or alter the address used for replies.
Source net.Addr
// FullLength is the length of the complete plaintext datagram before it was
// copied into the caller's buffer.
FullLength int
// Truncated reports whether the caller's buffer was shorter than FullLength.
// The unread remainder has already been discarded.
Truncated bool
}
DatagramInfo describes one authenticated Application Data record consumed by Conn.ReadDatagram.
type EarlyDataReplayCache ¶
type EarlyDataReplayCache interface {
// CheckAndStore atomically admits identity until expires. It returns true
// only when the identity was not already live and the cache retained the new
// entry for its validity period within the cache's replay domain. It must
// fail closed by returning false when it cannot store the entry.
CheckAndStore(identity string, expires time.Time) bool
}
EarlyDataReplayCache controls server acceptance of PSK identities for 0-RTT. Implementations must be safe for concurrent use and should share a replay domain with every server process that shares SessionTicketKey and accepts early data.
A replay cache is a required mitigation, not a guarantee that early data cannot be replayed. Applications must still give 0-RTT operations idempotent or otherwise replay-safe semantics.
func NewLRUEarlyDataReplayCache ¶
func NewLRUEarlyDataReplayCache(capacity int) EarlyDataReplayCache
NewLRUEarlyDataReplayCache creates a bounded, concurrency-safe in-process 0-RTT replay cache. Expired entries are removed as new identities are checked. Live entries are never evicted to make room: a full cache fails closed and rejects new early data until entries expire.
A non-positive capacity is rejected by returning nil. The cache covers only one process; distributed servers sharing ticket keys need a replay cache with a correspondingly shared consistency domain.
type Listener ¶
type Listener struct {
// contains filtered or unexported fields
}
Listener demultiplexes DTLS associations from one packet transport. It intentionally does not implement net.Listener because Conn exposes a connected datagram API rather than a byte stream.
Accept returns a Conn after receiving an initial datagram for a new peer; the DTLS handshake and peer authentication occur when Handshake or the first datagram I/O method is called. Listener bounds pending state and per-peer queues according to Config.
func Listen ¶
Listen creates and owns a UDP socket on address, then returns a DTLS association listener. Network must be "udp", "udp4", or "udp6". A nil config selects defaults. Close releases the socket and all associations demultiplexed from it.
Example ¶
package main
import (
"crypto/tls"
"log"
dtls13 "github.com/puernya/go-dtls"
)
func main() {
certificate, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
listener, err := dtls13.Listen("udp", ":4433", &dtls13.Config{
Certificates: []tls.Certificate{certificate},
NextProtos: []string{"example/1"},
})
if err != nil {
log.Fatal(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("accept: %v", err)
return
}
go func() {
defer conn.Close()
if err := conn.Handshake(); err != nil {
log.Printf("DTLS handshake: %v", err)
return
}
buffer := make([]byte, 1200)
n, info, err := conn.ReadDatagram(buffer)
if err != nil {
log.Printf("read datagram: %v", err)
return
}
if info.Truncated {
log.Printf("discarded %d-byte datagram", info.FullLength)
return
}
if _, err := conn.WriteDatagram(buffer[:n]); err != nil {
log.Printf("write datagram: %v", err)
}
}()
}
}
Output:
func NewListener ¶
func NewListener(inner net.PacketConn, config *Config) *Listener
NewListener creates a DTLS association listener over inner. Ownership of inner is transferred to the Listener; Close closes it. inner must preserve datagram boundaries and provide source addresses, normally by being a UDP net.PacketConn.
NewListener cannot return an error for compatibility with construction over an existing transport. A nil transport and configuration initialization errors are retained and returned by Accept.
func (*Listener) Accept ¶
Accept waits for the next candidate DTLS association and returns it as a server-side Conn. The returned connection has not necessarily completed its handshake; call Conn.Handshake to authenticate it before handing it to code that assumes an authenticated peer. Closing the returned Conn removes its association state from the Listener.
Accept returns a retained NewListener configuration error, the underlying packet read error, or net.ErrClosed after the Listener is closed.
type ProtocolError ¶
type ProtocolError struct {
// Reason describes the protocol or state-machine violation.
Reason string
}
ProtocolError reports malformed protocol data or an operation that is not valid in the connection's current DTLS state. When caused by authenticated peer input, the connection sends the corresponding fatal alert when possible. Use errors.As to inspect the Reason.
func (*ProtocolError) Error ¶
func (e *ProtocolError) Error() string
Source Files
¶
- ack.go
- alert.go
- amplification.go
- auth_messages.go
- cipher_suite.go
- config.go
- conn.go
- connection_id.go
- cookie.go
- doc.go
- epoch.go
- errors.go
- flight.go
- handshake.go
- handshake_driver.go
- handshake_inbox.go
- hello.go
- hello_retry.go
- key_exchange.go
- key_schedule.go
- listener.go
- negotiate.go
- post_handshake.go
- post_handshake_auth.go
- protected_record.go
- record.go
- replay.go
- session.go
- signature.go
- traffic_update.go
- transcript.go
- verify.go