Skip to content

Commit

Permalink
RFC5280: add serial number cases
Browse files Browse the repository at this point in the history
These are marked with a "pedantic" feature flag,
since many implementations choose not to enforce them.

Signed-off-by: William Woodruff <[email protected]>
  • Loading branch information
woodruffw committed Oct 31, 2023
1 parent 2e95a36 commit 4ac5ed2
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 177 deletions.
4 changes: 2 additions & 2 deletions harness/gocryptox509/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func main() {
var context string
switch r {
case testcaseFailed:
fmt.Printf("%s\nerr=%+#v\n", tc.Description, err)
fmt.Printf("\terr=%+#v\n", err)
context = err.Error()
fail++
case testcasePassed:
Expand Down Expand Up @@ -158,7 +158,7 @@ func evaluateTestcase(testcase Testcase) (testcaseResult, error) {
}

for _, elem := range testcase.ExtendedKeyUsage {
expected_eku := KnownEKUs(elem.(string))
expected_eku := KnownEKUs(elem.(string))
ekus = append(ekus, extKeyUsagesMap[expected_eku])
}
}
Expand Down
3 changes: 2 additions & 1 deletion limbo-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"pedantic-public-suffix-wildcard",
"name-constraint-dn",
"eku",
"pedantic-webpki"
"pedantic-webpki",
"pedantic-serial-number"
],
"type": "string"
},
Expand Down
384 changes: 215 additions & 169 deletions limbo.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions limbo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ class Feature(str, Enum):
Tests that exercise "pedantic" corners of the CABF profile.
"""

pedantic_serial_number = "pedantic-serial-number"
"""
Tests that exercise "pedantic" serial number handling.
"""


class Testcase(BaseModel):
"""
Expand Down
3 changes: 1 addition & 2 deletions limbo/testcases/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,11 @@ def leaf_cert(
if key is None:
key = ec.generate_private_key(ec.SECP256R1())

builder = x509.CertificateBuilder()
builder = x509.CertificateBuilder(serial_number=serial)
builder = builder.subject_name(subject)
builder = builder.issuer_name(issuer)
builder = builder.not_valid_before(not_before)
builder = builder.not_valid_after(not_after)
builder = builder.serial_number(serial)
builder = builder.public_key(key.public_key()) # type: ignore[arg-type]
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(key.public_key()), # type: ignore[arg-type]
Expand Down
42 changes: 39 additions & 3 deletions limbo/testcases/rfc5280.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from datetime import datetime
from ipaddress import IPv4Address, IPv4Network
import random

from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import ec
Expand Down Expand Up @@ -144,9 +145,6 @@ def unknown_critical_extension_intermediate(builder: Builder) -> None:
)


# TODO: Empty serial number, overlength serial number.


@testcase
def critical_aki(builder: Builder) -> None:
"""
Expand Down Expand Up @@ -1409,3 +1407,41 @@ def san_noncritical_with_empty_subject(builder: Builder) -> None:
builder.trusted_certs(root).peer_certificate(leaf).expected_peer_name(
PeerName(kind="DNS", value="example.com")
).fails()


@testcase
def serial_number_too_long(builder: Builder) -> None:
"""
Produces an **invalid** chain due to an invalid EE cert.
The EE cert contains a serial number longer than 20 octets, which is
disallowed under RFC 5280.
"""

root = builder.root_ca()
# NOTE: Intentionally generate 22 octets, since many implementations are
# permissive of 21-octet encodings due to signedness errors.
leaf = builder.leaf_cert(root, serial=int.from_bytes(random.randbytes(22), signed=False))

builder = builder.server_validation().features([Feature.pedantic_serial_number])
builder.trusted_certs(root).peer_certificate(leaf).expected_peer_name(
PeerName(kind="DNS", value="example.com")
).fails()


@testcase
def serial_number_zero(builder: Builder) -> None:
"""
Produces an **invalid** chain due to an invalid EE cert.
The EE cert contains a serial number of zero, which is disallowed
under RFC 5280.
"""

root = builder.root_ca()
leaf = builder.leaf_cert(root, serial=0)

builder = builder.server_validation().features([Feature.pedantic_serial_number])
builder.trusted_certs(root).peer_certificate(leaf).expected_peer_name(
PeerName(kind="DNS", value="example.com")
).fails()

0 comments on commit 4ac5ed2

Please sign in to comment.