or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client.mdconstants.mddnssec.mddynamic-updates.mdedns0.mdindex.mdmessaging.mdrr-types.mdserver.mdtsig.mdutilities.mdzone-parsing.mdzone-transfer.md
tile.json

dnssec.mddocs/

DNSSEC Operations

Complete DNSSEC support including signing, validation, key generation, and all standard algorithms.

Algorithm Constants

Encryption Algorithms

const (
	RSAMD5           uint8 = 1   // RSA/MD5 (deprecated)
	DH               uint8 = 2   // Diffie-Hellman
	DSA              uint8 = 3   // DSA/SHA1
	RSASHA1          uint8 = 5   // RSA/SHA-1
	DSANSEC3SHA1     uint8 = 6   // DSA-NSEC3-SHA1
	RSASHA1NSEC3SHA1 uint8 = 7   // RSASHA1-NSEC3-SHA1
	RSASHA256        uint8 = 8   // RSA/SHA-256
	RSASHA512        uint8 = 10  // RSA/SHA-512
	ECCGOST          uint8 = 12  // GOST R 34.10-2001
	ECDSAP256SHA256  uint8 = 13  // ECDSA Curve P-256 with SHA-256
	ECDSAP384SHA384  uint8 = 14  // ECDSA Curve P-384 with SHA-384
	ED25519          uint8 = 15  // Ed25519
	ED448            uint8 = 16  // Ed448
	INDIRECT         uint8 = 252 // Reserved for Indirect Keys
	PRIVATEDNS       uint8 = 253 // Private algorithm
	PRIVATEOID       uint8 = 254 // Private algorithm OID
)

Hash Algorithms

const (
	SHA1   uint8 = 1 // SHA-1 (RFC 4034)
	SHA256 uint8 = 2 // SHA-256 (RFC 4509)
	GOST94 uint8 = 3 // GOST R 34.11-94 (RFC 5933)
	SHA384 uint8 = 4 // SHA-384 (Experimental)
	SHA512 uint8 = 5 // SHA-512 (Experimental)
)

DNSKEY Flags

const (
	SEP    = 1       // Secure Entry Point
	REVOKE = 1 << 7  // Key has been revoked
	ZONE   = 1 << 8  // Zone Key flag
)

Algorithm Mappings

// AlgorithmToString maps algorithm IDs to names
var AlgorithmToString = map[uint8]string{
	RSAMD5:           "RSAMD5",
	DH:               "DH",
	DSA:              "DSA",
	RSASHA1:          "RSASHA1",
	DSANSEC3SHA1:     "DSA-NSEC3-SHA1",
	RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
	RSASHA256:        "RSASHA256",
	RSASHA512:        "RSASHA512",
	ECCGOST:          "ECC-GOST",
	ECDSAP256SHA256:  "ECDSAP256SHA256",
	ECDSAP384SHA384:  "ECDSAP384SHA384",
	ED25519:          "ED25519",
	ED448:            "ED448",
	INDIRECT:         "INDIRECT",
	PRIVATEDNS:       "PRIVATEDNS",
	PRIVATEOID:       "PRIVATEOID",
}

// AlgorithmToHash maps algorithms to crypto.Hash
var AlgorithmToHash = map[uint8]crypto.Hash{
	RSAMD5:           crypto.MD5,
	DSA:              crypto.SHA1,
	RSASHA1:          crypto.SHA1,
	RSASHA1NSEC3SHA1: crypto.SHA1,
	RSASHA256:        crypto.SHA256,
	ECDSAP256SHA256:  crypto.SHA256,
	ECDSAP384SHA384:  crypto.SHA384,
	RSASHA512:        crypto.SHA512,
	ED25519:          0, // ED25519 does its own hashing
}

// HashToString maps hash IDs to names
var HashToString = map[uint8]string{
	SHA1:   "SHA1",
	SHA256: "SHA256",
	GOST94: "GOST94",
	SHA384: "SHA384",
	SHA512: "SHA512",
}

// StringToAlgorithm maps names to algorithm IDs
var StringToAlgorithm = map[string]uint8{
	"RSAMD5":           RSAMD5,
	"DH":               DH,
	"DSA":              DSA,
	"RSASHA1":          RSASHA1,
	"DSA-NSEC3-SHA1":   DSANSEC3SHA1,
	"RSASHA1-NSEC3-SHA1": RSASHA1NSEC3SHA1,
	"RSASHA256":        RSASHA256,
	"RSASHA512":        RSASHA512,
	"ECC-GOST":         ECCGOST,
	"ECDSAP256SHA256":  ECDSAP256SHA256,
	"ECDSAP384SHA384":  ECDSAP384SHA384,
	"ED25519":          ED25519,
	"ED448":            ED448,
	"INDIRECT":         INDIRECT,
	"PRIVATEDNS":       PRIVATEDNS,
	"PRIVATEOID":       PRIVATEOID,
}

// StringToHash maps names to hash IDs
var StringToHash = map[string]uint8{
	"SHA1":   SHA1,
	"SHA256": SHA256,
	"GOST94": GOST94,
	"SHA384": SHA384,
	"SHA512": SHA512,
}

DNSKEY Operations

DNSKEY Methods

// KeyTag calculates the keytag (key ID) of DNSKEY
func (k *DNSKEY) KeyTag() uint16

// ToDS converts DNSKEY to DS record with specified hash
func (k *DNSKEY) ToDS(h uint8) *DS

// ToCDNSKEY converts DNSKEY to CDNSKEY record
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY

// Generate generates a new private key for the DNSKEY
// bits: key size in bits (e.g., 2048 for RSA, 256 for ECDSA P-256)
// Returns private key suitable for signing
func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error)

// NewPrivateKey parses private key from BIND-style string format
// s: private key string
// Returns parsed private key
func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error)

// ReadPrivateKey reads BIND-style private key from reader
// q: reader containing private key
// file: filename for error reporting
// Returns parsed private key
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error)

// PrivateKeyString converts private key to BIND-style string format
// p: private key to convert
// Returns BIND-style private key string
func (k *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string

DS Methods

// ToCDS converts DS to CDS record
func (ds *DS) ToCDS() *CDS

Key Generation

// GeneratePrivateKey generates a DNSSEC private key
// algorithm: encryption algorithm (RSASHA256, ECDSAP256SHA256, ED25519, etc.)
// bits: key size in bits (e.g., 2048 for RSA, 256 for ECDSA P-256)
func GeneratePrivateKey(algorithm uint8, bits int) (crypto.PrivateKey, error)

Signing Operations

RRSIG Methods

// Sign signs an RRSet with the provided private key
// The RRSIG must have Inception, Expiration, KeyTag, SignerName, and Algorithm set
// If OrigTtl is zero, it uses the TTL from the RRset
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error

// Verify verifies the signature on an RRSet
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error

// ValidityPeriod checks if signature is valid at time t
func (rr *RRSIG) ValidityPeriod(t time.Time) bool

SIG(0) Operations

// Sign creates SIG(0) signature for message
func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error)

// Verify verifies SIG(0) signature on message
func (rr *SIG) Verify(k *KEY, msg []byte) error

NSEC3 Operations

// Cover checks if NSEC3 record covers the name
func (rr *NSEC3) Cover(name string) bool

// Match checks if NSEC3 record matches the name
func (rr *NSEC3) Match(name string) bool

// HashName hashes a domain name for NSEC3
// name: domain name to hash
// hash: hash algorithm
// iterations: number of iterations
// salt: hex encoded salt
func HashName(name string, hash uint8, iterations uint16, salt string) string

Usage Examples

Generating Keys

// Generate RSA key
rsaKey, err := dns.GeneratePrivateKey(dns.RSASHA256, 2048)
if err != nil {
	log.Fatal(err)
}

// Generate ECDSA P-256 key
ecdsaKey, err := dns.GeneratePrivateKey(dns.ECDSAP256SHA256, 256)
if err != nil {
	log.Fatal(err)
}

// Generate Ed25519 key
ed25519Key, err := dns.GeneratePrivateKey(dns.ED25519, 256)
if err != nil {
	log.Fatal(err)
}

Creating DNSKEY

import (
	"crypto/rsa"
	"encoding/base64"
)

// Assuming you have a private key
privKey := rsaKey.(*rsa.PrivateKey)
pubKeyBytes, _ := pubKeyToBytes(privKey.PublicKey)

