aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus@mullvad.net>2022-03-26 15:52:05 +0100
committerLinus Nordberg <linus@nordberg.se>2022-03-28 16:33:08 +0200
commit50564595a70f2c2ea0ad4a9b4b96e33bab6b3633 (patch)
tree9adcad91c3d46a305f16191ce66472131db1b2fb
parent93542012e45cfa53964a7160969db30fc36229b2 (diff)
add dns package
Mostly imported from sigsum-log-go. Added strict domain hint parsing, two tests, and an example. This should be part of the sigsum-go lib because some client tooling may also want to check domain hints.
-rw-r--r--pkg/dns/dns.go62
-rw-r--r--pkg/dns/dns_test.go102
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
+}