Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion cmd/pebble/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type config struct {
ExternalAccountBindingRequired bool
ExternalAccountMACKeys map[string]string
CAAIdentities []string
AccountHashPrefix string
// Configure policies to deny certain domains
DomainBlocklist []string
KeyAlgorithm string
Expand Down Expand Up @@ -125,9 +126,18 @@ func main() {
}
}

accountHashPrefix := c.Pebble.AccountHashPrefix
if accountHashPrefix == "" {
logger.Println("No AccountHashPrefix configured, using default [https://pebble.letsencrypt.org/hashed-accountsz/]")
accountHashPrefix = "https://pebble.letsencrypt.org/hashed-accounts/"
}
if !strings.HasSuffix(accountHashPrefix, "/") {
accountHashPrefix = accountHashPrefix + "/" // make it in dir format
}

db := db.NewMemoryStore()
ca := ca.New(logger, db, c.Pebble.OCSPResponderURL, keyAlg, alternateRoots, chainLength, profiles)
va := va.New(logger, c.Pebble.HTTPPort, c.Pebble.TLSPort, *strictMode, *resolverAddress, db)
va := va.New(logger, c.Pebble.HTTPPort, c.Pebble.TLSPort, *strictMode, *resolverAddress, db, accountHashPrefix)

for keyID, key := range c.Pebble.ExternalAccountMACKeys {
err := db.AddExternalAccountKeyByID(keyID, key)
Expand Down
79 changes: 76 additions & 3 deletions va/va.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package va

import (
"context"
"crypto"
"crypto/sha256"
"crypto/subtle"
"crypto/tls"
Expand All @@ -11,6 +12,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"hash"
"io"
"log"
"math/rand"
Expand Down Expand Up @@ -111,6 +113,7 @@ type VAImpl struct {
strict bool
customResolverAddr string
dnsClient *dns.Client
AccountHashPrefix string // exported so wfe can publish it in meta

// The VA having a DB client is indeed strange. This is only used to
// facilitate va.setOrderError changing the ARI related order replacement
Expand All @@ -123,6 +126,7 @@ func New(
httpPort, tlsPort int,
strict bool, customResolverAddr string,
db *db.MemoryStore,
accountHashPrefix string,
) *VAImpl {
va := &VAImpl{
log: log,
Expand All @@ -134,6 +138,7 @@ func New(
strict: strict,
customResolverAddr: customResolverAddr,
db: db,
AccountHashPrefix: accountHashPrefix,
}

if customResolverAddr != "" {
Expand Down Expand Up @@ -702,10 +707,78 @@ func (va VAImpl) validateDNSPersist01(task *vaTask) *core.ValidationRecord {
"Error parsing DNS-PERSIST-01 challenge TXT record with issuer-domain-name %q: missing mandatory accountURI parameter", receivedIssuer))
continue
}
if params.accountURI != task.AccountURL {

if params.accountURI == task.AccountURL {
syntaxErrs = append(syntaxErrs, fmt.Sprintf(
"Error parsing DNS-PERSIST-01 challenge TXT record with issuer-domain-name %q: this record uses raw AcccountURI, Please use hashed format",
receivedIssuer))
continue
}

if !strings.HasPrefix(params.accountURI, va.AccountHashPrefix) {
syntaxErrs = append(syntaxErrs, fmt.Sprintf(
"Error parsing DNS-PERSIST-01 challenge TXT record with issuer-domain-name %q: accountHashPrefix does not match, expexted %s",
receivedIssuer, va.AccountHashPrefix))
continue
}
// calculate account hash format, as defined in draft-ietf-acme-dns-persist-02
temp := strings.Split(strings.TrimPrefix(params.accountURI, va.AccountHashPrefix), "/")
if len(temp) != 2 {
syntaxErrs = append(syntaxErrs, fmt.Sprintf(
"Error parsing DNS-PERSIST-01 challenge TXT record with issuer-domain-name %q: accounthash does not contain hash algorithm.",
receivedIssuer))
continue
}

var hashalg hash.Hash
hashalgstring := temp[0] // hash alg: keep mind IANA have names in lowercase
observedhash := temp[1]

switch hashalgstring { // use a bunch of allowed hash algs
case "sha-256":
hashalg = crypto.SHA256.New()
case "sha-384":
hashalg = crypto.SHA384.New()
case "sha-512":
hashalg = crypto.SHA512.New()
case "sha3-256":
hashalg = crypto.SHA3_256.New()
case "sha3-384":
hashalg = crypto.SHA3_384.New()
case "sha3-512":
hashalg = crypto.SHA3_512.New()
default:
syntaxErrs = append(syntaxErrs, fmt.Sprintf(
"Error parsing DNS-PERSIST-01 challenge TXT record with issuer-domain-name %q: accounthash has unsupported hash algorithm %s.",
receivedIssuer, temp[0]))
continue
}
// building account hash
thumbprint, _ := task.Account.Key.Thumbprint(crypto.SHA256)
key := []byte(base64.RawURLEncoding.EncodeToString(thumbprint))
accountidentsurfix := append(key, []byte(task.AccountURL)...)

globalAccHashIn := []byte{0x1, '*'} // global accounthash, see Section 4.1
globalAccHashIn = append(globalAccHashIn, accountidentsurfix...)
hashalg.Write(globalAccHashIn)
globalAccHashStr := base64.RawURLEncoding.EncodeToString(hashalg.Sum(nil))

hashalg.Reset()
perdomainAcchashIn := []byte{byte(len(task.Identifier.Value))} // per domain accounthash value
perdomainAcchashIn = append(perdomainAcchashIn, []byte(task.Identifier.Value)...)
perdomainAcchashIn = append(perdomainAcchashIn, accountidentsurfix...)
hashalg.Write(perdomainAcchashIn)
perdomainAcchashStr := base64.RawURLEncoding.EncodeToString(hashalg.Sum(nil))

validAccountURIs := []string{globalAccHashStr, perdomainAcchashStr}

if !slices.ContainsFunc(validAccountURIs, func(sttr string) bool {
return subtle.ConstantTimeCompare([]byte(sttr), []byte(observedhash)) == 1
}) {
authorizationErrs = append(authorizationErrs, fmt.Sprintf(
"Error parsing DNS-PERSIST-01 challenge TXT record with issuer-domain-name %q: accounturi mismatch: expected %q, got %q",
receivedIssuer, task.AccountURL, params.accountURI))
"Error parsing DNS-PERSIST-01 challenge TXT record with issuer-domain-name %q: "+
"accounthash mismatch: expected %s for domain %s, %s for domain-correlation opt-out, got %q",
receivedIssuer, validAccountURIs[0], task.Identifier.Value, validAccountURIs[1], observedhash))
continue
}
// Per the dns-persist-01 specification, if the policy tag is present
Expand Down
Loading
Loading