Skip to content
Snippets Groups Projects
sig0.go 4.52 KiB
Newer Older
  • Learn to ignore specific revisions
  • package dns
    
    import (
    	"crypto"
    	"crypto/ecdsa"
    
    	"crypto/ed25519"
    
    ale's avatar
    ale committed
    	"encoding/binary"
    
    	"math/big"
    	"strings"
    	"time"
    )
    
    // Sign signs a dns.Msg. It fills the signature with the appropriate data.
    // The SIG record should have the SignerName, KeyTag, Algorithm, Inception
    // and Expiration set.
    func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
    	if k == nil {
    		return nil, ErrPrivKey
    	}
    
    	if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 {
    
    	rr.Hdr = RR_Header{Name: ".", Rrtype: TypeSIG, Class: ClassANY, Ttl: 0}
    	rr.OrigTtl, rr.TypeCovered, rr.Labels = 0, 0, 0
    
    	buf := make([]byte, m.Len()+Len(rr))
    
    	mbuf, err := m.PackBuffer(buf)
    	if err != nil {
    		return nil, err
    	}
    	if &buf[0] != &mbuf[0] {
    		return nil, ErrBuf
    	}
    	off, err := PackRR(rr, buf, len(mbuf), nil, false)
    	if err != nil {
    		return nil, err
    	}
    	buf = buf[:off:cap(buf)]
    
    
    	h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
    	if err != nil {
    		return nil, err
    
    	h.Write(buf[len(mbuf)+1+2+2+4+2:])
    
    	h.Write(buf[:len(mbuf)])
    
    	signature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm)
    
    	if err != nil {
    		return nil, err
    	}
    
    	rr.Signature = toBase64(signature)
    
    
    ale's avatar
    ale committed
    	buf = append(buf, signature...)
    
    	if len(buf) > int(^uint16(0)) {
    		return nil, ErrBuf
    	}
    	// Adjust sig data length
    	rdoff := len(mbuf) + 1 + 2 + 2 + 4
    
    ale's avatar
    ale committed
    	rdlen := binary.BigEndian.Uint16(buf[rdoff:])
    	rdlen += uint16(len(signature))
    	binary.BigEndian.PutUint16(buf[rdoff:], rdlen)
    
    	// Adjust additional count
    
    ale's avatar
    ale committed
    	adc := binary.BigEndian.Uint16(buf[10:])
    
    ale's avatar
    ale committed
    	binary.BigEndian.PutUint16(buf[10:], adc)
    
    	return buf, nil
    }
    
    // Verify validates the message buf using the key k.
    // It's assumed that buf is a valid message from which rr was unpacked.
    func (rr *SIG) Verify(k *KEY, buf []byte) error {
    	if k == nil {
    		return ErrKey
    	}
    
    	if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 {
    
    	h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
    	if err != nil {
    		return err
    	}
    
    ale's avatar
    ale committed
    	qdc := binary.BigEndian.Uint16(buf[4:])
    	anc := binary.BigEndian.Uint16(buf[6:])
    	auc := binary.BigEndian.Uint16(buf[8:])
    	adc := binary.BigEndian.Uint16(buf[10:])
    
    	offset := headerSize
    
    	for i := uint16(0); i < qdc && offset < buflen; i++ {
    		_, offset, err = UnpackDomainName(buf, offset)
    		if err != nil {
    			return err
    		}
    		// Skip past Type and Class
    		offset += 2 + 2
    	}
    	for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
    		_, offset, err = UnpackDomainName(buf, offset)
    		if err != nil {
    			return err
    		}
    		// Skip past Type, Class and TTL
    		offset += 2 + 2 + 4
    		if offset+1 >= buflen {
    			continue
    		}
    
    		rdlen := binary.BigEndian.Uint16(buf[offset:])
    
    ale's avatar
    ale committed
    		offset += 2
    
    		offset += int(rdlen)
    	}
    	if offset >= buflen {
    		return &Error{err: "overflowing unpacking signed message"}
    	}
    
    	// offset should be just prior to SIG
    	bodyend := offset
    	// owner name SHOULD be root
    	_, offset, err = UnpackDomainName(buf, offset)
    	if err != nil {
    		return err
    	}
    	// Skip Type, Class, TTL, RDLen
    	offset += 2 + 2 + 4 + 2
    	sigstart := offset
    	// Skip Type Covered, Algorithm, Labels, Original TTL
    	offset += 2 + 1 + 1 + 4
    	if offset+4+4 >= buflen {
    		return &Error{err: "overflow unpacking signed message"}
    	}
    
    ale's avatar
    ale committed
    	expire := binary.BigEndian.Uint32(buf[offset:])
    
    ale's avatar
    ale committed
    	incept := binary.BigEndian.Uint32(buf[offset:])
    
    	offset += 4
    	now := uint32(time.Now().Unix())
    	if now < incept || now > expire {
    		return ErrTime
    	}
    	// Skip key tag
    	offset += 2
    	var signername string
    	signername, offset, err = UnpackDomainName(buf, offset)
    	if err != nil {
    		return err
    	}
    	// If key has come from the DNS name compression might
    	// have mangled the case of the name
    
    	if !strings.EqualFold(signername, k.Header().Name) {
    
    		return &Error{err: "signer name doesn't match key name"}
    	}
    	sigend := offset
    
    	h.Write(buf[sigstart:sigend])
    	h.Write(buf[:10])
    	h.Write([]byte{
    
    		byte((adc - 1) << 8),
    		byte(adc - 1),
    	})
    
    	h.Write(buf[12:bodyend])
    
    	hashed := h.Sum(nil)
    
    	sig := buf[sigend:]
    	switch k.Algorithm {
    	case RSASHA1, RSASHA256, RSASHA512:
    		pk := k.publicKeyRSA()
    		if pk != nil {
    
    			return rsa.VerifyPKCS1v15(pk, cryptohash, hashed, sig)
    
    		}
    	case ECDSAP256SHA256, ECDSAP384SHA384:
    		pk := k.publicKeyECDSA()
    
    		r := new(big.Int).SetBytes(sig[:len(sig)/2])
    		s := new(big.Int).SetBytes(sig[len(sig)/2:])
    
    		if pk != nil {
    			if ecdsa.Verify(pk, hashed, r, s) {
    				return nil
    			}
    			return ErrSig
    		}
    
    	case ED25519:
    		pk := k.publicKeyED25519()
    		if pk != nil {
    			if ed25519.Verify(pk, hashed, sig) {
    				return nil
    			}
    			return ErrSig
    		}