dnskey := &dns.DNSKEY{
	Hdr: dns.RR_Header{
		Name:   "example.com.",
		Rrtype: dns.TypeDNSKEY,
		Class:  dns.ClassINET,
		Ttl:    3600,
	},
	Flags:     dns.ZONE | dns.SEP, // Zone key with SEP flag (KSK)
	Protocol:  3,
	Algorithm: dns.RSASHA256,
	PublicKey: base64.StdEncoding.EncodeToString(pubKeyBytes),
}

// Calculate key tag
keyTag := dnskey.KeyTag()
fmt.Printf("Key tag: %d\n", keyTag)

Converting DNSKEY to DS

// Create DS record with SHA-256
ds := dnskey.ToDS(dns.SHA256)

fmt.Printf("DS record: %s\n", ds.String())
// Output: example.com. 3600 IN DS 12345 8 2 ABC123...

Signing an RRset

// Create RRset to sign
rrset := []dns.RR{
	&dns.A{
		Hdr: dns.RR_Header{
			Name:   "example.com.",
			Rrtype: dns.TypeA,
			Class:  dns.ClassINET,
			Ttl:    3600,
		},
		A: net.ParseIP("192.0.2.1"),
	},
	&dns.A{
		Hdr: dns.RR_Header{
			Name:   "example.com.",
			Rrtype: dns.TypeA,
			Class:  dns.ClassINET,
			Ttl:    3600,
		},
		A: net.ParseIP("192.0.2.2"),
	},
}

// Create RRSIG
now := time.Now()
rrsig := &dns.RRSIG{
	Hdr: dns.RR_Header{
		Name:   "example.com.",
		Rrtype: dns.TypeRRSIG,
		Class:  dns.ClassINET,
		Ttl:    3600,
	},
	TypeCovered: dns.TypeA,
	Algorithm:   dns.RSASHA256,
	Labels:      2,
	OrigTtl:     3600,
	Expiration:  uint32(now.Add(30 * 24 * time.Hour).Unix()),
	Inception:   uint32(now.Unix()),
	KeyTag:      dnskey.KeyTag(),
	SignerName:  "example.com.",
}

// Sign the RRset
err := rrsig.Sign(privKey.(crypto.Signer), rrset)
if err != nil {
	log.Fatal(err)
}

fmt.Printf("RRSIG: %s\n", rrsig.String())

Verifying a Signature

// Verify RRSIG
err := rrsig.Verify(dnskey, rrset)
if err != nil {
	log.Printf("Signature verification failed: %v", err)
} else {
	log.Println("Signature valid")
}

// Check validity period
if rrsig.ValidityPeriod(time.Now()) {
	log.Println("Signature is currently valid")
} else {
	log.Println("Signature expired or not yet valid")
}

Working with NSEC3

nsec3 := &dns.NSEC3{
	Hdr: dns.RR_Header{
		Name:   "abc123.example.com.",
		Rrtype: dns.TypeNSEC3,
		Class:  dns.ClassINET,
		Ttl:    3600,
	},
	Hash:       dns.SHA1,
	Flags:      0,
	Iterations: 10,
	Salt:       "AABBCCDD",
	NextDomain: "def456",
	TypeBitMap: []uint16{dns.TypeA, dns.TypeRRSIG},
}

// Check if name is covered
if nsec3.Cover("www.example.com.") {
	log.Println("Name is covered by NSEC3")
}

// Check if name matches
if nsec3.Match("www.example.com.") {
	log.Println("Name matches NSEC3")
}

Hashing Names for NSEC3

// Hash a name for NSEC3
salt := "AABBCCDD"
iterations := uint16(10)
hash := dns.HashName("www.example.com.", dns.SHA1, iterations, salt)

fmt.Printf("Hashed name: %s\n", hash)

Creating NSEC Record

nsec := &dns.NSEC{
	Hdr: dns.RR_Header{
		Name:   "example.com.",
		Rrtype: dns.TypeNSEC,
		Class:  dns.ClassINET,
		Ttl:    3600,
	},
	NextDomain: "www.example.com.",
	TypeBitMap: []uint16{
		dns.TypeA,
		dns.TypeNS,
		dns.TypeSOA,
		dns.TypeRRSIG,
		dns.TypeNSEC,
		dns.TypeDNSKEY,
	},
}

SIG(0) Transaction Signature

// Create SIG(0) for message authentication
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)

