Skip to content

One-shot PEM reader #29588

@owlstead

Description

@owlstead

I was giving some hints on this StackOverflow question where the idea of a PEM reader / writer for .NET was coming up. Jeremy then posted that if such a reader was available that it would be a nice idea to make it a one-shot, RFC 7468 compliant reader using the new Span<char> framework.

So I've created a sample implementation / API proposal which performs the parsing of the "lax" (i.e. permissive). I've used the following additional goals / non-goals:

Goals:

  • no dynamic memory allocation that depends on the size of the input;
  • possibility to skip a description (in front) of the PEM structure;
  • possibility to detect the end of the PEM structure, if present;
  • related: allow for parsing multiple PEM structures from a single Span;
  • design in the spirit of other methods that use Span;

Non-goals:

  • returning the description in front of the PEM file;
  • detailed error reporting;
  • streaming implementation (requiring state machine or buffering);
  • parsing the base64 inside the header / footer lines (pre/post encapsulation boundaries);
  • support for stricter versions of PEM;
  • return or comparison of the label in the footer.

Niceties:

  • validation of the base64 inside the header / footer lines
  • return the amount of bytes so that a correctly sized buffer can be created in advance for the base64 decryption (yes, implemented).

Parsing the base64 is easily done afterwards with the existing parser, and we may just want to skip the PEM.

Note that I didn't get my Unit testing working for Core 3, so the implementation has not yet received any significant testing. First let me know what you think.

Implementation notes: this implementation searches for the post-encapsulation boundary (footer) before validating the base64. It might be possible to use a state machine instead to create a single pass parser. This adds significant complexity to the parsing though, and searching for a static string should be relatively performant already.

Code in followup comment for now, as I don't know yet where to put it.


(Edited in):

API Proposal

namespace System.Security.Cryptography
{
    public static class PemEncoding
    {
        public static bool TryFind(ReadOnlySpan<char> pemData, out PemFields fields) => throw null;
        public static PemFields Find(ReadOnlySpan<char> pemData) => throw null;

        public static int GetEncodedSize(int labelLength, int dataLength) => throw null;
        public static bool TryWrite(ReadOnlySpan<char> label, ReadOnlySpan<byte> data, Span<char> destination, out int charsWritten) => throw null;
        public static char[] Write(ReadOnlySpan<char> label, ReadOnlySpan<byte> data) => throw null;
    }

    public readonly struct PemFields
    {
        internal PemFields(Range label, Range base64data, Range location, int decodedDataLength) => throw null;

        public Range Location { get; }
        public Range Label { get; }
        public Range Base64Data { get; }
        public int DecodedDataLength { get; }
    }
 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions