Documentation
¶
Overview ¶
Package dtls decodes Datagram Transport Layer Security records and handshake messages per RFC 6347 (DTLS 1.2) and RFC 9147 (DTLS 1.3 — unified header form is not supported here; we decode the legacy DTLS 1.3 record layer that uses the same 13-byte header as 1.2).
Wrap-vs-native judgement
Native. Both DTLS RFCs are fully public; the wire format is a tight fixed-layout binary record header plus a well-documented handshake-message catalogue. No crypto is performed at this layer — handshake bodies of post-Finished records and ApplicationData payloads are encrypted (when the cipher state has advanced past the null cipher) and are surfaced as hex. Operators paste UDP payload bytes from a Wireshark Follow-UDP-Stream view, a `tcpdump -X udp port 443` line, or any DTLS- emitting tool and inspect every documented field.
What this package covers
**Record layer** (13 bytes fixed, RFC 6347 §4.1): ContentType (1 byte) + Version (2 bytes) + Epoch (2 bytes BE — incremented on each cipher state change) + Sequence Number (6 bytes BE — replay-protection nonce)
Length (2 bytes BE) + Fragment (Length bytes). The walker iterates concatenated records until the buffer is consumed.
**Content types** (RFC 5246 §6.2.1):
20 ChangeCipherSpec
21 Alert
22 Handshake
23 ApplicationData
24 Heartbeat (RFC 6520 — yes, that one)
**Version values**:
0xFEFF DTLS 1.0
0xFEFD DTLS 1.2
0xFEFC DTLS 1.3 (legacy-form records)
**Alert body** (2 bytes): Level (1 warning / 2 fatal) + Description with a **23-entry name table** covering close_notify, unexpected_message, bad_record_mac, decryption_failed, record_overflow, decompression_ failure, handshake_failure, no_certificate (TLS 1.0), bad_certificate, unsupported_certificate, certificate_ revoked, certificate_expired, certificate_unknown, illegal_parameter, unknown_ca, access_denied, decode_ error, decrypt_error, export_restriction, protocol_ version, insufficient_security, internal_error, user_canceled, no_renegotiation, unsupported_extension.
**ChangeCipherSpec body** (1 byte, always 0x01).
**Handshake message header** (12 bytes fixed, RFC 6347 §4.2.2): MsgType (1 byte) + Length (3 bytes BE — total reassembled message length) + MessageSeq (2 bytes BE — for fragment reassembly) + FragmentOffset (3 bytes BE) + FragmentLength (3 bytes BE) + FragmentBody.
0 HelloRequest
1 ClientHello
2 ServerHello
3 HelloVerifyRequest (DTLS-specific cookie exchange)
4 NewSessionTicket
8 EncryptedExtensions (TLS 1.3)
11 Certificate
12 ServerKeyExchange
13 CertificateRequest
14 ServerHelloDone
15 CertificateVerify
16 ClientKeyExchange
20 Finished
**ClientHello body** parsed: legacy_version + random (32 bytes) + session_id (length-prefixed) + cookie (length-prefixed, DTLS-specific) + cipher_suites (length-prefixed list of uint16 BE; rendered as count
raw hex blob) + compression_methods (length-prefixed list of uint8) + extensions (length-prefixed list of uint16 type + uint16 length + body bytes, rendered as count + raw hex blob).
**ServerHello body** parsed: legacy_version + random
session_id + selected cipher_suite (uint16 BE) + selected compression_method (uint8) + extensions.
**HelloVerifyRequest body** parsed: server_version + cookie (length-prefixed). The hallmark of DTLS's stateless cookie exchange (mitigates UDP amplification DoS).
**Heartbeat body** (RFC 6520): MessageType (1 byte: 1 Request / 2 Response) + PayloadLength (uint16 BE) + Payload + Padding. NB: a mismatched PayloadLength vs declared was the basis for Heartbleed (CVE-2014- 0160) — we surface the declared value AND the actual remaining bytes so operators can spot the gap.
What this package does NOT cover (deliberately out of scope)
Decryption of encrypted records — operators need session keys exported from the TLS handshake; the ciphertext is surfaced as hex.
DTLS 1.3 unified header records (RFC 9147 §4) — the ultra-compact variant with 8-bit-tag header is a future Spec.
Full TLS extension dissection (SNI / ALPN / supported_ groups / signature_algorithms / key_share / etc.) — extension bodies are surfaced as hex. The TLS extension catalogue is handled by `tls_handshake_decode` for cleartext TCP records; the same table would apply once cleartext bytes have been extracted from DTLS.
UDP / IP framing — feed the UDP payload bytes.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Alert ¶
type Alert struct {
Level int `json:"level"`
LevelName string `json:"level_name"`
Description int `json:"description"`
DescriptionName string `json:"description_name"`
}
Alert is the body of content type 21.
type ApplicationData ¶
type ApplicationData struct {
CipherTextLen int `json:"cipher_text_length"`
CipherTextHex string `json:"cipher_text_hex,omitempty"`
}
ApplicationData surfaces the ciphertext blob with length.
type CertEntry ¶ added in v0.474.0
type CertEntry struct {
Length int `json:"length"`
Certificate *x509decode.Certificate `json:"x509,omitempty"`
DERHex string `json:"der_hex,omitempty"`
DecodeError string `json:"decode_error,omitempty"`
}
CertEntry is one certificate from the chain, decoded via internal/x509decode.
type Certificate ¶ added in v0.474.0
type Certificate struct {
CertificateCount int `json:"certificate_count"`
Certificates []*CertEntry `json:"certificates,omitempty"`
Notes []string `json:"notes,omitempty"`
}
Certificate is a decoded DTLS Certificate handshake message. Its body uses the same layout as TLS 1.2 (a 3-byte certificate_list length followed by 3-byte-length-prefixed DER certificates); each DER certificate is decoded via internal/x509decode. Only attempted on an unfragmented handshake message (the dispatch above returns early when IsFragmented).
type ClientHello ¶
type ClientHello struct {
LegacyVersion string `json:"legacy_version"`
LegacyVersionHex string `json:"legacy_version_hex"`
RandomHex string `json:"random_hex"`
SessionIDHex string `json:"session_id_hex,omitempty"`
SessionIDLength int `json:"session_id_length"`
CookieHex string `json:"cookie_hex,omitempty"`
CookieLength int `json:"cookie_length"`
CipherSuiteCount int `json:"cipher_suite_count"`
CipherSuitesHex string `json:"cipher_suites_hex,omitempty"`
CompressionCount int `json:"compression_count"`
CompressionMethods string `json:"compression_methods_hex,omitempty"`
ExtensionsLength int `json:"extensions_length,omitempty"`
ExtensionsHex string `json:"extensions_hex,omitempty"`
}
ClientHello body fields.
type Handshake ¶
type Handshake struct {
MsgType int `json:"msg_type"`
MsgTypeName string `json:"msg_type_name"`
Length int `json:"total_message_length"`
MessageSeq uint16 `json:"message_seq"`
FragmentOffset int `json:"fragment_offset"`
FragmentLength int `json:"fragment_length"`
IsFragmented bool `json:"is_fragmented"`
ClientHello *ClientHello `json:"client_hello,omitempty"`
ServerHello *ServerHello `json:"server_hello,omitempty"`
HelloVerifyRequest *HelloVerifyRequest `json:"hello_verify_request,omitempty"`
Certificate *Certificate `json:"certificate,omitempty"`
}
Handshake is the dissected DTLS handshake header + per-type body. When the fragment is encrypted (post-ChangeCipherSpec), only the FragmentHex on the parent Record is meaningful and this struct is nil.
type Heartbeat ¶
type Heartbeat struct {
MessageType int `json:"message_type"`
MessageTypeName string `json:"message_type_name"`
PayloadLength int `json:"payload_length_declared"`
ActualRemaining int `json:"actual_remaining_bytes"`
PayloadHex string `json:"payload_hex,omitempty"`
HeartbleedHint string `json:"heartbleed_hint,omitempty"`
}
Heartbeat is the body of content type 24 (RFC 6520).
type HelloVerifyRequest ¶
type HelloVerifyRequest struct {
ServerVersion string `json:"server_version"`
ServerVersionHex string `json:"server_version_hex"`
CookieHex string `json:"cookie_hex"`
CookieLength int `json:"cookie_length"`
}
HelloVerifyRequest body fields.
type Record ¶
type Record struct {
ContentType int `json:"content_type"`
ContentTypeName string `json:"content_type_name"`
Version string `json:"version"`
VersionHex string `json:"version_hex"`
Epoch uint16 `json:"epoch"`
SequenceNumber uint64 `json:"sequence_number"`
Length int `json:"length"`
FragmentHex string `json:"fragment_hex,omitempty"`
Handshake *Handshake `json:"handshake,omitempty"`
Alert *Alert `json:"alert,omitempty"`
ChangeCipherSpec *uint8 `json:"change_cipher_spec,omitempty"`
Heartbeat *Heartbeat `json:"heartbeat,omitempty"`
ApplicationData *ApplicationData `json:"application_data,omitempty"`
}
Record is one DTLS record-layer frame.
type Result ¶
type Result struct {
Records []Record `json:"records"`
RecordCount int `json:"record_count"`
TotalBytes int `json:"total_bytes"`
Summary string `json:"summary"`
}
Result is the top-level decoded view.
type ServerHello ¶
type ServerHello struct {
LegacyVersion string `json:"legacy_version"`
LegacyVersionHex string `json:"legacy_version_hex"`
RandomHex string `json:"random_hex"`
SessionIDHex string `json:"session_id_hex,omitempty"`
SessionIDLength int `json:"session_id_length"`
CipherSuiteHex string `json:"cipher_suite_hex"`
CompressionMethod int `json:"compression_method"`
ExtensionsLength int `json:"extensions_length,omitempty"`
ExtensionsHex string `json:"extensions_hex,omitempty"`
}
ServerHello body fields.