diff options
Diffstat (limited to 'pkg/dns')
| -rw-r--r-- | pkg/dns/dns.go | 62 | ||||
| -rw-r--r-- | pkg/dns/dns_test.go | 102 | 
2 files changed, 164 insertions, 0 deletions
| diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go new file mode 100644 index 0000000..3bc0583 --- /dev/null +++ b/pkg/dns/dns.go @@ -0,0 +1,62 @@ +// package dns checks if a domain name is aware of a hashed public key.  A +// look-up is performed if the specified domain name matches "^_sigsum_v0.*". +package dns + +import ( +	"context" +	"fmt" +	"net" +	"strings" + +	"git.sigsum.org/sigsum-lib-go/pkg/hex" +	"git.sigsum.org/sigsum-lib-go/pkg/types" +) + +const ( +	prefix = "_sigsum_v0." +) + +// Verifier can verify that a domain name is aware of a public key +type Verifier interface { +	Verify(ctx context.Context, name string, key *types.PublicKey) error +} + +// DefaultResolver implements the Verifier interface with Go's default resolver +type DefaultResolver struct { +	resolver net.Resolver +} + +func NewDefaultResolver() Verifier { +	return &DefaultResolver{} +} + +func (dr *DefaultResolver) Verify(ctx context.Context, name string, pub *types.PublicKey) error { +	if err := validPrefix(name); err != nil { +		return fmt.Errorf("dns: %s", err) +	} +	rsps, err := dr.resolver.LookupTXT(ctx, name) +	if err != nil { +		return fmt.Errorf("dns: look-up failed: %s", name) +	} +	if err := validResponse(pub, rsps); err != nil { +		return fmt.Errorf("dns: %s", err) +	} +	return nil +} + +func validResponse(pub *types.PublicKey, rsps []string) error { +	keyHash := hex.Serialize(types.HashFn(pub[:])[:]) +	for _, rsp := range rsps { +		if rsp == keyHash { +			return nil +		} +	} +	return fmt.Errorf("unknown key hash %s", keyHash) +} + +func validPrefix(name string) error { +	if !strings.HasPrefix(name, prefix) { +		return fmt.Errorf("domain name prefix must be %s", prefix) +	} +	return nil +} diff --git a/pkg/dns/dns_test.go b/pkg/dns/dns_test.go new file mode 100644 index 0000000..e8e2c01 --- /dev/null +++ b/pkg/dns/dns_test.go @@ -0,0 +1,102 @@ +package dns + +import ( +	"context" +	"log" +	"testing" +	"time" + +	"git.sigsum.org/sigsum-lib-go/pkg/hex" +	"git.sigsum.org/sigsum-lib-go/pkg/types" +) + +func Example() { +	name := "_sigsum_v0.testonly.sigsum.org" +	pub := mustDecodePublicKey("cda2517e17dcba133eb0e71bf77473f94a77d7e61b1de4e1e64adfd0938d6182") + +	timeout := 10 * time.Second +	ctx, cancel := context.WithTimeout(context.Background(), timeout) +	defer cancel() + +	dr := NewDefaultResolver() +	if err := dr.Verify(ctx, name, pub); err != nil { +		log.Fatal(err.Error()) +	} + +	// Output: +} + +func TestValidResponse(t *testing.T) { +	pub := mustDecodePublicKey("cda2517e17dcba133eb0e71bf77473f94a77d7e61b1de4e1e64adfd0938d6182") +	for _, table := range []struct { +		desc   string +		rsps   []string +		wantOK bool +	}{ +		{ +			desc: "invalid: upper-case hex-encoding", +			rsps: []string{ +				"", +				"abc", +				"C522D929B261241EEF174B51B8472FA5D5F961892089A7B85FD25CE73271ABCA", +				"defghi", +			}, +		}, +		{ +			desc: "valid", +			rsps: []string{ +				"", +				"abc", +				"c522d929b261241eef174b51b8472fa5d5f961892089a7b85fd25ce73271abca", +				"defghi", +			}, +			wantOK: true, +		}, +	} { +		err := validResponse(pub, table.rsps) +		if got, want := err == nil, table.wantOK; got != want { +			t.Errorf("got error but wanted none in test %q: %v", table.desc, err) +		} +	} +} + +func TestValidPrefix(t *testing.T) { +	for _, table := range []struct { +		desc   string +		name   string +		wantOK bool +	}{ +		{ +			desc: "invalid: bad prefix (1/2)", +			name: "x_sigsum_v0.sigsum.org", +		}, +		{ +			desc: "invalid: bad prefix (2/2)", +			name: "_sigsum_v0x.sigsum.org", +		}, +		{ +			desc:   "valid", +			name:   "_sigsum_v0.sigsum.org", +			wantOK: true, +		}, +	} { +		err := validPrefix(table.name) +		if got, want := err == nil, table.wantOK; got != want { +			t.Errorf("got error but wanted none in test %q: %v", table.desc, err) +		} +	} +} + +func mustDecodePublicKey(str string) *types.PublicKey { +	b, err := hex.Deserialize(str) +	if err != nil { +		log.Fatal(err.Error()) +	} +	if len(b) != types.PublicKeySize { +		log.Fatal("invalid key size") +	} + +	var pub types.PublicKey +	copy(pub[:], b) +	return &pub +} | 
