Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fork google/martian/v3/mitm to build expired certs #37

Merged
merged 1 commit into from
Sep 6, 2023

Conversation

bassosimone
Copy link
Contributor

To move forward with ooni/probe#1803, I want to generate expired certificates. To this end, I have forked the mitm package to peek into internals and further customize certificate generation.

I forked at google/martian@19163e1.

The diff between the original code and my code is the following:

--- mitmx/mitm.go.orig	2023-09-06 10:42:30
+++ mitmx/mitmx.go	2023-09-06 10:46:28
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.

-// Package mitm provides tooling for MITMing TLS connections. It provides
+// Package mitmx provides tooling for MITMing TLS connections. It provides
 // tooling to create CA certs and generate TLS configs that can be used to MITM
 // a TLS connection with a provided CA certificate.
-package mitm
+//
+// This package is a fork of the mitm package in github.com/google/martian/v3.
+package mitmx

 import (
 	"bytes"
@@ -51,7 +53,6 @@
 	validity               time.Duration
 	org                    string
 	h2Config               *h2.Config
-	getCertificate         func(*tls.ClientHelloInfo) (*tls.Certificate, error)
 	roots                  *x509.CertPool
 	skipVerify             bool
 	handshakeErrorCallback func(*http.Request, error)
@@ -63,6 +64,12 @@
 // NewAuthority creates a new CA certificate and associated
 // private key.
 func NewAuthority(name, organization string, validity time.Duration) (*x509.Certificate, *rsa.PrivateKey, error) {
+	return NewAuthorityWithTimeNow(name, organization, validity, time.Now)
+}
+
+// NewAuthorityWithTimeNow is like NewAuthority but allows to customize the time.Now func
+func NewAuthorityWithTimeNow(
+	name, organization string, validity time.Duration, timeNow func() time.Time) (*x509.Certificate, *rsa.PrivateKey, error) {
 	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 	if err != nil {
 		return nil, nil, err
@@ -96,8 +103,8 @@
 		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
 		BasicConstraintsValid: true,
-		NotBefore:             time.Now().Add(-validity),
-		NotAfter:              time.Now().Add(validity),
+		NotBefore:             timeNow().Add(-validity),
+		NotAfter:              timeNow().Add(validity),
 		DNSNames:              []string{name},
 		IsCA:                  true,
 	}
@@ -260,7 +267,22 @@
 	}

 	log.Debugf("mitm: cache miss for %s", hostname)
+
+	tlsc, err = c.NewCertWithoutCacheWithTimeNow(hostname, time.Now)
+	if err != nil {
+		return nil, err
+	}

+	c.certmu.Lock()
+	c.certs[hostname] = tlsc
+	c.certmu.Unlock()
+
+	return tlsc, nil
+}
+
+// NewCertWithoutCacheWithTimeNow is the most fundamental building block for building a certificate
+// that completely bypasses caching and allows for setting a custom time.Now func.
+func (c *Config) NewCertWithoutCacheWithTimeNow(hostname string, timeNow func() time.Time) (*tls.Certificate, error) {
 	serial, err := rand.Int(rand.Reader, MaxSerialNumber)
 	if err != nil {
 		return nil, err
@@ -276,8 +298,8 @@
 		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
 		BasicConstraintsValid: true,
-		NotBefore:             time.Now().Add(-c.validity),
-		NotAfter:              time.Now().Add(c.validity),
+		NotBefore:             timeNow().Add(-c.validity),
+		NotAfter:              timeNow().Add(c.validity),
 	}

 	if ip := net.ParseIP(hostname); ip != nil {
@@ -297,15 +319,11 @@
 		return nil, err
 	}

-	tlsc = &tls.Certificate{
+	tlsc := &tls.Certificate{
 		Certificate: [][]byte{raw, c.ca.Raw},
 		PrivateKey:  c.priv,
 		Leaf:        x509c,
 	}

-	c.certmu.Lock()
-	c.certs[hostname] = tlsc
-	c.certmu.Unlock()
-
 	return tlsc, nil
 }

To move forward with ooni/probe#1803, I want to
generate expired certificates. To this end, I have forked the mitm package
to peek into internals and further customize certificate generation.

I forked at google/martian@19163e1.

The diff between the original code and my code is the following:

```diff
--- mitmx/mitm.go.orig	2023-09-06 10:42:30
+++ mitmx/mitmx.go	2023-09-06 10:46:28
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.

-// Package mitm provides tooling for MITMing TLS connections. It provides
+// Package mitmx provides tooling for MITMing TLS connections. It provides
 // tooling to create CA certs and generate TLS configs that can be used to MITM
 // a TLS connection with a provided CA certificate.
-package mitm
+//
+// This package is a fork of the mitm package in github.com/google/martian/v3.
+package mitmx

 import (
 	"bytes"
@@ -51,7 +53,6 @@
 	validity               time.Duration
 	org                    string
 	h2Config               *h2.Config
-	getCertificate         func(*tls.ClientHelloInfo) (*tls.Certificate, error)
 	roots                  *x509.CertPool
 	skipVerify             bool
 	handshakeErrorCallback func(*http.Request, error)
@@ -63,6 +64,12 @@
 // NewAuthority creates a new CA certificate and associated
 // private key.
 func NewAuthority(name, organization string, validity time.Duration) (*x509.Certificate, *rsa.PrivateKey, error) {
+	return NewAuthorityWithTimeNow(name, organization, validity, time.Now)
+}
+
+// NewAuthorityWithTimeNow is like NewAuthority but allows to customize the time.Now func
+func NewAuthorityWithTimeNow(
+	name, organization string, validity time.Duration, timeNow func() time.Time) (*x509.Certificate, *rsa.PrivateKey, error) {
 	priv, err := rsa.GenerateKey(rand.Reader, 2048)
 	if err != nil {
 		return nil, nil, err
@@ -96,8 +103,8 @@
 		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
 		BasicConstraintsValid: true,
-		NotBefore:             time.Now().Add(-validity),
-		NotAfter:              time.Now().Add(validity),
+		NotBefore:             timeNow().Add(-validity),
+		NotAfter:              timeNow().Add(validity),
 		DNSNames:              []string{name},
 		IsCA:                  true,
 	}
@@ -260,7 +267,22 @@
 	}

 	log.Debugf("mitm: cache miss for %s", hostname)
+
+	tlsc, err = c.NewCertWithoutCacheWithTimeNow(hostname, time.Now)
+	if err != nil {
+		return nil, err
+	}

+	c.certmu.Lock()
+	c.certs[hostname] = tlsc
+	c.certmu.Unlock()
+
+	return tlsc, nil
+}
+
+// NewCertWithoutCacheWithTimeNow is the most fundamental building block for building a certificate
+// that completely bypasses caching and allows for setting a custom time.Now func.
+func (c *Config) NewCertWithoutCacheWithTimeNow(hostname string, timeNow func() time.Time) (*tls.Certificate, error) {
 	serial, err := rand.Int(rand.Reader, MaxSerialNumber)
 	if err != nil {
 		return nil, err
@@ -276,8 +298,8 @@
 		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
 		BasicConstraintsValid: true,
-		NotBefore:             time.Now().Add(-c.validity),
-		NotAfter:              time.Now().Add(c.validity),
+		NotBefore:             timeNow().Add(-c.validity),
+		NotAfter:              timeNow().Add(c.validity),
 	}

 	if ip := net.ParseIP(hostname); ip != nil {
@@ -297,15 +319,11 @@
 		return nil, err
 	}

-	tlsc = &tls.Certificate{
+	tlsc := &tls.Certificate{
 		Certificate: [][]byte{raw, c.ca.Raw},
 		PrivateKey:  c.priv,
 		Leaf:        x509c,
 	}

-	c.certmu.Lock()
-	c.certs[hostname] = tlsc
-	c.certmu.Unlock()
-
 	return tlsc, nil
 }
```
@bassosimone bassosimone merged commit c91fe50 into main Sep 6, 2023
4 checks passed
@bassosimone bassosimone deleted the issue/1803 branch September 6, 2023 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant