acme: add client.DNS01ChallengeRecord

This change adds implementation for dns-01 challenge responses.

Change-Id: I22b2b304ca4a2caeec2980feff19a6c2bc277d8c
Reviewed-on: https://go-review.googlesource.com/27613
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/acme/internal/acme/acme.go b/acme/internal/acme/acme.go
index 9eb35a3..0439f7e 100644
--- a/acme/internal/acme/acme.go
+++ b/acme/internal/acme/acme.go
@@ -471,6 +471,20 @@
 	return v.challenge(), nil
 }
 
+// DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response.
+// A TXT record containing the returned value must be provisioned under
+// "_acme-challenge" name of the domain being validated.
+//
+// The token argument is a Challenge.Token value.
+func (c *Client) DNS01ChallengeRecord(token string) (string, error) {
+	ka, err := keyAuth(c.Key.Public(), token)
+	if err != nil {
+		return "", err
+	}
+	b := sha256.Sum256([]byte(ka))
+	return base64.RawURLEncoding.EncodeToString(b[:]), nil
+}
+
 // HTTP01ChallengeResponse returns the response for an http-01 challenge.
 // Servers should respond with the value to HTTP requests at the URL path
 // provided by HTTP01ChallengePath to validate the challenge and prove control
diff --git a/acme/internal/acme/acme_test.go b/acme/internal/acme/acme_test.go
index 364cd68..2ae69a9 100644
--- a/acme/internal/acme/acme_test.go
+++ b/acme/internal/acme/acme_test.go
@@ -1044,6 +1044,22 @@
 	}
 }
 
+func TestDNS01ChallengeRecord(t *testing.T) {
+	// echo -n xxx.<testKeyECThumbprint> | \
+	//      openssl dgst -binary -sha256 | \
+	//      base64 | tr -d '=' | tr '/+' '_-'
+	const value = "8DERMexQ5VcdJ_prpPiA0mVdp7imgbCgjsG4SqqNMIo"
+
+	client := &Client{Key: testKeyEC}
+	val, err := client.DNS01ChallengeRecord("xxx")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if val != value {
+		t.Errorf("val = %q; want %q", val, value)
+	}
+}
+
 func TestBackoff(t *testing.T) {
 	tt := []struct{ min, max time.Duration }{
 		{time.Second, 2 * time.Second},