This adds hash.go and creates a identityHash that is used for algorithms that do their own hashing (ED25519) for instance. This unifies the hash variable naming between dnssec and sig(0) signing and removes the special casing that existed for ED25519. This unifies the variable naming between sig(0) and dnssec signing and verifying. I didn't want to used crypto.RegisterHash as not to fiddle with the global namespaces of hashes, so the value of '0' from AlgorithmsToHash is handled specially in dnssec and sig(0) code. Note that ED448 isn't implemented at all. Signed-off-by: Miek Gieben <miek@miek.nl>
195 lines
4.5 KiB
Go
195 lines
4.5 KiB
Go
package dns
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/rsa"
|
|
"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 {
|
|
return nil, ErrKey
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// Write SIG rdata
|
|
h.Write(buf[len(mbuf)+1+2+2+4+2:])
|
|
// Write message
|
|
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)
|
|
|
|
buf = append(buf, signature...)
|
|
if len(buf) > int(^uint16(0)) {
|
|
return nil, ErrBuf
|
|
}
|
|
// Adjust sig data length
|
|
rdoff := len(mbuf) + 1 + 2 + 2 + 4
|
|
rdlen := binary.BigEndian.Uint16(buf[rdoff:])
|
|
rdlen += uint16(len(signature))
|
|
binary.BigEndian.PutUint16(buf[rdoff:], rdlen)
|
|
// Adjust additional count
|
|
adc := binary.BigEndian.Uint16(buf[10:])
|
|
adc++
|
|
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 {
|
|
return ErrKey
|
|
}
|
|
|
|
h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buflen := len(buf)
|
|
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:])
|
|
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"}
|
|
}
|
|
expire := binary.BigEndian.Uint32(buf[offset:])
|
|
offset += 4
|
|
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
|
|
}
|
|
}
|
|
return ErrKeyAlg
|
|
}
|