Simple Public Key Infrastructure (SPKI) S-Expressions
RFC 0000
Ronald L. Rivest
MIT CSAIL
32 Vassar Street, Room 32-G692
Cambridge
Massachusetts
02139
United States of America
rivest@mit.edu
https://www.csail.mit.edu/person/ronald-l-rivest
Donald E. Eastlake 3rd
Independent
2386 Panoramic Circle
Apopka
Florida
32703
United States of America
+1-508-333-2270
d3e3e3@gmail.com
January 2025
Applications and Real Time
example
This memo specifies the data structure representation that was devised to support Simple Public Key Infrastructure (SPKI, RFC 2692) certificates and with the intent that it be more widely applicable. It has been and is being used elsewhere. There are multiple implementations in a variety of programming languages. Uses of this representation are referred to in this document as "S-expressions". This memo makes precise the encodings of these SPKI S-expressions: it gives a "canonical form" for them, describes two "transport" representations, and also describes an "advanced" format for display to people.
Introduction
This memo specifies the data structure representation that was devised to support Simple Public Key Infrastructure (SPKI) [RFC2692] certificates and with the intent that it be more widely applicable (see Section 1.3, History). It is suitable for representing arbitrary, complex data structures and has been and is being used elsewhere. Uses of this representation herein are referred to as "S-expressions".
This memo makes precise the encodings of these SPKI S-expressions: it gives a "canonical form" for them, describes two "transport" representations, and also describe an "advanced" format for display to people. There are multiple implementations of S-expressions in a variety of programming languages including Python, Ruby, and C (see Appendix A).
These S-expressions are either byte-strings ("octet-strings") or lists of simpler S-expressions. Here is a sample S-expression:

    (snicker "abc" (#03# |YWJj|))

It is a list of length three containing the following:
- the octet-string "snicker"
- the octet-string "abc"
- a sub-list containing two elements: the hexadecimal constant #03# (which represents a one octet long octet-string with the value of that octet being 0x03) and the base-64 constant |YWJj| (which represents the same octet-string as "abc")
This document specifies how to construct and use these S-expressions.
The design goals for S-expressions were as follows:
generality:
S-expressions should be good at representing arbitrary data.
readability:
It should be easy for someone to examine and understand the structure of an S-expression.
economy:
S-expressions should represent data compactly.
transportability:
S-expressions should be easy to transport over communication media (such as email) that are known to be less than perfect.
flexibility:
S-expressions should make it relatively simple to modify and extend data structures.
canonicalization:
It should be easy to produce a unique "canonical" form of an S-expression, for digital signature purposes.
efficiency:
S-expressions should admit in-memory representations that allow efficient processing.
For implementors of new applications and protocols other technologies also worthy of consideration include the following: [XML], CBOR [RFC8949], and JSON [RFC7159].
Uses of S-Expressions
The S-expressions specified herein are in active use today between GnuPG [GnuPG] and Ribose's RNP [Ribose]. Ribose has implemented C++ software to compose and parse these S-expressions [RNPGP_SEXPP]. The GNU software is here [Libgcrypt] and there are other implementations (see Appendix A).
They are used or referenced in the following RFCs:
- [RFC2693] for [SPKI]
- [RFC3275] XML-Signature Syntax and Processing
In addition, S-Expressions are the inspiration for the encodings in other protocols. For example, [RFC3259] or Section 6 of [CDDLfreezer].
Formalization
An Internet Draft [formal] has been posted showing a formal model of SPKI S-Expressions and which formally demonstrates that the examples and ABNF in this document are correct.
Historical Note
The S-expressions described here were originally developed for "SDSI" (the Simple Distributed Security Infrastructure by Lampson and Rivest [SDSI]) in 1996, although their origins date back to McCarthy's [LISP] programming language. They were further refined and improved during the merger of SDSI and SPKI [SPKI] [RFC2692] [RFC2693] during the first half of 1997. S-expressions are more readable and flexible than, Bernstein's "net-strings" [BERN], which were developed contemporaneously.
Although a specification was made publicly available as a file named draft-rivest-sexp-00.txt on 4 May 1997, that file was never actually submitted to the IETF. This document is a clarified and modernized version of that document.
Conventions Used in This Document
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
S-expressions -- informal introduction
Informally, an S-expression is either:
- an octet-string, or
- a finite list of simpler S-expressions.
An octet-string is a finite sequence of eight-bit octets. An octet-string may be zero length. There may be many different but equivalent ways of representing an octet-string

    abc         -- as a token
    "abc"       -- as a quoted string
    #616263#    -- as a hexadecimal string
    3:abc       -- as a length-prefixed "verbatim" encoding
    |YWJj|      -- as a base-64 encoding of the octet-string
                     "abc"

The above encodings are all equivalent in that they all denote the same octet-string. Details of these encodings are given below, and how to give a "display type" to a simple-string is also described in Section 4.6.
A list is a finite sequence of zero or more simpler S-expressions. A list is represented by using parentheses to surround the sequence of encodings of its elements, as in:

    (abc (de #6667#) "ghi jkl")

As can be seen, there is variability possible in the encoding of an S-expression. In some applications, it is desirable to standardize or restrict the encodings; in other cases, it is desirable to have no restrictions. The following are the target cases these S-expressions aim to handle:
- a "transport" or "basic" encoding for transporting the S-expression between computers.
- a "canonical" encoding, used when signing the S-expression.
- an "advanced" encoding used for input/output to people.
- an "in-memory" encoding used for processing the S-expression in the computer.
In this document, related encoding techniques for each of these uses are provided.
Character set
This document specifies encodings of S-expressions. Except when giving "verbatim" encodings, the character set used is limited to the following characters in ASCII [RFC0020]:
Alphabetic:

    A B ... Z a b ... z

Numeric:

    0 1 ... 9

Whitespace:

    space, horizontal tab, vertical tab, form-feed
    carriage-return, line-feed

The following graphics characters, which are called "pseudo-alphabetic" in this document:

    -  hyphen or minus
    .  period
    /  slash
    _  underscore
    :  colon
    *  asterisk
    +  plus
    =  equal

The following graphics characters, which are "reserved punctuation":

    (  left parenthesis
    )  right parenthesis
    [  left bracket
    ]  right bracket
    {  left brace
    }  right brace
    |  vertical bar
    #  number sign
    "  double quote
    &  ampersand
    \  backslash

The following characters are unused and unavailable, except in "verbatim" and "quoted string" encodings:

    !  exclamation point
    %  percent
    ^  circumflex
    ~  tilde
    ;  semicolon
    '  single-quote (apostrophe)
    ,  comma
    <  less than
    >  greater than
    ?  question mark

Octet-string representation types
This section describes in detail the ways in which an octet-string may be represented.
Recall that an octet-string is any finite sequence of octets, and that an octet-string may have length zero.
Verbatim representation
A verbatim encoding of an octet-string consists of three parts:
- the length (number of octets) of the octet-string, given in decimal, most significant digit first, with no leading zeros.
- a colon ":"
- the octet-string itself, verbatim.
There are no blanks or whitespace separating the parts. No "escape sequences" are interpreted in the octet-string. This encoding is also called a "binary" or "raw" encoding.
Here are some sample verbatim encodings:

    3:abc
    7:subject
    4:::":
    12:hello world!
    10:abcdefghij
    0:

Quoted-string representation
The quoted-string representation of an octet-string consists of:
- an optional decimal length field
- an initial double-quote (")
- the octet-string with the "C" programming language [C] escape conventions (\n, etc.)
- a final double-quote (")
The specified length is the length of the resulting string after any backslash escape sequences have been converted to the octet value they denote. The string does not have any "terminating NULL" that [C] includes, and the length does not count such an octet.
The length is optional.
The escape conventions within the quoted string are as follows (these follow the "C" [C] programming language conventions, with an extension for ignoring line terminators of just CR, LF, CRLF, or LFCR and more restrictive octal and hexadecimal value formats):

    \a     -- audible alert (bell)
    \b     -- backspace
    \t     -- horizontal tab
    \v     -- vertical tab
    \n     -- new-line
    \f     -- form-feed
    \r     -- carriage-return
    \"     -- double-quote
    \'     -- single-quote
    \?     -- question mark
    \\     -- back-slash
    \ooo   -- character with octal value ooo (all three
              digits MUST be present)
    \xhh   -- character with hexadecimal value hh (both
              digits MUST be present)
    \<carriage-return>   -- causes carriage-return
              to be ignored.
    \<line-feed>         -- causes linefeed to be
              ignored.
    \<carriage-return><line-feed>   -- causes
              CRLF to be ignored.
    \<line-feed><carriage-return>   -- causes
              LFCR to be ignored.

Here are some examples of quoted-string encodings:

    "subject"
    "hi there"
    7"subject"
    "\xFE is the same octet as \376"
    3"\n\n\n"
    "This has\n two lines."
    "This has \
     one line."
    ""

Token representation
An octet-string that meets the following conditions may be given directly as a "token":
- it does not begin with a digit;
- it contains only characters that are: alphabetic (upper or lower case), numeric, or one of the following eight "pseudo-alphabetic" punctuation marks: - . / _ : * + =
- it is length 1 or greater.
Note: Upper and lower case are not equivalent. A token may begin with punctuation, including ":".
Here are some examples of token representations:

    subject
    not-before
    :=..
    class-of-1997
    //example.net/names/smith
    *

Hexadecimal representation
An octet-string may be represented with a hexadecimal encoding consisting of:
- an (optional) decimal length of the octet-string
- a sharp-sign "#"
- a hexadecimal encoding of the octet-string, with each octet represented with two hexadecimal digits, most significant digit first. There MUST be an even number of such digits.
- a final sharp-sign "#"
There may be whitespace inserted in the midst of the hexadecimal encoding arbitrarily; it is ignored. It is an error to have characters other than whitespace and hexadecimal digits.
Here are some examples of hexadecimal encodings:

    #616263#    -- represents "abc"
    3#616263#   -- also represents "abc"
    # 616
      263 #     -- also represents "abc"
    ##          -- represents the zero-length string

Base-64 representation of octet-strings
An octet-string may be represented in a base-64 encoding [RFC4648] consisting of:
- an (optional) decimal length of the octet-string
- a vertical bar "|"
- the base-64 [RFC4648] encoding of the octet-string.
- a final vertical bar "|"
Base-64 encoding produces four characters of output for each three octets of input. If the length of the input divided by three leaves a remainder of one or two, it produces an output block of length four ending in two or one equals signs, respectively. These equals signs MUST be included on output but input routines MAY accept inputs where one or two equals signs are dropped.
Whitespace inserted in the midst of the base-64 encoding is ignored. It is an error to have characters other than whitespace and base-64 characters.
Here are some examples of base-64 encodings:

    |YWJj|       -- represents "abc"
    | Y W
      J j |      -- also represents "abc"
    3|YWJj|      -- also represents "abc"
    |YWJjZA==|   -- represents "abcd"
    |YWJjZA|     -- also represents "abcd"
    ||           -- represents the zero-length string

Note the difference between this base-64 encoding of an octet-string using vertical bars ("| |") and the base-64 encoding of an S-expression using curly braces ("{ }") in Section 6.1.
Display-Hints and Internationalization
An octet-string can contain any type of data representable by a finite octet-string, for example text, a fixed or variable length integer, or an image. Normally the application producing / consuming S-expressions will understand their structure and the data type and encoding of the octet-strings within the S-expressions used by that application. If the octet-string consists of text, use of UTF-8 encoding is RECOMMENDED [RFC2130] [RFC3629].
The purposes of a display-hint is to provide information on how to display an octet-string to a user. It has no other function. Many of the MIME [RFC2046] types work here.
A display-hint is an octet-string representation surrounded by square brackets. There may be whitespace separating the display hint octet-string from the surrounding brackets. Any of the legal octet-string representations may be used for the display-hint string but a display-hint may not be applied to a display-hint string, that is, display-hints may not be nested.
A display-hint that can be used for UTF-8 encoded text is shown in the following example where the octet-string is text saying "bob", with an umlaut over the central "o", followed by a smilie emoji.

    ["text/plain; charset=utf-8"]"b\xC3\xB7b\xE2\x98\xBA"

Every octet-string representation is either preceded by a single display-hint or not so preceded. There may be whitespace between the close square bracket and the octet-string to which the hint applies.
Here are some other examples of display-hints:

    [image/gif]
    [charset=unicode-1-1]
    [  text/richtext  ]
    ["text/plain; charset=iso-8859-1"]
    [application/postscript]
    [audio/basic]
    ["http://example.com/display-types/funky.html"]

An octet-string that has no display-hint may be considered to have a MIME [RFC2046] type specified by the application or use. In the absence of such a specification, the default is as follows:

    [application/octet-stream]

When an S-Expression is being encoded in one of the representations described in Section 6, any display-hint present is included. If a display-hint is the default, it is not suppressed nor is the default display-hint included in the representation for an octet-string without a display-hint.
Comparison of octet-strings
It is RECOMMENDED that two octet-strings be considered equivalent for most computational and algorithmic purposes if and only if they have the same display-hint and the same data octet-strings. However, a particular application might need a different criterion. For example, it might ignore the display hint on comparisons.
Note that octet-strings are "case-sensitive"; the octet-string "abc" is not equal to the octet-string "ABC".
An octet-string without a display-hint may be compared to another octet-string (with or without a display hint) by considering it as an octet-string with the default display-hint specified for the applications or, in the absence of such specification, the general default display-hint specified in Section 4.6 .
Lists
Just as with octet-strings, there are variations in representing a list. Whitespace may be used to separate list elements, but they are only required to separate two octet-strings when otherwise the two octet-strings might be interpreted as one, as when one token follows another. To be precise, an octet-string represented as a token (Section 4.3) MUST be separated by whitespace from a following token, verbatim representation, or any of the following if they are prefixed with a length: quoted-string, hexadecimal, or base-64 representation. Also, whitespace may follow the initial left parenthesis, or precede the final right parenthesis of a list.
Here are some examples of encodings of lists:

    (a bob c)

    ( a ( bob c ) ( ( d e ) ( e f ) )  )

    (11:certificate(6:issuer3:bob)(7:subject5:alice))

    (|ODpFeGFtcGxlIQ==| "1997" murphy 3:XC+)

    ()

S-expression representation types
There are three "types" of representation:
- canonical
- basic transport
- advanced transport
The first two MUST be supported by any implementation; the last is OPTIONAL. As part of basic representation, the base-64 [RFC4648] representation of an S-expression may be used as described in Section 6.1.
Base-64 representation of S-expressions
An S-expression may be represented in a base-64 encoding [RFC4648] consisting of:
- an opening curly brace "{"
- the base-64 [RFC4648] encoding of the S-expression.
- a final closing curly brace "}"
Base-64 encoding produces four characters of output for each three octets of input. If the length of the input divided by three leaves a remainder of one or two, it produces an output block of length four ending in two or one equals signs, respectively. These equals signs MUST be included on output but input routines MAY accept inputs where one or two equals signs are dropped.
Whitespace inserted in the midst of the base-64 encoding, after the opening curly brace, or before the closing curly brace is ignored. It is an error to have characters other than whitespace and base-64 characters.
Note the difference between this base-64 encoding of an S-expression using curly braces ("{ }") and the base-64 encoding of an octet-string using vertical bars ("| |") in Section 4.5.
Canonical representation
This canonical representation is used for digital signature purposes and transport over channels not sensitive to specific octet values. It is uniquely defined for each S-expression. It is not particularly readable, but that is not the point. It is intended to be very easy to parse, to be reasonably economical, and to be unique for any S-expression. (See [CANON].)
The "canonical" form of an S-expression represents each octet-string in verbatim mode, and represents each list with no blanks separating elements from each other or from the surrounding parentheses (see also Section 7.2).
Here are some examples of canonical representations of S-expressions:

    (6:issuer3:bob)
    (4:icon[12:image/bitmap]9:xxxxxxxxx)
    (7:subject(3:ref5:alice6:mother))
    10:foo)]}>bar
    0:

Basic transport representation
There are two forms of the "basic transport" representation:
- the canonical representation
- an [RFC4648] base-64 representation of the canonical representation, surrounded by braces (see Section 6.1).
The basic transport representations (see Section 7.3) are intended to provide a universal means of representing S-expressions for transport from one machine to another. The base-64 encoding would be appropriate if the channel over which the S-expression is being sent might be sensitive to octets of some special values, such as an octet of all zero bits (NULL) or an octet of all one bits (DEL), or the channel is sensitive to "line length" such that occasional line terminating whitespace is needed.
Here are two examples of an S-expression represented in basic transport mode:

  (1:a1:b1:c)

  {KDE6YTE6YjE
     6Yyk= }

The second example above is the same S-expression as the first encoded in base-64.
Advanced transport representation
The "advanced transport" representation is intended to provide more flexible and readable notations for documentation, design, debugging, and (in some cases) user interface.
The advanced transport representation allows all of the octet-string representation forms described above in Section 4: quoted strings, base-64, hexadecimal, tokens, representations of strings with omitted lengths, and so on. (See Section 7.1).
ABNF of the syntax
ABNF is the Augmented Backus-Naur Form for syntax specifications as defined in [RFC5234]. The ABNF for advanced representation of S-expressions is given first and the basic and canonical forms derived therefrom. The rule names below in all capital letters are defined in Appendix B.1 of [RFC5234].
ABNF for advanced transport

   sexp           =  *whitespace value *whitespace

   whitespace     =  SP / HTAB / vtab / CR / LF / ff

   vtab           =  %x0B   ; vertical tab

   ff             =  %x0C   ; form feed

   value          =  string / ("(" *(value / whitespace) ")")

   string         =  [display] simple-string

   display        =  "[" *whitespace simple-string *whitespace "]"
                     *whitespace

   simple-string  =  verbatim / quoted-string / token / hexadecimal /
                     base-64

   verbatim       =  decimal ":" *OCTET
                       ; the length followed by a colon and the exact
                       ; number of OCTETs indicated by the length

   decimal        =  %x30 / (%x31-39 *DIGIT)

   quoted-string  =  [decimal] DQUOTE *(printable / escaped) DQUOTE

   printable      =  %x20-21 / %x23-5B / %x5D-7E
                       ; All US-ASCII printable but double-quote and
                       ; backslash

   escaped        =  backslash (%x3F / %x61 / %x62 / %x66 / %x6E /
                     %x72 / %x74 / %x76 / DQUOTE / quote / backslash
                     / 3(%x30-37) / (%x78 2HEXDIG) / CR / LF /
                     (CR LF) / (LF CR))

   backslash      =  %x5C

   quote          =  %x27   ; single quote

   token          =  (ALPHA / simple-punc) *(ALPHA / DIGIT /
                        simple-punc)

   simple-punc    =  "-" / "." / "/" / "_" / ":" / "*" / "+" / "="

   hexadecimal    =  [decimal] "#" *whitespace *hexadecimals "#"

   hexadecimals   =  2(HEXDIG *whitespace)

   base-64        =  [decimal] "|" *whitespace *base-64-chars
                        [base-64-end] "|"

   base-64-chars  =  4(base-64-char *whitespace)

   base-64-char   =  ALPHA / DIGIT / "+" / "/"

   base-64-end    =  base-64-chars /
                     3(base-64-char *whitespace) ["=" *whitespace] /
                     2(base-64-char *whitespace) *2("=" *whitespace)

ABNF for canonical

   c-sexp         =  c-string / ("(" *c-sexp ")")

   c-string       =  [ "[" verbatim "]" ] verbatim

ABNF for basic transport

   b-sexp         =  c-sexp / b-base-64

   b-base-64      =  "{" *whitespace *base-64-chars base-64-end "}"
                       ; encodes a c-sexp, which has a minimum
                       ; length of 2

Restricted S-expressions
This document has described S-expressions in general form. Applications may wish to restrict their use of S-expressions in various ways as well as to specify a different default display-hint. Here are some possible restrictions that might be considered:
- no advanced representations (only canonical and basic)
- no display-hints
- no lengths on hexadecimal, quoted-strings, or base-64 encodings
- no empty lists
- no empty octet-strings
- no lists having another list as its first element
- no base-64 or hexadecimal encodings
- fixed limits on the size of octet-strings
As provided in Section 6, conformant implementations will support canonical and basic representation but support for advanced representation is not generally required. Thus advanced representation can only be used in applications which mandate its support or where a capability discovery mechanism indicates support.
In-memory representations
For processing, the S-expression would typically be parsed and represented in memory in a way that is more amenable to efficient processing. This document suggests two alternatives:
- "list-structure"
- "array-layout"
These are only sketched here, as they are only suggestive. The [SexpCode] code illustrates these styles in more detail.
List-structure memory representation
Here there are separate records for simple-strings, strings, and lists or list nodes. An S-expression of the form ("abc" "de") could be encoded as two records for the simple-strings, two for the strings, and two for the list elements, where a record is a relatively small block of memory and, except for simple-string, might have pointers in it to other records. This is a fairly conventional representation as discussed in Section 4 of [LISP2].
Array-layout memory representation
Here each S-expression is represented as a contiguous array of octets. The first octet codes the "type" of the S-expression:
01   octet-string
02   octet-string with display-hint
03   beginning of list (and 00 is used for "end of list")
Each of the three types is immediately followed by a k-octet integer indicating the size (in octets) of the following representation. Here k is an integer that depends on the implementation, it might be anywhere from 2 to 8, but would be fixed for a given implementation; it determines the size of the objects that can be handled. The transport and canonical representations are independent of the choice of k made by the implementation.
Although the lengths of lists are not given in the usual S-expression notations, it is easy to fill them in when parsing; when you reach a right-parenthesis you know how long the list representation was, and where to go back to fill in the missing length.
Octet-string
This is represented as follows:

    01 <length> <octet-string>

For example (here k = 2)

    01 0003 a b c

Octet-string with display-hint
This is represented as follows:

    02 <length>
      01 <length> <octet-string>    /* for display-type */
      01 <length> <octet-string>    /* for octet-string */

For example, the S-expression

    [gif] #61626364#

would be represented as (with k = 2)

    02 000d
      01 0003  g  i  f
      01 0004 61 62 63 64

List
This is represented as

    03 <length> <item1> <item2> <item3> ... <item> 00

For example, the list (abc [d]ef (g)) is represented in memory as (with k = 2)

    03 001b
      01 0003 a b c
      02 0009
        01 0001 d
        01 0002 e f
      03 0005
        01 0001 g
      00
    00

Security Considerations
As a pure data representation format, there are few security considerations to S-expressions. A canonical form is required for the consistent creation and verification of digital signatures. This is provided in Section 6.2.
The default display-hint (see Section 4.6) can be specified for an application. Note that if S-expressions containing untyped octet-strings represented for that application are processed by a different application, those untyped octet-string may be treated as if they had a different display-hint.
IANA Considerations
This document requires no IANA actions.
Normative References
[C]

The C Programming Language
Brian W. Kernighan
Dennis M. Ritchie
1988
ISBN 0-13-110370-9
Informative References
[BERN]

Netstrings
Daniel J. Bernstein
1997-02-01
Work in progress
[CANON2]

Canonical S-expressions
Wikipedia
[CANON3]

Csexp - Canonical S-expressions
Rudi Grinberg
2023-03-24
[CDDLfreezer]

A feature freezer for the Concise Data Definition Language (CDDL)
Carsten Bormann
Universität Bremen TZI
2023-09-12
work in progress
[formal]

A Formalization of Symbolic Expressions
Marc Petit-Huguenin
Impedance Mismatch LLC
2024-05-24
work in progress
[GnuPG]

The GNU Privacy Guard
Free Software Foundation, Inc.
[Inferno]

Inferno S-expressions
Uriel
Random Contrarian Insurgent Organization
[Libgcrypt]

The Libgcrypt Library
GnuPG
2023-04-06
Libgcrypt version 1.10.2
[LISP]

LISP 1.5 Programmer's Manual
Michael I. Levin
The Computer Center and Research Laboratory of Electronics, Massachusetts Institute of Technology
John McCarthy
15 August 1962
ISBN-13 978-0-262-12011-0
ISBN-10 0262130114
[LISP2]

Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I
John McCarthy
Massachusetts Institute of Technology
April 1960
[Ribose]

Open-source projects for developers and designers
Ribose Group Inc.
13 April 2023
[RNPGP_SEXPP]

S-Expressions parser and generator library in C++ (SEXP in C++)
Ribose RNP
2023-06-28
version 0.8.7
[SDSI]

A Simple Distributed Security Architecture
Ronald L. Rivest
Butler Lampson
2 October 1996
working document
SDSI version 1.1
[SexpCode]

SEXP---(S-expressions)
J. P. Malkiewicz
2015-06-10
[SEXPP]

SexpProcessor
Ryan "zenspider" Davis
2015-06-10
[SFEXP]

Small Fast X-Expression Library
Matthew Sottile
2023-03-24
[SPKI]

SPKI/SDSI 2.0 A Simple Distributed Security Infrastructure
Ronald L. Rivest
MIT Lab for Computer Science
[XML]

Extensible Markup Language (XML) 1.0
Tim Bray
Textuality and Netscape
Jean Paoli
Microsoft
C. M. Sperberg-McQueen
W3C
Eve Maler
Sun Microsystems
François Yergeau
2008-11-26
[RFC2692]
[RFC8949]
[RFC7159]
[RFC2693]
[RFC3275]
[RFC3259]
[RFC2119]
[RFC8174]
[RFC0020]
[RFC4648]
[RFC2130]
[RFC3629]
[RFC2046]
[RFC5234]
Implementations
At this time there are multiple implementations, many open source, available that are intended to read and parse some or all of the various S-expression formats specified here. In particular, see the following likely incomplete list:
- Project GNU's [Libgcrypt].
- Ribose's RNP [RNPGP_SEXPP] in C++.
- Github project of J. P. Malkiewicz [SexpCode] in C.
- The Inferno implementation [Inferno].
- Small Fast X-Expression Library [SFEXP].
- S-expression Processor [SEXPP] in Ruby.
- Canonical S-expressions [CANON] (OCAML).
Change History
RFC Editor Note: Please delete this section before publication.
-00 Changes
This sub-section summarizes significant changes between the original 1997 -00 version of this document and the 2023 -00 version submitted to the IETF.
1. Convert to XML v3.
2. Update Ron Rivest author information and, with his permission, add Donald Eastlake as an author.
3. Add minimal "IANA Considerations" and "Security Considerations" sections.
4. Since implementation requirements terminology is used, add the usual paragraph about it as a sub-section of Section 1 and add references to [RFC2119] and [RFC8174].
5. Divide references into Normative and Informational and update base-64 reference to be to [RFC4648].
6. Add a couple of sentences to the "Historical note" section about the history of -00 versions of the draft.
Changes from -00 to -01
1. Fix glitches and errors in the BNF.
2. Add Acknowledgements section to list Marc Petit-Huguenin (who provided BNF improvements) and John Klensin.
3. Update code references in Appendix A and add to Informative References section. Note: The code in the Malkiewicz github repository may be the code that was originally at http://theory.lcs.mit.edu/~rivest/sexp.html
4. Add this Change History Appendix.
5. Move "Historical Notes" which were formerly a separate section at the end of the document up to be a sub-section of Section 1.
6. Add references to [LISP], [RFC2692], and [RFC2693].
7. Add simple security considerations.
8. Minor editorial fixes/improvements.
Changes from -01 to -02
1. Change default MIME Type in Section 4.6 to have charset=utf-8 [RFC4648].
2. Change BNF to ABNF and add reference to [RFC5234].
3. Move Marc Petit-Huguenin to a Contributors section for his work on the ABNF.
Changes from -02 to -03
1. Add current S-expression usage Section 1.2.
2. Add the white book [C] as a reference.
3. Add reference to the Ribose RNP code [RNPGP_SEXPP].
4. Minor editorial improvements.
Changes from -03 to -04
Trivial keep-alive update.
Changes from -04 to -05
1. Add reference to [Inferno] implementation.
2. Eliminate remaining references to being a "proposal".
3. Emphasize that a particular application can specify a different default display-hint.
4. Add reference to [RFC0020] for ASCII.
5. Minor editorial improvements.
Changes from -05 to -06
1. Move implementations list to Appendix A. Add numerous implementations.
2. Change default display-hint to "application/octet-stream".
3. Expand Abstract and include most of Abstract in the Introduction.
4. Use different tokens for the top-level rule in the three ABNF encodings so that the rules would not collide if all were used. Fix ABNF for "printable".
5. Add an illustration of list-structure memory representation.
6. Editorial improvements.
Changes from -06 to -07
1. Re-order some top-level sections.
2. Replace "list-structure" memory figure with explanation and [LISP2] reference.
3. Re-organize ABNF to give full ABNF for advanced transport first and then mostly derive canonical and basic from advanced.
4. Correct reference to [RFC5234] to be to Appendix B.1, not Appendix A.
5. Attempt to clarify the difference between canonicalization and equality.
6. Add the explicit Section 6.1 on base-64 representation of S-expressions.
7. Globally hyphenate "octet-string" and "display-hint", generally replace "byte" with "octet".
8. Add some more examples here and there.
9. Fix typos. Other editorial improvements.
Changes from -07 to -08
1. A variety of minor fixes and more precise wording.
2. Give exact circumstances under which a space is needed to separate successive octet-string representations in a list.
3. Additional editorial improvements.
Changes from -08 to -09
1. Add mention of and reference to [formal].
2. Add mention in the text that whitespace can appear just after the opening curly brace and before just before the closing curly brace of base-64 encoding (the ABNF was correct).
3. Minor editorial improvements.
Changes from -09 to -10
1. Revert default display hint to more closely follow the original SPKI S-expressions.
2. Editorial improvements.
Changes from -10 to -12
Minor ABNF fixes and editorial changes.
Changes from -12 to -13
Added recommendation and references for using UTF-8 to support Interntionalization for text octet-strings. Minor other updates based on IESG reviews.
Acknowledgements
Special thanks to Daniel K. Gillmore for his extensive comments.
The comments and suggestions of the following are gratefully acknowledged: John Klensin and Caleb Malchik.
Contributors
Special thanks to Marc Petit-Huguenin, particularly for his extensive work and advice on the ABNF and on locating and fixing unclear parts of earlier versions of this document:
Marc Petit-Huguenin
Impedance Mismatch LLC
marc@petit-huguenin.org