diff --git a/ca.go b/ca.go index 381f9d5..9c8e799 100644 --- a/ca.go +++ b/ca.go @@ -85,10 +85,7 @@ func caMustNewAuthority(name, organization string, validity time.Duration, // // SPDX-License-Identifier: Apache-2.0. type CA struct { - // CACert is the public certificate used by the CA. - CACert *x509.Certificate - - // These fields are not exported + caCert *x509.Certificate capriv any keyID []byte org string @@ -101,6 +98,8 @@ func MustNewCA() *CA { return MustNewCAWithTimeNow(time.Now) } +var _ CertificationAuthority = &CA{} + // MustNewCA is like [NewCA] but uses a custom [time.Now] func. // // This code is derived from github.com/google/martian/v3. @@ -123,7 +122,7 @@ func MustNewCAWithTimeNow(timeNow func() time.Time) *CA { keyID := h.Sum(nil) return &CA{ - CACert: ca, + caCert: ca, capriv: privateKey, priv: priv, keyID: keyID, @@ -132,11 +131,23 @@ func MustNewCAWithTimeNow(timeNow func() time.Time) *CA { } } -// CertPool returns an [x509.CertPool] using the given [*CA]. -func (c *CA) CertPool() *x509.CertPool { - pool := x509.NewCertPool() - pool.AddCert(c.CACert) - return pool +// CACert implements [CertificationAuthority]. +func (ca *CA) CACert() *x509.Certificate { + return ca.caCert +} + +// DefaultCertPool implements [CertificationAuthority]. +func (c *CA) DefaultCertPool() *x509.CertPool { + p := x509.NewCertPool() + p.AddCert(c.caCert) + return p +} + +// MustNewServerTLSConfig implements [CertificationAuthority]. +func (ca *CA) MustNewServerTLSConfig(commonName string, extraNames ...string) *tls.Config { + return &tls.Config{ + Certificates: []tls.Certificate{*ca.MustNewCert(commonName, extraNames...)}, + } } // MustNewCert creates a new certificate for the given common name or PANICS. @@ -188,26 +199,16 @@ func (c *CA) MustNewCertWithTimeNow(timeNow func() time.Time, commonName string, } } - raw := Must1(x509.CreateCertificate(rand.Reader, tmpl, c.CACert, c.priv.Public(), c.capriv)) + raw := Must1(x509.CreateCertificate(rand.Reader, tmpl, c.caCert, c.priv.Public(), c.capriv)) // Parse certificate bytes so that we have a leaf certificate. x509c := Must1(x509.ParseCertificate(raw)) tlsc := &tls.Certificate{ - Certificate: [][]byte{raw, c.CACert.Raw}, + Certificate: [][]byte{raw, c.caCert.Raw}, PrivateKey: c.priv, Leaf: x509c, } return tlsc } - -// MustServerTLSConfig generates a server-side [*tls.Config] that uses the given [*CA] and -// a generated certificate for the given common name and extra names. -// -// See [CA.MustNewCert] documentation for more details about what common name and extra names should be. -func (ca *CA) MustServerTLSConfig(commonName string, extraNames ...string) *tls.Config { - return &tls.Config{ - Certificates: []tls.Certificate{*ca.MustNewCert(commonName, extraNames...)}, - } -} diff --git a/ca_test.go b/ca_test.go index ac7fcac..0222441 100644 --- a/ca_test.go +++ b/ca_test.go @@ -112,7 +112,7 @@ func TestCAWeCanGenerateAnExpiredCertificate(t *testing.T) { Handler: http.NewServeMux(), TLSConfig: &tls.Config{ Certificates: []tls.Certificate{ - *serverStack.CA().MustNewCertWithTimeNow(func() time.Time { + *serverStack.ca.MustNewCertWithTimeNow(func() time.Time { return time.Date(2017, time.July, 17, 0, 0, 0, 0, time.UTC) }, "www.example.com", diff --git a/example_star_test.go b/example_star_test.go index 0fd3062..11e309a 100644 --- a/example_star_test.go +++ b/example_star_test.go @@ -77,7 +77,7 @@ func Example_starTopologyHTTPSAndDNS() { } httpsServer := &http.Server{ Handler: mux, - TLSConfig: httpsServerStack.CA().MustServerTLSConfig("tyrell.wellick.name"), + TLSConfig: httpsServerStack.MustNewServerTLSConfig("tyrell.wellick.name"), } go httpsServer.ServeTLS(httpsListener, "", "") // empty string: use .TLSConfig defer httpsServer.Close() diff --git a/integration_test.go b/integration_test.go index 261d3ea..f9d7db8 100644 --- a/integration_test.go +++ b/integration_test.go @@ -308,7 +308,7 @@ func TestRoutingWorksHTTPS(t *testing.T) { t.Fatal(err) } httpServer := &http.Server{ - TLSConfig: serverStack.CA().MustServerTLSConfig("example.local", "10.0.0.1"), + TLSConfig: serverStack.MustNewServerTLSConfig("example.local", "10.0.0.1"), Handler: mux, } go httpServer.ServeTLS(listener, "", "") // empty strings mean: use TLSConfig diff --git a/model.go b/model.go index 9b67b8b..cc0283c 100644 --- a/model.go +++ b/model.go @@ -6,6 +6,7 @@ package netem import ( "context" + "crypto/tls" "crypto/x509" "net" "syscall" @@ -23,6 +24,21 @@ const ( FrameFlagDrop ) +// CertificationAuthority is a TLS certification authority. +type CertificationAuthority interface { + // CACert returns the CA certificate used by the server, which + // allows you to add to an existing [*x509.CertPool]. + CACert() *x509.Certificate + + // DefaultCertPool returns the default cert pool to use. + DefaultCertPool() *x509.CertPool + + // MustNewServerTLSConfig constructs a server certificate for + // the given common name and extra names, all of which could be + // either IPv4/IPv6 addresses or domain names. + MustNewServerTLSConfig(commonName string, extraNames ...string) *tls.Config +} + // Frame contains an IPv4 or IPv6 packet. type Frame struct { // Deadline is the time when this frame should be delivered. @@ -154,14 +170,9 @@ type UDPLikeConn interface { // UnderlyingNetwork replaces for functions in the [net] package. type UnderlyingNetwork interface { - // CA returns the CA we're using. - CA() *CA - - // CACert returns the CA cert we're using. - CACert() *x509.Certificate - - // DefaultCertPool returns the underlying cert pool to be used. - DefaultCertPool() *x509.CertPool + // CertificationAuthority allows accessing the certification authority + // associated with this host or set of hosts. + CertificationAuthority // DialContext dials a TCP or UDP connection. Unlike [net.DialContext], this // function does not implement dialing when address contains a domain. diff --git a/ndt0.go b/ndt0.go index 180acc8..a8f6205 100644 --- a/ndt0.go +++ b/ndt0.go @@ -249,7 +249,7 @@ func RunNDT0Server( } // generate a config for the given SNI and for the given IP addr - tlsConfig := stack.CA().MustServerTLSConfig(serverIPAddr.String(), serverNames...) + tlsConfig := stack.MustNewServerTLSConfig(serverIPAddr.String(), serverNames...) // conditionally use TLS ns := &Net{stack} diff --git a/unetstack.go b/unetstack.go index 464133e..b6c238c 100644 --- a/unetstack.go +++ b/unetstack.go @@ -45,9 +45,10 @@ type UNetStack struct { } var ( - _ HTTPUnderlyingNetwork = &UNetStack{} - _ NIC = &UNetStack{} - _ UnderlyingNetwork = &UNetStack{} + _ CertificationAuthority = &UNetStack{} + _ HTTPUnderlyingNetwork = &UNetStack{} + _ NIC = &UNetStack{} + _ UnderlyingNetwork = &UNetStack{} ) // NewUNetStack constructs a new [UNetStack] instance. @@ -104,20 +105,19 @@ func NewUNetStack( return stack, nil } -// CA implements UnderlyingNetwork. -func (gs *UNetStack) CA() *CA { - return gs.ca +// CACert implements CertificationAuthority. +func (gs *UNetStack) CACert() *x509.Certificate { + return gs.ca.CACert() } -// CACert implements UnderlyingNetwork. -func (gs *UNetStack) CACert() *x509.Certificate { - return gs.ca.CACert +// DefaultCertPool implements CertificationAuthority. +func (gs *UNetStack) DefaultCertPool() *x509.CertPool { + return gs.ca.DefaultCertPool() } -// MustServerTLSConfig is used by [github.com/ooni/probe-cli] code -// when generating configuration for servers using TLS. -func (gs *UNetStack) MustServerTLSConfig(commonName string, extraNames ...string) *tls.Config { - return gs.ca.MustServerTLSConfig(commonName, extraNames...) +// MustNewServerTLSConfig implements CertificationAuthority. +func (gs *UNetStack) MustNewServerTLSConfig(commonName string, extraNames ...string) *tls.Config { + return gs.ca.MustNewServerTLSConfig(commonName, extraNames...) } // Logger implements HTTPUnderlyingNetwork. @@ -160,11 +160,6 @@ func (gs *UNetStack) Close() error { return gs.ns.Close() } -// DefaultCertPool implements UnderlyingNetwork. -func (gs *UNetStack) DefaultCertPool() *x509.CertPool { - return gs.ca.CertPool() -} - // DialContext implements UnderlyingNetwork. func (gs *UNetStack) DialContext( ctx context.Context, network string, address string) (net.Conn, error) {