From 8396375c6eab69a42048b24c4dbf73caf1e3a37e Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Sun, 24 May 2020 10:42:40 -0700 Subject: [PATCH 1/2] Check tbsCertificate signature algorithm matches certificate Per RFC 5280 section 4.1.1.2, couldn't find an existing lint. --- ...ignature_alg_matches_cert_signature_alg.go | 92 +++++++++++++++++++ ...ure_alg_matches_cert_signature_alg_test.go | 41 +++++++++ v2/testdata/mismatchingSigAlgsBadOID.pem | 37 ++++++++ v2/testdata/mismatchingSigAlgsBadParams.pem | 37 ++++++++ 4 files changed, 207 insertions(+) create mode 100644 v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go create mode 100644 v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go create mode 100644 v2/testdata/mismatchingSigAlgsBadOID.pem create mode 100644 v2/testdata/mismatchingSigAlgsBadParams.pem diff --git a/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go new file mode 100644 index 000000000..df9c7c6c0 --- /dev/null +++ b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go @@ -0,0 +1,92 @@ +package rfc + +import ( + "bytes" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v2/lint" + "github.com/zmap/zlint/v2/util" + "golang.org/x/crypto/cryptobyte" + cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" +) + +/* + * ZLint Copyright 2020 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/******************************************************************* +RFC 5280: 4.1.1.2 +[the Certificate signatureAlgorithm] field MUST contain the same +algorithm identifier as the signature field in the sequence +tbsCertificate +********************************************************************/ + +type mismatchingSigAlg struct{} + +func (l *mismatchingSigAlg) Initialize() error { + return nil +} + +func (l *mismatchingSigAlg) CheckApplies(_ *x509.Certificate) bool { + return true +} + +func (l *mismatchingSigAlg) Execute(c *x509.Certificate) *lint.LintResult { + // parse out certificate signatureAlgorithm + input := cryptobyte.String(c.Raw) + var cert cryptobyte.String + if !input.ReadASN1(&cert, cryptobyte_asn1.SEQUENCE) { + return &lint.LintResult{Status: lint.Fatal, Details: "error reading certificate"} + } + if !cert.SkipASN1(cryptobyte_asn1.SEQUENCE) { + return &lint.LintResult{Status: lint.Fatal, Details: "error reading certificate.tbsCertificate"} + } + var certSigAlg cryptobyte.String + if !cert.ReadASN1(&certSigAlg, cryptobyte_asn1.SEQUENCE) { + return &lint.LintResult{Status: lint.Fatal, Details: "error reading certificate.signatureAlgorithm"} + } + + // parse out tbsCertificate signature + input = cryptobyte.String(c.RawTBSCertificate) + var tbsCert cryptobyte.String + if !input.ReadASN1(&tbsCert, cryptobyte_asn1.SEQUENCE) { + return &lint.LintResult{Status: lint.Fatal, Details: "error reading tbsCertificate"} + } + if !tbsCert.SkipOptionalASN1(cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) { + return &lint.LintResult{Status: lint.Fatal, Details: "error reading tbsCertificate.version"} + } + if !tbsCert.SkipASN1(cryptobyte_asn1.INTEGER) { + return &lint.LintResult{Status: lint.Fatal, Details: "error reading tbsCertificate.serialNumber"} + } + var tbsSigAlg cryptobyte.String + if !tbsCert.ReadASN1(&tbsSigAlg, cryptobyte_asn1.SEQUENCE) { + return &lint.LintResult{Status: lint.Fatal, Details: "error reading tbsCertificate.signature"} + } + + if !bytes.Equal(certSigAlg, tbsSigAlg) { + return &lint.LintResult{Status: lint.Error} + } + + return &lint.LintResult{Status: lint.Pass} +} + +func init() { + lint.RegisterLint(&lint.Lint{ + Name: "e_cert_sig_alg_not_match_tbs_sig_alg", + Description: "Certificate signature field must match TBSCertificate signature field", + Citation: "RFC 5280, Section 4.1.1.2", + Source: lint.RFC5280, + EffectiveDate: util.RFC5280Date, + Lint: &mismatchingSigAlg{}, + }) +} diff --git a/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go new file mode 100644 index 000000000..6d36e589c --- /dev/null +++ b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go @@ -0,0 +1,41 @@ +package rfc + +import ( + "testing" + + "github.com/zmap/zlint/v2/lint" + "github.com/zmap/zlint/v2/test" +) + +func TestSigAlgMismatch(t *testing.T) { + testCases := []struct { + name string + filepath string + expectedStatus lint.LintStatus + }{ + { + name: "error cert with mismatching signature algorithms (bad OID)", + filepath: "mismatchingSigAlgsBadOID.pem", + expectedStatus: lint.Error, + }, + { + name: "error cert with mismatching signature algorithms (bad parameters)", + filepath: "mismatchingSigAlgsBadParams.pem", + expectedStatus: lint.Error, + }, + { + name: "pass cert with matching signature algorithms", + filepath: "ecdsaP256.pem", + expectedStatus: lint.Pass, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := test.TestLint("e_cert_sig_alg_not_match_tbs_sig_alg", tc.filepath) + if result.Status != tc.expectedStatus { + t.Errorf("expected result %v was %v", tc.expectedStatus, result.Status) + } + }) + } +} diff --git a/v2/testdata/mismatchingSigAlgsBadOID.pem b/v2/testdata/mismatchingSigAlgsBadOID.pem new file mode 100644 index 000000000..080a674a2 --- /dev/null +++ b/v2/testdata/mismatchingSigAlgsBadOID.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: ecdsa-with-SHA384 + Issuer: + Validity + Not Before: Nov 10 23:00:00 2009 GMT + Not After : Aug 10 23:00:00 2019 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:27:0c:60:cf:ca:31:8a:91:db:8f:67:c7:95:04: + 6d:df:18:31:99:2c:97:8a:71:8d:7b:c2:b9:79:3d: + 02:86:21:a9:1b:5a:0c:55:03:cc:cc:18:2c:1b:96: + 52:81:dc:70:62:7f:c9:f4:67:cd:d3:f4:43:31:b4: + a5:75:0b:19:3b + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: critical + DNS:asd + Signature Algorithm: ecdsa-with-SHA384 + 30:44:02:20:3b:b7:b6:5d:a4:13:a1:f2:d8:42:5e:36:b9:e5: + 41:7a:90:1c:ea:45:3f:11:6e:b7:b0:7d:b0:f7:bc:22:8b:35: + 02:20:25:7a:88:77:4c:8a:fd:9d:e8:93:33:93:6d:f7:c3:80: + ba:a1:1c:51:3e:04:b6:7f:c1:ff:a2:3a:c4:87:ac:f9 +-----BEGIN CERTIFICATE----- +MIIBAjCBqqADAgECAgEAMAoGCCqGSM49BAMCMAAwHhcNMDkxMTEwMjMwMDAwWhcN +MTkwODEwMjMwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJwxgz8ox +ipHbj2fHlQRt3xgxmSyXinGNe8K5eT0ChiGpG1oMVQPMzBgsG5ZSgdxwYn/J9GfN +0/RDMbSldQsZO6MVMBMwEQYDVR0RAQH/BAcwBYIDYXNkMAoGCCqGSM49BAMDA0cA +MEQCIDu3tl2kE6Hy2EJeNrnlQXqQHOpFPxFut7B9sPe8Ios1AiAleoh3TIr9neiT +M5Nt98OAuqEcUT4Etn/B/6I6xIes+Q== +-----END CERTIFICATE----- diff --git a/v2/testdata/mismatchingSigAlgsBadParams.pem b/v2/testdata/mismatchingSigAlgsBadParams.pem new file mode 100644 index 000000000..fc20f8dac --- /dev/null +++ b/v2/testdata/mismatchingSigAlgsBadParams.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Nov 10 23:00:00 2009 GMT + Not After : Aug 10 23:00:00 2019 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:27:0c:60:cf:ca:31:8a:91:db:8f:67:c7:95:04: + 6d:df:18:31:99:2c:97:8a:71:8d:7b:c2:b9:79:3d: + 02:86:21:a9:1b:5a:0c:55:03:cc:cc:18:2c:1b:96: + 52:81:dc:70:62:7f:c9:f4:67:cd:d3:f4:43:31:b4: + a5:75:0b:19:3b + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: critical + DNS:asd + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:3b:b7:b6:5d:a4:13:a1:f2:d8:42:5e:36:b9:e5: + 41:7a:90:1c:ea:45:3f:11:6e:b7:b0:7d:b0:f7:bc:22:8b:35: + 02:20:25:7a:88:77:4c:8a:fd:9d:e8:93:33:93:6d:f7:c3:80: + ba:a1:1c:51:3e:04:b6:7f:c1:ff:a2:3a:c4:87:ac:f9 +-----BEGIN CERTIFICATE----- +MIIBBDCBqqADAgECAgEAMAoGCCqGSM49BAMCMAAwHhcNMDkxMTEwMjMwMDAwWhcN +MTkwODEwMjMwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJwxgz8ox +ipHbj2fHlQRt3xgxmSyXinGNe8K5eT0ChiGpG1oMVQPMzBgsG5ZSgdxwYn/J9GfN +0/RDMbSldQsZO6MVMBMwEQYDVR0RAQH/BAcwBYIDYXNkMAwGCCqGSM49BAMCBQAD +RwAwRAIgO7e2XaQTofLYQl42ueVBepAc6kU/EW63sH2w97wiizUCICV6iHdMiv2d +6JMzk233w4C6oRxRPgS2f8H/ojrEh6z5 +-----END CERTIFICATE----- From a946b3a8f4e9dce7bae7eab822cae1905e095f4a Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Mon, 1 Jun 2020 13:02:08 -0700 Subject: [PATCH 2/2] Add ignore to integration + reuse tbsCert --- v2/integration/config.json | 5 ++- ...ignature_alg_matches_cert_signature_alg.go | 32 ++++++++----------- ...ure_alg_matches_cert_signature_alg_test.go | 14 ++++++++ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/v2/integration/config.json b/v2/integration/config.json index bc49b4cd4..48e97679d 100644 --- a/v2/integration/config.json +++ b/v2/integration/config.json @@ -741,6 +741,9 @@ }, "w_subject_dn_trailing_whitespace": { "WarnCount": 64 + }, + "e_cert_sig_alg_not_match_tbs_sig_alg": { + "ErrCount": 10 } } -} \ No newline at end of file +} diff --git a/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go index df9c7c6c0..a8fd8c666 100644 --- a/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go +++ b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg.go @@ -1,15 +1,3 @@ -package rfc - -import ( - "bytes" - - "github.com/zmap/zcrypto/x509" - "github.com/zmap/zlint/v2/lint" - "github.com/zmap/zlint/v2/util" - "golang.org/x/crypto/cryptobyte" - cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" -) - /* * ZLint Copyright 2020 Regents of the University of Michigan * @@ -31,6 +19,18 @@ algorithm identifier as the signature field in the sequence tbsCertificate ********************************************************************/ +package rfc + +import ( + "bytes" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v2/lint" + "github.com/zmap/zlint/v2/util" + "golang.org/x/crypto/cryptobyte" + cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" +) + type mismatchingSigAlg struct{} func (l *mismatchingSigAlg) Initialize() error { @@ -48,7 +48,8 @@ func (l *mismatchingSigAlg) Execute(c *x509.Certificate) *lint.LintResult { if !input.ReadASN1(&cert, cryptobyte_asn1.SEQUENCE) { return &lint.LintResult{Status: lint.Fatal, Details: "error reading certificate"} } - if !cert.SkipASN1(cryptobyte_asn1.SEQUENCE) { + var tbsCert cryptobyte.String + if !cert.ReadASN1(&tbsCert, cryptobyte_asn1.SEQUENCE) { return &lint.LintResult{Status: lint.Fatal, Details: "error reading certificate.tbsCertificate"} } var certSigAlg cryptobyte.String @@ -57,11 +58,6 @@ func (l *mismatchingSigAlg) Execute(c *x509.Certificate) *lint.LintResult { } // parse out tbsCertificate signature - input = cryptobyte.String(c.RawTBSCertificate) - var tbsCert cryptobyte.String - if !input.ReadASN1(&tbsCert, cryptobyte_asn1.SEQUENCE) { - return &lint.LintResult{Status: lint.Fatal, Details: "error reading tbsCertificate"} - } if !tbsCert.SkipOptionalASN1(cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) { return &lint.LintResult{Status: lint.Fatal, Details: "error reading tbsCertificate.version"} } diff --git a/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go index 6d36e589c..6c584b8bb 100644 --- a/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go +++ b/v2/lints/rfc/lint_tbs_signature_alg_matches_cert_signature_alg_test.go @@ -1,3 +1,17 @@ +/* + * ZLint Copyright 2020 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + package rfc import (