parent
23c4faca9d
commit
59aea23afe
26
client.go
26
client.go
|
@ -23,6 +23,7 @@ type Conn struct {
|
||||||
net.Conn // a net.Conn holding the connection
|
net.Conn // a net.Conn holding the connection
|
||||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
UDPSize uint16 // minimum receive buffer for UDP messages
|
||||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||||
|
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
|
||||||
tsigRequestMAC string
|
tsigRequestMAC string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@ type Client struct {
|
||||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||||
|
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
|
||||||
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
|
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
|
||||||
group singleflight
|
group singleflight
|
||||||
}
|
}
|
||||||
|
@ -175,7 +177,7 @@ func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err erro
|
||||||
co.UDPSize = c.UDPSize
|
co.UDPSize = c.UDPSize
|
||||||
}
|
}
|
||||||
|
|
||||||
co.TsigSecret = c.TsigSecret
|
co.TsigSecret, co.TsigProvider = c.TsigSecret, c.TsigProvider
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
// write with the appropriate write timeout
|
// write with the appropriate write timeout
|
||||||
co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
|
co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
|
||||||
|
@ -222,11 +224,15 @@ func (co *Conn) ReadMsg() (*Msg, error) {
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
if t := m.IsTsig(); t != nil {
|
if t := m.IsTsig(); t != nil {
|
||||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
if co.TsigProvider != nil {
|
||||||
return m, ErrSecret
|
err = tsigVerifyProvider(p, co.TsigProvider, co.tsigRequestMAC, false)
|
||||||
|
} else {
|
||||||
|
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||||
|
return m, ErrSecret
|
||||||
|
}
|
||||||
|
// Need to work on the original message p, as that was used to calculate the tsig.
|
||||||
|
err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
||||||
}
|
}
|
||||||
// Need to work on the original message p, as that was used to calculate the tsig.
|
|
||||||
err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
|
||||||
}
|
}
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
@ -304,10 +310,14 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
|
||||||
var out []byte
|
var out []byte
|
||||||
if t := m.IsTsig(); t != nil {
|
if t := m.IsTsig(); t != nil {
|
||||||
mac := ""
|
mac := ""
|
||||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
if co.TsigProvider != nil {
|
||||||
return ErrSecret
|
out, mac, err = tsigGenerateProvider(m, co.TsigProvider, co.tsigRequestMAC, false)
|
||||||
|
} else {
|
||||||
|
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||||
|
return ErrSecret
|
||||||
|
}
|
||||||
|
out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
||||||
}
|
}
|
||||||
out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
|
||||||
// Set for the next read, although only used in zone transfers
|
// Set for the next read, although only used in zone transfers
|
||||||
co.tsigRequestMAC = mac
|
co.tsigRequestMAC = mac
|
||||||
} else {
|
} else {
|
||||||
|
|
24
doc.go
24
doc.go
|
@ -194,6 +194,30 @@ request an AXFR for miek.nl. with TSIG key named "axfr." and secret
|
||||||
You can now read the records from the transfer as they come in. Each envelope
|
You can now read the records from the transfer as they come in. Each envelope
|
||||||
is checked with TSIG. If something is not correct an error is returned.
|
is checked with TSIG. If something is not correct an error is returned.
|
||||||
|
|
||||||
|
A custom TSIG implementation can be used. This requires additional code to
|
||||||
|
perform any session establishment and signature generation/verification. The
|
||||||
|
client must be configured with an implementation of the TsigProvider interface:
|
||||||
|
|
||||||
|
type Provider struct{}
|
||||||
|
|
||||||
|
func (*Provider) Generate(msg []byte, tsig *dns.TSIG) ([]byte, error) {
|
||||||
|
// Use tsig.Hdr.Name and tsig.Algorithm in your code to
|
||||||
|
// generate the MAC using msg as the payload.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Provider) Verify(msg []byte, tsig *dns.TSIG) error {
|
||||||
|
// Use tsig.Hdr.Name and tsig.Algorithm in your code to verify
|
||||||
|
// that msg matches the value in tsig.MAC.
|
||||||
|
}
|
||||||
|
|
||||||
|
c := new(dns.Client)
|
||||||
|
c.TsigProvider = new(Provider)
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion("miek.nl.", dns.TypeMX)
|
||||||
|
m.SetTsig(keyname, dns.HmacSHA1, 300, time.Now().Unix())
|
||||||
|
...
|
||||||
|
// TSIG RR is calculated by calling your Generate method
|
||||||
|
|
||||||
Basic use pattern validating and replying to a message that has TSIG set.
|
Basic use pattern validating and replying to a message that has TSIG set.
|
||||||
|
|
||||||
server := &dns.Server{Addr: ":53", Net: "udp"}
|
server := &dns.Server{Addr: ":53", Net: "udp"}
|
||||||
|
|
118
tsig.go
118
tsig.go
|
@ -24,6 +24,56 @@ const (
|
||||||
HmacMD5 = "hmac-md5.sig-alg.reg.int." // Deprecated: HmacMD5 is no longer supported.
|
HmacMD5 = "hmac-md5.sig-alg.reg.int." // Deprecated: HmacMD5 is no longer supported.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TsigProvider provides the API to plug-in a custom TSIG implementation.
|
||||||
|
type TsigProvider interface {
|
||||||
|
// Generate is passed the DNS message to be signed and the partial TSIG RR. It returns the signature and nil, otherwise an error.
|
||||||
|
Generate(msg []byte, t *TSIG) ([]byte, error)
|
||||||
|
// Verify is passed the DNS message to be verified and the TSIG RR. If the signature is valid it will return nil, otherwise an error.
|
||||||
|
Verify(msg []byte, t *TSIG) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type tsigHMACProvider string
|
||||||
|
|
||||||
|
func (key tsigHMACProvider) Generate(msg []byte, t *TSIG) ([]byte, error) {
|
||||||
|
// If we barf here, the caller is to blame
|
||||||
|
rawsecret, err := fromBase64([]byte(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var h hash.Hash
|
||||||
|
switch CanonicalName(t.Algorithm) {
|
||||||
|
case HmacSHA1:
|
||||||
|
h = hmac.New(sha1.New, rawsecret)
|
||||||
|
case HmacSHA224:
|
||||||
|
h = hmac.New(sha256.New224, rawsecret)
|
||||||
|
case HmacSHA256:
|
||||||
|
h = hmac.New(sha256.New, rawsecret)
|
||||||
|
case HmacSHA384:
|
||||||
|
h = hmac.New(sha512.New384, rawsecret)
|
||||||
|
case HmacSHA512:
|
||||||
|
h = hmac.New(sha512.New, rawsecret)
|
||||||
|
default:
|
||||||
|
return nil, ErrKeyAlg
|
||||||
|
}
|
||||||
|
h.Write(msg)
|
||||||
|
return h.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key tsigHMACProvider) Verify(msg []byte, t *TSIG) error {
|
||||||
|
b, err := key.Generate(msg, t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mac, err := hex.DecodeString(t.MAC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !hmac.Equal(b, mac) {
|
||||||
|
return ErrSig
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TSIG is the RR the holds the transaction signature of a message.
|
// TSIG is the RR the holds the transaction signature of a message.
|
||||||
// See RFC 2845 and RFC 4635.
|
// See RFC 2845 and RFC 4635.
|
||||||
type TSIG struct {
|
type TSIG struct {
|
||||||
|
@ -98,14 +148,13 @@ type timerWireFmt struct {
|
||||||
// timersOnly is false.
|
// timersOnly is false.
|
||||||
// If something goes wrong an error is returned, otherwise it is nil.
|
// If something goes wrong an error is returned, otherwise it is nil.
|
||||||
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
|
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
|
||||||
|
return tsigGenerateProvider(m, tsigHMACProvider(secret), requestMAC, timersOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tsigGenerateProvider(m *Msg, provider TsigProvider, requestMAC string, timersOnly bool) ([]byte, string, error) {
|
||||||
if m.IsTsig() == nil {
|
if m.IsTsig() == nil {
|
||||||
panic("dns: TSIG not last RR in additional")
|
panic("dns: TSIG not last RR in additional")
|
||||||
}
|
}
|
||||||
// If we barf here, the caller is to blame
|
|
||||||
rawsecret, err := fromBase64([]byte(secret))
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
rr := m.Extra[len(m.Extra)-1].(*TSIG)
|
rr := m.Extra[len(m.Extra)-1].(*TSIG)
|
||||||
m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
|
m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
|
||||||
|
@ -119,25 +168,13 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
|
||||||
}
|
}
|
||||||
|
|
||||||
t := new(TSIG)
|
t := new(TSIG)
|
||||||
var h hash.Hash
|
|
||||||
switch CanonicalName(rr.Algorithm) {
|
|
||||||
case HmacSHA1:
|
|
||||||
h = hmac.New(sha1.New, rawsecret)
|
|
||||||
case HmacSHA224:
|
|
||||||
h = hmac.New(sha256.New224, rawsecret)
|
|
||||||
case HmacSHA256:
|
|
||||||
h = hmac.New(sha256.New, rawsecret)
|
|
||||||
case HmacSHA384:
|
|
||||||
h = hmac.New(sha512.New384, rawsecret)
|
|
||||||
case HmacSHA512:
|
|
||||||
h = hmac.New(sha512.New, rawsecret)
|
|
||||||
default:
|
|
||||||
return nil, "", ErrKeyAlg
|
|
||||||
}
|
|
||||||
h.Write(buf)
|
|
||||||
// Copy all TSIG fields except MAC and its size, which are filled using the computed digest.
|
// Copy all TSIG fields except MAC and its size, which are filled using the computed digest.
|
||||||
*t = *rr
|
*t = *rr
|
||||||
t.MAC = hex.EncodeToString(h.Sum(nil))
|
mac, err := provider.Generate(buf, rr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
t.MAC = hex.EncodeToString(mac)
|
||||||
t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
|
t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
|
||||||
|
|
||||||
tbuf := make([]byte, Len(t))
|
tbuf := make([]byte, Len(t))
|
||||||
|
@ -156,49 +193,28 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
|
||||||
// If the signature does not validate err contains the
|
// If the signature does not validate err contains the
|
||||||
// error, otherwise it is nil.
|
// error, otherwise it is nil.
|
||||||
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
|
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
|
||||||
return tsigVerify(msg, secret, requestMAC, timersOnly, uint64(time.Now().Unix()))
|
return tsigVerify(msg, tsigHMACProvider(secret), requestMAC, timersOnly, uint64(time.Now().Unix()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func tsigVerifyProvider(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool) error {
|
||||||
|
return tsigVerify(msg, provider, requestMAC, timersOnly, uint64(time.Now().Unix()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual implementation of TsigVerify, taking the current time ('now') as a parameter for the convenience of tests.
|
// actual implementation of TsigVerify, taking the current time ('now') as a parameter for the convenience of tests.
|
||||||
func tsigVerify(msg []byte, secret, requestMAC string, timersOnly bool, now uint64) error {
|
func tsigVerify(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool, now uint64) error {
|
||||||
rawsecret, err := fromBase64([]byte(secret))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Strip the TSIG from the incoming msg
|
// Strip the TSIG from the incoming msg
|
||||||
stripped, tsig, err := stripTsig(msg)
|
stripped, tsig, err := stripTsig(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
msgMAC, err := hex.DecodeString(tsig.MAC)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
|
buf, err := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var h hash.Hash
|
if err := provider.Verify(buf, tsig); err != nil {
|
||||||
switch CanonicalName(tsig.Algorithm) {
|
return err
|
||||||
case HmacSHA1:
|
|
||||||
h = hmac.New(sha1.New, rawsecret)
|
|
||||||
case HmacSHA224:
|
|
||||||
h = hmac.New(sha256.New224, rawsecret)
|
|
||||||
case HmacSHA256:
|
|
||||||
h = hmac.New(sha256.New, rawsecret)
|
|
||||||
case HmacSHA384:
|
|
||||||
h = hmac.New(sha512.New384, rawsecret)
|
|
||||||
case HmacSHA512:
|
|
||||||
h = hmac.New(sha512.New, rawsecret)
|
|
||||||
default:
|
|
||||||
return ErrKeyAlg
|
|
||||||
}
|
|
||||||
h.Write(buf)
|
|
||||||
if !hmac.Equal(h.Sum(nil), msgMAC) {
|
|
||||||
return ErrSig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fudge factor works both ways. A message can arrive before it was signed because
|
// Fudge factor works both ways. A message can arrive before it was signed because
|
||||||
|
|
128
tsig_test.go
128
tsig_test.go
|
@ -3,6 +3,7 @@ package dns
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -79,23 +80,23 @@ func TestTsigErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the signature is valid but 'time signed' is too far from the "current time".
|
// the signature is valid but 'time signed' is too far from the "current time".
|
||||||
if err := tsigVerify(buildMsgData(timeSigned), testSecret, "", false, timeSigned+301); err != ErrTime {
|
if err := tsigVerify(buildMsgData(timeSigned), tsigHMACProvider(testSecret), "", false, timeSigned+301); err != ErrTime {
|
||||||
t.Fatalf("expected an error '%v' but got '%v'", ErrTime, err)
|
t.Fatalf("expected an error '%v' but got '%v'", ErrTime, err)
|
||||||
}
|
}
|
||||||
if err := tsigVerify(buildMsgData(timeSigned), testSecret, "", false, timeSigned-301); err != ErrTime {
|
if err := tsigVerify(buildMsgData(timeSigned), tsigHMACProvider(testSecret), "", false, timeSigned-301); err != ErrTime {
|
||||||
t.Fatalf("expected an error '%v' but got '%v'", ErrTime, err)
|
t.Fatalf("expected an error '%v' but got '%v'", ErrTime, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// the signature is invalid and 'time signed' is too far.
|
// the signature is invalid and 'time signed' is too far.
|
||||||
// the signature should be checked first, so we should see ErrSig.
|
// the signature should be checked first, so we should see ErrSig.
|
||||||
if err := tsigVerify(buildMsgData(timeSigned+301), testSecret, "", false, timeSigned); err != ErrSig {
|
if err := tsigVerify(buildMsgData(timeSigned+301), tsigHMACProvider(testSecret), "", false, timeSigned); err != ErrSig {
|
||||||
t.Fatalf("expected an error '%v' but got '%v'", ErrSig, err)
|
t.Fatalf("expected an error '%v' but got '%v'", ErrSig, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tweak the algorithm name in the wire data, resulting in the "unknown algorithm" error.
|
// tweak the algorithm name in the wire data, resulting in the "unknown algorithm" error.
|
||||||
msgData := buildMsgData(timeSigned)
|
msgData := buildMsgData(timeSigned)
|
||||||
copy(msgData[67:], "bogus")
|
copy(msgData[67:], "bogus")
|
||||||
if err := tsigVerify(msgData, testSecret, "", false, timeSigned); err != ErrKeyAlg {
|
if err := tsigVerify(msgData, tsigHMACProvider(testSecret), "", false, timeSigned); err != ErrKeyAlg {
|
||||||
t.Fatalf("expected an error '%v' but got '%v'", ErrKeyAlg, err)
|
t.Fatalf("expected an error '%v' but got '%v'", ErrKeyAlg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ func TestTsigErrors(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := tsigVerify(msgData, testSecret, "", false, timeSigned); err != ErrNoSig {
|
if err := tsigVerify(msgData, tsigHMACProvider(testSecret), "", false, timeSigned); err != ErrNoSig {
|
||||||
t.Fatalf("expected an error '%v' but got '%v'", ErrNoSig, err)
|
t.Fatalf("expected an error '%v' but got '%v'", ErrNoSig, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ func TestTsigErrors(t *testing.T) {
|
||||||
if msgData, err = msg.Pack(); err != nil {
|
if msgData, err = msg.Pack(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = tsigVerify(msgData, testSecret, "", false, timeSigned)
|
err = tsigVerify(msgData, tsigHMACProvider(testSecret), "", false, timeSigned)
|
||||||
if err == nil || !strings.Contains(err.Error(), "overflow") {
|
if err == nil || !strings.Contains(err.Error(), "overflow") {
|
||||||
t.Errorf("expected error to contain %q, but got %v", "overflow", err)
|
t.Errorf("expected error to contain %q, but got %v", "overflow", err)
|
||||||
}
|
}
|
||||||
|
@ -231,9 +232,122 @@ func TestTSIGHMAC224And384(t *testing.T) {
|
||||||
if mac != tc.expectedMAC {
|
if mac != tc.expectedMAC {
|
||||||
t.Fatalf("MAC doesn't match: expected '%s' but got '%s'", tc.expectedMAC, mac)
|
t.Fatalf("MAC doesn't match: expected '%s' but got '%s'", tc.expectedMAC, mac)
|
||||||
}
|
}
|
||||||
if err = tsigVerify(msgData, tc.secret, "", false, timeSigned); err != nil {
|
if err = tsigVerify(msgData, tsigHMACProvider(tc.secret), "", false, timeSigned); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const testGoodKeyName = "goodkey."
|
||||||
|
|
||||||
|
var (
|
||||||
|
testErrBadKey = errors.New("this is an intentional error")
|
||||||
|
testGoodMAC = []byte{0, 1, 2, 3}
|
||||||
|
)
|
||||||
|
|
||||||
|
// testProvider always generates the same MAC and only accepts the one signature
|
||||||
|
type testProvider struct {
|
||||||
|
GenerateAllKeys bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *testProvider) Generate(_ []byte, t *TSIG) ([]byte, error) {
|
||||||
|
if t.Hdr.Name == testGoodKeyName || provider.GenerateAllKeys {
|
||||||
|
return testGoodMAC, nil
|
||||||
|
}
|
||||||
|
return nil, testErrBadKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*testProvider) Verify(_ []byte, t *TSIG) error {
|
||||||
|
if t.Hdr.Name == testGoodKeyName {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return testErrBadKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTsigGenerateProvider(t *testing.T) {
|
||||||
|
tables := []struct {
|
||||||
|
keyname string
|
||||||
|
mac []byte
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testGoodKeyName,
|
||||||
|
testGoodMAC,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"badkey.",
|
||||||
|
nil,
|
||||||
|
testErrBadKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
t.Run(table.keyname, func(t *testing.T) {
|
||||||
|
tsig := TSIG{
|
||||||
|
Hdr: RR_Header{Name: table.keyname, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0},
|
||||||
|
Algorithm: HmacSHA1,
|
||||||
|
TimeSigned: timeSigned,
|
||||||
|
Fudge: 300,
|
||||||
|
OrigId: 42,
|
||||||
|
}
|
||||||
|
req := &Msg{
|
||||||
|
MsgHdr: MsgHdr{Opcode: OpcodeUpdate},
|
||||||
|
Question: []Question{Question{Name: "example.com.", Qtype: TypeSOA, Qclass: ClassINET}},
|
||||||
|
Extra: []RR{&tsig},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, mac, err := tsigGenerateProvider(req, new(testProvider), "", false)
|
||||||
|
if err != table.err {
|
||||||
|
t.Fatalf("error doesn't match: expected '%s' but got '%s'", table.err, err)
|
||||||
|
}
|
||||||
|
expectedMAC := hex.EncodeToString(table.mac)
|
||||||
|
if mac != expectedMAC {
|
||||||
|
t.Fatalf("MAC doesn't match: expected '%s' but got '%s'", table.mac, expectedMAC)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTsigVerifyProvider(t *testing.T) {
|
||||||
|
tables := []struct {
|
||||||
|
keyname string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testGoodKeyName,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"badkey.",
|
||||||
|
testErrBadKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
t.Run(table.keyname, func(t *testing.T) {
|
||||||
|
tsig := TSIG{
|
||||||
|
Hdr: RR_Header{Name: table.keyname, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0},
|
||||||
|
Algorithm: HmacSHA1,
|
||||||
|
TimeSigned: timeSigned,
|
||||||
|
Fudge: 300,
|
||||||
|
OrigId: 42,
|
||||||
|
}
|
||||||
|
req := &Msg{
|
||||||
|
MsgHdr: MsgHdr{Opcode: OpcodeUpdate},
|
||||||
|
Question: []Question{Question{Name: "example.com.", Qtype: TypeSOA, Qclass: ClassINET}},
|
||||||
|
Extra: []RR{&tsig},
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := &testProvider{true}
|
||||||
|
msgData, _, err := tsigGenerateProvider(req, provider, "", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if err = tsigVerify(msgData, provider, "", false, timeSigned); err != table.err {
|
||||||
|
t.Fatalf("error doesn't match: expected '%s' but got '%s'", table.err, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue