package dns
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"math/big"
"sort"
"strings"
"time"
)
const (
_ uint8 = iota
RSAMD5
DH
DSA
_
RSASHA1
DSANSEC3SHA1
RSASHA1NSEC3SHA1
RSASHA256
_
RSASHA512
_
ECCGOST
ECDSAP256SHA256
ECDSAP384SHA384
ED25519
ED448
INDIRECT uint8 = 252
PRIVATEDNS uint8 = 253
PRIVATEOID uint8 = 254
)
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" ,
}
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 ,
}
const (
_ uint8 = iota
SHA1
SHA256
GOST94
SHA384
SHA512
)
var HashToString = map [uint8 ]string {
SHA1 : "SHA1" ,
SHA256 : "SHA256" ,
GOST94 : "GOST94" ,
SHA384 : "SHA384" ,
SHA512 : "SHA512" ,
}
const (
SEP = 1
REVOKE = 1 << 7
ZONE = 1 << 8
)
type rrsigWireFmt struct {
TypeCovered uint16
Algorithm uint8
Labels uint8
OrigTtl uint32
Expiration uint32
Inception uint32
KeyTag uint16
SignerName string `dns:"domain-name"`
}
type dnskeyWireFmt struct {
Flags uint16
Protocol uint8
Algorithm uint8
PublicKey string `dns:"base64"`
}
func (k *DNSKEY ) KeyTag () uint16 {
if k == nil {
return 0
}
var keytag int
switch k .Algorithm {
case RSAMD5 :
modulus , _ := fromBase64 ([]byte (k .PublicKey ))
if len (modulus ) > 1 {
x := binary .BigEndian .Uint16 (modulus [len (modulus )-3 :])
keytag = int (x )
}
default :
keywire := new (dnskeyWireFmt )
keywire .Flags = k .Flags
keywire .Protocol = k .Protocol
keywire .Algorithm = k .Algorithm
keywire .PublicKey = k .PublicKey
wire := make ([]byte , DefaultMsgSize )
n , err := packKeyWire (keywire , wire )
if err != nil {
return 0
}
wire = wire [:n ]
for i , v := range wire {
if i &1 != 0 {
keytag += int (v )
} else {
keytag += int (v ) << 8
}
}
keytag += keytag >> 16 & 0xFFFF
keytag &= 0xFFFF
}
return uint16 (keytag )
}
func (k *DNSKEY ) ToDS (h uint8 ) *DS {
if k == nil {
return nil
}
ds := new (DS )
ds .Hdr .Name = k .Hdr .Name
ds .Hdr .Class = k .Hdr .Class
ds .Hdr .Rrtype = TypeDS
ds .Hdr .Ttl = k .Hdr .Ttl
ds .Algorithm = k .Algorithm
ds .DigestType = h
ds .KeyTag = k .KeyTag ()
keywire := new (dnskeyWireFmt )
keywire .Flags = k .Flags
keywire .Protocol = k .Protocol
keywire .Algorithm = k .Algorithm
keywire .PublicKey = k .PublicKey
wire := make ([]byte , DefaultMsgSize )
n , err := packKeyWire (keywire , wire )
if err != nil {
return nil
}
wire = wire [:n ]
owner := make ([]byte , 255 )
off , err1 := PackDomainName (CanonicalName (k .Hdr .Name ), owner , 0 , nil , false )
if err1 != nil {
return nil
}
owner = owner [:off ]
var hash crypto .Hash
switch h {
case SHA1 :
hash = crypto .SHA1
case SHA256 :
hash = crypto .SHA256
case SHA384 :
hash = crypto .SHA384
case SHA512 :
hash = crypto .SHA512
default :
return nil
}
s := hash .New ()
s .Write (owner )
s .Write (wire )
ds .Digest = hex .EncodeToString (s .Sum (nil ))
return ds
}
func (k *DNSKEY ) ToCDNSKEY () *CDNSKEY {
c := &CDNSKEY {DNSKEY : *k }
c .Hdr = k .Hdr
c .Hdr .Rrtype = TypeCDNSKEY
return c
}
func (d *DS ) ToCDS () *CDS {
c := &CDS {DS : *d }
c .Hdr = d .Hdr
c .Hdr .Rrtype = TypeCDS
return c
}
func (rr *RRSIG ) Sign (k crypto .Signer , rrset []RR ) error {
h0 := rrset [0 ].Header ()
rr .Hdr .Rrtype = TypeRRSIG
rr .Hdr .Name = h0 .Name
rr .Hdr .Class = h0 .Class
if rr .OrigTtl == 0 {
rr .OrigTtl = h0 .Ttl
}
rr .TypeCovered = h0 .Rrtype
rr .Labels = uint8 (CountLabel (h0 .Name ))
if strings .HasPrefix (h0 .Name , "*" ) {
rr .Labels --
}
return rr .signAsIs (k , rrset )
}
func (rr *RRSIG ) signAsIs (k crypto .Signer , rrset []RR ) error {
if k == nil {
return ErrPrivKey
}
if rr .KeyTag == 0 || len (rr .SignerName ) == 0 || rr .Algorithm == 0 {
return ErrKey
}
sigwire := new (rrsigWireFmt )
sigwire .TypeCovered = rr .TypeCovered
sigwire .Algorithm = rr .Algorithm
sigwire .Labels = rr .Labels
sigwire .OrigTtl = rr .OrigTtl
sigwire .Expiration = rr .Expiration
sigwire .Inception = rr .Inception
sigwire .KeyTag = rr .KeyTag
sigwire .SignerName = CanonicalName (rr .SignerName )
signdata := make ([]byte , DefaultMsgSize )
n , err := packSigWire (sigwire , signdata )
if err != nil {
return err
}
signdata = signdata [:n ]
wire , err := rawSignatureData (rrset , rr )
if err != nil {
return err
}
h , cryptohash , err := hashFromAlgorithm (rr .Algorithm )
if err != nil {
return err
}
switch rr .Algorithm {
case RSAMD5 , DSA , DSANSEC3SHA1 :
return ErrAlg
default :
h .Write (signdata )
h .Write (wire )
signature , err := sign (k , h .Sum (nil ), cryptohash , rr .Algorithm )
if err != nil {
return err
}
rr .Signature = toBase64 (signature )
return nil
}
}
func sign(k crypto .Signer , hashed []byte , hash crypto .Hash , alg uint8 ) ([]byte , error ) {
signature , err := k .Sign (rand .Reader , hashed , hash )
if err != nil {
return nil , err
}
switch alg {
case RSASHA1 , RSASHA1NSEC3SHA1 , RSASHA256 , RSASHA512 , ED25519 :
return signature , nil
case ECDSAP256SHA256 , ECDSAP384SHA384 :
ecdsaSignature := &struct {
R , S *big .Int
}{}
if _ , err := asn1 .Unmarshal (signature , ecdsaSignature ); err != nil {
return nil , err
}
var intlen int
switch alg {
case ECDSAP256SHA256 :
intlen = 32
case ECDSAP384SHA384 :
intlen = 48
}
signature := intToBytes (ecdsaSignature .R , intlen )
signature = append (signature , intToBytes (ecdsaSignature .S , intlen )...)
return signature , nil
default :
return nil , ErrAlg
}
}
func (rr *RRSIG ) Verify (k *DNSKEY , rrset []RR ) error {
if !IsRRset (rrset ) {
return ErrRRset
}
if rr .KeyTag != k .KeyTag () {
return ErrKey
}
if rr .Hdr .Class != k .Hdr .Class {
return ErrKey
}
if rr .Algorithm != k .Algorithm {
return ErrKey
}
signerName := CanonicalName (rr .SignerName )
if !equal (signerName , k .Hdr .Name ) {
return ErrKey
}
if k .Protocol != 3 {
return ErrKey
}
if k .Flags &ZONE == 0 {
return ErrKey
}
if h0 := rrset [0 ].Header (); h0 .Class != rr .Hdr .Class ||
h0 .Rrtype != rr .TypeCovered ||
uint8 (CountLabel (h0 .Name )) < rr .Labels ||
!equal (h0 .Name , rr .Hdr .Name ) ||
!strings .HasSuffix (CanonicalName (h0 .Name ), signerName ) {
return ErrRRset
}
sigwire := new (rrsigWireFmt )
sigwire .TypeCovered = rr .TypeCovered
sigwire .Algorithm = rr .Algorithm
sigwire .Labels = rr .Labels
sigwire .OrigTtl = rr .OrigTtl
sigwire .Expiration = rr .Expiration
sigwire .Inception = rr .Inception
sigwire .KeyTag = rr .KeyTag
sigwire .SignerName = signerName
signeddata := make ([]byte , DefaultMsgSize )
n , err := packSigWire (sigwire , signeddata )
if err != nil {
return err
}
signeddata = signeddata [:n ]
wire , err := rawSignatureData (rrset , rr )
if err != nil {
return err
}
sigbuf := rr .sigBuf ()
h , cryptohash , err := hashFromAlgorithm (rr .Algorithm )
if err != nil {
return err
}
switch rr .Algorithm {
case RSASHA1 , RSASHA1NSEC3SHA1 , RSASHA256 , RSASHA512 :
pubkey := k .publicKeyRSA ()
if pubkey == nil {
return ErrKey
}
h .Write (signeddata )
h .Write (wire )
return rsa .VerifyPKCS1v15 (pubkey , cryptohash , h .Sum (nil ), sigbuf )
case ECDSAP256SHA256 , ECDSAP384SHA384 :
pubkey := k .publicKeyECDSA ()
if pubkey == nil {
return ErrKey
}
r := new (big .Int ).SetBytes (sigbuf [:len (sigbuf )/2 ])
s := new (big .Int ).SetBytes (sigbuf [len (sigbuf )/2 :])
h .Write (signeddata )
h .Write (wire )
if ecdsa .Verify (pubkey , h .Sum (nil ), r , s ) {
return nil
}
return ErrSig
case ED25519 :
pubkey := k .publicKeyED25519 ()
if pubkey == nil {
return ErrKey
}
if ed25519 .Verify (pubkey , append (signeddata , wire ...), sigbuf ) {
return nil
}
return ErrSig
default :
return ErrAlg
}
}
func (rr *RRSIG ) ValidityPeriod (t time .Time ) bool {
var utc int64
if t .IsZero () {
utc = time .Now ().UTC ().Unix ()
} else {
utc = t .UTC ().Unix ()
}
modi := (int64 (rr .Inception ) - utc ) / year68
mode := (int64 (rr .Expiration ) - utc ) / year68
ti := int64 (rr .Inception ) + modi *year68
te := int64 (rr .Expiration ) + mode *year68
return ti <= utc && utc <= te
}
func (rr *RRSIG ) sigBuf () []byte {
sigbuf , err := fromBase64 ([]byte (rr .Signature ))
if err != nil {
return nil
}
return sigbuf
}
func (k *DNSKEY ) publicKeyRSA () *rsa .PublicKey {
keybuf , err := fromBase64 ([]byte (k .PublicKey ))
if err != nil {
return nil
}
if len (keybuf ) < 1 +1 +64 {
return nil
}
explen := uint16 (keybuf [0 ])
keyoff := 1
if explen == 0 {
explen = uint16 (keybuf [1 ])<<8 | uint16 (keybuf [2 ])
keyoff = 3
}
if explen > 4 || explen == 0 || keybuf [keyoff ] == 0 {
return nil
}
modoff := keyoff + int (explen )
modlen := len (keybuf ) - modoff
if modlen < 64 || modlen > 512 || keybuf [modoff ] == 0 {
return nil
}
pubkey := new (rsa .PublicKey )
var expo uint64
for _ , v := range keybuf [keyoff :modoff ] {
expo <<= 8
expo |= uint64 (v )
}
if expo > 1 <<31 -1 {
return nil
}
pubkey .E = int (expo )
pubkey .N = new (big .Int ).SetBytes (keybuf [modoff :])
return pubkey
}
func (k *DNSKEY ) publicKeyECDSA () *ecdsa .PublicKey {
keybuf , err := fromBase64 ([]byte (k .PublicKey ))
if err != nil {
return nil
}
pubkey := new (ecdsa .PublicKey )
switch k .Algorithm {
case ECDSAP256SHA256 :
pubkey .Curve = elliptic .P256 ()
if len (keybuf ) != 64 {
return nil
}
case ECDSAP384SHA384 :
pubkey .Curve = elliptic .P384 ()
if len (keybuf ) != 96 {
return nil
}
}
pubkey .X = new (big .Int ).SetBytes (keybuf [:len (keybuf )/2 ])
pubkey .Y = new (big .Int ).SetBytes (keybuf [len (keybuf )/2 :])
return pubkey
}
func (k *DNSKEY ) publicKeyED25519 () ed25519 .PublicKey {
keybuf , err := fromBase64 ([]byte (k .PublicKey ))
if err != nil {
return nil
}
if len (keybuf ) != ed25519 .PublicKeySize {
return nil
}
return keybuf
}
type wireSlice [][]byte
func (p wireSlice ) Len () int { return len (p ) }
func (p wireSlice ) Swap (i , j int ) { p [i ], p [j ] = p [j ], p [i ] }
func (p wireSlice ) Less (i , j int ) bool {
_ , ioff , _ := UnpackDomainName (p [i ], 0 )
_ , joff , _ := UnpackDomainName (p [j ], 0 )
return bytes .Compare (p [i ][ioff +10 :], p [j ][joff +10 :]) < 0
}
func rawSignatureData(rrset []RR , s *RRSIG ) (buf []byte , err error ) {
wires := make (wireSlice , len (rrset ))
for i , r := range rrset {
r1 := r .copy ()
h := r1 .Header ()
h .Ttl = s .OrigTtl
labels := SplitDomainName (h .Name )
if len (labels ) > int (s .Labels ) {
h .Name = "*." + strings .Join (labels [len (labels )-int (s .Labels ):], "." ) + "."
}
h .Name = CanonicalName (h .Name )
switch x := r1 .(type ) {
case *NS :
x .Ns = CanonicalName (x .Ns )
case *MD :
x .Md = CanonicalName (x .Md )
case *MF :
x .Mf = CanonicalName (x .Mf )
case *CNAME :
x .Target = CanonicalName (x .Target )
case *SOA :
x .Ns = CanonicalName (x .Ns )
x .Mbox = CanonicalName (x .Mbox )
case *MB :
x .Mb = CanonicalName (x .Mb )
case *MG :
x .Mg = CanonicalName (x .Mg )
case *MR :
x .Mr = CanonicalName (x .Mr )
case *PTR :
x .Ptr = CanonicalName (x .Ptr )
case *MINFO :
x .Rmail = CanonicalName (x .Rmail )
x .Email = CanonicalName (x .Email )
case *MX :
x .Mx = CanonicalName (x .Mx )
case *RP :
x .Mbox = CanonicalName (x .Mbox )
x .Txt = CanonicalName (x .Txt )
case *AFSDB :
x .Hostname = CanonicalName (x .Hostname )
case *RT :
x .Host = CanonicalName (x .Host )
case *SIG :
x .SignerName = CanonicalName (x .SignerName )
case *PX :
x .Map822 = CanonicalName (x .Map822 )
x .Mapx400 = CanonicalName (x .Mapx400 )
case *NAPTR :
x .Replacement = CanonicalName (x .Replacement )
case *KX :
x .Exchanger = CanonicalName (x .Exchanger )
case *SRV :
x .Target = CanonicalName (x .Target )
case *DNAME :
x .Target = CanonicalName (x .Target )
}
wire := make ([]byte , Len (r1 )+1 )
off , err1 := PackRR (r1 , wire , 0 , nil , false )
if err1 != nil {
return nil , err1
}
wire = wire [:off ]
wires [i ] = wire
}
sort .Sort (wires )
for i , wire := range wires {
if i > 0 && bytes .Equal (wire , wires [i -1 ]) {
continue
}
buf = append (buf , wire ...)
}
return buf , nil
}
func packSigWire(sw *rrsigWireFmt , msg []byte ) (int , error ) {
off , err := packUint16 (sw .TypeCovered , msg , 0 )
if err != nil {
return off , err
}
off , err = packUint8 (sw .Algorithm , msg , off )
if err != nil {
return off , err
}
off , err = packUint8 (sw .Labels , msg , off )
if err != nil {
return off , err
}
off , err = packUint32 (sw .OrigTtl , msg , off )
if err != nil {
return off , err
}
off , err = packUint32 (sw .Expiration , msg , off )
if err != nil {
return off , err
}
off , err = packUint32 (sw .Inception , msg , off )
if err != nil {
return off , err
}
off , err = packUint16 (sw .KeyTag , msg , off )
if err != nil {
return off , err
}
off , err = PackDomainName (sw .SignerName , msg , off , nil , false )
if err != nil {
return off , err
}
return off , nil
}
func packKeyWire(dw *dnskeyWireFmt , msg []byte ) (int , error ) {
off , err := packUint16 (dw .Flags , msg , 0 )
if err != nil {
return off , err
}
off , err = packUint8 (dw .Protocol , msg , off )
if err != nil {
return off , err
}
off , err = packUint8 (dw .Algorithm , msg , off )
if err != nil {
return off , err
}
off , err = packStringBase64 (dw .PublicKey , msg , off )
if err != nil {
return off , err
}
return off , nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .