Skip to content

Commit b491abb

Browse files
authored
va: Add RFC 8738 test cases (#8073)
Followup to #8020
1 parent b4308df commit b491abb

File tree

3 files changed

+152
-6
lines changed

3 files changed

+152
-6
lines changed

va/http.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,9 @@ func (va *ValidationAuthorityImpl) extractRequestTarget(req *http.Request) (iden
285285
// to pick the port based on the reqScheme default port.
286286
reqHost := req.URL.Hostname()
287287
var reqPort int
288+
// URL.Port() will return "" for an invalid port, not just an empty port. To
289+
// reject invalid ports, we rely on the calling function having used
290+
// URL.Parse(), which does enforce validity.
288291
if req.URL.Port() != "" {
289292
parsedPort, err := strconv.Atoi(req.URL.Port())
290293
if err != nil {

va/http_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,3 +1658,71 @@ func TestLimitedReader(t *testing.T) {
16581658
t.Errorf("Problem Detail contained an invalid UTF-8 string")
16591659
}
16601660
}
1661+
1662+
type hostHeaderHandler struct {
1663+
host string
1664+
}
1665+
1666+
func (handler *hostHeaderHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1667+
handler.host = req.Host
1668+
}
1669+
1670+
// TestHTTPHostHeader tests compliance with RFC 8555, Sec. 8.3 & RFC 8738, Sec.
1671+
// 5.
1672+
func TestHTTPHostHeader(t *testing.T) {
1673+
testCases := []struct {
1674+
Name string
1675+
Ident identifier.ACMEIdentifier
1676+
IPv6 bool
1677+
want string
1678+
}{
1679+
{
1680+
Name: "DNS name",
1681+
Ident: identifier.NewDNS("example.com"),
1682+
want: "example.com",
1683+
},
1684+
{
1685+
Name: "IPv4 address",
1686+
Ident: identifier.NewIP(netip.MustParseAddr("127.0.0.1")),
1687+
want: "127.0.0.1",
1688+
},
1689+
{
1690+
Name: "IPv6 address",
1691+
Ident: identifier.NewIP(netip.MustParseAddr("::1")),
1692+
IPv6: true,
1693+
want: "[::1]",
1694+
},
1695+
}
1696+
1697+
for _, tc := range testCases {
1698+
t.Run(tc.Name, func(t *testing.T) {
1699+
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
1700+
defer cancel()
1701+
1702+
handler := hostHeaderHandler{}
1703+
testSrv := httptest.NewUnstartedServer(&handler)
1704+
1705+
if tc.IPv6 {
1706+
l, err := net.Listen("tcp", "[::1]:0")
1707+
if err != nil {
1708+
panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
1709+
}
1710+
testSrv.Listener = l
1711+
}
1712+
1713+
testSrv.Start()
1714+
defer testSrv.Close()
1715+
1716+
// Setup VA. By providing the testSrv to setup the VA will use the
1717+
// testSrv's randomly assigned port as its HTTP port.
1718+
va, _ := setup(testSrv, "", nil, nil)
1719+
1720+
var got string
1721+
_, _, _ = va.processHTTPValidation(ctx, tc.Ident, "/ok")
1722+
got = handler.host
1723+
if got != tc.want {
1724+
t.Errorf("Got host %#v, but want %#v", got, tc.want)
1725+
}
1726+
})
1727+
}
1728+
}

va/tlsalpn_test.go

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"crypto/x509/pkix"
1212
"encoding/asn1"
1313
"encoding/hex"
14-
"errors"
1514
"fmt"
1615
"math/big"
1716
"net"
@@ -92,11 +91,6 @@ func tlsalpn01SrvWithCert(t *testing.T, acmeCert *tls.Certificate, tlsVersion ui
9291
Certificates: []tls.Certificate{},
9392
ClientAuth: tls.NoClientCert,
9493
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
95-
// This is a backstop test for RFC 8738, Section 6. Go's
96-
// tls.hostnameInSNI already does the right thing.
97-
if net.ParseIP(clientHello.ServerName) != nil {
98-
return nil, errors.New("TLS client used a bare IP address for SNI")
99-
}
10094
return acmeCert, nil
10195
},
10296
NextProtos: []string{"http/1.1", ACMETLS1Protocol},
@@ -865,3 +859,84 @@ func TestTLSALPN01BadIdentifier(t *testing.T) {
865859
prob := detailedError(err)
866860
test.AssertContains(t, prob.Error(), "Identifier type for TLS-ALPN-01 challenge was not DNS or IP")
867861
}
862+
863+
// TestTLSALPN01ServerName tests compliance with RFC 8737, Sec. 3 (step 3) & RFC
864+
// 8738, Sec. 6.
865+
func TestTLSALPN01ServerName(t *testing.T) {
866+
testCases := []struct {
867+
Name string
868+
Ident identifier.ACMEIdentifier
869+
CertNames []string
870+
CertIPs []net.IP
871+
IPv6 bool
872+
want string
873+
}{
874+
{
875+
Name: "DNS name",
876+
Ident: identifier.NewDNS("example.com"),
877+
CertNames: []string{"example.com"},
878+
want: "example.com",
879+
},
880+
{
881+
// RFC 8738, Sec. 6.
882+
Name: "IPv4 address",
883+
Ident: identifier.NewIP(netip.MustParseAddr("127.0.0.1")),
884+
CertIPs: []net.IP{net.ParseIP("127.0.0.1")},
885+
want: "1.0.0.127.in-addr.arpa",
886+
},
887+
{
888+
// RFC 8738, Sec. 6.
889+
Name: "IPv6 address",
890+
Ident: identifier.NewIP(netip.MustParseAddr("::1")),
891+
CertIPs: []net.IP{net.ParseIP("::1")},
892+
IPv6: true,
893+
want: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa",
894+
},
895+
}
896+
897+
for _, tc := range testCases {
898+
t.Run(tc.Name, func(t *testing.T) {
899+
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
900+
defer cancel()
901+
902+
tlsConfig := &tls.Config{
903+
Certificates: []tls.Certificate{},
904+
ClientAuth: tls.NoClientCert,
905+
NextProtos: []string{"http/1.1", ACMETLS1Protocol},
906+
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
907+
got := clientHello.ServerName
908+
if got != tc.want {
909+
return nil, fmt.Errorf("Got host %#v, but want %#v", got, tc.want)
910+
}
911+
return testTLSCert(tc.CertNames, tc.CertIPs, []pkix.Extension{testACMEExt}), nil
912+
},
913+
}
914+
915+
hs := httptest.NewUnstartedServer(http.DefaultServeMux)
916+
hs.TLS = tlsConfig
917+
hs.Config.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){
918+
ACMETLS1Protocol: func(_ *http.Server, conn *tls.Conn, _ http.Handler) {
919+
_ = conn.Close()
920+
},
921+
}
922+
if tc.IPv6 {
923+
l, err := net.Listen("tcp", "[::1]:0")
924+
if err != nil {
925+
panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
926+
}
927+
hs.Listener = l
928+
}
929+
hs.StartTLS()
930+
defer hs.Close()
931+
932+
va, _ := setup(hs, "", nil, nil)
933+
934+
// The actual test happens in the tlsConfig.GetCertificate function,
935+
// which the validation will call and depend on for its success.
936+
_, err := va.validateTLSALPN01(ctx, tc.Ident, expectedKeyAuthorization)
937+
if err != nil {
938+
t.Errorf("Validation failed: %v", err)
939+
}
940+
})
941+
}
942+
}

0 commit comments

Comments
 (0)