diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..749ed19f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + + - package-ecosystem: "gomod" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 5f853846..4958bc02 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -14,12 +14,12 @@ jobs: steps: - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: ^1.16 - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build run: go build -v ./... diff --git a/.github/workflows/gofmt.yml b/.github/workflows/gofmt.yml index b780f27a..86a7655a 100644 --- a/.github/workflows/gofmt.yml +++ b/.github/workflows/gofmt.yml @@ -14,10 +14,10 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Check gofmt - uses: Jerome1337/gofmt-action@v1.0.4 + uses: Jerome1337/gofmt-action@v1.0.5 with: gofmt-path: './' gofmt-flags: -l -s diff --git a/.github/workflows/psl-update.yml b/.github/workflows/psl-update.yml index f9dee0c8..c40354a7 100644 --- a/.github/workflows/psl-update.yml +++ b/.github/workflows/psl-update.yml @@ -11,10 +11,10 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: ^1.16 @@ -23,7 +23,7 @@ jobs: run: echo "::set-output name=now::$(date +'%Y-%m-%dT%H:%M:%S %Z')" - name: Update publicsuffix-go - run: go get -u github.com/weppos/publicsuffix-go@master + run: go get -u github.com/weppos/publicsuffix-go@main - name: Run go mod tidy run: go mod tidy @@ -36,7 +36,7 @@ jobs: - name: Create pull-request id: cpr - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v7 with: commit-message: "deps: update publicsuffix-go for ${{ steps.get-date.outputs.now }}" title: "deps: update weppos/publicsuffix-go for ${{ steps.get-date.outputs.now }}" diff --git a/cryptobyte/asn1.go b/cryptobyte/asn1.go index a755e56e..af771301 100644 --- a/cryptobyte/asn1.go +++ b/cryptobyte/asn1.go @@ -750,3 +750,42 @@ func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool { return true } + +const defaultUTCTimeFormatStr = "060102150405Z0700" + +// ReadASN1UTCTime decodes an ASN.1 UTCTime into out and advances. +// It reports whether the read was successful. +func (s *String) ReadASN1UTCTime(out *time.Time) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.UTCTime) { + return false + } + t := string(bytes) + + formatStr := defaultUTCTimeFormatStr + var err error + res, err := time.Parse(formatStr, t) + if err != nil { + // Fallback to minute precision if we can't parse second + // precision. If we are following X.509 or X.690 we shouldn't + // support this, but we do. + formatStr = "0601021504Z0700" + res, err = time.Parse(formatStr, t) + } + if err != nil { + return false + } + + if serialized := res.Format(formatStr); serialized != t { + return false + } + + if res.Year() >= 2050 { + // UTCTime interprets the low order digits 50-99 as 1950-99. + // This only applies to its use in the X.509 profile. + // See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + res = res.AddDate(-100, 0, 0) + } + *out = res + return true +} diff --git a/cryptobyte/builder.go b/cryptobyte/builder.go index ca7b1db5..c7ded757 100644 --- a/cryptobyte/builder.go +++ b/cryptobyte/builder.go @@ -106,13 +106,13 @@ func (b *Builder) AddBytes(v []byte) { // supplied to them. The child builder passed to the continuation can be used // to build the content of the length-prefixed sequence. For example: // -// parent := cryptobyte.NewBuilder() -// parent.AddUint8LengthPrefixed(func (child *Builder) { -// child.AddUint8(42) -// child.AddUint8LengthPrefixed(func (grandchild *Builder) { -// grandchild.AddUint8(5) -// }) -// }) +// parent := cryptobyte.NewBuilder() +// parent.AddUint8LengthPrefixed(func (child *Builder) { +// child.AddUint8(42) +// child.AddUint8LengthPrefixed(func (grandchild *Builder) { +// grandchild.AddUint8(5) +// }) +// }) // // It is an error to write more bytes to the child than allowed by the reserved // length prefix. After the continuation returns, the child must be considered diff --git a/ct/asn1/asn1.go b/ct/asn1/asn1.go index 1ba54fe4..1993c456 100644 --- a/ct/asn1/asn1.go +++ b/ct/asn1/asn1.go @@ -5,7 +5,7 @@ // Package asn1 implements parsing of DER-encoded ASN.1 data structures, // as defined in ITU-T Rec X.690. // -// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// See also “A Layman's Guide to a Subset of ASN.1, BER, and DER,” // http://luca.ntop.org/Teaching/Appunti/asn1.html. // // START CT CHANGES diff --git a/ct/asn1/marshal.go b/ct/asn1/marshal.go index 85224789..9527dbf3 100755 --- a/ct/asn1/marshal.go +++ b/ct/asn1/marshal.go @@ -18,7 +18,9 @@ import ( // A forkableWriter is an in-memory buffer that can be // 'forked' to create new forkableWriters that bracket the // original. After -// pre, post := w.fork(); +// +// pre, post := w.fork(); +// // the overall sequence of bytes represented is logically w+pre+post. type forkableWriter struct { *bytes.Buffer @@ -562,6 +564,9 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) length: bodyLen + tags.Len(), isCompound: true, }) + if err != nil { + return + } } return nil diff --git a/ct/client/logclient.go b/ct/client/logclient.go index 0b7ef016..fad32570 100644 --- a/ct/client/logclient.go +++ b/ct/client/logclient.go @@ -375,12 +375,18 @@ func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) { entries := make([]ct.LogEntry, len(resp.Entries)) for index, entry := range resp.Entries { leafBytes, err := base64.StdEncoding.DecodeString(entry.LeafInput) + if err != nil { + return nil, err + } leaf, err := ct.ReadMerkleTreeLeaf(bytes.NewBuffer(leafBytes)) if err != nil { return nil, err } entries[index].Leaf = *leaf chainBytes, err := base64.StdEncoding.DecodeString(entry.ExtraData) + if err != nil { + return nil, err + } var chain []ct.ASN1Cert switch leaf.TimestampedEntry.EntryType { diff --git a/ct/types.go b/ct/types.go index 5e4748f2..d00c004f 100644 --- a/ct/types.go +++ b/ct/types.go @@ -21,7 +21,8 @@ const ( /////////////////////////////////////////////////////////////////////////////// // LogEntryType represents the LogEntryType enum from section 3.1 of the RFC: -// enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType; +// +// enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType; type LogEntryType uint16 func (e LogEntryType) String() string { diff --git a/ct/x509/pkcs8_test.go b/ct/x509/pkcs8_test.go index 4114efd0..86f5f289 100644 --- a/ct/x509/pkcs8_test.go +++ b/ct/x509/pkcs8_test.go @@ -12,7 +12,8 @@ import ( var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` // Generated using: -// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt +// +// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt var pkcs8ECPrivateKeyHex = `3081ed020100301006072a8648ce3d020106052b810400230481d53081d20201010441850d81618c5da1aec74c2eed608ba816038506975e6427237c2def150c96a3b13efbfa1f89f1be15cdf4d0ac26422e680e65a0ddd4ad3541ad76165fbf54d6e34ba18189038186000400da97bcedba1eb6d30aeb93c9f9a1454598fa47278df27d6f60ea73eb672d8dc528a9b67885b5b5dcef93c9824f7449ab512ee6a27e76142f56b94b474cfd697e810046c8ca70419365245c1d7d44d0db82c334073835d002232714548abbae6e5700f5ef315ee08b929d8581383dcf2d1c98c2f8a9fccbf79c9579f7b2fd8a90115ac2` func TestPKCS8(t *testing.T) { diff --git a/ct/x509/root_bsd.go b/ct/x509/root_bsd.go index 13719338..8c04bdcd 100644 --- a/ct/x509/root_bsd.go +++ b/ct/x509/root_bsd.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build dragonfly || freebsd || netbsd || openbsd // +build dragonfly freebsd netbsd openbsd package x509 diff --git a/ct/x509/root_cgo_darwin.go b/ct/x509/root_cgo_darwin.go index 2c3de0f6..8fe54cbe 100644 --- a/ct/x509/root_cgo_darwin.go +++ b/ct/x509/root_cgo_darwin.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build cgo && !arm && !arm64 && !ios // +build cgo,!arm,!arm64,!ios package x509 diff --git a/ct/x509/root_darwin.go b/ct/x509/root_darwin.go index bc35a1cf..5071676e 100644 --- a/ct/x509/root_darwin.go +++ b/ct/x509/root_darwin.go @@ -38,21 +38,21 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate // // The strategy is as follows: // -// 1. Run "security trust-settings-export" and "security -// trust-settings-export -d" to discover the set of certs with some -// user-tweaked trust policy. We're too lazy to parse the XML (at -// least at this stage of Go 1.8) to understand what the trust -// policy actually is. We just learn that there is _some_ policy. +// 1. Run "security trust-settings-export" and "security +// trust-settings-export -d" to discover the set of certs with some +// user-tweaked trust policy. We're too lazy to parse the XML (at +// least at this stage of Go 1.8) to understand what the trust +// policy actually is. We just learn that there is _some_ policy. // -// 2. Run "security find-certificate" to dump the list of system root -// CAs in PEM format. +// 2. Run "security find-certificate" to dump the list of system root +// CAs in PEM format. // -// 3. For each dumped cert, conditionally verify it with "security -// verify-cert" if that cert was in the set discovered in Step 1. -// Without the Step 1 optimization, running "security verify-cert" -// 150-200 times takes 3.5 seconds. With the optimization, the -// whole process takes about 180 milliseconds with 1 untrusted root -// CA. (Compared to 110ms in the cgo path) +// 3. For each dumped cert, conditionally verify it with "security +// verify-cert" if that cert was in the set discovered in Step 1. +// Without the Step 1 optimization, running "security verify-cert" +// 150-200 times takes 3.5 seconds. With the optimization, the +// whole process takes about 180 milliseconds with 1 untrusted root +// CA. (Compared to 110ms in the cgo path) func execSecurityRoots() (*CertPool, error) { hasPolicy, err := getCertsWithTrustPolicy() if err != nil { diff --git a/ct/x509/root_darwin_arm_gen.go b/ct/x509/root_darwin_arm_gen.go index fc2488ad..46313a4e 100644 --- a/ct/x509/root_darwin_arm_gen.go +++ b/ct/x509/root_darwin_arm_gen.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Generates root_darwin_armx.go. diff --git a/ct/x509/root_darwin_armx.go b/ct/x509/root_darwin_armx.go index ad1c53d8..113acb6f 100644 --- a/ct/x509/root_darwin_armx.go +++ b/ct/x509/root_darwin_armx.go @@ -4,6 +4,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build cgo && darwin && (arm || arm64 || ios) // +build cgo // +build darwin // +build arm arm64 ios diff --git a/ct/x509/root_js.go b/ct/x509/root_js.go new file mode 100644 index 00000000..4240207a --- /dev/null +++ b/ct/x509/root_js.go @@ -0,0 +1,19 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build js && wasm +// +build js,wasm + +package x509 + +// Possible certificate files; stop after finding one. +var certFiles = []string{} + +func loadSystemRoots() (*CertPool, error) { + return NewCertPool(), nil +} + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} diff --git a/ct/x509/root_nocgo_darwin.go b/ct/x509/root_nocgo_darwin.go index 19bbe21f..0286a7d7 100644 --- a/ct/x509/root_nocgo_darwin.go +++ b/ct/x509/root_nocgo_darwin.go @@ -4,6 +4,9 @@ // Use non-cgo on Darwin to prevent duplicate symbols on cgo +//go:build !arm && !arm64 && !ios +// +build !arm,!arm64,!ios + package x509 func loadSystemRoots() (*CertPool, error) { diff --git a/ct/x509/root_plan9.go b/ct/x509/root_plan9.go index ebeb7dfc..605044e7 100644 --- a/ct/x509/root_plan9.go +++ b/ct/x509/root_plan9.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build plan9 // +build plan9 package x509 diff --git a/ct/x509/root_unix.go b/ct/x509/root_unix.go index 65b5a5fd..f6ae062d 100644 --- a/ct/x509/root_unix.go +++ b/ct/x509/root_unix.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris // +build dragonfly freebsd linux nacl netbsd openbsd solaris package x509 diff --git a/ct/x509/root_unix_test.go b/ct/x509/root_unix_test.go index a9e06a99..5c08ea15 100644 --- a/ct/x509/root_unix_test.go +++ b/ct/x509/root_unix_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris // +build dragonfly freebsd linux netbsd openbsd solaris package x509 diff --git a/ct/x509/sec1.go b/ct/x509/sec1.go index b0ea06e3..8c70b121 100644 --- a/ct/x509/sec1.go +++ b/ct/x509/sec1.go @@ -19,8 +19,10 @@ const ecPrivKeyVersion = 1 // ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure. // References: -// RFC5915 -// SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf +// +// RFC5915 +// SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf +// // Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in // most cases it is not. type ecPrivateKey struct { diff --git a/ct/x509/sec1_test.go b/ct/x509/sec1_test.go index 95f18e77..fd99a5b9 100644 --- a/ct/x509/sec1_test.go +++ b/ct/x509/sec1_test.go @@ -11,7 +11,8 @@ import ( ) // Generated using: -// openssl ecparam -genkey -name secp384r1 -outform PEM +// +// openssl ecparam -genkey -name secp384r1 -outform PEM var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50` func TestParseECPrivateKey(t *testing.T) { diff --git a/ct/x509/x509.go b/ct/x509/x509.go index 14d20892..01a123d6 100644 --- a/ct/x509/x509.go +++ b/ct/x509/x509.go @@ -66,6 +66,9 @@ func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorith N: pub.N, E: pub.E, }) + if err != nil { + return + } publicKeyAlgorithm.Algorithm = oidPublicKeyRSA // This is a NULL parameters value which is technically // superfluous, but most other code includes it and, by @@ -241,18 +244,19 @@ const ( // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } var ( - oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} - oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} - oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} - oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} - oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} - oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} - oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + oidExtensionCTPrecertificatePoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3} ) @@ -289,17 +293,19 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm // RFC 3279, 2.3 Public Key Algorithms // // pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// rsadsi(113549) pkcs(1) 1 } +// +// rsadsi(113549) pkcs(1) 1 } // // rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } // // id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// x9-57(10040) x9cm(4) 1 } // -// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters +// x9-57(10040) x9cm(4) 1 } +// +// # RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters // -// id-ecPublicKey OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } +// id-ecPublicKey OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } var ( oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} @@ -320,18 +326,18 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm // RFC 5480, 2.1.1.1. Named Curve // -// secp224r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +// secp224r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 33 } // -// secp256r1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) -// prime(1) 7 } +// secp256r1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +// prime(1) 7 } // -// secp384r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +// secp384r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 34 } // -// secp521r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +// secp521r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 35 } // // NB: secp256r1 is equivalent to prime256v1 var ( diff --git a/data/test/certificates/fpki.go b/data/test/certificates/fpki.go index 06a8a93e..9bc0b5bb 100644 --- a/data/test/certificates/fpki.go +++ b/data/test/certificates/fpki.go @@ -1915,7 +1915,7 @@ zaehYm7UZ14x // HexHashFederalBridgeCA2013SignedByCommonPolicyCASerial11424 is the hex // SHA256 fingerprint of -//FederalBridgeCA2013SignedByCommonPolicyCASerial11424. +// FederalBridgeCA2013SignedByCommonPolicyCASerial11424. const HexHashFederalBridgeCA2013SignedByCommonPolicyCASerial11424 = "8ed99089806b1005d6a6417c50f182325b670b9d87b17f3fd7aefc360a300e91" // PEMFederalBridgeCA2013SignedByIdenTrust is the certificate for the Federal @@ -2540,7 +2540,7 @@ A2EC/0rskqTcLe4qNJMHtyznGI8= ` // HexHashFederalCommonPolicyCASignedBySelf is the hex SHA256 fingerprint of -//FederalCommonPolicyCASignedBySelf. +// FederalCommonPolicyCASignedBySelf. const HexHashFederalCommonPolicyCASignedBySelf = "894ebc0b23da2a50c0186b7f8f25ef1f6b2935af32a94584ef80aaf877a3a06e" // PEMFederalCommonPolicyCASignedByFederalBridgeCA is the certificate for the diff --git a/encoding/asn1/asn1.go b/encoding/asn1/asn1.go index 87b9eddb..212ef9c3 100644 --- a/encoding/asn1/asn1.go +++ b/encoding/asn1/asn1.go @@ -5,7 +5,7 @@ // Package asn1 implements parsing of DER-encoded ASN.1 data structures, // as defined in ITU-T Rec X.690. // -// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// See also “A Layman's Guide to a Subset of ASN.1, BER, and DER,” // http://luca.ntop.org/Teaching/Appunti/asn1.html. package asn1 diff --git a/go.mod b/go.mod index ab81fb6f..963ede19 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,16 @@ module github.com/zmap/zcrypto require ( - github.com/kr/pretty v0.1.0 // indirect - github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 + github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 - github.com/sirupsen/logrus v1.3.0 - github.com/stretchr/testify v1.4.0 - github.com/weppos/publicsuffix-go v0.15.1-0.20210607115855-ec3753e8c6e1 - github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521 - github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4 - golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 - golang.org/x/net v0.0.0-20201110031124-69a78807bb2b + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.10.0 + github.com/weppos/publicsuffix-go v0.40.3-0.20250127173806-e489a31678ca + github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 + github.com/zmap/zcertificate v0.0.1 + golang.org/x/crypto v0.32.0 + golang.org/x/net v0.34.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 - gopkg.in/yaml.v2 v2.3.0 // indirect ) go 1.16 diff --git a/go.sum b/go.sum index 14d67e28..074e35b8 100644 --- a/go.sum +++ b/go.sum @@ -1,56 +1,140 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteTqeSpenyTrOVj5zkiyCaflLa8B+CD0324otT+o= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= +github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d h1:tLWCMSjfL8XyZwpu1RzI2UpJSPbZCOZ6DVHQFnlpL7A= +github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/weppos/publicsuffix-go v0.15.1-0.20210607115855-ec3753e8c6e1 h1:1QMSsYHQs/tq8Z/GshPwJpEP7ddhjz3+B/LujFvkNpU= -github.com/weppos/publicsuffix-go v0.15.1-0.20210607115855-ec3753e8c6e1/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= -github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521 h1:kKCF7VX/wTmdg2ZjEaqlq99Bjsoiz7vH6sFniF/vI4M= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= +github.com/weppos/publicsuffix-go v0.40.3-0.20250127173806-e489a31678ca h1:Cno+VZU+XBPscI/pEMZPlzAB21kjo00dy8OWpxa3ECo= +github.com/weppos/publicsuffix-go v0.40.3-0.20250127173806-e489a31678ca/go.mod h1:43Dfyxu2dpmLg56at26Q4k9gwf3yWSUiwk8kGnwzULk= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= -github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4 h1:17HHAgFKlLcZsDOjBOUrd5hDihb1ggf+1a5dTbkgkIY= +github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 h1:Nzukz5fNOBIHOsnP+6I79kPx3QhLv8nBy2mfFhBRq30= +github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= +github.com/zmap/zcertificate v0.0.1 h1:2X15TRx4Fr6qzKItfwUdww294OeRSmHILLa+Xn2Uv+s= +github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk= +github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= +github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= +github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/json/ecdhe.go b/json/ecdhe.go index 5d3d1917..aa808ab1 100644 --- a/json/ecdhe.go +++ b/json/ecdhe.go @@ -94,7 +94,7 @@ func (c *TLSCurveID) MarshalJSON() ([]byte, error) { return json.Marshal(&aux) } -//UnmarshalJSON implements the json.Unmarshaler interface +// UnmarshalJSON implements the json.Unmarshaler interface func (c *TLSCurveID) UnmarshalJSON(b []byte) error { aux := struct { ID uint16 `json:"id"` diff --git a/tls/cipher_suites.go b/tls/cipher_suites.go index a81b5575..231f69fc 100644 --- a/tls/cipher_suites.go +++ b/tls/cipher_suites.go @@ -105,9 +105,9 @@ type tlsAead interface { // Incidences of unsupported cipher-suites are annotated in-line with comments // The following guidelines should be noted: -// * DSS Suites: certificates are not supported (Certificate) -// * PSK Suites: Not supported/implemented (Symmetric Key) -// * Non-ephemeral, Anonymous DH: Not supported/implemented (Kex) +// - DSS Suites: certificates are not supported (Certificate) +// - PSK Suites: Not supported/implemented (Symmetric Key) +// - Non-ephemeral, Anonymous DH: Not supported/implemented (Kex) var implementedCipherSuites = []*cipherSuite{ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, 32, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305}, {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, 32, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305}, diff --git a/tls/common.go b/tls/common.go index fefa68eb..01983d60 100644 --- a/tls/common.go +++ b/tls/common.go @@ -438,6 +438,17 @@ type Config struct { // supported for ECDHE key exchanges ExplicitCurvePreferences bool + // EC Point Formats. Specifies what compressed points the client supports + SupportedPoints []uint8 + + // Online Certificate Status Protocol (OCSP) stapling, + // formally knows as TLS Certificate Status Request. + // If this option enabled, the certificate status won't be checked + NoOcspStapling bool + + // Specifies what compression methods the client supports + CompressionMethods []uint8 + // If enabled, specifies the signature and hash algorithms to be accepted by // a server, or sent by a client SignatureAndHashes []SigAndHash @@ -472,6 +483,9 @@ type Config struct { // Explicitly set Client random ClientRandom []byte + // Explicitly set Server random + ServerRandom []byte + // Explicitly set ClientHello with raw data ExternalClientHello []byte @@ -578,6 +592,7 @@ func (c *Config) Clone() *Config { ExplicitCurvePreferences: c.ExplicitCurvePreferences, sessionTicketKeys: sessionTicketKeys, ClientFingerprintConfiguration: c.ClientFingerprintConfiguration, + CertsOnly: c.CertsOnly, // originalConfig is deliberately not duplicated. // Not merged from upstream: diff --git a/tls/generate_cert.go b/tls/generate_cert.go index bd4472a9..db28b497 100644 --- a/tls/generate_cert.go +++ b/tls/generate_cert.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Generate a self-signed X.509 certificate for a TLS server. Outputs to diff --git a/tls/handshake_client.go b/tls/handshake_client.go index dab45c30..c1d9d94f 100644 --- a/tls/handshake_client.go +++ b/tls/handshake_client.go @@ -319,14 +319,28 @@ func (c *Conn) clientHandshake() error { return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") } + supportedPoints := []uint8{pointFormatUncompressed} + if c.config.SupportedPoints != nil { + supportedPoints = c.config.SupportedPoints + } + oscpStapling := true + if c.config.NoOcspStapling { + oscpStapling = false + } + + compressionMethods := []uint8{compressionNone} + if c.config.CompressionMethods != nil { + compressionMethods = c.config.CompressionMethods + } + hello = &clientHelloMsg{ vers: c.config.maxVersion(), - compressionMethods: []uint8{compressionNone}, + compressionMethods: compressionMethods, random: make([]byte, 32), - ocspStapling: true, + ocspStapling: oscpStapling, serverName: c.config.ServerName, supportedCurves: c.config.curvePreferences(), - supportedPoints: []uint8{pointFormatUncompressed}, + supportedPoints: supportedPoints, nextProtoNeg: len(c.config.NextProtos) > 0, secureRenegotiation: true, alpnProtocols: c.config.NextProtos, diff --git a/tls/handshake_server.go b/tls/handshake_server.go index a97532f4..0b41a4d9 100644 --- a/tls/handshake_server.go +++ b/tls/handshake_server.go @@ -188,12 +188,18 @@ Curves: } hs.hello.vers = c.vers + hs.hello.random = make([]byte, 32) - _, err = io.ReadFull(c.config.rand(), hs.hello.random) - if err != nil { - c.sendAlert(alertInternalError) - return false, err + if len(c.config.ServerRandom) == 32 { + copy(hs.hello.random, c.config.ServerRandom) + } else { + _, err := io.ReadFull(c.config.rand(), hs.hello.random) + if err != nil { + c.sendAlert(alertInternalError) + return false, err + } } + hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation hs.hello.compressionMethod = compressionNone hs.hello.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && c.config.ExtendedMasterSecret diff --git a/tls/key_agreement.go b/tls/key_agreement.go index 5f103ea5..53aaf65f 100644 --- a/tls/key_agreement.go +++ b/tls/key_agreement.go @@ -384,6 +384,9 @@ func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) } sig, err = asn1.Marshal(ecdsaSignature{r, s}) + if err != nil { + return nil, errors.New("failed to marshal ECDSA signature: " + err.Error()) + } case signatureRSA: privKey, ok := cert.PrivateKey.(*rsa.PrivateKey) if !ok { @@ -458,44 +461,44 @@ func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clie case signatureECDSA: augECDSA, ok := cert.PublicKey.(*x509.AugmentedECDSA) if !ok { - return nil, errors.New("ECDHE ECDSA: could not covert cert.PublicKey to x509.AugmentedECDSA") + return digest, errors.New("ECDHE ECDSA: could not covert cert.PublicKey to x509.AugmentedECDSA") } pubKey := augECDSA.Pub ecdsaSig := new(ecdsaSignature) if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { - return nil, err + return digest, err } if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return nil, errors.New("ECDSA signature contained zero or negative values") + return digest, errors.New("ECDSA signature contained zero or negative values") } if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { - return nil, errors.New("ECDSA verification failure") + return digest, errors.New("ECDSA verification failure") } case signatureRSA: pubKey, ok := cert.PublicKey.(*rsa.PublicKey) if !ok { - return nil, errors.New("ECDHE RSA requires a RSA server public key") + return digest, errors.New("ECDHE RSA requires a RSA server public key") } if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { - return nil, err + return digest, err } case signatureDSA: pubKey, ok := cert.PublicKey.(*dsa.PublicKey) if !ok { - return nil, errors.New("DSS ciphers require a DSA server public key") + return digest, errors.New("DSS ciphers require a DSA server public key") } dsaSig := new(dsaSignature) if _, err := asn1.Unmarshal(sig, dsaSig); err != nil { - return nil, err + return digest, err } if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return nil, errors.New("DSA signature contained zero or negative values") + return digest, errors.New("DSA signature contained zero or negative values") } if !dsa.Verify(pubKey, digest, dsaSig.R, dsaSig.S) { - return nil, errors.New("DSA verification failure") + return digest, errors.New("DSA verification failure") } default: - return nil, errors.New("unknown ECDHE signature algorithm") + return digest, errors.New("unknown ECDHE signature algorithm") } ka.valid = true return digest, nil diff --git a/tls/tls_handshake.go b/tls/tls_handshake.go index e124a6ad..6f72520b 100644 --- a/tls/tls_handshake.go +++ b/tls/tls_handshake.go @@ -54,19 +54,21 @@ type ParsedAndRawSCT struct { } type ServerHello struct { - Version TLSVersion `json:"version"` - Random []byte `json:"random"` - SessionID []byte `json:"session_id"` - CipherSuite CipherSuite `json:"cipher_suite"` - // TODO FIXME: Why is this a raw uint8, not a CompressionMethod? - CompressionMethod uint8 `json:"compression_method"` + Version TLSVersion `json:"version"` + Random []byte `json:"random"` + SessionID []byte `json:"session_id"` + CipherSuite CipherSuite `json:"cipher_suite"` + CompressionMethod CompressionMethod `json:"compression_method"` OcspStapling bool `json:"ocsp_stapling"` TicketSupported bool `json:"ticket"` SecureRenegotiation bool `json:"secure_renegotiation"` HeartbeatSupported bool `json:"heartbeat"` ExtendedRandom []byte `json:"extended_random,omitempty"` ExtendedMasterSecret bool `json:"extended_master_secret"` + NextProtoNeg bool `json:"next_protocol_negotiation"` SignedCertificateTimestamps []ParsedAndRawSCT `json:"scts,omitempty"` + AlpnProtocol string `json:"alpn_protocol,omitempty"` + UnknownExtensions [][]byte `json:"unknown_extensions,omitempty"` } // SimpleCertificate holds a *x509.Certificate and a []byte for the certificate @@ -341,7 +343,7 @@ func (m *serverHelloMsg) MakeLog() *ServerHello { sh.SessionID = make([]byte, len(m.sessionId)) copy(sh.SessionID, m.sessionId) sh.CipherSuite = CipherSuite(m.cipherSuite) - sh.CompressionMethod = m.compressionMethod + sh.CompressionMethod = CompressionMethod(m.compressionMethod) sh.OcspStapling = m.ocspStapling sh.TicketSupported = m.ticketSupported sh.SecureRenegotiation = m.secureRenegotiation @@ -350,6 +352,7 @@ func (m *serverHelloMsg) MakeLog() *ServerHello { sh.ExtendedRandom = make([]byte, len(m.extendedRandom)) copy(sh.ExtendedRandom, m.extendedRandom) } + sh.NextProtoNeg = m.nextProtoNeg if len(m.scts) > 0 { for _, rawSCT := range m.scts { var out ParsedAndRawSCT @@ -363,6 +366,13 @@ func (m *serverHelloMsg) MakeLog() *ServerHello { } } sh.ExtendedMasterSecret = m.extendedMasterSecret + sh.AlpnProtocol = m.alpnProtocol + sh.UnknownExtensions = make([][]byte, len(m.unknownExtensions)) + for i, extBytes := range m.unknownExtensions { + tempBytes := make([]byte, len(extBytes)) + copy(tempBytes, extBytes) + sh.UnknownExtensions[i] = tempBytes + } return sh } diff --git a/tls/tls_names.go b/tls/tls_names.go index 60874d00..3ea468e1 100644 --- a/tls/tls_names.go +++ b/tls/tls_names.go @@ -436,6 +436,8 @@ func init() { curveNames[258] = "ffdhe4096" curveNames[259] = "ffdhe6144" curveNames[260] = "ffdhe8192" + curveNames[4587] = "secp256r1mlkem768" // draft-kwiatkowski-tls-ecdhe-mlkem + curveNames[4588] = "x25519mlkem768" // draft-kwiatkowski-tls-ecdhe-mlkem curveNames[65281] = "arbitrary_explicit_prime_curves" curveNames[65282] = "arbitrary_explicit_char2_curves" diff --git a/verifier/revocation.go b/verifier/revocation.go index 0f589e62..b3906370 100644 --- a/verifier/revocation.go +++ b/verifier/revocation.go @@ -59,13 +59,13 @@ func CheckOCSP(ctx context.Context, c *x509.Certificate, issuer *x509.Certificat return false, nil, errors.New("Failed to parse OCSP Response: " + err.Error()) } - if ocspResp.IsRevoked { - isRevoked = true - info = &RevocationInfo{ - RevocationTime: ocspResp.RevokedAt, - NextUpdate: ocspResp.NextUpdate, - Reason: ocspResp.RevocationReason, - } + isRevoked = ocspResp.IsRevoked + info = &RevocationInfo{ + NextUpdate: ocspResp.NextUpdate, + } + if isRevoked { + info.RevocationTime = &ocspResp.RevokedAt + info.Reason = ocspResp.RevocationReason } return @@ -89,16 +89,15 @@ func CheckCRL(ctx context.Context, c *x509.Certificate, certList *pkix.Certifica return false, nil, err } - if crlData.IsRevoked { - isRevoked = true - info = &RevocationInfo{ - RevocationTime: crlData.RevocationTime, - NextUpdate: crlData.NextUpdate, - } + isRevoked = crlData.IsRevoked - if crlData.CertificateEntryExtensions.Reason != nil { - info.Reason = *crlData.CertificateEntryExtensions.Reason - } + info = &RevocationInfo{ + NextUpdate: crlData.NextUpdate, + } + + if isRevoked && crlData.CertificateEntryExtensions.Reason != nil { + info.Reason = *crlData.CertificateEntryExtensions.Reason + info.RevocationTime = &crlData.RevocationTime } return diff --git a/verifier/verifier.go b/verifier/verifier.go index 6b1bab1d..e88eda77 100644 --- a/verifier/verifier.go +++ b/verifier/verifier.go @@ -64,6 +64,12 @@ type VerificationResult struct { // CRLRevocationInfo provides revocation info when CRLRevoked is true CRLRevocationInfo *RevocationInfo + // OCSPCheckError will be non-nil when there was some sort of error from OCSP check + OCSPCheckError error + + // CRLCheckError will be non-nil when there was some sort of error from CRL check + CRLCheckError error + // ValiditionError will be non-nil when there was some sort of error during // validation not involving a name mismatch, e.g. if a chain could not be // built. @@ -139,8 +145,8 @@ type VerifyProcedure interface { // RevocationInfo provides basic revocation information type RevocationInfo struct { - RevocationTime time.Time NextUpdate time.Time + RevocationTime *time.Time Reason crl.RevocationReasonCode } @@ -275,11 +281,11 @@ func (v *Verifier) VerifyWithContext(ctx context.Context, c *x509.Certificate, o } else { issuer = nil } - res.OCSPRevoked, res.OCSPRevocationInfo, _ = rp.CheckOCSP(ctx, c, issuer) + res.OCSPRevoked, res.OCSPRevocationInfo, res.OCSPCheckError = rp.CheckOCSP(ctx, c, issuer) } if opts.ShouldCheckCRL && len(c.CRLDistributionPoints) > 0 { - res.CRLRevoked, res.CRLRevocationInfo, _ = rp.CheckCRL(ctx, c, nil) + res.CRLRevoked, res.CRLRevocationInfo, res.CRLCheckError = rp.CheckCRL(ctx, c, nil) } // Determine certificate type. diff --git a/x509/crl_parser.go b/x509/crl_parser.go new file mode 100644 index 00000000..b33404f6 --- /dev/null +++ b/x509/crl_parser.go @@ -0,0 +1,409 @@ +package x509 + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "time" + "unicode/utf16" + "unicode/utf8" + + "github.com/zmap/zcrypto/cryptobyte" + cryptobyte_asn1 "github.com/zmap/zcrypto/cryptobyte/asn1" + "github.com/zmap/zcrypto/encoding/asn1" + "github.com/zmap/zcrypto/x509/pkix" +) + +const x509v2Version = 1 + +// RevokedCertificate represents an entry in the revokedCertificates sequence of +// a CRL. +// STARTBLOCK: This type does not exist in upstream. +type RevokedCertificate struct { + // Raw contains the raw bytes of the revokedCertificates entry. It is set when + // parsing a CRL; it is ignored when generating a CRL. + Raw []byte + + // SerialNumber represents the serial number of a revoked certificate. It is + // both used when creating a CRL and populated when parsing a CRL. It MUST NOT + // be nil. + SerialNumber *big.Int + // RevocationTime represents the time at which the certificate was revoked. It + // is both used when creating a CRL and populated when parsing a CRL. It MUST + // NOT be nil. + RevocationTime time.Time + // ReasonCode represents the reason for revocation, using the integer enum + // values specified in RFC 5280 Section 5.3.1. When creating a CRL, a value of + // nil or zero will result in the reasonCode extension being omitted. When + // parsing a CRL, a value of nil represents a no reasonCode extension, while a + // value of 0 represents a reasonCode extension containing enum value 0 (this + // SHOULD NOT happen, but can and does). + ReasonCode *int + + // Extensions contains raw X.509 extensions. When creating a CRL, the + // Extensions field is ignored, see ExtraExtensions. + Extensions []pkix.Extension + // ExtraExtensions contains any additional extensions to add directly to the + // revokedCertificate entry. It is up to the caller to ensure that this field + // does not contain any extensions which duplicate extensions created by this + // package (currently, the reasonCode extension). The ExtraExtensions field is + // not populated when parsing a CRL, see Extensions. + ExtraExtensions []pkix.Extension +} + +// ENDBLOCK + +// ParseRevocationList parses a X509 v2 Certificate Revocation List from the given +// ASN.1 DER data. +func ParseRevocationList(der []byte) (*RevocationList, error) { + rl := &RevocationList{} + + input := cryptobyte.String(der) + // we read the SEQUENCE including length and tag bytes so that + // we can populate RevocationList.Raw, before unwrapping the + // SEQUENCE so it can be operated on + if !input.ReadASN1Element(&input, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed crl") + } + rl.Raw = input + if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed crl") + } + + var tbs cryptobyte.String + // do the same trick again as above to extract the raw + // bytes for Certificate.RawTBSCertificate + if !input.ReadASN1Element(&tbs, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed tbs crl") + } + rl.RawTBSRevocationList = tbs + if !tbs.ReadASN1(&tbs, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed tbs crl") + } + + var version int + if !tbs.PeekASN1Tag(cryptobyte_asn1.INTEGER) { + return nil, errors.New("x509: unsupported crl version") + } + if !tbs.ReadASN1Integer(&version) { + return nil, errors.New("x509: malformed crl") + } + if version != x509v2Version { + return nil, fmt.Errorf("x509: unsupported crl version: %d", version) + } + + var sigAISeq cryptobyte.String + if !tbs.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed signature algorithm identifier") + } + // Before parsing the inner algorithm identifier, extract + // the outer algorithm identifier and make sure that they + // match. + var outerSigAISeq cryptobyte.String + if !input.ReadASN1(&outerSigAISeq, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed algorithm identifier") + } + if !bytes.Equal(outerSigAISeq, sigAISeq) { + return nil, errors.New("x509: inner and outer signature algorithm identifiers don't match") + } + sigAI, err := parseAI(sigAISeq) + if err != nil { + return nil, err + } + rl.SignatureAlgorithm = getSignatureAlgorithmFromAI(sigAI) + + var signature asn1.BitString + if !input.ReadASN1BitString(&signature) { + return nil, errors.New("x509: malformed signature") + } + rl.Signature = signature.RightAlign() + + var issuerSeq cryptobyte.String + if !tbs.ReadASN1Element(&issuerSeq, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed issuer") + } + rl.RawIssuer = issuerSeq + issuerRDNs, err := parseName(issuerSeq) + if err != nil { + return nil, err + } + rl.Issuer.FillFromRDNSequence(issuerRDNs) + + rl.ThisUpdate, err = parseTime(&tbs) + if err != nil { + return nil, err + } + if tbs.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime) || tbs.PeekASN1Tag(cryptobyte_asn1.UTCTime) { + rl.NextUpdate, err = parseTime(&tbs) + if err != nil { + return nil, err + } + } + + if tbs.PeekASN1Tag(cryptobyte_asn1.SEQUENCE) { + // NOTE: The block does not exist in upstream. + rcs := make([]RevokedCertificate, 0) + // ENDBLOCK + var revokedSeq cryptobyte.String + if !tbs.ReadASN1(&revokedSeq, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed crl") + } + for !revokedSeq.Empty() { + var certSeq cryptobyte.String + // NOTE: The block is different from upstream. Upstream: ReadASN1 + if !revokedSeq.ReadASN1Element(&certSeq, cryptobyte_asn1.SEQUENCE) { + // ENDBLOCK + return nil, errors.New("x509: malformed crl") + } + rc := RevokedCertificate{Raw: certSeq} + if !certSeq.ReadASN1(&certSeq, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed crl") + } + rc.SerialNumber = new(big.Int) + if !certSeq.ReadASN1Integer(rc.SerialNumber) { + return nil, errors.New("x509: malformed serial number") + } + rc.RevocationTime, err = parseTime(&certSeq) + if err != nil { + return nil, err + } + var extensions cryptobyte.String + var present bool + if !certSeq.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed extensions") + } + if present { + for !extensions.Empty() { + var extension cryptobyte.String + if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed extension") + } + ext, err := parseExtension(extension) + if err != nil { + return nil, err + } + // STARTBLOCK: This block does not exist in upstream. + if ext.Id.Equal(oidExtensionReasonCode) { + val := cryptobyte.String(ext.Value) + rc.ReasonCode = new(int) + if !val.ReadASN1Enum(rc.ReasonCode) { + return nil, fmt.Errorf("x509: malformed reasonCode extension") + } + } + // ENDBLOCK + rc.Extensions = append(rc.Extensions, ext) + } + } + // STARTBLOCK: The block does not exist in upstream. + rcs = append(rcs, rc) + // ENDBLOCK + } + rl.RevokedCertificates = rcs + } + + var extensions cryptobyte.String + var present bool + if !tbs.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) { + return nil, errors.New("x509: malformed extensions") + } + if present { + if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed extensions") + } + for !extensions.Empty() { + var extension cryptobyte.String + if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: malformed extension") + } + ext, err := parseExtension(extension) + if err != nil { + return nil, err + } + if ext.Id.Equal(oidExtensionAuthorityKeyId) { + rl.AuthorityKeyId = ext.Value + } else if ext.Id.Equal(oidExtensionCRLNumber) { + value := cryptobyte.String(ext.Value) + rl.Number = new(big.Int) + if !value.ReadASN1Integer(rl.Number) { + return nil, errors.New("x509: malformed crl number") + } + } + rl.Extensions = append(rl.Extensions, ext) + } + } + + return rl, nil +} + +// isPrintable reports whether the given b is in the ASN.1 PrintableString set. +// This is a simplified version of encoding/asn1.isPrintable. +func isPrintable(b byte) bool { + return 'a' <= b && b <= 'z' || + 'A' <= b && b <= 'Z' || + '0' <= b && b <= '9' || + '\'' <= b && b <= ')' || + '+' <= b && b <= '/' || + b == ' ' || + b == ':' || + b == '=' || + b == '?' || + // This is technically not allowed in a PrintableString. + // However, x509 certificates with wildcard strings don't + // always use the correct string type so we permit it. + b == '*' || + // This is not technically allowed either. However, not + // only is it relatively common, but there are also a + // handful of CA certificates that contain it. At least + // one of which will not expire until 2027. + b == '&' +} + +// parseASN1String parses the ASN.1 string types T61String, PrintableString, +// UTF8String, BMPString, IA5String, and NumericString. This is mostly copied +// from the respective encoding/asn1.parse... methods, rather than just +// increasing the API surface of that package. +func parseASN1String(tag cryptobyte_asn1.Tag, value []byte) (string, error) { + switch tag { + case cryptobyte_asn1.T61String: + return string(value), nil + case cryptobyte_asn1.PrintableString: + for _, b := range value { + if !isPrintable(b) { + return "", errors.New("invalid PrintableString") + } + } + return string(value), nil + case cryptobyte_asn1.UTF8String: + if !utf8.Valid(value) { + return "", errors.New("invalid UTF-8 string") + } + return string(value), nil + case cryptobyte_asn1.Tag(asn1.TagBMPString): + if len(value)%2 != 0 { + return "", errors.New("invalid BMPString") + } + + // Strip terminator if present. + if l := len(value); l >= 2 && value[l-1] == 0 && value[l-2] == 0 { + value = value[:l-2] + } + + s := make([]uint16, 0, len(value)/2) + for len(value) > 0 { + s = append(s, uint16(value[0])<<8+uint16(value[1])) + value = value[2:] + } + + return string(utf16.Decode(s)), nil + case cryptobyte_asn1.IA5String: + s := string(value) + if isIA5String(s) != nil { + return "", errors.New("invalid IA5String") + } + return s, nil + case cryptobyte_asn1.Tag(asn1.TagNumericString): + for _, b := range value { + if !('0' <= b && b <= '9' || b == ' ') { + return "", errors.New("invalid NumericString") + } + } + return string(value), nil + } + return "", fmt.Errorf("unsupported string type: %v", tag) +} + +// parseName parses a DER encoded Name as defined in RFC 5280. We may +// want to export this function in the future for use in crypto/tls. +func parseName(raw cryptobyte.String) (*pkix.RDNSequence, error) { + if !raw.ReadASN1(&raw, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: invalid RDNSequence") + } + + var rdnSeq pkix.RDNSequence + for !raw.Empty() { + var rdnSet pkix.RelativeDistinguishedNameSET + var set cryptobyte.String + if !raw.ReadASN1(&set, cryptobyte_asn1.SET) { + return nil, errors.New("x509: invalid RDNSequence") + } + for !set.Empty() { + var atav cryptobyte.String + if !set.ReadASN1(&atav, cryptobyte_asn1.SEQUENCE) { + return nil, errors.New("x509: invalid RDNSequence: invalid attribute") + } + var attr pkix.AttributeTypeAndValue + if !atav.ReadASN1ObjectIdentifier(&attr.Type) { + return nil, errors.New("x509: invalid RDNSequence: invalid attribute type") + } + var rawValue cryptobyte.String + var valueTag cryptobyte_asn1.Tag + if !atav.ReadAnyASN1(&rawValue, &valueTag) { + return nil, errors.New("x509: invalid RDNSequence: invalid attribute value") + } + var err error + attr.Value, err = parseASN1String(valueTag, rawValue) + if err != nil { + return nil, fmt.Errorf("x509: invalid RDNSequence: invalid attribute value: %s", err) + } + rdnSet = append(rdnSet, attr) + } + + rdnSeq = append(rdnSeq, rdnSet) + } + + return &rdnSeq, nil +} + +func parseAI(der cryptobyte.String) (pkix.AlgorithmIdentifier, error) { + ai := pkix.AlgorithmIdentifier{} + if !der.ReadASN1ObjectIdentifier(&ai.Algorithm) { + return ai, errors.New("x509: malformed OID") + } + if der.Empty() { + return ai, nil + } + var params cryptobyte.String + var tag cryptobyte_asn1.Tag + if !der.ReadAnyASN1Element(¶ms, &tag) { + return ai, errors.New("x509: malformed parameters") + } + ai.Parameters.Tag = int(tag) + ai.Parameters.FullBytes = params + return ai, nil +} + +func parseTime(der *cryptobyte.String) (time.Time, error) { + var t time.Time + switch { + case der.PeekASN1Tag(cryptobyte_asn1.UTCTime): + if !der.ReadASN1UTCTime(&t) { + return t, errors.New("x509: malformed UTCTime") + } + case der.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime): + if !der.ReadASN1GeneralizedTime(&t) { + return t, errors.New("x509: malformed GeneralizedTime") + } + default: + return t, errors.New("x509: unsupported time format") + } + return t, nil +} + +func parseExtension(der cryptobyte.String) (pkix.Extension, error) { + var ext pkix.Extension + if !der.ReadASN1ObjectIdentifier(&ext.Id) { + return ext, errors.New("x509: malformed extension OID field") + } + if der.PeekASN1Tag(cryptobyte_asn1.BOOLEAN) { + if !der.ReadASN1Boolean(&ext.Critical) { + return ext, errors.New("x509: malformed extension critical field") + } + } + var val cryptobyte.String + if !der.ReadASN1(&val, cryptobyte_asn1.OCTET_STRING) { + return ext, errors.New("x509: malformed extension value field") + } + ext.Value = val + return ext, nil +} diff --git a/x509/crl_parser_test.go b/x509/crl_parser_test.go new file mode 100644 index 00000000..cb89672b --- /dev/null +++ b/x509/crl_parser_test.go @@ -0,0 +1,589 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + _ "crypto/sha256" + _ "crypto/sha512" + "math/big" + "reflect" + "strings" + "testing" + "time" + + "github.com/zmap/zcrypto/encoding/asn1" + "github.com/zmap/zcrypto/x509/pkix" +) + +const ( + // STARTBLOCK: This constant does not exist in upstream. + derGenTimeZ0000Base64 = "MIIBSzCB0wIBATAKBggqhkjOPQQDAzBJMQswCQYDVQQGEwJYWDEVMBMGA1UEChMMQm91bGRlciBUZXN0MSMwIQYDVQQDExooVEVTVCkgRWxlZ2FudCBFbGVwaGFudCBFMRgTMjA1MDA3MDYxNjQzMzhaMDAwMBcNMjIwNzE1MTY0MzM4WjAbMBkCCAOuUdtRFVo8Fw0yMjA3MDYxNTQzMzhaoDYwNDAfBgNVHSMEGDAWgBQB2rt6yyUgjl551vmWQi8CQSkHvjARBgNVHRQECgIIFv9LJt+yGA8wCgYIKoZIzj0EAwMDZwAwZAIwVrITRYutGjFpfNht08CLsAQSvnc4i6UM0Pi8+U3T8DRHImIiuB9cQ+qxULB6pKhBAjBbuGCwTop7vCfGO7Fz6N0ruITInFtt6BDR5izWUMfXXa7mXhSQ6ig9hOHOWRxR00I=" + //This constant does not exist in upstream. + derUTCTimeYYMMDDHHMMZTTBase64 = "MIIBRTCBzQIBATAKBggqhkjOPQQDAzBJMQswCQYDVQQGEwJYWDEVMBMGA1UEChMMQm91bGRlciBUZXN0MSMwIQYDVQQDExooVEVTVCkgRWxlZ2FudCBFbGVwaGFudCBFMRcNMjIwNzA2MTY0M1owOBcNMjIwNzE1MTY0MzM4WjAbMBkCCAOuUdtRFVo8Fw0yMjA3MDYxNTQzMzhaoDYwNDAfBgNVHSMEGDAWgBQB2rt6yyUgjl551vmWQi8CQSkHvjARBgNVHRQECgIIFv9LJt+yGA8wCgYIKoZIzj0EAwMDZwAwZAIwVrITRYutGjFpfNht08CLsAQSvnc4i6UM0Pi8+U3T8DRHImIiuB9cQ+qxULB6pKhBAjBbuGCwTop7vCfGO7Fz6N0ruITInFtt6BDR5izWUMfXXa7mXhSQ6ig9hOHOWRxR00I=" + // ENDBLOCK +) + +func TestCreateRevocationList(t *testing.T) { + ec256Priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("Failed to generate ECDSA P256 key: %s", err) + } + _, ed25519Priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("Failed to generate Ed25519 key: %s", err) + } + reasonKeyCompromise := 1 + tests := []struct { + name string + key crypto.Signer + issuer *Certificate + template *RevocationList + expectedError string + }{ + { + name: "nil template", + key: ec256Priv, + issuer: nil, + template: nil, + expectedError: "x509: template can not be nil", + }, + { + name: "nil issuer", + key: ec256Priv, + issuer: nil, + template: &RevocationList{}, + expectedError: "x509: issuer can not be nil", + }, + { + name: "issuer doesn't have crlSign key usage bit set", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCertSign, + }, + template: &RevocationList{}, + expectedError: "x509: issuer must have the crlSign key usage bit set", + }, + { + name: "issuer missing SubjectKeyId", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + }, + template: &RevocationList{}, + expectedError: "x509: issuer certificate doesn't contain a subject key identifier", + }, + { + name: "nextUpdate before thisUpdate", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + ThisUpdate: time.Time{}.Add(time.Hour), + NextUpdate: time.Time{}, + }, + expectedError: "x509: template.ThisUpdate is after template.NextUpdate", + }, + { + name: "nil Number", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + expectedError: "x509: template contains nil Number field", + }, + { + name: "invalid signature algorithm", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + SignatureAlgorithm: SHA256WithRSA, + RevokedCertificates: []RevokedCertificate{ + { + SerialNumber: big.NewInt(2), + RevocationTime: time.Time{}.Add(time.Hour), + }, + }, + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + expectedError: "x509: requested SignatureAlgorithm does not match private key type", + }, + { + name: "long Number", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + Number: big.NewInt(0).SetBytes(append([]byte{1}, make([]byte, 20)...)), + }, + expectedError: "x509: CRL number exceeds 20 octets", + }, + { + name: "long Number (20 bytes, MSB set)", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + Number: big.NewInt(0).SetBytes(append([]byte{255}, make([]byte, 19)...)), + }, + expectedError: "x509: CRL number exceeds 20 octets", + }, + { + name: "valid", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + RevokedCertificates: []RevokedCertificate{ + { + SerialNumber: big.NewInt(2), + RevocationTime: time.Time{}.Add(time.Hour), + }, + }, + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + }, + { + name: "valid, reason code", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + RevokedCertificates: []RevokedCertificate{ + { + SerialNumber: big.NewInt(2), + RevocationTime: time.Time{}.Add(time.Hour), + ReasonCode: &reasonKeyCompromise, + }, + }, + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + }, + { + name: "valid, extra entry extension", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + RevokedCertificates: []RevokedCertificate{ + { + SerialNumber: big.NewInt(2), + RevocationTime: time.Time{}.Add(time.Hour), + ReasonCode: &reasonKeyCompromise, + ExtraExtensions: []pkix.Extension{ + { + Id: []int{1, 1}, + Value: []byte{5, 0}, + }, + }, + }, + }, + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + }, + { + name: "valid, Ed25519 key", + key: ed25519Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + RevokedCertificates: []RevokedCertificate{ + { + SerialNumber: big.NewInt(2), + RevocationTime: time.Time{}.Add(time.Hour), + }, + }, + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + }, + { + name: "valid, non-default signature algorithm", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + SignatureAlgorithm: ECDSAWithSHA512, + RevokedCertificates: []RevokedCertificate{ + { + SerialNumber: big.NewInt(2), + RevocationTime: time.Time{}.Add(time.Hour), + }, + }, + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + }, + { + name: "valid, extra extension", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + RevokedCertificates: []RevokedCertificate{ + { + SerialNumber: big.NewInt(2), + RevocationTime: time.Time{}.Add(time.Hour), + }, + }, + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + ExtraExtensions: []pkix.Extension{ + { + Id: []int{2, 5, 29, 99}, + Value: []byte{5, 0}, + }, + }, + }, + }, + { + name: "valid, empty list", + key: ec256Priv, + issuer: &Certificate{ + KeyUsage: KeyUsageCRLSign, + Subject: pkix.Name{ + CommonName: "testing", + }, + SubjectKeyId: []byte{1, 2, 3}, + }, + template: &RevocationList{ + Number: big.NewInt(5), + ThisUpdate: time.Time{}.Add(time.Hour * 24), + NextUpdate: time.Time{}.Add(time.Hour * 48), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + crl, err := CreateRevocationList(rand.Reader, tc.template, tc.issuer, tc.key) + if err != nil && tc.expectedError == "" { + t.Fatalf("CreateRevocationList failed unexpectedly: %s", err) + } else if err != nil && tc.expectedError != err.Error() { + t.Fatalf("CreateRevocationList failed unexpectedly, wanted: %s, got: %s", tc.expectedError, err) + } else if err == nil && tc.expectedError != "" { + t.Fatalf("CreateRevocationList didn't fail, expected: %s", tc.expectedError) + } + if tc.expectedError != "" { + return + } + + parsedCRL, err := ParseRevocationList(crl) + if err != nil { + t.Fatalf("Failed to parse generated CRL: %s", err) + } + + if tc.template.SignatureAlgorithm != UnknownSignatureAlgorithm && + parsedCRL.SignatureAlgorithm != tc.template.SignatureAlgorithm { + t.Fatalf("SignatureAlgorithm mismatch: got %v; want %v.", parsedCRL.SignatureAlgorithm, + tc.template.SignatureAlgorithm) + } + + if len(parsedCRL.RevokedCertificates) != len(tc.template.RevokedCertificates) { + t.Fatalf("RevokedCertificates length mismatch: got %d; want %d.", + len(parsedCRL.RevokedCertificates), len(tc.template.RevokedCertificates)) + } + for i, rc := range parsedCRL.RevokedCertificates { + erc := tc.template.RevokedCertificates[i] + if rc.SerialNumber.Cmp(erc.SerialNumber) != 0 { + t.Errorf("RevokedCertificates entry %d serial mismatch: got %s; want %s.", + i, rc.SerialNumber.String(), erc.SerialNumber.String()) + } + if rc.RevocationTime != erc.RevocationTime { + t.Errorf("RevokedCertificates entry %d date mismatch: got %v; want %v.", + i, rc.RevocationTime, erc.RevocationTime) + } + numExtra := 0 + if erc.ReasonCode != nil { + if rc.ReasonCode == nil { + t.Errorf("RevokedCertificates entry %d reason mismatch: got nil; want %v.", + i, *erc.ReasonCode) + } + if *rc.ReasonCode != *erc.ReasonCode { + t.Errorf("RevokedCertificates entry %d reason mismatch: got %v; want %v.", + i, *rc.ReasonCode, *erc.ReasonCode) + } + numExtra = 1 + } else { + if rc.ReasonCode != nil { + t.Errorf("RevokedCertificates entry %d reason mismatch: got %v; want nil.", + i, *rc.ReasonCode) + } + } + if len(rc.Extensions) != numExtra+len(erc.ExtraExtensions) { + t.Errorf("RevokedCertificates entry %d has wrong number of extensions: got %d; want %d", + i, len(rc.Extensions), numExtra+len(erc.ExtraExtensions)) + } + } + + if len(parsedCRL.Extensions) != 2+len(tc.template.ExtraExtensions) { + t.Fatalf("Generated CRL has wrong number of extensions, wanted: %d, got: %d", 2+len(tc.template.ExtraExtensions), len(parsedCRL.Extensions)) + } + expectedAKI, err := asn1.Marshal(authKeyId{Id: tc.issuer.SubjectKeyId}) + if err != nil { + t.Fatalf("asn1.Marshal failed: %s", err) + } + akiExt := pkix.Extension{ + Id: oidExtensionAuthorityKeyId, + Value: expectedAKI, + } + if !reflect.DeepEqual(parsedCRL.Extensions[0], akiExt) { + t.Fatalf("Unexpected first extension: got %v, want %v", + parsedCRL.Extensions[0], akiExt) + } + expectedNum, err := asn1.Marshal(tc.template.Number) + if err != nil { + t.Fatalf("asn1.Marshal failed: %s", err) + } + crlExt := pkix.Extension{ + Id: oidExtensionCRLNumber, + Value: expectedNum, + } + if !reflect.DeepEqual(parsedCRL.Extensions[1], crlExt) { + t.Fatalf("Unexpected second extension: got %v, want %v", + parsedCRL.Extensions[1], crlExt) + } + if len(parsedCRL.Extensions[2:]) == 0 && len(tc.template.ExtraExtensions) == 0 { + // If we don't have anything to check return early so we don't + // hit a [] != nil false positive below. + return + } + if !reflect.DeepEqual(parsedCRL.Extensions[2:], tc.template.ExtraExtensions) { + t.Fatalf("Extensions mismatch: got %v; want %v.", + parsedCRL.Extensions[2:], tc.template.ExtraExtensions) + } + }) + } +} + +func TestParseRevocationList(t *testing.T) { + derBytes := fromBase64(derCRLBase64) + certList, err := ParseRevocationList(derBytes) + if err != nil { + t.Errorf("error parsing: %s", err) + return + } + numCerts := len(certList.RevokedCertificates) + expected := 88 + if numCerts != expected { + t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) + } + + // STARTBLOCK: This block does not exist in upstream. + // Check that 'thisUpdate' of 'GENERALIZEDTIME 20500706164338Z0000' (time + // zone is UTC but also explicitly specified) is considered invalid. + derBytes = fromBase64(derGenTimeZ0000Base64) + _, err = ParseRevocationList(derBytes) + assertError(t, err, "expected error parsing CRL") + assertContains(t, err.Error(), "x509: malformed GeneralizedTime") + + // Check that 'thisUpdate' of 'UTCTIME 2207061643Z08' (YYMMDDHHMMZTT) is + // considered invalid. + derBytes = fromBase64(derUTCTimeYYMMDDHHMMZTTBase64) + _, err = ParseRevocationList(derBytes) + assertContains(t, err.Error(), "x509: malformed UTCTime") + // ENDBLOCK +} + +func TestRevocationListCheckSignatureFrom(t *testing.T) { + goodKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + if err != nil { + t.Fatalf("failed to generate test key: %s", err) + } + badKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + if err != nil { + t.Fatalf("failed to generate test key: %s", err) + } + tests := []struct { + name string + issuer *Certificate + err string + }{ + { + name: "valid", + issuer: &Certificate{ + Version: 3, + BasicConstraintsValid: true, + IsCA: true, + PublicKeyAlgorithm: ECDSA, + PublicKey: goodKey.Public(), + }, + }, + { + name: "valid, key usage set", + issuer: &Certificate{ + Version: 3, + BasicConstraintsValid: true, + IsCA: true, + PublicKeyAlgorithm: ECDSA, + PublicKey: goodKey.Public(), + KeyUsage: KeyUsageCRLSign, + }, + }, + { + name: "invalid issuer, wrong key usage", + issuer: &Certificate{ + Version: 3, + BasicConstraintsValid: true, + IsCA: true, + PublicKeyAlgorithm: ECDSA, + PublicKey: goodKey.Public(), + KeyUsage: KeyUsageCertSign, + }, + err: "x509: invalid signature: parent certificate cannot sign this kind of certificate", + }, + { + name: "invalid issuer, no basic constraints/ca", + issuer: &Certificate{ + Version: 3, + PublicKeyAlgorithm: ECDSA, + PublicKey: goodKey.Public(), + }, + err: "x509: invalid signature: parent certificate cannot sign this kind of certificate", + }, + { + name: "invalid issuer, unsupported public key type", + issuer: &Certificate{ + Version: 3, + BasicConstraintsValid: true, + IsCA: true, + PublicKeyAlgorithm: UnknownPublicKeyAlgorithm, + PublicKey: goodKey.Public(), + }, + err: "x509: cannot verify signature: algorithm unimplemented", + }, + { + name: "wrong key", + issuer: &Certificate{ + Version: 3, + BasicConstraintsValid: true, + IsCA: true, + PublicKeyAlgorithm: ECDSA, + PublicKey: badKey.Public(), + }, + err: "x509: ECDSA verification failure", + }, + } + + crlIssuer := &Certificate{ + BasicConstraintsValid: true, + IsCA: true, + PublicKeyAlgorithm: ECDSA, + PublicKey: goodKey.Public(), + KeyUsage: KeyUsageCRLSign, + SubjectKeyId: []byte{1, 2, 3}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + crlDER, err := CreateRevocationList(rand.Reader, &RevocationList{Number: big.NewInt(1)}, crlIssuer, goodKey) + if err != nil { + t.Fatalf("failed to generate CRL: %s", err) + } + crl, err := ParseRevocationList(crlDER) + if err != nil { + t.Fatalf("failed to parse test CRL: %s", err) + } + err = crl.CheckSignatureFrom(tc.issuer) + if err != nil && err.Error() != tc.err { + t.Errorf("unexpected error: got %s, want %s", err, tc.err) + } else if err == nil && tc.err != "" { + t.Errorf("CheckSignatureFrom did not fail: want %s", tc.err) + } + }) + } +} + +func assertError(t testing.TB, err error, message string) { + t.Helper() + if err == nil { + t.Fatalf("%s: expected error but received none", message) + } +} + +// AssertContains determines whether needle can be found in haystack +func assertContains(t testing.TB, haystack string, needle string) { + t.Helper() + if !strings.Contains(haystack, needle) { + t.Fatalf("String [%s] does not contain [%s]", haystack, needle) + } +} diff --git a/x509/ct/serialization_test.go b/x509/ct/serialization_test.go index 8e1ec452..34f6912e 100644 --- a/x509/ct/serialization_test.go +++ b/x509/ct/serialization_test.go @@ -177,9 +177,9 @@ func defaultSCT() SignedCertificateTimestamp { Signature: defaultSCTSignature()} } -////////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// // Tests start here: -////////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////////// func TestMarshalDigitallySigned(t *testing.T) { b, err := MarshalDigitallySigned( DigitallySigned{ diff --git a/x509/extended_key_usage.go b/x509/extended_key_usage.go index cde3a8e0..85ea4a72 100644 --- a/x509/extended_key_usage.go +++ b/x509/extended_key_usage.go @@ -29,6 +29,7 @@ const ( OID_EKU_APPLE_CRYPTO_TIER1_QOS = "1.2.840.113635.100.4.6.2" OID_EKU_APPLE_CRYPTO_TIER2_QOS = "1.2.840.113635.100.4.6.3" OID_EKU_APPLE_CRYPTO_TIER3_QOS = "1.2.840.113635.100.4.6.4" + OID_EKU_ADOBE_AUTHENTIC_DOCUMENT_TRUST = "1.2.840.113583.1.1.5" OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING = "1.3.6.1.4.1.311.10.3.1" OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE = "1.3.6.1.4.1.311.10.3.10" OID_EKU_MICROSOFT_KEY_RECOVERY_3 = "1.3.6.1.4.1.311.10.3.11" @@ -95,6 +96,7 @@ var ( oidExtKeyUsageAppleCryptoTier1Qos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6, 2} oidExtKeyUsageAppleCryptoTier2Qos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6, 3} oidExtKeyUsageAppleCryptoTier3Qos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6, 4} + oidExtKeyUsageAdobeAuthenticDocumentTrust = asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 1, 5} oidExtKeyUsageMicrosoftCertTrustListSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 1} oidExtKeyUsageMicrosoftQualifiedSubordinate = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 10} oidExtKeyUsageMicrosoftKeyRecovery3 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 11} @@ -161,6 +163,7 @@ const ( ExtKeyUsageAppleCryptoTier1Qos ExtKeyUsageAppleCryptoTier2Qos ExtKeyUsageAppleCryptoTier3Qos + ExtKeyUsageAdobeAuthenticDocumentTrust ExtKeyUsageMicrosoftCertTrustListSigning ExtKeyUsageMicrosoftQualifiedSubordinate ExtKeyUsageMicrosoftKeyRecovery3 @@ -227,6 +230,7 @@ type auxExtendedKeyUsage struct { AppleCryptoTier1Qos bool `json:"apple_crypto_tier1_qos,omitempty" oid:"1.2.840.113635.100.4.6.2"` AppleCryptoTier2Qos bool `json:"apple_crypto_tier2_qos,omitempty" oid:"1.2.840.113635.100.4.6.3"` AppleCryptoTier3Qos bool `json:"apple_crypto_tier3_qos,omitempty" oid:"1.2.840.113635.100.4.6.4"` + AdobeAuthenticDocumentTrust bool `json:"adobe_authentic_document_trust,omitempty" oid:"1.2.840.113583.1.1.5"` MicrosoftCertTrustListSigning bool `json:"microsoft_cert_trust_list_signing,omitempty" oid:"1.3.6.1.4.1.311.10.3.1"` MicrosoftQualifiedSubordinate bool `json:"microsoft_qualified_subordinate,omitempty" oid:"1.3.6.1.4.1.311.10.3.10"` MicrosoftKeyRecovery3 bool `json:"microsoft_key_recovery_3,omitempty" oid:"1.3.6.1.4.1.311.10.3.11"` @@ -314,6 +318,8 @@ func (aux *auxExtendedKeyUsage) populateFromASN1(oid asn1.ObjectIdentifier) { aux.AppleCryptoTier2Qos = true case OID_EKU_APPLE_CRYPTO_TIER3_QOS: aux.AppleCryptoTier3Qos = true + case OID_EKU_ADOBE_AUTHENTIC_DOCUMENT_TRUST: + aux.AdobeAuthenticDocumentTrust = true case OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING: aux.MicrosoftCertTrustListSigning = true case OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE: @@ -447,6 +453,8 @@ func (aux *auxExtendedKeyUsage) populateFromExtKeyUsage(eku ExtKeyUsage) { aux.AppleCryptoTier2Qos = true case ExtKeyUsageAppleCryptoTier3Qos: aux.AppleCryptoTier3Qos = true + case ExtKeyUsageAdobeAuthenticDocumentTrust: + aux.AdobeAuthenticDocumentTrust = true case ExtKeyUsageMicrosoftCertTrustListSigning: aux.MicrosoftCertTrustListSigning = true case ExtKeyUsageMicrosoftQualifiedSubordinate: @@ -566,6 +574,7 @@ func init() { ekuOIDs[OID_EKU_APPLE_CRYPTO_TIER1_QOS] = oidExtKeyUsageAppleCryptoTier1Qos ekuOIDs[OID_EKU_APPLE_CRYPTO_TIER2_QOS] = oidExtKeyUsageAppleCryptoTier2Qos ekuOIDs[OID_EKU_APPLE_CRYPTO_TIER3_QOS] = oidExtKeyUsageAppleCryptoTier3Qos + ekuOIDs[OID_EKU_ADOBE_AUTHENTIC_DOCUMENT_TRUST] = oidExtKeyUsageAdobeAuthenticDocumentTrust ekuOIDs[OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING] = oidExtKeyUsageMicrosoftCertTrustListSigning ekuOIDs[OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE] = oidExtKeyUsageMicrosoftQualifiedSubordinate ekuOIDs[OID_EKU_MICROSOFT_KEY_RECOVERY_3] = oidExtKeyUsageMicrosoftKeyRecovery3 @@ -631,6 +640,7 @@ func init() { ekuConstants[OID_EKU_APPLE_CRYPTO_TIER1_QOS] = ExtKeyUsageAppleCryptoTier1Qos ekuConstants[OID_EKU_APPLE_CRYPTO_TIER2_QOS] = ExtKeyUsageAppleCryptoTier2Qos ekuConstants[OID_EKU_APPLE_CRYPTO_TIER3_QOS] = ExtKeyUsageAppleCryptoTier3Qos + ekuConstants[OID_EKU_ADOBE_AUTHENTIC_DOCUMENT_TRUST] = ExtKeyUsageAdobeAuthenticDocumentTrust ekuConstants[OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING] = ExtKeyUsageMicrosoftCertTrustListSigning ekuConstants[OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE] = ExtKeyUsageMicrosoftQualifiedSubordinate ekuConstants[OID_EKU_MICROSOFT_KEY_RECOVERY_3] = ExtKeyUsageMicrosoftKeyRecovery3 diff --git a/x509/extended_key_usage_gen.go b/x509/extended_key_usage_gen.go index 087fbda5..d70dff35 100644 --- a/x509/extended_key_usage_gen.go +++ b/x509/extended_key_usage_gen.go @@ -1,5 +1,6 @@ // The following directive is necessary to make the package coherent: +//go:build ignore // +build ignore // This program generates extended_key_usage.go. It can be invoked by running diff --git a/x509/extensions.go b/x509/extensions.go index b1edaf26..ffb87a26 100644 --- a/x509/extensions.go +++ b/x509/extensions.go @@ -27,6 +27,8 @@ var ( oidExtSubjectKeyId = asn1.ObjectIdentifier{2, 5, 29, 14} oidExtExtendedKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37} oidExtCertificatePolicy = asn1.ObjectIdentifier{2, 5, 29, 32} + oidExtensionCRLNumber = asn1.ObjectIdentifier{2, 5, 29, 20} + oidExtensionReasonCode = asn1.ObjectIdentifier{2, 5, 29, 21} oidExtAuthorityInfoAccess = oidExtensionAuthorityInfoAccess oidExtensionCTPrecertificatePoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3} @@ -121,16 +123,16 @@ func (cp *CertificatePoliciesData) MarshalJSON() ([]byte, error) { // GeneralNames corresponds an X.509 GeneralName defined in // Section 4.2.1.6 of RFC 5280. // -// GeneralName ::= CHOICE { -// otherName [0] AnotherName, -// rfc822Name [1] IA5String, -// dNSName [2] IA5String, -// x400Address [3] ORAddress, -// directoryName [4] Name, -// ediPartyName [5] EDIPartyName, -// uniformResourceIdentifier [6] IA5String, -// iPAddress [7] OCTET STRING, -// registeredID [8] OBJECT IDENTIFIER } +// GeneralName ::= CHOICE { +// otherName [0] AnotherName, +// rfc822Name [1] IA5String, +// dNSName [2] IA5String, +// x400Address [3] ORAddress, +// directoryName [4] Name, +// ediPartyName [5] EDIPartyName, +// uniformResourceIdentifier [6] IA5String, +// iPAddress [7] OCTET STRING, +// registeredID [8] OBJECT IDENTIFIER } type GeneralNames struct { DirectoryNames []pkix.Name DNSNames []string @@ -404,6 +406,7 @@ func (e *ExtendedKeyUsageExtension) UnmarshalJSON(b []byte) error { // The string functions for CertValidationLevel are auto-generated via // `go generate ` or running `go generate` in the package directory +// //go:generate stringer -type=CertValidationLevel -output=generated_certvalidationlevel_string.go type CertValidationLevel int @@ -694,21 +697,21 @@ type AuthorityInfoAccess struct { } /* - id-CABFOrganizationIdentifier OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) international-organizations(23) ca-browser-forum(140) certificate-extensions(3) cabf-organization-identifier(1) } + id-CABFOrganizationIdentifier OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) international-organizations(23) ca-browser-forum(140) certificate-extensions(3) cabf-organization-identifier(1) } - ext-CABFOrganizationIdentifier EXTENSION ::= { SYNTAX CABFOrganizationIdentifier IDENTIFIED BY id-CABFOrganizationIdentifier } + ext-CABFOrganizationIdentifier EXTENSION ::= { SYNTAX CABFOrganizationIdentifier IDENTIFIED BY id-CABFOrganizationIdentifier } - CABFOrganizationIdentifier ::= SEQUENCE { + CABFOrganizationIdentifier ::= SEQUENCE { - registrationSchemeIdentifier PrintableString (SIZE(3)), + registrationSchemeIdentifier PrintableString (SIZE(3)), - registrationCountry PrintableString (SIZE(2)), + registrationCountry PrintableString (SIZE(2)), - registrationStateOrProvince [0] IMPLICIT PrintableString OPTIONAL (SIZE(0..128)), + registrationStateOrProvince [0] IMPLICIT PrintableString OPTIONAL (SIZE(0..128)), - registrationReference UTF8String + registrationReference UTF8String - } + } */ type CABFOrganizationIDASN struct { RegistrationSchemeIdentifier string `asn1:"printable"` diff --git a/x509/json.go b/x509/json.go index a1b73a08..af26c495 100644 --- a/x509/json.go +++ b/x509/json.go @@ -25,7 +25,7 @@ var kMinTime, kMaxTime time.Time func init() { var err error - kMinTime, err = time.Parse(time.RFC3339, "1970-01-01T00:00:00Z") + kMinTime, err = time.Parse(time.RFC3339, "0001-01-01T00:00:00Z") if err != nil { panic(err) } @@ -218,7 +218,7 @@ func (v *validity) MarshalJSON() ([]byte, error) { aux := auxValidity{ Start: clampTime(v.NotBefore.UTC()).Format(time.RFC3339), End: clampTime(v.NotAfter.UTC()).Format(time.RFC3339), - ValidityPeriod: int(v.NotAfter.Sub(v.NotBefore).Seconds()), + ValidityPeriod: int(v.NotAfter.Unix() - v.NotBefore.Unix()), } return json.Marshal(&aux) } @@ -235,7 +235,6 @@ func (v *validity) UnmarshalJSON(b []byte) error { if v.NotAfter, err = time.Parse(time.RFC3339, aux.End); err != nil { return err } - return nil } diff --git a/x509/json_test.go b/x509/json_test.go index 85d6855e..1950b26b 100644 --- a/x509/json_test.go +++ b/x509/json_test.go @@ -236,6 +236,10 @@ func TestPublicKeyAlgorithmJSON(t *testing.T) { } func TestValidityJSON(t *testing.T) { + preEpoch, err := time.Parse("20060102150405", "19040824000000") + if err != nil { + t.Error(err) + } tests := []validity{ { NotBefore: time.Unix(1400000000, 0), @@ -245,6 +249,18 @@ func TestValidityJSON(t *testing.T) { NotBefore: time.Unix(1000000000, 0), NotAfter: time.Unix(1700000000, 0), }, + { + NotBefore: preEpoch, + NotAfter: preEpoch, + }, + { + NotBefore: kMinTime, + NotAfter: kMaxTime, + }, + { + NotBefore: kMaxTime, + NotAfter: kMinTime, + }, } for i, v := range tests { j, err := json.Marshal(&v) diff --git a/x509/pkcs8_test.go b/x509/pkcs8_test.go index 4114efd0..86f5f289 100644 --- a/x509/pkcs8_test.go +++ b/x509/pkcs8_test.go @@ -12,7 +12,8 @@ import ( var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` // Generated using: -// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt +// +// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt var pkcs8ECPrivateKeyHex = `3081ed020100301006072a8648ce3d020106052b810400230481d53081d20201010441850d81618c5da1aec74c2eed608ba816038506975e6427237c2def150c96a3b13efbfa1f89f1be15cdf4d0ac26422e680e65a0ddd4ad3541ad76165fbf54d6e34ba18189038186000400da97bcedba1eb6d30aeb93c9f9a1454598fa47278df27d6f60ea73eb672d8dc528a9b67885b5b5dcef93c9824f7449ab512ee6a27e76142f56b94b474cfd697e810046c8ca70419365245c1d7d44d0db82c334073835d002232714548abbae6e5700f5ef315ee08b929d8581383dcf2d1c98c2f8a9fccbf79c9579f7b2fd8a90115ac2` func TestPKCS8(t *testing.T) { diff --git a/x509/pkix/pkix.go b/x509/pkix/pkix.go index 176b6c44..57eed3b4 100644 --- a/x509/pkix/pkix.go +++ b/x509/pkix/pkix.go @@ -256,13 +256,13 @@ func (r RDNSequence) String() string { // ToRDNSequence converts n into a single RDNSequence. The following // attributes are encoded as multi-value RDNs: // -// - Country -// - Organization -// - OrganizationalUnit -// - Locality -// - Province -// - StreetAddress -// - PostalCode +// - Country +// - Organization +// - OrganizationalUnit +// - Locality +// - Province +// - StreetAddress +// - PostalCode // // Each ExtraNames entry is encoded as an individual RDN. func (n Name) ToRDNSequence() (ret RDNSequence) { @@ -272,6 +272,7 @@ func (n Name) ToRDNSequence() (ret RDNSequence) { if len(n.CommonName) > 0 { ret = n.appendRDNs(ret, []string{n.CommonName}, oidCommonName) } + ret = n.appendRDNs(ret, n.EmailAddress, oidDNEmailAddress) ret = n.appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) ret = n.appendRDNs(ret, n.Organization, oidOrganization) ret = n.appendRDNs(ret, n.StreetAddress, oidStreetAddress) diff --git a/x509/revocation/microsoft/microsoft.go b/x509/revocation/microsoft/microsoft.go index a6986ef1..763ffee5 100644 --- a/x509/revocation/microsoft/microsoft.go +++ b/x509/revocation/microsoft/microsoft.go @@ -58,7 +58,7 @@ func Check(disallowed *DisallowedCerts, cert *x509.Certificate) *Entry { // Implementation details below - includes home-baked parsing Microsoft sst format // VBASigSerializedCertStore - The serialized digital certificate store specifies -//structures for storing a digital certificate store containing a single digital +// structures for storing a digital certificate store containing a single digital // certificate and, optionally, a list of properties associated with the certificate. type VBASigSerializedCertStore struct { Version uint32 // must be 0x00000000 diff --git a/x509/sec1.go b/x509/sec1.go index 4ef211ff..8d5c1657 100644 --- a/x509/sec1.go +++ b/x509/sec1.go @@ -18,8 +18,10 @@ const ecPrivKeyVersion = 1 // ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure. // References: -// RFC 5915 -// SEC1 - http://www.secg.org/sec1-v2.pdf +// +// RFC 5915 +// SEC1 - http://www.secg.org/sec1-v2.pdf +// // Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in // most cases it is not. type ecPrivateKey struct { diff --git a/x509/x509.go b/x509/x509.go index 8057b0b0..61b10e38 100644 --- a/x509/x509.go +++ b/x509/x509.go @@ -18,6 +18,7 @@ import ( _ "crypto/sha512" "io" "strings" + "unicode" "bytes" "crypto" @@ -26,7 +27,6 @@ import ( "crypto/rsa" _ "crypto/sha1" _ "crypto/sha256" - _ "crypto/sha512" "encoding/pem" "errors" "fmt" @@ -336,6 +336,7 @@ var ( oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112} oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} @@ -492,17 +493,19 @@ func GetSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm // RFC 3279, 2.3 Public Key Algorithms // // pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// rsadsi(113549) pkcs(1) 1 } +// +// rsadsi(113549) pkcs(1) 1 } // // rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } // // id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// x9-57(10040) x9cm(4) 1 } // -// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters +// x9-57(10040) x9cm(4) 1 } +// +// # RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters // -// id-ecPublicKey OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } +// id-ecPublicKey OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } var ( oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} @@ -527,18 +530,18 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm // RFC 5480, 2.1.1.1. Named Curve // -// secp224r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +// secp224r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 33 } // -// secp256r1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) -// prime(1) 7 } +// secp256r1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +// prime(1) 7 } // -// secp384r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +// secp384r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 34 } // -// secp521r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +// secp521r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 35 } // // NB: secp256r1 is equivalent to prime256v1 var ( @@ -1557,7 +1560,7 @@ func parseGeneralNames(value []byte) (otherNames []pkix.OtherName, dnsNames, ema return } -//TODO +// TODO func parseCertificate(in *certificate) (*Certificate, error) { out := new(Certificate) out.Raw = in.Raw @@ -2271,7 +2274,7 @@ func buildExtensions(template *Certificate, _ []byte) (ret []pkix.Extension, err l = 2 } - ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) + ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: a[0:l], BitLength: asn1BitLength(a[0:l])}) if err != nil { return } @@ -3090,3 +3093,323 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error func (c *CertificateRequest) CheckSignature() error { return CheckSignatureFromKey(c.PublicKey, c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature) } + +// RevocationList contains the fields used to create an X.509 v2 Certificate +// Revocation list with CreateRevocationList. +type RevocationList struct { + // Raw contains the complete ASN.1 DER content of the CRL (tbsCertList, + // signatureAlgorithm, and signatureValue.) + Raw []byte + // RawTBSRevocationList contains just the tbsCertList portion of the ASN.1 + // DER. + RawTBSRevocationList []byte + // RawIssuer contains the DER encoded Issuer. + RawIssuer []byte + + // Issuer contains the DN of the issuing certificate. + Issuer pkix.Name + // AuthorityKeyId is used to identify the public key associated with the + // issuing certificate. It is populated from the authorityKeyIdentifier + // extension when parsing a CRL. It is ignored when creating a CRL; the + // extension is populated from the issuing certificate itself. + AuthorityKeyId []byte + + Signature []byte + // SignatureAlgorithm is used to determine the signature algorithm to be + // used when signing the CRL. If 0 the default algorithm for the signing + // key will be used. + SignatureAlgorithm SignatureAlgorithm + + // RevokedCertificates is used to populate the revokedCertificates + // sequence in the CRL, it may be empty. RevokedCertificates may be nil, + // in which case an empty CRL will be created. + RevokedCertificates []RevokedCertificate + + // Number is used to populate the X.509 v2 cRLNumber extension in the CRL, + // which should be a monotonically increasing sequence number for a given + // CRL scope and CRL issuer. It is also populated from the cRLNumber + // extension when parsing a CRL. + Number *big.Int + + // ThisUpdate is used to populate the thisUpdate field in the CRL, which + // indicates the issuance date of the CRL. + ThisUpdate time.Time + // NextUpdate is used to populate the nextUpdate field in the CRL, which + // indicates the date by which the next CRL will be issued. NextUpdate + // must be greater than ThisUpdate. + NextUpdate time.Time + + // Extensions contains raw X.509 extensions. When creating a CRL, + // the Extensions field is ignored, see ExtraExtensions. + Extensions []pkix.Extension + + // ExtraExtensions contains any additional extensions to add directly to + // the CRL. + ExtraExtensions []pkix.Extension +} + +// These structures reflect the ASN.1 structure of X.509 CRLs better than +// the existing crypto/x509/pkix variants do. These mirror the existing +// certificate structs in this file. +// +// Notably, we include issuer as an asn1.RawValue, mirroring the behavior of +// tbsCertificate and allowing raw (unparsed) subjects to be passed cleanly. +type certificateList struct { + TBSCertList tbsCertificateList + SignatureAlgorithm pkix.AlgorithmIdentifier + SignatureValue asn1.BitString +} + +type tbsCertificateList struct { + Raw asn1.RawContent + Version int `asn1:"optional,default:0"` + Signature pkix.AlgorithmIdentifier + Issuer asn1.RawValue + ThisUpdate time.Time + NextUpdate time.Time `asn1:"optional"` + RevokedCertificates []pkix.RevokedCertificate `asn1:"optional"` + Extensions []pkix.Extension `asn1:"tag:0,optional,explicit"` +} + +func isIA5String(s string) error { + for _, r := range s { + // Per RFC5280 "IA5String is limited to the set of ASCII characters" + if r > unicode.MaxASCII { + return fmt.Errorf("x509: %q cannot be encoded as an IA5String", s) + } + } + + return nil +} + +func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm { + if ai.Algorithm.Equal(oidSignatureEd25519) { + // RFC 8410, Section 3 + // > For all of the OIDs, the parameters MUST be absent. + if len(ai.Parameters.FullBytes) != 0 { + return UnknownSignatureAlgorithm + } + } + + if !ai.Algorithm.Equal(oidSignatureRSAPSS) { + for _, details := range signatureAlgorithmDetails { + if ai.Algorithm.Equal(details.oid) { + return details.algo + } + } + return UnknownSignatureAlgorithm + } + + // RSA PSS is special because it encodes important parameters + // in the Parameters. + + var params pssParameters + if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil { + return UnknownSignatureAlgorithm + } + + var mgf1HashFunc pkix.AlgorithmIdentifier + if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil { + return UnknownSignatureAlgorithm + } + + // PSS is greatly overburdened with options. This code forces them into + // three buckets by requiring that the MGF1 hash function always match the + // message hash function (as recommended in RFC 3447, Section 8.1), that the + // salt length matches the hash length, and that the trailer field has the + // default value. + if (len(params.Hash.Parameters.FullBytes) != 0 && !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes)) || + !params.MGF.Algorithm.Equal(oidMGF1) || + !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || + (len(mgf1HashFunc.Parameters.FullBytes) != 0 && !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes)) || + params.TrailerField != 1 { + return UnknownSignatureAlgorithm + } + + switch { + case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32: + return SHA256WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48: + return SHA384WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64: + return SHA512WithRSAPSS + } + + return UnknownSignatureAlgorithm +} + +// CreateRevocationList creates a new X.509 v2 Certificate Revocation List, +// according to RFC 5280, based on template. +// +// The CRL is signed by priv which should be the private key associated with +// the public key in the issuer certificate. +// +// The issuer may not be nil, and the crlSign bit must be set in KeyUsage in +// order to use it as a CRL issuer. +// +// The issuer distinguished name CRL field and authority key identifier +// extension are populated using the issuer certificate. issuer must have +// SubjectKeyId set. +func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Certificate, priv crypto.Signer) ([]byte, error) { + if template == nil { + return nil, errors.New("x509: template can not be nil") + } + if issuer == nil { + return nil, errors.New("x509: issuer can not be nil") + } + if (issuer.KeyUsage & KeyUsageCRLSign) == 0 { + return nil, errors.New("x509: issuer must have the crlSign key usage bit set") + } + if len(issuer.SubjectKeyId) == 0 { + return nil, errors.New("x509: issuer certificate doesn't contain a subject key identifier") + } + if template.NextUpdate.Before(template.ThisUpdate) { + return nil, errors.New("x509: template.ThisUpdate is after template.NextUpdate") + } + if template.Number == nil { + return nil, errors.New("x509: template contains nil Number field") + } + + hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm) + if err != nil { + return nil, err + } + + // Convert the ReasonCode field to a proper extension, and force revocation + // times to UTC per RFC 5280. + // STARTBLOCK: This block differs from upstream. Upstream: revokedCertsUTC := make([]pkix.RevokedCertificate, len(template.RevokedCertificates)) + revokedCerts := make([]pkix.RevokedCertificate, len(template.RevokedCertificates)) + for i, rc := range template.RevokedCertificates { + prc := pkix.RevokedCertificate{ + SerialNumber: rc.SerialNumber, + RevocationTime: rc.RevocationTime.UTC(), + } + + // Copy over any extra extensions, except for a Reason Code extension, + // because we'll synthesize that ourselves to ensure it is correct. + exts := make([]pkix.Extension, 0) + for _, ext := range rc.ExtraExtensions { + if ext.Id.Equal(oidExtensionReasonCode) { + continue + } + exts = append(exts, ext) + } + + // Only add a reasonCode extension if the reason is non-zero, as per + // RFC 5280 Section 5.3.1. + if rc.ReasonCode != nil && *rc.ReasonCode != 0 { + reasonBytes, err := asn1.Marshal(asn1.Enumerated(*rc.ReasonCode)) + if err != nil { + return nil, err + } + + exts = append(exts, pkix.Extension{ + Id: oidExtensionReasonCode, + Value: reasonBytes, + }) + } + + if len(exts) > 0 { + prc.Extensions = exts + } + revokedCerts[i] = prc + } + // ENDBLOCK + + aki, err := asn1.Marshal(authKeyId{Id: issuer.SubjectKeyId}) + if err != nil { + return nil, err + } + + if numBytes := template.Number.Bytes(); len(numBytes) > 20 || (len(numBytes) == 20 && numBytes[0]&0x80 != 0) { + return nil, errors.New("x509: CRL number exceeds 20 octets") + } + crlNum, err := asn1.Marshal(template.Number) + if err != nil { + return nil, err + } + + // Correctly use the issuer's subject sequence if one is specified. + issuerSubject, err := subjectBytes(issuer) + if err != nil { + return nil, err + } + + tbsCertList := tbsCertificateList{ + Version: 1, // v2 + Signature: signatureAlgorithm, + Issuer: asn1.RawValue{FullBytes: issuerSubject}, + ThisUpdate: template.ThisUpdate.UTC(), + NextUpdate: template.NextUpdate.UTC(), + Extensions: []pkix.Extension{ + { + Id: oidExtensionAuthorityKeyId, + Value: aki, + }, + { + Id: oidExtensionCRLNumber, + Value: crlNum, + }, + }, + } + if len(revokedCerts) > 0 { + tbsCertList.RevokedCertificates = revokedCerts + } + + if len(template.ExtraExtensions) > 0 { + tbsCertList.Extensions = append(tbsCertList.Extensions, template.ExtraExtensions...) + } + + tbsCertListContents, err := asn1.Marshal(tbsCertList) + if err != nil { + return nil, err + } + + // Optimization to only marshal this struct once, when signing and + // then embedding in certificateList below. + tbsCertList.Raw = tbsCertListContents + + input := tbsCertListContents + if hashFunc != 0 { + h := hashFunc.New() + h.Write(tbsCertListContents) + input = h.Sum(nil) + } + var signerOpts crypto.SignerOpts = hashFunc + if template.SignatureAlgorithm.isRSAPSS() { + signerOpts = &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: hashFunc, + } + } + + signature, err := priv.Sign(rand, input, signerOpts) + if err != nil { + return nil, err + } + + return asn1.Marshal(certificateList{ + TBSCertList: tbsCertList, + SignatureAlgorithm: signatureAlgorithm, + SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, + }) +} + +// CheckSignatureFrom verifies that the signature on rl is a valid signature +// from issuer. +func (rl *RevocationList) CheckSignatureFrom(parent *Certificate) error { + if parent.Version == 3 && !parent.BasicConstraintsValid || + parent.BasicConstraintsValid && !parent.IsCA { + return ConstraintViolationError{} + } + + if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCRLSign == 0 { + return ConstraintViolationError{} + } + + if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { + return ErrUnsupportedAlgorithm + } + + return parent.CheckSignature(rl.SignatureAlgorithm, rl.RawTBSRevocationList, rl.Signature) +} diff --git a/x509/x509_test.go b/x509/x509_test.go index 2416156d..e7df1a0b 100644 --- a/x509/x509_test.go +++ b/x509/x509_test.go @@ -1115,13 +1115,15 @@ func TestMaxPathLen(t *testing.T) { } // This CSR was generated with OpenSSL: -// openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key -config openssl.cnf +// +// openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key -config openssl.cnf // // The openssl.cnf needs to include this section: -// [ v3_req ] -// basicConstraints = CA:FALSE -// keyUsage = nonRepudiation, digitalSignature, keyEncipherment -// subjectAltName = email:gopher@golang.org,DNS:test.example.com +// +// [ v3_req ] +// basicConstraints = CA:FALSE +// keyUsage = nonRepudiation, digitalSignature, keyEncipherment +// subjectAltName = email:gopher@golang.org,DNS:test.example.com const csrBase64 = "MIIC4zCCAcsCAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOY+MVedRg2JEnyeLcSzcsMv2VcsTfkB5+Etd6hihAh6MrGezNyASMMKuQN6YhCX1icQDiQtGsDLTtheNnSXK06tAhHjAP/hGlszRJp+5+rP2M58fDBAkUBEhskbCUWwpY14jFtVuGNJ8vF8h8IeczdolvQhX9lVai9G0EUXJMliMKdjA899H0mRs9PzHyidyrXFNiZlQXfD8Kg7gETn2Ny965iyI6ujAIYSCvam6TnxRHYH2MBKyVGvsYGbPYUQJCsgdgyajEg6ekihvQY3SzO1HSAlZAd7d1QYO4VeWJ2mY6Wu3Jpmh+AmG19S9CcHqGjd0bhuAX9cpPOKgnEmqn0CAwEAAaBZMFcGCSqGSIb3DQEJDjFKMEgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLgYDVR0RBCcwJYERZ29waGVyQGdvbGFuZy5vcmeCEHRlc3QuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAC9+QpKfdabxwCWwf4IEe1cKjdXLS1ScSuw27a3kZzQiPV78WJMa6dB8dqhdH5BRwGZ/qsgLrO6ZHlNeIv2Ib41Ccq71ecHW/nXc94A1BzJ/bVdI9LZcmTUvR1/m1jCpN7UqQ0ml1u9VihK7Pe762hEYxuWDQzYEU0l15S/bXmqeq3eF1A59XT/2jwe5+NV0Wwf4UQlkTXsAQMsJ+KzrQafd8Qv2A49o048uRvmjeJDrXLawGVianZ7D5A6Fpd1rZh6XcjqBpmgLw41DRQWENOdzhy+HyphKRv1MlY8OLkNqpGMhu8DdgJVGoT16DGiickoEa7Z3UCPVNgdTkT9jq7U=" const sanManyOtherName = "MEmgEAYIKwYBBAHZWy6gBAICAc2CCHRlc3QuZ292oA8GCCsGAQQB2VsuoAMCASqCB2dvdi5nb3agEQYIKwYBBAHZWy6gBQIDAXUA" diff --git a/x509/x509_test_import.go b/x509/x509_test_import.go index 4a0645c7..4df5f74c 100644 --- a/x509/x509_test_import.go +++ b/x509/x509_test_import.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // This file is run by the x509 tests to ensure that a program with minimal diff --git a/x509/zintermediate/zintermediate.go b/x509/zintermediate/zintermediate.go index 001d7e4a..e752caec 100644 --- a/x509/zintermediate/zintermediate.go +++ b/x509/zintermediate/zintermediate.go @@ -30,8 +30,8 @@ // chains through it, but will output it as valid. // // Examples: -// $ zintermediate --roots roots.pem candidates.csv > intermediates.pem // +// $ zintermediate --roots roots.pem candidates.csv > intermediates.pem package main import ( diff --git a/zcrypto_schemas/zcrypto.py b/zcrypto_schemas/zcrypto.py index 40a020be..c5dded23 100644 --- a/zcrypto_schemas/zcrypto.py +++ b/zcrypto_schemas/zcrypto.py @@ -366,7 +366,7 @@ def getUnknowns(known, range, unknown="unknown"): "end": Timestamp(doc="Timestamp of when certificate expires. Timezone is UTC."), "length": Signed64BitInteger(doc="The length of time, in seconds, that the certificate is valid."), }, category="Validity Period"), - "signature_algorithm": SignatureAlgorithm(doc="Identifies the algorithm used by the CA to sign the certificate."), + "signature_algorithm": SignatureAlgorithm(doc="Identifies the algorithm used by the CA to sign the certificate.", category="Signature"), "subject_key_info": SubRecord({ "fingerprint_sha256": HexString(doc="The SHA2-256 digest calculated over the certificate's DER-encoded SubjectPublicKeyInfo field."), "key_algorithm": PublicKeyAlgorithm(doc="Identifies the type of key and any relevant parameters."), @@ -392,7 +392,7 @@ def getUnknowns(known, range, unknown="unknown"): "max_path_len": Signed32BitInteger(doc="When present, gives the maximum number of non-self-issued intermediate certificates that may follow this certificate in a valid certification path."), }, category="Basic Constraints", doc="The parsed id-ce-basicConstraints extension (2.5.29.19); see RFC 5280."), "subject_alt_name": GeneralNames(category="Subject Alternate Names (SANs)", doc="The parsed Subject Alternative Name extension (id-ce-subjectAltName, 2.5.29.17).", required=False), - "issuer_alt_name": GeneralNames(doc="The parsed Issuer Alternative Name extension (id-ce-issuerAltName, 2.5.29.18).", required=False), + "issuer_alt_name": GeneralNames(category="Issuer Alternate Names (IANs)", doc="The parsed Issuer Alternative Name extension (id-ce-issuerAltName, 2.5.29.18).", required=False), "crl_distribution_points": ListOf(URL(), category="CRL Distribution Points", doc="The parsed id-ce-cRLDistributionPoints extension (2.5.29.31). Contents are a list of distributionPoint URLs (other distributionPoint types are omitted)."), # NOTE: inherit the SubjAuthKeyId docs "authority_key_id": SubjAuthKeyId(category="Authority Key ID (AKID)"), @@ -606,7 +606,7 @@ def getUnknowns(known, range, unknown="unknown"): "heartbeat": Boolean(doc="This is true if the client has the Heartbeat Supported extension (see https://tools.ietf.org/html/rfc6520)."), "extended_random": Binary(doc="The value of the Extended Random extension, if present (see https://tools.ietf.org/html/draft-rescorla-tls-extended-random-02)."), "extended_master_secret": Boolean(doc="This is true if the client has the Extended Master Secret extension (see https://tools.ietf.org/html/rfc7627)."), - "next_protocol_negotiation": Boolean(doc="This is true if the client has the Next Protocol Negotiation extension (see https://tools.ietf.org/id/draft-agl-tls-nextprotoneg-03.html)."), + "next_protocol_negotiation": Boolean(doc="This is true if the client has the Next Protocol Negotiation extension (see https://datatracker.ietf.org/doc/html/draft-agl-tls-nextprotoneg-04)."), "server_name": String(doc="This contains the server name from the Server Name Identification (SNI) extension, if present (see https://tools.ietf.org/html/rfc6066#section-3)."), "scts": Boolean(doc="This is true if the client has the Signed Certificate Timestamp extension, if present (see https://tools.ietf.org/html/rfc6962#section-3.3.1)"), "supported_curves": ListOf(CurveID(), doc="The list of supported curves in the Supported Elliptic Curves extension, if present (see https://tools.ietf.org/html/rfc4492#section-5.1.1)"), @@ -624,18 +624,20 @@ def getUnknowns(known, range, unknown="unknown"): "random": Binary(doc="This structure is generated by the server and MUST be independently generated from the ClientHello.random."), "session_id": Binary(doc="This is the identity of the session corresponding to this connection."), "cipher_suite": CipherSuite(doc="The single cipher suite selected by the server from the list in ClientHello.cipher_suites."), - # TODO FIXME: This is a uint8 in the go code, but it should probably be a CompressionMethod...? - "compression_method": Unsigned8BitInteger(doc="The single compression algorithm selected by the server from the list in ClientHello.compression_methods."), + "compression_method": CompressionMethod(doc="The single compression algorithm selected by the server from the list in ClientHello.compression_methods."), "ocsp_stapling": Boolean(doc="This is true if the OCSP Stapling extension is set (see https://www.ietf.org/rfc/rfc6961.txt for details)."), "ticket": Boolean(doc="This is true if the server has the Session Ticket extension (see https://tools.ietf.org/html/rfc5077)."), "secure_renegotiation": Boolean(doc="This is true if the client has the Secure Renegotiation extension (see https://tools.ietf.org/html/rfc5746)."), "heartbeat": Boolean(doc="This is true if the client has the Heartbeat Supported extension (see https://tools.ietf.org/html/rfc6520)."), "extended_random": Binary(doc="The value of the Extended Random extension, if present (see https://tools.ietf.org/html/draft-rescorla-tls-extended-random-02)."), + "next_protocol_negotiation": Boolean(doc="This is true if the server has the Next Protocol Negotiation extension (see https://datatracker.ietf.org/doc/html/draft-agl-tls-nextprotoneg-04)."), "extended_master_secret": Boolean(doc="This is true if the server has the Extended Master Secret extension (see https://tools.ietf.org/html/rfc7627)."), "scts": ListOf(SubRecord({ "parsed": SCTRecord(), "raw": Binary(), }), doc="The values in the SignedCertificateTimestampList of the Signed Certificate Timestamp, if present."), + "alpn_protocol": String(doc="This contains the selected protocol from the Application-Layer Protocol Negotiation extension, if present (see https://tools.ietf.org/html/rfc7301)."), + "unknown_extensions": ListOf(Binary(), doc="A list of any unrecognized extensions in raw form."), }, doc="The Server Hello message (see https://tools.ietf.org/html/rfc5246#section-7.4.1.3).") # tls/tls_handshake.go: ServerKeyExchange