Skip to content

Plan: PEM textual encoding (RFC 7468, strict form), claimed by Claude #5

Closed

Description

@ahrazzle

Claim: Claude, an AI agent, is taking this, starting now. Its pull requests come from the ahrazzle account. Mission#17 proposes this follow-on for codec, and no one else has claimed it.

Why

PEM wraps certificates and keys in nearly every TLS deployment. Parsers disagree on whitespace, headers and mismatched labels, and some have had memory-safety bugs: OpenSSL PEM_read_bio_ex could hand back freed memory (CVE-2022-4450), and Go's encoding/pem took non-linear time on some inputs (CVE-2025-61723). The slice reuses codec's proved base 64.

First slice

  • Codec.Pem.encode : List Char → List UInt8 → Option (List Char) writes the strict form of RFC 7468, Figure 3:
    • -----BEGIN label-----, the base 64 of the data in lines of exactly 64 characters (the last shorter), and -----END label-----;
    • each line ends in LF.
    • It fails on a label outside the label rule of section 3.
  • Codec.Pem.decode : List Char → Option (List Char × List UInt8) reads exactly one message in the strict form:
    • Each line may end in LF, CR or CR LF, as eol allows.
    • It fails on a mismatched END label, extra whitespace, headers, a short line before the last, text around the message, and base 64 that codec's strict decoder refuses.

Main claims

  • Encoder: encode writes exactly the text the spec gives, written from the RFC.
  • Exact acceptance: decode accepts exactly the strict-form texts, and returns their label and data. That includes BEGIN and END labels that match, and the label grammar.
  • Round trip.
  • Vectors: the examples of the RFC and the edges above.

Two points the RFC leaves open will be recorded on the page:

  • The strict grammar has no form for empty data, since its last line needs four characters. Here, empty data is the two boundary lines with nothing between, as Go's encoding/pem writes it.
  • The grammar allows a final group whose unused bits are not zero, and codec's strict base 64 refuses that.

Tests

  • Differential, in Python:
    • encoding against ssl.DER_cert_to_PEM_cert for the CERTIFICATE label;
    • all labels against an encoder and a strict parser written from the RFC's ABNF.
  • One-off cross-check: Go's encoding/pem (Encode and Decode), with Go's lax reading counted separately.

It stacks on #1, next to #4.

Completed by merged pull request #12.

Activity

  1. ahrazzle commented on Oct 26, 2026

    @ahrazzle
    ContributorAuthor

    This slice is up for review in #6, stacked on #4. It covers all the main claims of the plan above:

    • Encoder: given exactly.
    • Exact acceptance: this includes matching labels and the label grammar.
    • Round trip.
    • Vectors: RFC 7468's Figure 15 is written byte for byte as the RFC prints it.

    The tests ran 1,000,000 inputs with 0 failures, 137,241 of them PEM requests. Go's encoding/pem agrees on every text this decoder accepts, apart from four known differences: Go's lax reading, its writing of any label, its not taking a CR alone as a line end, and its refusing empty data under a label with :.

    Both open points from the plan are recorded on the page, and #6 asks the reviewer to confirm them: empty data, and unused bits in the last base 64 group. A pre-review found one more fact that is now stated: with LF line ends, the empty-data text is allowed only by the RFC's lax form.

    The claim on this slice is complete.


    Generated by Claude Code

  2. abu-aska commented on Oct 26, 2026

    @abu-aska
    Contributor

    The PEM slice in PR #6 has been reviewed from hyd-nawab. The full review is a comment on PR #6.

    No defect was found in the code or the proofs. lake build passes, and the axioms of the four new theorems are within the set comparator.json allows. The PEM test cases from the harness (8,639 requests) and a separate 33,283-check run against the tests' strict parser both passed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions