Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add CRL Lints for the ReasonCode extension from the baseline requirements and RFC 5280 #715

Merged
merged 9 commits into from
Aug 1, 2023
60 changes: 60 additions & 0 deletions v3/lints/cabf_br/lint_cabf_crl_reason_code_not_critical.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package cabf_br

/*
* ZLint Copyright 2023 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.
*/

import (
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
)

type crlReasonCodeNotCritical struct{}

func init() {
lint.RegisterRevocationListLint(&lint.RevocationListLint{
LintMetadata: lint.LintMetadata{
Name: "e_cab_crl_reason_code_not_critical",
Description: "If present, CRL Reason Code extension MUST NOT be marked critical.",
Citation: "BRs: 7.2.2",
Source: lint.CABFBaselineRequirements,
EffectiveDate: util.CABEffectiveDate,
aaomidi marked this conversation as resolved.
Show resolved Hide resolved
},
Lint: NewCrlReasonCodeNotCritical,
})
}

func NewCrlReasonCodeNotCritical() lint.RevocationListLintInterface {
return &crlReasonCodeNotCritical{}
}

func (l *crlReasonCodeNotCritical) CheckApplies(c *x509.RevocationList) bool {
return len(c.RevokedCertificates) > 0
}

func (l *crlReasonCodeNotCritical) Execute(c *x509.RevocationList) *lint.LintResult {
for _, c := range c.RevokedCertificates {
if c.ReasonCode == nil {
continue
}
for _, ext := range c.Extensions {
if ext.Id.Equal(util.ReasonCodeOID) {
if ext.Critical {
return &lint.LintResult{Status: lint.Error, Details: "CRL Reason Code extension MUST NOT be marked as critical."}
}
}
}
}
return &lint.LintResult{Status: lint.Pass}
}
64 changes: 64 additions & 0 deletions v3/lints/cabf_br/lint_cabf_crl_reason_code_not_critical_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cabf_br

import (
"strings"
"testing"

"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/test"
)

/*
* ZLint Copyright 2023 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.
*/

func TestCrlReasonCodeNotCritical(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
path string
want lint.LintStatus
wantSubStr string
}{
{
name: "CRL reason code critical",
path: "crlReasonCodeCrit.pem",
want: lint.Error,
wantSubStr: "MUST NOT be marked as critical",
},
{
name: "CRL with reason code 5",
path: "crlWithReasonCode5.pem",
want: lint.Pass,
},
{
name: "CRL no revoked certificates",
path: "crlEmpty.pem",
want: lint.NA,
},
}
aaomidi marked this conversation as resolved.
Show resolved Hide resolved

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
gotStatus := test.TestRevocationListLint(t, "e_cab_crl_reason_code_not_critical", tc.path)
if tc.want != gotStatus.Status {
t.Errorf("%s: expected %s, got %s", tc.path, tc.want, gotStatus.Status)
}
if !strings.Contains(gotStatus.Details, tc.wantSubStr) {
t.Errorf("%s: expected %s, got %s", tc.path, tc.wantSubStr, gotStatus.Details)
}
})
}

}
68 changes: 68 additions & 0 deletions v3/lints/cabf_br/lint_cabf_crl_valid_reason_codes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cabf_br

/*
* ZLint Copyright 2023 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.
*/

import (
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
)

type crlHasValidReasonCodes struct{}

func init() {
lint.RegisterRevocationListLint(&lint.RevocationListLint{
LintMetadata: lint.LintMetadata{
Name: "e_cab_crl_has_valid_reason_code",
Description: "Only the following CRLReasons MAY be present: 1, 3, 4, 5, 9.",
Citation: "BRs: 7.2.2",
Source: lint.CABFBaselineRequirements,
EffectiveDate: util.CABFBRs_1_8_7_Date,
},
Lint: NewCrlHasValidReasonCode,
})
}

func NewCrlHasValidReasonCode() lint.RevocationListLintInterface {
return &crlHasValidReasonCodes{}
}

func (l *crlHasValidReasonCodes) CheckApplies(c *x509.RevocationList) bool {
return len(c.RevokedCertificates) > 0
}

var validReasons = map[int]bool{
1: true,
3: true,
4: true,
5: true,
9: true,
}