sig := &dns.SIG{
	RRSIG: dns.RRSIG{
		Hdr: dns.RR_Header{
			Name:   ".",
			Rrtype: dns.TypeSIG,
			Class:  dns.ClassANY,
			Ttl:    0,
		},
		TypeCovered: 0,
		Algorithm:   dns.RSASHA256,
		Labels:      0,
		OrigTtl:     0,
		Expiration:  uint32(time.Now().Add(5 * time.Minute).Unix()),
		Inception:   uint32(time.Now().Unix()),
		KeyTag:      key.KeyTag(),
		SignerName:  "example.com.",
	},
}

// Sign message
signedMsg, err := sig.Sign(privKey.(crypto.Signer), m)
if err != nil {
	log.Fatal(err)
}

Complete Zone Signing Example

// Zone signing workflow
zone := "example.com."
zsk, _ := dns.GeneratePrivateKey(dns.RSASHA256, 2048) // Zone Signing Key
ksk, _ := dns.GeneratePrivateKey(dns.RSASHA256, 4096) // Key Signing Key

// Create DNSKEY records
zskDNSKEY := createDNSKEY(zone, zsk, dns.ZONE)
kskDNSKEY := createDNSKEY(zone, ksk, dns.ZONE|dns.SEP)

// Create DS from KSK
ds := kskDNSKEY.ToDS(dns.SHA256)

// Sign DNSKEY RRset with KSK
dnskeyRRset := []dns.RR{zskDNSKEY, kskDNSKEY}
dnskeyRRSIG := createRRSIG(zone, dns.TypeDNSKEY, kskDNSKEY.KeyTag())
err := dnskeyRRSIG.Sign(ksk.(crypto.Signer), dnskeyRRset)

// Sign other RRsets with ZSK
aRRset := []dns.RR{/* ... */}
aRRSIG := createRRSIG(zone, dns.TypeA, zskDNSKEY.KeyTag())
err = aRRSIG.Sign(zsk.(crypto.Signer), aRRset)

Key Rollover Pattern

// KSK rollover: double signature method
oldKSK, _ := loadKey("Kexample.com.+008+12345")
newKSK, _ := dns.GeneratePrivateKey(dns.RSASHA256, 4096)

oldKSKDNSKEY := createDNSKEY(zone, oldKSK, dns.ZONE|dns.SEP)
newKSKDNSKEY := createDNSKEY(zone, newKSK, dns.ZONE|dns.SEP)

// Publish both keys
dnskeyRRset := []dns.RR{zskDNSKEY, oldKSKDNSKEY, newKSKDNSKEY}

// Sign with both KSKs
oldRRSIG := createRRSIG(zone, dns.TypeDNSKEY, oldKSKDNSKEY.KeyTag())
oldRRSIG.Sign(oldKSK.(crypto.Signer), dnskeyRRset)

newRRSIG := createRRSIG(zone, dns.TypeDNSKEY, newKSKDNSKEY.KeyTag())
newRRSIG.Sign(newKSK.(crypto.Signer), dnskeyRRset)

// Publish both DS records at parent
oldDS := oldKSKDNSKEY.ToDS(dns.SHA256)
newDS := newKSKDNSKEY.ToDS(dns.SHA256)

Time Format Functions

// TimeToString converts Unix timestamp to DNSSEC time format (YYYYMMDDHHmmSS)
func TimeToString(t uint32) string

// StringToTime converts DNSSEC time format to Unix timestamp
func StringToTime(s string) (uint32, error)

Time Format Example

// Convert current time to DNSSEC format
now := uint32(time.Now().Unix())
timeStr := dns.TimeToString(now)
fmt.Printf("DNSSEC time: %s\n", timeStr) // e.g., "20240115120000"

// Parse DNSSEC time
timestamp, err := dns.StringToTime("20240115120000")
if err != nil {
	log.Fatal(err)
}

Error Constants

var (
	ErrAlg     error // Bad algorithm
	ErrKey     error // Bad key
	ErrKeyAlg  error // Bad key algorithm
	ErrKeySize error // Bad key size
	ErrNoSig   error // No signature found
	ErrPrivKey error // Bad private key
	ErrSig     error // Bad signature
)

Related Topics

  • Resource Record Types - DNSSEC RR types (DNSKEY, DS, RRSIG, NSEC, NSEC3)
  • DNS Messaging - Adding DNSSEC records to messages
  • Zone Parsing - Parsing DNSSEC records from zone files
  • Constants - Complete algorithm and flag constants