Skip to content

Migrate away from c-t-go/x509 and c-t-go/asn1 #198

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

Merged
merged 17 commits into from
Mar 18, 2025
Merged
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
4 changes: 2 additions & 2 deletions ctlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ package sctfe
import (
"context"
"crypto"
"crypto/x509"
"encoding/asn1"
"errors"
"fmt"
"net/http"
"strings"
"time"

"github.com/google/certificate-transparency-go/asn1"
"github.com/google/certificate-transparency-go/x509"
"github.com/transparency-dev/static-ct/internal/scti"
"github.com/transparency-dev/static-ct/internal/x509util"
"github.com/transparency-dev/static-ct/storage"
Expand Down
19 changes: 13 additions & 6 deletions internal/hammer/loadtest/workers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,20 @@ package loadtest
import (
"context"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"math/rand/v2"
"time"

"slices"

"github.com/google/certificate-transparency-go/x509"
"github.com/transparency-dev/formats/log"
"github.com/transparency-dev/merkle/proof"
"github.com/transparency-dev/merkle/rfc6962"
"github.com/transparency-dev/static-ct/internal/client"
"github.com/transparency-dev/static-ct/internal/types"
"github.com/transparency-dev/static-ct/internal/x509util"
"github.com/transparency-dev/trillian-tessera/api/layout"
"github.com/transparency-dev/trillian-tessera/ctonly"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -427,7 +426,7 @@ func entryFromChain(chain []*x509.Certificate, isPrecert bool, timestamp uint64)

// Next, post-process the DER-encoded TBSCertificate, to remove the CT poison
// extension and possibly update the issuer field.
defangedTBS, err := x509.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer)
defangedTBS, err := x509util.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer)
if err != nil {
return nil, fmt.Errorf("failed to remove poison extension: %v", err)
}
Expand All @@ -445,6 +444,14 @@ func entryFromChain(chain []*x509.Certificate, isPrecert bool, timestamp uint64)
// isPreIssuer indicates whether a certificate is a pre-cert issuer with the specific
// certificate transparency extended key usage.
// Copied from certificate-transparency-go/serialization.go and internal/scti/handlers.go.
func isPreIssuer(issuer *x509.Certificate) bool {
return slices.Contains(issuer.ExtKeyUsage, x509.ExtKeyUsageCertificateTransparency)
// TODO(phboneff): unify these.
func isPreIssuer(cert *x509.Certificate) bool {
// Look for the extension in the Extensions field and not ExtKeyUsage
// since crypto/x509 does not recognize this extension as an ExtKeyUsage.
for _, ext := range cert.Extensions {
if types.OIDExtKeyUsageCertificateTransparency.Equal(ext.Id) {
return true
}
}
return false
}
46 changes: 17 additions & 29 deletions internal/scti/chain_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ package scti

import (
"bytes"
"crypto/x509"
"encoding/asn1"
"errors"
"fmt"
"strconv"
"strings"
"time"

"github.com/google/certificate-transparency-go/asn1"
"github.com/google/certificate-transparency-go/x509"
"github.com/transparency-dev/static-ct/internal/types"
"github.com/transparency-dev/static-ct/internal/x509fork"
"github.com/transparency-dev/static-ct/internal/x509util"
"k8s.io/klog/v2"
)
Expand Down Expand Up @@ -125,7 +127,7 @@ func NewChainValidationOpts(trustedRoots *x509util.PEMCertPool, rejectExpired, r
// by the spec.
func isPrecertificate(cert *x509.Certificate) (bool, error) {
for _, ext := range cert.Extensions {
if x509.OIDExtensionCTPoison.Equal(ext.Id) {
if types.OIDExtensionCTPoison.Equal(ext.Id) {
if !ext.Critical || !bytes.Equal(asn1.NullBytes, ext.Value) {
return false, fmt.Errorf("CT poison ext is not critical or invalid: %v", ext)
}
Expand Down Expand Up @@ -154,8 +156,8 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5

for i, certBytes := range rawChain {
cert, err := x509.ParseCertificate(certBytes)
if x509.IsFatal(err) {
return nil, err
if err != nil {
return nil, fmt.Errorf("x509.ParseCertificate(): %v", err)
}

chain = append(chain, cert)
Expand Down Expand Up @@ -225,32 +227,18 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5
}
}

// We can now do the verification. Use fairly lax options for verification, as
// CT is intended to observe certificates rather than police them.
verifyOpts := x509.VerifyOptions{
Roots: validationOpts.trustedRoots.CertPool(),
CurrentTime: now,
Intermediates: intermediatePool.CertPool(),
DisableTimeChecks: true,
// Precertificates have the poison extension; also the Go library code does not
// support the standard PolicyConstraints extension (which is required to be marked
// critical, RFC 5280 s4.2.1.11), so never check unhandled critical extensions.
DisableCriticalExtensionChecks: true,
// Pre-issued precertificates have the Certificate Transparency EKU; also some
// leaves have unknown EKUs that should not be bounced just because the intermediate
// does not also have them (cf. https://github.com/golang/go/issues/24590) so
// disable EKU checks inside the x509 library, but we've already done our own check
// on the leaf above.
DisableEKUChecks: true,
// Path length checks get confused by the presence of an additional
// pre-issuer intermediate, so disable them.
DisablePathLenChecks: true,
DisableNameConstraintChecks: true,
DisableNameChecks: false,
KeyUsages: validationOpts.extKeyUsages,
// We can now do the verification. Use x509fork with looser verification
// constraints to:
// - allow pre-certificates and chains with pre-issuers
// - allow certificate without policing them since this is not CT's responsibility
// See /internal/x509fork/README.md for further information.
verifyOpts := x509fork.VerifyOptions{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming idea for this fork package: lax509 :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sending a different PR to avoid making this one even larger.

Roots: validationOpts.trustedRoots.CertPool(),
Intermediates: intermediatePool.CertPool(),
KeyUsages: validationOpts.extKeyUsages,
}

verifiedChains, err := cert.Verify(verifyOpts)
verifiedChains, err := x509fork.Verify(cert, verifyOpts)
if err != nil {
return nil, err
}
Expand Down
19 changes: 10 additions & 9 deletions internal/scti/chain_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
package scti

import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"strings"
"testing"
"time"

"github.com/google/certificate-transparency-go/asn1"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/certificate-transparency-go/x509/pkix"
"github.com/transparency-dev/static-ct/internal/testdata"
"github.com/transparency-dev/static-ct/internal/types"
"github.com/transparency-dev/static-ct/internal/x509util"
)

Expand Down Expand Up @@ -168,13 +169,13 @@ func wipeExtensions(cert *x509.Certificate) *x509.Certificate {

func makePoisonNonCritical(cert *x509.Certificate) *x509.Certificate {
// Invalid as a pre-cert because poison extension needs to be marked as critical.
cert.Extensions = []pkix.Extension{{Id: x509.OIDExtensionCTPoison, Critical: false, Value: asn1.NullBytes}}
cert.Extensions = []pkix.Extension{{Id: types.OIDExtensionCTPoison, Critical: false, Value: asn1.NullBytes}}
return cert
}

func makePoisonNonNull(cert *x509.Certificate) *x509.Certificate {
// Invalid as a pre-cert because poison extension is not ASN.1 NULL value.
cert.Extensions = []pkix.Extension{{Id: x509.OIDExtensionCTPoison, Critical: false, Value: []byte{0x42, 0x42, 0x42}}}
cert.Extensions = []pkix.Extension{{Id: types.OIDExtensionCTPoison, Critical: false, Value: []byte{0x42, 0x42, 0x42}}}
return cert
}

Expand Down Expand Up @@ -414,7 +415,7 @@ func TestValidateChain(t *testing.T) {
if len(gotPath) != test.wantPathLen {
t.Errorf("|ValidateChain()|=%d; want %d", len(gotPath), test.wantPathLen)
for _, c := range gotPath {
t.Logf("Subject: %s Issuer: %s", x509util.NameToString(c.Subject), x509util.NameToString(c.Issuer))
t.Logf("Subject: %s Issuer: %s", c.Subject, c.Issuer)
}
}
})
Expand Down Expand Up @@ -618,8 +619,8 @@ func pemToCert(t *testing.T, pemData string) *x509.Certificate {
}

cert, err := x509.ParseCertificate(bytes.Bytes)
if x509.IsFatal(err) {
t.Fatal(err)
if err != nil {
t.Fatalf("x509.ParseCertificate(): %v", err)
}

return cert
Expand Down Expand Up @@ -690,7 +691,7 @@ func TestPreIssuedCert(t *testing.T) {
t.Fatalf("failed to ValidateChain: %v", err)
}
for i, c := range chain {
t.Logf("chain[%d] = \n%s", i, x509util.CertificateToString(c))
t.Logf("chain[%d] = \n%s", i, c.Subject)
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion internal/scti/ctlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/x509"
"errors"
"fmt"

"github.com/google/certificate-transparency-go/x509"
"github.com/transparency-dev/static-ct/internal/types"
"github.com/transparency-dev/static-ct/modules/dedup"
"github.com/transparency-dev/static-ct/storage"
Expand Down
19 changes: 12 additions & 7 deletions internal/scti/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package scti
import (
"context"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
Expand All @@ -28,13 +29,11 @@ import (
"sync"
"time"

"slices"

"github.com/google/certificate-transparency-go/tls"
"github.com/google/certificate-transparency-go/x509"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/transparency-dev/static-ct/internal/types"
"github.com/transparency-dev/static-ct/internal/x509util"
"github.com/transparency-dev/static-ct/modules/dedup"
tessera "github.com/transparency-dev/trillian-tessera"
"github.com/transparency-dev/trillian-tessera/ctonly"
Expand Down Expand Up @@ -493,7 +492,7 @@ func entryFromChain(chain []*x509.Certificate, isPrecert bool, timestamp uint64)

// Next, post-process the DER-encoded TBSCertificate, to remove the CT poison
// extension and possibly update the issuer field.
defangedTBS, err := x509.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer)
defangedTBS, err := x509util.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer)
if err != nil {
return nil, fmt.Errorf("failed to remove poison extension: %v", err)
}
Expand All @@ -510,7 +509,13 @@ func entryFromChain(chain []*x509.Certificate, isPrecert bool, timestamp uint64)

// isPreIssuer indicates whether a certificate is a pre-cert issuer with the specific
// certificate transparency extended key usage.
// copied form certificate-transparency-go/serialization.go
func isPreIssuer(issuer *x509.Certificate) bool {
return slices.Contains(issuer.ExtKeyUsage, x509.ExtKeyUsageCertificateTransparency)
func isPreIssuer(cert *x509.Certificate) bool {
// Look for the extension in the Extensions field and not ExtKeyUsage
// since crypto/x509 does not recognize this extension as an ExtKeyUsage.
for _, ext := range cert.Extensions {
if types.OIDExtKeyUsageCertificateTransparency.Equal(ext.Id) {
return true
}
}
return false
Comment on lines +512 to +520
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen this in a couple of places, feels like it might reasonably live exported in x509util?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - there's a TODO to merge them, I'll do this in a followup PR. This PR was 90% done before the other isPreissuer was added.

}
2 changes: 1 addition & 1 deletion internal/scti/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"bytes"
"context"
"crypto"
"crypto/x509"
"encoding/hex"
"encoding/json"
"fmt"
Expand All @@ -30,7 +31,6 @@ import (
"time"

"github.com/golang/mock/gomock"
"github.com/google/certificate-transparency-go/x509"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/transparency-dev/static-ct/internal/testdata"
Expand Down
7 changes: 3 additions & 4 deletions internal/scti/requestlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ package scti

import (
"context"
"crypto/x509"
"encoding/hex"
"time"

"github.com/google/certificate-transparency-go/x509"
"github.com/google/certificate-transparency-go/x509util"
"k8s.io/klog/v2"
)

Expand Down Expand Up @@ -86,8 +85,8 @@ func (dlr *DefaultRequestLog) addDERToChain(_ context.Context, d []byte) {
// certificate that is part of a submitted chain.
func (dlr *DefaultRequestLog) addCertToChain(_ context.Context, cert *x509.Certificate) {
klog.V(vLevel).Infof("RL: Cert: Sub: %s Iss: %s notBef: %s notAft: %s",
x509util.NameToString(cert.Subject),
x509util.NameToString(cert.Issuer),
cert.Subject,
cert.Issuer,
cert.NotBefore.Format(time.RFC1123Z),
cert.NotAfter.Format(time.RFC1123Z))
}
Expand Down
2 changes: 1 addition & 1 deletion internal/scti/signatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import (
"crypto"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/binary"
"fmt"
"time"

"github.com/google/certificate-transparency-go/tls"
"github.com/google/certificate-transparency-go/x509"
tfl "github.com/transparency-dev/formats/log"
"github.com/transparency-dev/static-ct/internal/types"
"golang.org/x/mod/sumdb/note"
Expand Down
8 changes: 4 additions & 4 deletions internal/scti/signatures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ import (
"bytes"
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"testing"
"time"

"github.com/google/certificate-transparency-go/tls"
"github.com/google/certificate-transparency-go/x509"
"github.com/kylelemons/godebug/pretty"
"github.com/transparency-dev/static-ct/internal/testdata"
"github.com/transparency-dev/static-ct/internal/types"
Expand Down Expand Up @@ -245,7 +245,7 @@ func TestSerializeV1STHSignatureKAT(t *testing.T) {

func TestBuildV1MerkleTreeLeafForCert(t *testing.T) {
cert, err := x509util.CertificateFromPEM([]byte(testdata.LeafSignedByFakeIntermediateCertPEM))
if x509.IsFatal(err) {
if err != nil {
t.Fatalf("failed to set up test cert: %v", err)
}

Expand Down Expand Up @@ -308,7 +308,7 @@ func TestBuildV1MerkleTreeLeafForCert(t *testing.T) {

func TestSignV1SCTForPrecertificate(t *testing.T) {
cert, err := x509util.CertificateFromPEM([]byte(testdata.PrecertPEMValid))
if x509.IsFatal(err) {
if err != nil {
t.Fatalf("failed to set up test precert: %v", err)
}

Expand Down Expand Up @@ -367,7 +367,7 @@ func TestSignV1SCTForPrecertificate(t *testing.T) {
if got, want := keyHash[:], leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash[:]; !bytes.Equal(got, want) {
t.Fatalf("Issuer key hash bytes mismatch, got %v, expected %v", got, want)
}
defangedTBS, _ := x509.RemoveCTPoison(cert.RawTBSCertificate)
defangedTBS, _ := x509util.RemoveCTPoison(cert.RawTBSCertificate)
if got, want := leaf.TimestampedEntry.PrecertEntry.TBSCertificate, defangedTBS; !bytes.Equal(got, want) {
t.Fatalf("TBS cert mismatch, got %v, expected %v", got, want)
}
Expand Down
Loading
Loading