func (l *crlHasValidReasonCodes) Execute(c *x509.RevocationList) *lint.LintResult {
for _, c := range c.RevokedCertificates {
if c.ReasonCode == nil {
continue
}
code := *c.ReasonCode
if code == 0 {
return &lint.LintResult{Status: lint.Error, Details: "The reason code CRL entry extension SHOULD be absent instead of using the unspecified (0) reasonCode value."}
}
if _, ok := validReasons[code]; !ok {
return &lint.LintResult{Status: lint.Error, Details: "Reason code not included in BR: 7.2.2"}
}
}
return &lint.LintResult{Status: lint.Pass}
}
76 changes: 76 additions & 0 deletions v3/lints/cabf_br/lint_cabf_crl_valid_reason_codes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package cabf_br

import (
"strings"
"testing"

"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/test"
)

/*
* ZLint Copyright 2023 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.
*/

func TestCrlValidReasonCodes(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
path string
want lint.LintStatus
wantSubStr string
}{
{
name: "CRL with reason code 0",
path: "crlWithReasonCode0.pem",
want: lint.Error,
wantSubStr: "The reason code CRL entry extension SHOULD be absent instead of using the unspecified",
},
{
// This test case is significant since reason code 2 is not allowed by CABF
name: "CRL with reason code 2",
path: "crlWithReasonCode2.pem",
want: lint.Error,
wantSubStr: "Reason code not included in BR: 7.2.2",
},
{
name: "CRL with reason code 5",
path: "crlWithReasonCode5.pem",
want: lint.Pass,
},
{
name: "CRL with reason code 7",
path: "crlWithReasonCode7.pem",
want: lint.Error,
wantSubStr: "Reason code not included in BR: 7.2.2",
},
{
name: "CRL thisUpdate before enforcement",
path: "crlThisUpdate20230505.pem",
want: lint.NE,
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
gotStatus := test.TestRevocationListLint(t, "e_cab_crl_has_valid_reason_code", tc.path)
if tc.want != gotStatus.Status {
t.Errorf("%s: expected %s, got %s", tc.path, tc.want, gotStatus.Status)
}
if !strings.Contains(gotStatus.Details, tc.wantSubStr) {
t.Errorf("%s: expected %s, got %s", tc.path, tc.wantSubStr, gotStatus.Details)
}
})
}
}
73 changes: 73 additions & 0 deletions v3/lints/rfc/lint_crl_valid_reason_codes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package rfc

/*
* ZLint Copyright 2023 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.
*/

import (
"fmt"

"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
)

type crlHasValidReasonCode struct{}

/*
***********************************************
RFC 5280: 5.3.1

CRL issuers are strongly
encouraged to include meaningful reason codes in CRL entries;
however, the reason code CRL entry extension SHOULD be absent instead
of using the unspecified (0) reasonCode value.

***********************************************
*/
func init() {
lint.RegisterRevocationListLint(&lint.RevocationListLint{
LintMetadata: lint.LintMetadata{
Name: "e_crl_has_valid_reason_code",
Description: "If a CRL entry has a reason code, it MUST be in RFC5280 section 5.3.1 and SHOULD be absent instead of using unspecified (0)",
Citation: "RFC 5280: 5.3.1",
Source: lint.RFC5280,
EffectiveDate: util.RFC5280Date,
},
Lint: NewCrlHasValidReasonCode,
})
}

func NewCrlHasValidReasonCode() lint.RevocationListLintInterface {
return &crlHasValidReasonCode{}
}

func (l *crlHasValidReasonCode) CheckApplies(c *x509.RevocationList) bool {
return len(c.RevokedCertificates) > 0
}

func (l *crlHasValidReasonCode) Execute(c *x509.RevocationList) *lint.LintResult {
for _, c := range c.RevokedCertificates {
if c.ReasonCode == nil {
continue
}
code := *c.ReasonCode
if code == 0 {
return &lint.LintResult{Status: lint.Warn, Details: "The reason code CRL entry extension SHOULD be absent instead of using the unspecified (0) reasonCode value."}
}
if code == 7 || code > 10 {
return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("Reason code, %v, not included in RFC 5280 section 5.3.1", code)}
}
}
return &lint.LintResult{Status: lint.Pass}
}
Loading