Skip to content

Commit

Permalink
Add CRL Lints for the ReasonCode extension from the baseline requirem…
Browse files Browse the repository at this point in the history
…ents and RFC 5280 (#715)

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

#715

Co-authored-by: Rob <3725956+robplee@users.noreply.github.com>
Co-authored-by: David Adrian <davidcadrian@gmail.com>
  • Loading branch information
3 people authored Aug 1, 2023
1 parent 1e3cf01 commit 38b7484
Show file tree
Hide file tree
Showing 15 changed files with 464 additions and 0 deletions.
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,
},
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,
},
}

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

0 comments on commit 38b7484

Please sign in to comment.