SIG0 misc - doc, bounds checking, refactor tests

* expand documentation a little and tweak comments
* add bounds checking in Verify()
* refactor tests
This commit is contained in:
Andrew Tunnell-Jones 2014-11-02 23:20:08 +00:00
parent deb8fe381f
commit 9862d7044a
2 changed files with 46 additions and 49 deletions

43
sig0.go
View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// SIG(0)
// SIG0
//
// From RFC 2931:
//
@ -10,6 +10,10 @@
// ... protection for glue records, DNS requests, protection for message headers
// on requests and responses, and protection of the overall integrity of a response.
//
// Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and
// RSASHA512.
//
// Signing subsequent messages in multi-message sessions is not implemented.
//
package dns
@ -19,7 +23,6 @@ import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"fmt"
"math/big"
"strings"
"time"
@ -163,29 +166,34 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
adc, offset := unpackUint16(buf, 10)
var err error
for i := uint16(0); i < qdc && offset < buflen; i++ {
// decode a name
_, offset, err = UnpackDomainName(buf, offset)
if err != nil {
return err
}
// skip past Type and Class
// Skip past Type and Class
offset += 2 + 2
}
for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
// decode a name
_, offset, err = UnpackDomainName(buf, offset)
if err != nil {
return err
}
// skip past Type, Class and TTL
// Skip past Type, Class and TTL
offset += 2 + 2 + 4
if offset+1 >= buflen {
continue
}
var rdlen uint16
rdlen, offset = unpackUint16(buf, offset)
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
// owner name SHOULD be root
_, offset, err = UnpackDomainName(buf, offset)
if err != nil {
return err
@ -193,20 +201,21 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
// Skip Type, Class, TTL, RDLen
offset += 2 + 2 + 4 + 2
sigstart := offset
offset += 2 + 1 + 1 + 4 // skip Type Covered, Algorithm, Labels, Original TTL
// TODO: This should be moved out and used elsewhere
unpackUint32 := func(buf []byte, off int) (uint32, int) {
r := uint32(buf[off])<<24 | uint32(buf[off+1])<<16 | uint32(buf[off+2])<<8 | uint32(buf[off+3])
return r, off + 4
// Skip Type Covered, Algorithm, Labels, Original TTL
offset += 2 + 1 + 1 + 4
if offset+4+4 >= buflen {
return &Error{err: "overflow unpacking signed message"}
}
var expire, incept uint32
expire, offset = unpackUint32(buf, offset)
incept, offset = unpackUint32(buf, offset)
expire := uint32(buf[offset])<<24 | uint32(buf[offset+1])<<16 | uint32(buf[offset+2])<<8 | uint32(buf[offset+3])
offset += 4
incept := uint32(buf[offset])<<24 | uint32(buf[offset+1])<<16 | uint32(buf[offset+2])<<8 | uint32(buf[offset+3])
offset += 4
now := uint32(time.Now().Unix())
if now < incept || now > expire {
return ErrTime
}
offset += 2 // skip key tag
// Skip key tag
offset += 2
var signername string
signername, offset, err = UnpackDomainName(buf, offset)
if err != nil {
@ -215,7 +224,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
// If key has come from the DNS name compression might
// have mangled the case of the name
if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
return fmt.Errorf("Signer name doesn't match key name")
return &Error{err: "signer name doesn't match key name"}
}
sigend := offset
hasher.Write(buf[sigstart:sigend])

View File

@ -6,51 +6,39 @@ import (
)
func TestSIG0(t *testing.T) {
keys := []struct {
alg uint8
rr *KEY
pk PrivateKey
}{{alg: DSA}, {alg: ECDSAP256SHA256}, {alg: ECDSAP384SHA384}, {alg: RSASHA1}, {alg: RSASHA256}, {alg: RSASHA512}}
for i := range keys {
keys[i].rr = new(KEY)
keys[i].rr.Hdr.Name = AlgorithmToString[keys[i].alg] + "."
keys[i].rr.Hdr.Rrtype = TypeKEY
keys[i].rr.Hdr.Class = ClassINET
keys[i].rr.Algorithm = keys[i].alg
m := new(Msg)
m.SetQuestion("example.org.", TypeSOA)
for _, alg := range []uint8{DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512} {
algstr := AlgorithmToString[alg]
keyrr := new(KEY)
keyrr.Hdr.Name = algstr + "."
keyrr.Hdr.Rrtype = TypeKEY
keyrr.Hdr.Class = ClassINET
keyrr.Algorithm = alg
keysize := 1024
switch keys[i].alg {
switch alg {
case ECDSAP256SHA256:
keysize = 256
case ECDSAP384SHA384:
keysize = 384
}
pk, err := keys[i].rr.Generate(keysize)
pk, err := keyrr.Generate(keysize)
if err != nil {
t.Logf("Failed to generate key for “%s”: %v", AlgorithmToString[keys[i].alg], err)
t.Logf("Failed to generate key for “%s”: %v", algstr, err)
t.Fail()
continue
}
keys[i].pk = pk
}
m := new(Msg)
m.SetQuestion("example.org.", TypeSOA)
for _, key := range keys {
if key.pk == nil {
continue
}
algstr := AlgorithmToString[key.alg]
now := uint32(time.Now().Unix())
sigrr := new(SIG)
sigrr.Hdr.Name = "."
sigrr.Hdr.Rrtype = TypeSIG
sigrr.Hdr.Class = ClassANY
sigrr.Algorithm = key.rr.Algorithm
sigrr.Algorithm = alg
sigrr.Expiration = now + 300
sigrr.Inception = now - 300
sigrr.KeyTag = key.rr.KeyTag()
sigrr.SignerName = key.rr.Hdr.Name
mb, err := sigrr.Sign(key.pk, m)
sigrr.KeyTag = keyrr.KeyTag()
sigrr.SignerName = keyrr.Hdr.Name
mb, err := sigrr.Sign(pk, m)
if err != nil {
t.Logf("Failed to sign message using “%s”: %v", algstr, err)
t.Fail()
@ -81,22 +69,22 @@ func TestSIG0(t *testing.T) {
if rr == sigrrwire {
id = "sigrrwire"
}
if err := rr.Verify(key.rr, mb); err != nil {
if err := rr.Verify(keyrr, mb); err != nil {
t.Logf("Failed to verify “%s” signed SIG(%s): %v", algstr, id, err)
t.Fail()
continue
}
}
mb[13]++
if err := sigrr.Verify(key.rr, mb); err == nil {
if err := sigrr.Verify(keyrr, mb); err == nil {
t.Logf("Verify succeeded on an altered message using “%s”", algstr)
t.Fail()
continue
}
sigrr.Expiration = 2
sigrr.Inception = 1
mb, _ = sigrr.Sign(key.pk, m)
if err := sigrr.Verify(key.rr, mb); err == nil {
mb, _ = sigrr.Sign(pk, m)
if err := sigrr.Verify(keyrr, mb); err == nil {
t.Logf("Verify succeeded on an expired message using “%s”", algstr)
t.Fail()
continue