RFC 9804: Simple Public Key Infrastructure (SPKI) S-Expressions
- R. Rivest,
- D. Eastlake 3rd
Abstract
This memo specifies the data structure representation that was
devised to support Simple Public Key Infrastructure (SPKI)
certificates, as detailed in RFC 2692, 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
Status of This Memo
This document is not an Internet Standards Track specification; it is published for informational purposes.¶
This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Not all documents approved by the IESG are candidates for any level of Internet Standard; see Section 2 of RFC 7841.¶
Information about the current status of this document, any
errata, and how to provide feedback on it may be obtained at
https://
Copyright Notice
Copyright (c) 2025 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents
(https://
1. Introduction
This memo specifies the data structure representation that was
devised to support Simple Public Key Infrastructure (SPKI) certificates [RFC2692], with the intent that it be more
widely applicable (see Section 1.3, "Historical Note"). 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
This memo makes precise the encodings of these SPKI
S-expressions: It gives a "canonical form" for them, describes two
"transport" representations
These S-expressions are either octet-strings or lists of simpler S-expressions. Here is a sample S-expression:¶
It is a list of length three containing the following:¶
This document specifies how to construct and use these S-expressions.¶
The design goals for S-expressions were as follows:¶
For implementors of new applications and protocols other technologies also worthy of consideration include the following: XML [XML], CBOR [RFC8949], and JSON [RFC8259].¶
1.1. 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 the Libgcrypt library [Libgcrypt], and there are other implementations (see Appendix A).¶
S-expressions are also used or referenced in the following RFCs:¶
In addition, S-expressions are the inspiration for the encodings in other protocols. For example, [RFC3259] or Section 6 of [CDDL-freezer].¶
1.2. Formalization
[Formal] is an Internet-Draft that shows a formal model of SPKI S-expressions and formally demonstrates that the examples and ABNF in this document are correct.¶
1.3. 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 "netstrings" [BERN], which were developed contemporaneous
1.4. 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.¶
2. S-expressions -- Informal Introduction
Informally, an S-expression is either:¶
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¶
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:¶
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:¶
In this document, related encoding techniques for each of these uses are provided.¶
3. 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:¶
Numeric:¶
Whitespace:¶
The following graphics characters, which are called
"pseudo
The following graphics characters, which are "reserved punctuation":¶
The following characters are unused and unavailable, except in "verbatim" and "quoted string" encodings:¶
4. 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.¶
4.1. Verbatim Representation
A verbatim encoding of an octet-string consists of three parts:¶
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:¶
4.2. Quoted-String Representation
The quoted-string representation of an octet-string consists of:¶
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 [C88] 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 programming language [C88] conventions, with an extension for ignoring line terminators of just CR, LF, CRLF, or LFCR and more restrictive octal and hexadecimal value formats):¶
Here are some examples of quoted-string encodings:¶
4.3. Token Representation
An octet-string that meets the following conditions may be given directly as a "token":¶
Note: Upper and lower case are not equivalent. A token may begin with punctuation, including ":".¶
Here are some examples of token representations
4.4. Hexadecimal Representation
An octet-string may be represented with a hexadecimal encoding consisting of:¶
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:¶
4.5. Base-64 Representation of Octet-Strings
An octet-string may be represented in a base-64 encoding [RFC4648] consisting of:¶
Base-64 encoding produces four characters of output for each three octets of input. When the length of the input is divided by three:¶
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:¶
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.¶
4.6. Display-Hints and Internationalization
An octet-string can contain any type of data representable by a finite octet-string, e.g., text, a fixed or variable-length integer, or an image. Normally, the application producing and/or consuming S-expressions will understand their structure, the data type, and the 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 purpose 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 media types [RFC2046] 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 represents "böb☺", that is, "bob" with an umlaut over the "o", followed by the Unicode [Unicode] character WHITE SMILING FACE (U+263A).¶
Every octet-string representation is or is not preceded by a single display-hint. 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:¶
An octet-string that has no display-hint may be considered to have a media type [RFC2046] specified by the application or use. In the absence of such a specification, the default is as follows:¶
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.¶
4.7. 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
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 .¶
5. 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:¶
6. S-Expression Representation Types
There are three "types" of representation:¶
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.¶
6.1. Base-64 Representation of S-Expressions
An S-expression may be represented in a base-64 encoding [RFC4648] consisting of:¶
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.¶
6.2. 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, reasonably economical, and unique for any S-expression. See [CANON1] and [CANON2].¶
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.3. Basic Transport Representation
There are two forms of the "basic transport" representation:¶
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 if 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:¶
The second example above is the same S-expression as the first encoded in base-64.¶
6.4. 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.¶
7. 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 are derived therefrom. The rule names below in all capital letters are defined in Appendix B.1 of [RFC5234].¶
8. 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:¶
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 that mandate its support or where a capability discovery mechanism indicates support.¶
9. 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:¶
These are only sketched here, as they are only suggestive. The code in [SexpCode] illustrates these styles in more detail.¶
9.1. 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].¶
9.2. 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:¶
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 it 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.¶
9.2.1. Octet-String
This is represented as follows:¶
For example (here, k = 2):¶
9.2.2. Octet-String with Display-Hint
This is represented as follows:¶
For example, the S-expression:¶
would be represented as (with k = 2):¶
9.2.3. List
This is represented as:¶
For example, the list (abc [d]ef (g)) is represented in memory as (with k = 2):¶
10. 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.¶
11. IANA Considerations
This document has no IANA actions.¶
12. References
12.1. Normative References
- [C88]
- Kernighan, B. and D. Ritchie, "The C Programming Language", ISBN 0-13-110370-9, .
- [RFC0020]
-
Cerf, V., "ASCII format for network interchange", STD 80, RFC 20, DOI 10
.17487 , , <https:///RFC0020 www >..rfc -editor .org /info /rfc20 - [RFC2119]
-
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10
.17487 , , <https:///RFC2119 www >..rfc -editor .org /info /rfc2119 - [RFC3629]
-
Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, DOI 10
.17487 , , <https:///RFC3629 www >..rfc -editor .org /info /rfc3629 - [RFC4648]
-
Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10
.17487 , , <https:///RFC4648 www >..rfc -editor .org /info /rfc4648 - [RFC5234]
-
Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, DOI 10
.17487 , , <https:///RFC5234 www >..rfc -editor .org /info /rfc5234 - [RFC8174]
-
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10
.17487 , , <https:///RFC8174 www >..rfc -editor .org /info /rfc8174
12.2. Informative References
- [BERN]
-
Bernstein, D. J., "Netstrings", Work in Progress, Internet-Draft, draft
-bernstein , , <https://-netstrings -02 datatracker >..ietf .org /doc /html /draft -bernstein -netstrings -02 - [CANON1]
-
Wikipedia, "Canonical S-expressions", <https://
en >..wikipedia .org /wiki /Canonical _S -expressions - [CANON2]
-
Grinberg, R., "Csexp - Canonical S-expressions", , <https://
github >..com /ocaml -dune /csexp - [CDDL-freezer]
-
Bormann, C., "A feature freezer for the Concise Data Definition Language (CDDL)", Work in Progress, Internet-Draft, draft
-bormann , , <https://-cbor -cddl -freezer -15 datatracker >..ietf .org /doc /html /draft -bormann -cbor -cddl -freezer -15 - [Formal]
-
Petit-Huguenin, M., "A Formalization of Symbolic Expressions", Work in Progress, Internet-Draft, draft
-petithuguenin , , <https://-ufmrg -formal -sexpr -06 datatracker >..ietf .org /doc /html /draft -petithuguenin -ufmrg -formal -sexpr -06 - [GnuPG]
-
GnuPG, "The GNU Privacy Guard", <https://
www >..gnupg .org / - [Inferno]
-
"Inferno S-expressions", Inferno Manual Page, <https://
man >..cat -v .org /inferno /6 /sexprs - [Libgcrypt]
-
GnuPG, "The Libgcrypt Library", Libgcrypt version 1.10.2, , <https://
www >..gnupg .org /documentation /manuals /gcrypt / - [LISP]
-
McCarthy, J., Abrahams, P. W., Edwards, D. J., Hart, T. P., and M. Levin, "LISP 1.5 Programmer's Manual", ISBN-13 978
-0 , ISBN-10 0262130114, , <https://-262 -12011 -0 www >..softwarepreserv ation .org /projects /LISP /book /LISP %201 .5 %20Programmers %20Manual .pdf - [LISP2]
-
McCarthy, J., "Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I", , <https://
people >..cs .umass .edu /~emery /classes /cmpsci691st /readings /PL /LISP .pdf - [RFC2046]
-
Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types", RFC 2046, DOI 10
.17487 , , <https:///RFC2046 www >..rfc -editor .org /info /rfc2046 - [RFC2130]
-
Weider, C., Preston, C., Simonsen, K., Alvestrand, H., Atkinson, R., Crispin, M., and P. Svanberg, "The Report of the IAB Character Set Workshop held 29 February - 1 March, 1996", RFC 2130, DOI 10
.17487 , , <https:///RFC2130 www >..rfc -editor .org /info /rfc2130 - [RFC2692]
-
Ellison, C., "SPKI Requirements", RFC 2692, DOI 10
.17487 , , <https:///RFC2692 www >..rfc -editor .org /info /rfc2692 - [RFC2693]
-
Ellison, C., Frantz, B., Lampson, B., Rivest, R., Thomas, B., and T. Ylonen, "SPKI Certificate Theory", RFC 2693, DOI 10
.17487 , , <https:///RFC2693 www >..rfc -editor .org /info /rfc2693 - [RFC3259]
-
Ott, J., Perkins, C., and D. Kutscher, "A Message Bus for Local Coordination", RFC 3259, DOI 10
.17487 , , <https:///RFC3259 www >..rfc -editor .org /info /rfc3259 - [RFC3275]
-
Eastlake 3rd, D., Reagle, J., and D. Solo, "(Extensible Markup Language) XML-Signature Syntax and Processing", RFC 3275, DOI 10
.17487 , , <https:///RFC3275 www >..rfc -editor .org /info /rfc3275 - [RFC8259]
-
Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10
.17487 , , <https:///RFC8259 www >..rfc -editor .org /info /rfc8259 - [RFC8949]
-
Bormann, C. and P. Hoffman, "Concise Binary Object Representation (CBOR)", STD 94, RFC 8949, DOI 10
.17487 , , <https:///RFC8949 www >..rfc -editor .org /info /rfc8949 - [Ribose]
-
Ribose Group Inc., "Open-source projects for developers and designers", <https://
open >..ribose .com / - [RNPGP_SEXPP]
-
"S-Expressions parser and generator library in C++ (SEXP in C++)", Version 0.9.2, commit 249c6e3, , <https://
github >..com /rnpgp /sexpp - [SDSI]
-
Rivest, R. and B. Lampson, "A Simple Distributed Security Architecture", Working document for SDSI version 1.1, , <https://
people >..csail .mit .edu /rivest /pubs /RL96 .ver -1 .1 .html - [SexpCode]
-
"SEXP
---(S , commit 4aa7c36, , <https://-expressions )" github >..com /jpmalkiewicz /rivest -sexp - [SEXPP]
-
"SexpProcessor", commit a90f90f, , <https://
github >..com /seattlerb /sexp _processor - [SFEXP]
-
"Small Fast X-Expression Library", commit b7d3bea, , <https://
github >..com /mjsottile /sfsexp - [SPKI]
-
Rivest, R., "SPKI/SDSI 2.0 A Simple Distributed Security Infrastructure", <https://
people >..csail .mit .edu /rivest /pubs /RL96 .slides -maryland .pdf - [Unicode]
-
The Unicode Consortium, "The Unicode Standard", <https://
www >..unicode .org /versions /latest / - [XML]
-
Bray, T., Paoli, J., Sperberg
-Mc , Maler, E., and F. Yergeau, "Extensible Markup Language (XML) 1.0", W3C Recommendation, , <https://Queen, C.M. www >. Latest version available at <https://.w3 .org /TR /2008 /REC -xml -20081126 / www >..w3 .org /TR /REC -xml /
Appendix A. Implementations
At this time there are multiple implementations
Acknowledgements
Special thanks to Daniel K. Gillmor 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 draft versions of this document:¶