rfc8785xml2.original.xml | rfc8785.xml | |||
---|---|---|---|---|
<?xml version='1.0' encoding='utf-8'?> | ||||
<!DOCTYPE rfc SYSTEM "rfc2629-xhtml.ent"> | ||||
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" | ||||
submissionType="independent" | ||||
category="info" | ||||
tocDepth="4" | ||||
docName="draft-rundgren-json-canonicalization-scheme-17" | ||||
number="8785" | ||||
ipr="trust200902" | ||||
obsoletes="" | ||||
updates="" | ||||
xml:lang="en" | ||||
tocInclude="true" | ||||
symRefs="true" | ||||
sortRefs="true" | ||||
version="3"> | ||||
<front> | ||||
<title abbrev="JSON Canonicalization Scheme"> | ||||
JSON Canonicalization Scheme (JCS) | ||||
</title> | ||||
<seriesInfo name="RFC" value="8785"/> | ||||
<author fullname="Anders Rundgren" initials="A." surname="Rundgren"> | ||||
<organization>Independent</organization> | ||||
<address> | ||||
<postal> | ||||
<city>Montpellier</city> | ||||
<country>France</country> | ||||
</postal> | ||||
<email>anders.rundgren.net@gmail.com</email> | ||||
<uri>https://www.linkedin.com/in/andersrundgren/</uri> | ||||
</address> | ||||
</author> | ||||
<author fullname="Bret Jordan" initials="B." surname="Jordan"> | ||||
<organization>Broadcom</organization> | ||||
<address> | ||||
<postal> | ||||
<street>1320 Ridder Park Drive</street> | ||||
<region>CA</region> | ||||
<code>95131</code> | ||||
<city>San Jose</city> | ||||
<country>United States of America</country> | ||||
</postal> | ||||
<email>bret.jordan@broadcom.com</email> | ||||
</address> | ||||
</author> | ||||
<author fullname="Samuel Erdtman" initials="S." surname="Erdtman"> | ||||
<organization>Spotify AB</organization> | ||||
<address> | ||||
<postal> | ||||
<street>Birger Jarlsgatan 61, 4tr</street> | ||||
<code>113 56</code> | ||||
<city>Stockholm</city> | ||||
<country>Sweden</country> | ||||
</postal> | ||||
<email>erdtman@spotify.com</email> | ||||
</address> | ||||
</author> | ||||
<date year="2020" month="June" /> | ||||
<area>Security</area> | ||||
<workgroup/> | ||||
<keyword>JSON</keyword> | ||||
<keyword>ECMAScript</keyword> | ||||
<keyword>Signatures</keyword> | ||||
<keyword>Cryptography</keyword> | ||||
<keyword>Canonicalization</keyword> | ||||
<abstract> | ||||
<t> | ||||
Cryptographic operations like hashing and signing need the data to be | ||||
expressed in an invariant format so that the operations are reliably | ||||
repeatable. | ||||
One way to address this is to create a canonical representation of | ||||
the data. Canonicalization also permits data to be exchanged in its | ||||
original form on the "wire" while cryptographic operations | ||||
performed on the canonicalized counterpart of the data in the | ||||
producer and consumer endpoints generate consistent results. | ||||
</t> | ||||
<t> | ||||
This document describes the JSON Canonicalization Scheme (JCS). | ||||
This specification defines how to create a canonical representation | ||||
of JSON data by building on the strict serialization methods for | ||||
JSON primitives defined by ECMAScript, constraining JSON data to | ||||
the Internet JSON (I-JSON) subset, and by using deterministic property | ||||
sorting. | ||||
</t> | ||||
</abstract> | ||||
</front> | ||||
<middle> | ||||
<section anchor="Introduction" numbered="true" toc="default"> | ||||
<name>Introduction</name> | ||||
<t> | ||||
This document describes the JSON Canonicalization Scheme (JCS). | ||||
This specification defines how to create a canonical representation | ||||
of JSON <xref target="RFC8259" format="default"/> data by building | ||||
on the strict serialization methods for | ||||
JSON primitives defined by ECMAScript <xref target="ECMA-262" | ||||
format="default"/>, | ||||
constraining JSON data to the I-JSON <xref target="RFC7493" | ||||
format="default"/> | ||||
subset, and by using deterministic property sorting. The output from | ||||
JCS is a | ||||
"hashable" representation of JSON data that can be used by | ||||
cryptographic methods. | ||||
The subsequent paragraphs outline the primary design considerations. | ||||
</t> | ||||
<t> | ||||
Cryptographic operations like hashing and signing need the data to be | ||||
expressed in an invariant format so that the operations are reliably | ||||
repeatable. | ||||
One way to accomplish this is to convert the data into | ||||
a format that has a simple and fixed representation, | ||||
like base64url <xref target="RFC4648" format="default"/>. | ||||
This is how JSON Web Signature (JWS) <xref target="RFC7515" | ||||
format="default"/> addressed this issue. | ||||
Another solution is to create a canonical version of the data, | ||||
similar to what was done for the XML signature <xref target="XMLDSIG" | ||||
format="default"/> standard. | ||||
</t> | ||||
<t> | ||||
The primary advantage with a canonicalizing scheme is that data | ||||
can be kept in its original form. This is the core rationale behind | ||||
JCS. | ||||
Put another way, using canonicalization enables a JSON object to | ||||
remain a JSON object | ||||
even after being signed. This can simplify system design, | ||||
documentation, and logging. | ||||
</t> | ||||
<t> | ||||
To avoid "reinventing the wheel", JCS relies on the serialization of | ||||
JSON primitives | ||||
(strings, numbers, and literals), as defined by ECMAScript (aka | ||||
JavaScript) | ||||
<xref target="ECMA-262" format="default"/> beginning with version 6. | ||||
</t> | ||||
<t> | ||||
Seasoned XML developers may recall difficulties getting XML signatures | ||||
to validate. This was usually due to different interpretations of the | ||||
quite intricate | ||||
XML canonicalization rules as well as of the equally complex | ||||
Web Services security standards. | ||||
The reasons why JCS should not suffer from similar issues are: | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
JSON does not have a namespace concept and default values. | ||||
</li> | ||||
<li> | ||||
Data is constrained to the I&nbhy;JSON <xref target="RFC7493" | ||||
format="default"/> subset. | ||||
This eliminates the need for specific parsers for dealing with | ||||
canonicalization. | ||||
</li> | ||||
<li> | ||||
JCS-compatible serialization of JSON primitives is currently | ||||
supported | ||||
by most web browsers as well as by Node.js <xref target="NODEJS" | ||||
format="default"/>. | ||||
</li> | ||||
<li> | ||||
The full JCS specification is currently supported by multiple | ||||
open-source implementations (see <xref target="open.source" | ||||
format="default"/>). | ||||
See also <xref target="impl.guidelines" format="default"/> for | ||||
implementation | ||||
guidelines. | ||||
</li> | ||||
</ul> | ||||
<t> | ||||
JCS is compatible with some existing systems relying on JSON | ||||
canonicalization such as JSON Web Key (JWK) Thumbprint <xref | ||||
target="RFC7638" format="default"/> and Keybase <xref target="KEYBASE" | ||||
format="default"/>. | ||||
</t> | ||||
<t> | ||||
For potential uses outside of cryptography, see <xref | ||||
target="I-D.rundgren-comparable-json" format="default"/>. | ||||
</t> | ||||
<t> | ||||
The intended audiences of this document are JSON tool vendors as | ||||
well as designers of JSON-based cryptographic solutions. | ||||
The reader is assumed to be knowledgeable in ECMAScript, including the | ||||
"JSON" object. | ||||
</t> | ||||
</section> | ||||
<section anchor="Terminology" numbered="true" toc="default"> | ||||
<name>Terminology</name> | ||||
<t> | ||||
Note that this document is not on the IETF standards track. However, a | ||||
conformant | ||||
implementation is supposed to adhere to the specified behavior for | ||||
security and interoperability reasons. This text uses BCP 14 to | ||||
describe that necessary behavior. | ||||
</t> | ||||
<t> | ||||
The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", | ||||
"<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL | ||||
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", | ||||
"<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>", | ||||
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are | ||||
to be interpreted as described in BCP 14 <xref target="RFC2119"/> | ||||
<xref target="RFC8174"/> when, and only when, they appear in all capitals, | ||||
as shown here. | ||||
</t> | ||||
</section> | ||||
<section anchor="Operation" numbered="true" toc="default"> | ||||
<name>Detailed Operation</name> | ||||
<t> | ||||
This section describes the details related to creating | ||||
a canonical JSON representation and how they are addressed by JCS. | ||||
</t> | ||||
<t> | ||||
<xref target="impl.guidelines" format="default"/> describes | ||||
the <bcp14>RECOMMENDED</bcp14> way of adding JCS support to existing | ||||
JSON tools. | ||||
</t> | ||||
<section anchor="json.datacreation" numbered="true" toc="default"> | ||||
<name>Creation of Input Data</name> | ||||
<t> | ||||
Data to be canonically serialized is usually created by: | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
Parsing previously generated JSON data. | ||||
</li> | ||||
<li> | ||||
Programmatically creating data. | ||||
</li> | ||||
</ul> | ||||
<t> | ||||
Irrespective of the method used, the data to be serialized | ||||
<bcp14>MUST</bcp14> be adapted | ||||
for I&nbhy;JSON <xref target="RFC7493" format="default"/> | ||||
formatting, which implies the following: | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
JSON objects <bcp14>MUST NOT</bcp14> exhibit duplicate property | ||||
names. | ||||
</li> | ||||
<li> | ||||
JSON string data <bcp14>MUST</bcp14> be expressible | ||||
as Unicode <xref target="UNICODE" format="default"/>. | ||||
</li> | ||||
<li> | ||||
JSON number data <bcp14>MUST</bcp14> be expressible | ||||
as IEEE 754 <xref target="IEEE754" format="default"/> | ||||
double-precision values. | ||||
For applications needing higher precision or longer integers than | ||||
offered by IEEE 754 double precision, it is | ||||
<bcp14>RECOMMENDED</bcp14> to represent such | ||||
numbers as JSON strings; see <xref target="json.bignumbers" | ||||
format="default"/> for | ||||
details on how this can be performed in an interoperable and | ||||
extensible way. | ||||
</li> | ||||
</ul> | ||||
<t> | ||||
An additional constraint is that parsed JSON string data <bcp14>MUST | ||||
NOT</bcp14> be altered during subsequent serializations. For more | ||||
information, see <xref target="string.subtypes" format="default"/>. | ||||
</t> | ||||
<t> | ||||
Note: Although the Unicode standard offers the possibility of | ||||
rearranging certain character sequences, referred to as "Unicode | ||||
Normalization" <xref target="UCNORM" format="default"/>, | ||||
JCS-compliant string processing does not take this into | ||||
consideration. That is, all components involved in a scheme | ||||
depending on JCS <bcp14>MUST</bcp14> preserve Unicode string data | ||||
"as is". | ||||
</t> | ||||
</section> | ||||
<section anchor="json.canonicalization" numbered="true" toc="default"> | ||||
<name>Generation of Canonical JSON Data</name> | ||||
<t> | ||||
The following subsections describe the steps required to create a | ||||
canonical | ||||
JSON representation of the data elaborated on in the previous | ||||
section. | ||||
</t> | ||||
<t> | ||||
<xref target="canonicalize.js" format="default"/> shows sample code | ||||
for an ECMAScript-based canonicalizer, matching the JCS | ||||
specification. | ||||
</t> | ||||
<section anchor="json.whitespace" numbered="true" toc="default"> | ||||
<name>Whitespace</name> | ||||
<t> | ||||
Whitespace between JSON tokens <bcp14>MUST NOT</bcp14> be emitted. | ||||
</t> | ||||
</section> | ||||
<section anchor="json.serialization.data" numbered="true" | ||||
toc="default"> | ||||
<name>Serialization of Primitive Data Types</name> | ||||
<t> | ||||
Assume the following JSON object is parsed: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{ | ||||
"numbers": [333333333.33333329, 1E30, 4.50, | ||||
2e-3, 0.000000000000000000000000001], | ||||
"string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", | ||||
"literals": [null, true, false] | ||||
} | ||||
</sourcecode> | ||||
<t> | ||||
If the parsed data is subsequently serialized using a serializer | ||||
compliant with ECMAScript's "JSON.stringify()", the result would | ||||
(with a line wrap added for display purposes only) be rather | ||||
divergent with respect to the original data: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string": | ||||
"€$\u000f\nA'B\"\\\\\"/","literals":[null,true,false]} | ||||
</sourcecode> | ||||
<t> | ||||
The reason for the difference between the parsed data and its | ||||
serialized counterpart is due to a wide tolerance on input data | ||||
(as defined | ||||
by JSON <xref target="RFC8259" format="default"/>), while output | ||||
data (as defined by ECMAScript) | ||||
has a fixed representation. As can be seen in the example, | ||||
numbers are subject to rounding as well. | ||||
</t> | ||||
<t> | ||||
The following subsections describe the serialization of primitive | ||||
JSON data types | ||||
according to JCS. This part is identical to that of ECMAScript. | ||||
In the (unlikely) event that a future version of ECMAScript would | ||||
invalidate any of the following serialization methods, it will be | ||||
up to the developer community to | ||||
either stick to this specification or create a new specification. | ||||
</t> | ||||
<section anchor="json.ser.literals" numbered="true" toc="default"> | ||||
<name>Serialization of Literals</name> | ||||
<t> | ||||
In accordance with JSON <xref target="RFC8259" | ||||
format="default"/>, | ||||
the literals "null", "true", and | ||||
"false" <bcp14>MUST</bcp14> be serialized as null, true, and | ||||
false, respectively. | ||||
</t> | ||||
</section> | ||||
<section anchor="json.ser.string" numbered="true" toc="default"> | ||||
<name>Serialization of Strings</name> | ||||
<t> | ||||
For JSON string data (which includes JSON object property names | ||||
as well), each Unicode code point <bcp14>MUST</bcp14> be | ||||
serialized as described below (see Section 24.3.2.2 of <xref | ||||
target="ECMA-262" format="default"/>): | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
If the Unicode value falls within the traditional ASCII | ||||
control character range (U+0000 through U+001F), it | ||||
<bcp14>MUST</bcp14> be serialized using lowercase hexadecimal | ||||
Unicode notation (\uhhhh) unless it is in the set of | ||||
predefined JSON control characters U+0008, U+0009, U+000A, | ||||
U+000C, or U+000D, which <bcp14>MUST</bcp14> be serialized as | ||||
\b, \t, \n, \f, and \r, respectively. | ||||
</li> | ||||
<li> | ||||
If the Unicode value is outside of the ASCII control character | ||||
range, it <bcp14>MUST</bcp14> be serialized "as is" | ||||
unless it is equivalent to U+005C (\) or U+0022 ("), | ||||
which <bcp14>MUST</bcp14> be serialized as \\ and \", | ||||
respectively. | ||||
</li> | ||||
</ul> | ||||
<t> | ||||
Finally, the resulting sequence of Unicode code points | ||||
<bcp14>MUST</bcp14> be enclosed in double quotes ("). | ||||
</t> | ||||
<t> | ||||
Note: Since invalid Unicode data like "lone surrogates" (e.g., | ||||
U+DEAD) | ||||
may lead to interoperability issues including broken signatures, | ||||
occurrences of such data <bcp14>MUST</bcp14> cause a compliant | ||||
JCS implementation to terminate | ||||
with an appropriate error. | ||||
</t> | ||||
</section> | ||||
<section anchor="json.ser.number" numbered="true" toc="default"> | ||||
<name>Serialization of Numbers</name> | ||||
<t> | ||||
ECMAScript builds on the IEEE 754 <xref target="IEEE754" | ||||
format="default"/> double-precision standard for representing | ||||
JSON number data. Such data <bcp14>MUST</bcp14> be serialized | ||||
according to Section 7.1.12.1 of <xref target="ECMA-262" | ||||
format="default"/>, including the "Note 2" enhancement. | ||||
</t> | ||||
<t> | ||||
Due to the relative complexity of this part, the algorithm | ||||
itself is not included in this document. | ||||
For implementers of JCS-compliant number serialization, | ||||
Google's implementation in V8 <xref target="V8" | ||||
format="default"/> may serve as a reference. | ||||
Another compatible number serialization reference implementation | ||||
is Ryu <xref target="RYU" format="default"/>, | ||||
which is used by the JCS open-source Java implementation | ||||
mentioned in <xref target="open.source" format="default"/>. | ||||
<xref target="json.ieee754.test" format="default"/> holds a set | ||||
of IEEE 754 sample values and their | ||||
corresponding JSON serialization. | ||||
</t> | ||||
<t> | ||||
Note: Since Not a Number (NaN) and Infinity | ||||
are not permitted in JSON, occurrences of NaN or | ||||
Infinity <bcp14>MUST</bcp14> cause a compliant JCS | ||||
implementation to terminate with an appropriate error. | ||||
</t> | ||||
</section> | ||||
</section> | ||||
<section anchor="json.sorting.properties" numbered="true" | ||||
toc="default"> | ||||
<name>Sorting of Object Properties</name> | ||||
<t> | ||||
Although the previous step normalized the representation of | ||||
primitive JSON data types, the result would not yet qualify as | ||||
"canonical" since JSON object properties are not in lexicographic | ||||
(alphabetical) order. | ||||
</t> | ||||
<t> | ||||
Applied to the sample in <xref target="json.serialization.data" | ||||
format="default"/>, | ||||
a properly canonicalized version should (with a | ||||
line wrap added for display purposes only) read as: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{"literals":[null,true,false],"numbers":[333333333.3333333, | ||||
1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"} | ||||
</sourcecode> | ||||
<t> | ||||
The rules for lexicographic sorting of JSON object | ||||
properties according to JCS are as follows: | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
JSON object properties <bcp14>MUST</bcp14> be sorted | ||||
recursively, | ||||
which means that JSON child Objects | ||||
<bcp14>MUST</bcp14> have their properties sorted as well. | ||||
</li> | ||||
<li> | ||||
JSON array data <bcp14>MUST</bcp14> also be scanned for the | ||||
presence of JSON objects (if an object is found, then its | ||||
properties <bcp14>MUST</bcp14> be sorted), | ||||
but array element order <bcp14>MUST NOT</bcp14> be changed. | ||||
</li> | ||||
</ul> | ||||
<t> | ||||
When a JSON object is about to have its properties | ||||
sorted, the following measures <bcp14>MUST</bcp14> be adhered to: | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
The sorting process is applied to property name strings in their | ||||
"raw" (unescaped) form. | ||||
That is, a newline character is treated as U+000A. | ||||
</li> | ||||
<li> | ||||
Property name strings to be sorted are formatted | ||||
as arrays of UTF-16 <xref target="UNICODE" format="default"/> | ||||
code units. | ||||
The sorting is based on pure value comparisons, where code units | ||||
are treated as | ||||
unsigned integers, independent of locale settings. | ||||
</li> | ||||
<li> | ||||
<t> | ||||
Property name strings either have different values at some | ||||
index that is | ||||
a valid index for both strings, or their lengths are | ||||
different, or both. | ||||
If they have different values at one or more index | ||||
positions, let k be the smallest such index; then, the string | ||||
whose | ||||
value at position k has the smaller value, as determined by | ||||
using | ||||
the "<" operator, lexicographically precedes the other | ||||
string. | ||||
If there is no index position at which they differ, | ||||
then the shorter string lexicographically precedes the longer | ||||
string. | ||||
</t> | ||||
<t> | ||||
In plain English, this means that property names are sorted in | ||||
ascending order like the following: | ||||
</t> | ||||
<artwork type="ascii-art" align="left" alt=""><![CDATA[ | ||||
"" | ||||
"a" | ||||
"aa" | ||||
"ab" | ||||
]]></artwork> | ||||
</li> | ||||
</ul> | ||||
<t> | ||||
The rationale for basing the sorting algorithm on UTF-16 code | ||||
units is that | ||||
it maps directly to the string type in ECMAScript (featured in web | ||||
browsers | ||||
and Node.js), Java, and .NET. In addition, JSON only supports | ||||
escape sequences | ||||
expressed as UTF-16 code units, making knowledge and handling of | ||||
such data | ||||
a necessity anyway. | ||||
Systems using another internal representation of string data will | ||||
need to convert | ||||
JSON property name strings into arrays of UTF-16 code units before | ||||
sorting. | ||||
The conversion from UTF-8 or UTF-32 to UTF-16 is defined by the | ||||
Unicode <xref target="UNICODE" format="default"/> standard. | ||||
</t> | ||||
<t> | ||||
The following JSON test data can be used for verifying the correctness of | ||||
the sorting scheme in a JCS implementation: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{ | ||||
"\u20ac": "Euro Sign", | ||||
"\r": "Carriage Return", | ||||
"\ufb33": "Hebrew Letter Dalet With Dagesh", | ||||
"1": "One", | ||||
"\ud83d\ude00": "Emoji: Grinning Face", | ||||
"\u0080": "Control", | ||||
"\u00f6": "Latin Small Letter O With Diaeresis" | ||||
} | ||||
</sourcecode> | ||||
<t> | ||||
Expected argument order after sorting property strings: | ||||
</t> | ||||
<artwork type="ascii-art" align="left" alt=""><![CDATA[ "Carriage Ret | ||||
urn" | ||||
"One" | ||||
"Control" | ||||
"Latin Small Letter O With Diaeresis" | ||||
"Euro Sign" | ||||
"Emoji: Grinning Face" | ||||
"Hebrew Letter Dalet With Dagesh"]]></artwork> | ||||
<t> | ||||
Note: For the purpose of obtaining a deterministic property order, | ||||
sorting of data encoded in UTF-8 or UTF-32 would also work, but | ||||
the outcome for JSON data like above would differ and thus be | ||||
incompatible with this specification. | ||||
However, in practice, property names are rarely defined outside of | ||||
7-bit ASCII, making it possible to sort string data in UTF-8 or | ||||
UTF-32 format without conversion to UTF-16 and still be compatible | ||||
with JCS. Whether or not this is a viable option depends on the | ||||
environment JCS is used in. | ||||
</t> | ||||
</section> | ||||
<section anchor="json.utf8" numbered="true" toc="default"> | ||||
<name>UTF-8 Generation</name> | ||||
<t> | ||||
Finally, in order to create a platform-independent representation, | ||||
the result of the preceding step <bcp14>MUST</bcp14> be encoded in | ||||
UTF-8. | ||||
</t> | ||||
<t> | ||||
Applied to the sample in <xref target="json.sorting.properties" | ||||
format="default"/>, this | ||||
should yield the following bytes, here shown in hexadecimal | ||||
notation: | ||||
</t> | ||||
<artwork type="ascii-art" align="left" alt=""><![CDATA[ 7b 22 6c 69 7 | ||||
4 65 72 61 6c 73 22 3a 5b 6e 75 6c 6c 2c 74 72 | ||||
75 65 2c 66 61 6c 73 65 5d 2c 22 6e 75 6d 62 65 72 73 22 3a | ||||
5b 33 33 33 33 33 33 33 33 33 2e 33 33 33 33 33 33 33 2c 31 | ||||
65 2b 33 30 2c 34 2e 35 2c 30 2e 30 30 32 2c 31 65 2d 32 37 | ||||
5d 2c 22 73 74 72 69 6e 67 22 3a 22 e2 82 ac 24 5c 75 30 30 | ||||
30 66 5c 6e 41 27 42 5c 22 5c 5c 5c 5c 5c 22 2f 22 7d]]></artwork> | ||||
<t> | ||||
This data is intended to be usable as input to cryptographic | ||||
methods. | ||||
</t> | ||||
</section> | ||||
</section> | ||||
</section> | ||||
<section anchor="IANA" numbered="true" toc="default"> | ||||
<name>IANA Considerations</name> | ||||
<t> | ||||
This document has no IANA actions. | ||||
</t> | ||||
</section> | ||||
<section anchor="Security" numbered="true" toc="default"> | ||||
<name>Security Considerations</name> | ||||
<t> | ||||
It is crucial to perform sanity checks on input data to avoid | ||||
overflowing buffers and similar things that could affect the | ||||
integrity of the system. | ||||
</t> | ||||
<t> | ||||
When JCS is applied to signature schemes like the one described | ||||
in <xref target="impl.guidelines" format="default"/>, | ||||
applications <bcp14>MUST</bcp14> perform the following operations | ||||
before acting | ||||
upon received data: | ||||
</t> | ||||
<ol spacing="normal" type="1"> | ||||
<li> | ||||
Parse the JSON data and verify that it adheres to I-JSON. | ||||
</li> | ||||
<li> | ||||
Verify the data for correctness according to the conventions defined | ||||
by the | ||||
ecosystem where it is to be used. This also includes locating the | ||||
property holding the signature data. | ||||
</li> | ||||
<li> | ||||
Verify the signature. | ||||
</li> | ||||
</ol> | ||||
<t> | ||||
If any of these steps fail, the operation in progress | ||||
<bcp14>MUST</bcp14> be aborted. | ||||
</t> | ||||
</section> | ||||
</middle> | ||||
<back> | ||||
<displayreference target="I-D.rundgren-comparable-json" to="JSONCOMP"/> | ||||
<references> | ||||
<name>References</name> | ||||
<references> | ||||
<name>Normative References</name> | ||||
<xi:include | ||||
href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/> | ||||
<xi:include | ||||
href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8259.xml"/> | ||||
<xi:include | ||||
href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/> | ||||
<xi:include | ||||
href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7493.xml"/> | ||||
<reference anchor="ECMA-262" | ||||
target="https://www.ecma-international.org/ecma-262/10.0/index | ||||
.html"> | ||||
<front> | ||||
<title>ECMAScript 2019 Language Specification</title> | ||||
<author> | ||||
<organization>ECMA International</organization> | ||||
</author> | ||||
<date year="2019" month="June"/> | ||||
</front> | ||||
<refcontent>Standard ECMA-262 10th Edition</refcontent> | ||||
</reference> | ||||
<reference anchor="IEEE754" | ||||
target="https://ieeexplore.ieee.org/document/8766229"> | ||||
<front> | ||||
<title>IEEE Standard for Floating-Point Arithmetic</title> | ||||
<seriesInfo name="DOI" value="10.1109/IEEESTD.2019.8766229"/> | ||||
<author> | ||||
<organization>IEEE</organization> | ||||
</author> | ||||
</front> | ||||
<refcontent>IEEE 754-2019</refcontent> | ||||
</reference> | ||||
<reference anchor="UNICODE" | ||||
target="https://www.unicode.org/versions/latest/"> | ||||
<front> | ||||
<title>The Unicode Standard</title> | ||||
<author> | ||||
<organization>The Unicode Consortium</organization> | ||||
</author> | ||||
</front> | ||||
</reference> | ||||
<reference anchor="UCNORM" | ||||
target="https://www.unicode.org/reports/tr15/"> | ||||
<front> | ||||
<title>Unicode Normalization Forms</title> | ||||
<author> | ||||
<organization>The Unicode Consortium</organization> | ||||
</author> | ||||
</front> | ||||
</reference> | ||||
</references> | ||||
<references> | ||||
<name>Informative References</name> | ||||
<xi:include | ||||
href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7638.xml"/> | ||||
<xi:include | ||||
href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.4648.xml"/> | ||||
<xi:include | ||||
href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.7515.xml"/> | ||||
<xi:include | ||||
href="https://www.rfc-editor.org/refs/bibxml3/reference.I-D.rundgren-compara | ||||
ble-json.xml"/> | ||||
<reference anchor="V8" target="https://v8.dev/"> | ||||
<front> | ||||
<title>What is V8?</title> | ||||
<author> | ||||
<organization>Google LLC</organization> | ||||
</author> | ||||
</front> | ||||
</reference> | ||||
<reference anchor="RYU" target="https://github.com/ulfjack/ryu"> | ||||
<front> | ||||
<title>Ryu floating point number serializing algorithm</title> | ||||
<author></author> | ||||
<date month="May" year="2020"/> | ||||
</front> | ||||
<refcontent>commit 27d3c55</refcontent> | ||||
</reference> | ||||
<reference anchor="NODEJS" target="https://nodejs.org"> | ||||
<front> | ||||
<title>Node.js</title> | ||||
<author> | ||||
<organization>OpenJS Foundation | ||||
</organization> | ||||
</author> | ||||
</front> | ||||
</reference> | ||||
<reference anchor="KEYBASE" | ||||
target="https://keybase.io/docs/api/1.0/canonical_packings"> | ||||
<front> | ||||
<title>Canonical Packings for JSON and Msgpack</title> | ||||
<author> | ||||
<organization>Keybase | ||||
</organization> | ||||
</author> | ||||
</front> | ||||
</reference> | ||||
<reference anchor="OPENAPI" target="https://www.openapis.org/"> | ||||
<front> | ||||
<title>The OpenAPI Specification: a broadly adopted industry | ||||
standard for describing modern APIs</title> | ||||
<author> | ||||
<organization>OpenAPI Initiative | ||||
</organization> | ||||
</author> | ||||
</front> | ||||
</reference> | ||||
<reference anchor="XMLDSIG" | ||||
target="https://www.w3.org/TR/xmldsig-core1/"> | ||||
<front> | ||||
<title>XML Signature Syntax and Processing Version 1.1</title> | ||||
<author> | ||||
<organization>W3C</organization> | ||||
</author> | ||||
<date month="April" year="2013"/> | ||||
</front> | ||||
<refcontent>W3C Recommendation</refcontent> | ||||
</reference> | ||||
</references> | ||||
</references> | ||||
<section anchor="canonicalize.js" numbered="true" toc="default"> | ||||
<name>ECMAScript Sample Canonicalizer</name> | ||||
<t> | ||||
Below is an example of a JCS canonicalizer for usage with | ||||
ECMAScript-based systems: | ||||
</t> | ||||
<sourcecode type="javascript"> | ||||
//////////////////////////////////////////////////////////// | ||||
// Since the primary purpose of this code is highlighting // | ||||
// the core of the JCS algorithm, error handling and // | ||||
// UTF-8 generation were not implemented. // | ||||
//////////////////////////////////////////////////////////// | ||||
var canonicalize = function(object) { | ||||
var buffer = ''; | ||||
serialize(object); | ||||
return buffer; | ||||
function serialize(object) { | ||||
if (object === null || typeof object !== 'object' || | ||||
object.toJSON != null) { | ||||
///////////////////////////////////////////////// | ||||
// Primitive type or toJSON, use "JSON" // | ||||
///////////////////////////////////////////////// | ||||
buffer += JSON.stringify(object); | ||||
} else if (Array.isArray(object)) { | ||||
///////////////////////////////////////////////// | ||||
// Array - Maintain element order // | ||||
///////////////////////////////////////////////// | ||||
buffer += '['; | ||||
let next = false; | ||||
object.forEach((element) => { | ||||
if (next) { | ||||
buffer += ','; | ||||
} | ||||
next = true; | ||||
///////////////////////////////////////// | ||||
// Array element - Recursive expansion // | ||||
///////////////////////////////////////// | ||||
serialize(element); | ||||
}); | ||||
buffer += ']'; | ||||
} else { | ||||
///////////////////////////////////////////////// | ||||
// Object - Sort properties before serializing // | ||||
///////////////////////////////////////////////// | ||||
buffer += '{'; | ||||
let next = false; | ||||
Object.keys(object).sort().forEach((property) => { | ||||
if (next) { | ||||
buffer += ','; | ||||
} | ||||
next = true; | ||||
///////////////////////////////////////////// | ||||
// Property names are strings, use "JSON" // | ||||
///////////////////////////////////////////// | ||||
buffer += JSON.stringify(property); | ||||
buffer += ':'; | ||||
////////////////////////////////////////// | ||||
// Property value - Recursive expansion // | ||||
////////////////////////////////////////// | ||||
serialize(object[property]); | ||||
}); | ||||
buffer += '}'; | ||||
} | ||||
} | ||||
}; | ||||
</sourcecode> | ||||
</section> | ||||
<section anchor="json.ieee754.test" numbered="true" toc="default"> | ||||
<name>Number Serialization Samples</name> | ||||
<t> | ||||
The following table holds a set of ECMAScript-compatible number | ||||
serialization samples, | ||||
including some edge cases. The column | ||||
"IEEE 754" refers to the internal | ||||
ECMAScript representation of the "Number" data type, which is based on | ||||
the | ||||
IEEE 754 <xref target="IEEE754" format="default"/> standard using | ||||
64-bit (double-precision) values, | ||||
here expressed in hexadecimal. | ||||
</t> | ||||
<table anchor="ECMAScript-TABLE"> | ||||
<name>ECMAScript-Compatible JSON Number Serialization Samples</name> | ||||
<thead> | ||||
<tr> | ||||
<th align="center">IEEE 754</th> | ||||
<th align="center">JSON Representation</th> | ||||
<th align="center">Comment</th> | ||||
</tr> </thead> <tbody> | ||||
<tr> <td><!--remove for | ||||
TXT/PDF--><tt>0000000000000000</tt><!--remove for TXT/PDF--></td> | ||||
<td><!--remove for TXT/PDF--><tt>0<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>Zero<!--remove for TXT/PDF--></tt></td> </tr> | ||||
<tr> | ||||
<td><!--remove for TXT/PDF--><tt>8000000000000000<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>0<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>Minus zero<!--remove for | ||||
TXT/PDF--></tt></td> </tr> | ||||
<tr> <td><!--remove for | ||||
TXT/PDF--><tt>0000000000000001<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>5e-324<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>Min pos number<!--remove for | ||||
TXT/PDF--></tt></td> </tr> | ||||
<tr> <td><!--remove for | ||||
TXT/PDF--><tt>8000000000000001<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>-5e-324<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>Min neg number<!--remove for | ||||
TXT/PDF--></tt></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>7fefffffffffffff<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>1.7976931348623157e+308<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for TXT/PDF--><tt>Max pos number<!--remove | ||||
for TXT/PDF--></tt></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>ffefffffffffffff<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>-1.7976931348623157e+308<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for TXT/PDF--><tt>Max neg number<!--remove | ||||
for TXT/PDF--></tt></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>4340000000000000<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>9007199254740992<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for TXT/PDF--><tt>Max pos | ||||
int    (1)<!--remove for TXT/PDF--></tt></td> </tr> <tr> | ||||
<td><!--remove for TXT/PDF--><tt>c340000000000000<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>-9007199254740992<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>Max neg | ||||
int    (1)<!--remove for TXT/PDF--></tt></td> </tr> <tr> | ||||
<td><!--remove for TXT/PDF--><tt>4430000000000000<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>295147905179352830000<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for | ||||
TXT/PDF--><tt>~2**68         (2)<!- | ||||
-remove | ||||
for TXT/PDF--></tt></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>7fffffffffffffff<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> | ||||
<td><!--remove for | ||||
TXT/PDF--><tt>NaN          &#x | ||||
a0; (3)<!--remov\ | ||||
e for TXT/PDF--></tt></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>7ff0000000000000<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> | ||||
<td><!--remove for | ||||
TXT/PDF--><tt>Infinity       (3)<!--remove | ||||
for TXT/PDF--></tt></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>44b52d02c7e14af5<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>9.999999999999997e+22<!--remove for | ||||
TXT/PDF--></tt></td> <td></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>44b52d02c7e14af6<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>1e+23<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> </tr> | ||||
<tr> <td><!--remove for TXT/PDF--><tt>44b52d02c7e14af7<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>1.0000000000000001e+23<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> </tr> | ||||
<tr> <td><!--remove for TXT/PDF--><tt>444b1ae4d6e2ef4e<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>999999999999999700000<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> </tr> | ||||
<tr> <td><!--remove for TXT/PDF--><tt>444b1ae4d6e2ef4f<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>999999999999999900000<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> </tr> | ||||
<tr> <td><!--remove for TXT/PDF--><tt>444b1ae4d6e2ef50<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for TXT/PDF--><tt>1e+21 <!--remove for | ||||
TXT/PDF--></tt></td> <td></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>3eb0c6f7a0b5ed8c<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>9.999999999999997e-7<!--remove for | ||||
TXT/PDF--></tt></td> <td></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>3eb0c6f7a0b5ed8d<!--remove for TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>0.000001<!--remove for TXT/PDF--></tt></td> | ||||
<td> | ||||
</td> | ||||
</tr> | ||||
<tr> | ||||
<td><!--remove for TXT/PDF--><tt>41b3de4355555553<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>333333333.3333332<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> </tr> | ||||
<tr> <td><!--remove for TXT/PDF--><tt>41b3de4355555554<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>333333333.33333325<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> </tr> | ||||
<tr> <td><!--remove for TXT/PDF--><tt>41b3de4355555555<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>333333333.3333333<!--remove for TXT/PDF--></tt></td> | ||||
<td></td> </tr> | ||||
<tr> <td><!--remove for TXT/PDF--><tt>41b3de4355555556<!--remove for | ||||
TXT/PDF--></tt></td> <td><!--remove for | ||||
TXT/PDF--><tt>333333333.3333334<!--remove for | ||||
TXT/PDF--></tt></td><td></td> </tr> <tr> <td><!--remove for | ||||
TXT/PDF--><tt>41b3de4355555557<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>333333333.33333343<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td></td> | ||||
</tr> | ||||
<tr> | ||||
<td><!--remove for TXT/PDF--><tt>becbf647612f3696<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>-0.0000033333333333333333<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td></td> | ||||
</tr> | ||||
<tr> | ||||
<td><!--remove for TXT/PDF--><tt>43143ff3c1cb0959<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>1424953923781206.2<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
<td><!--remove for TXT/PDF--><tt>Round to even  (4)<!--remove for | ||||
TXT/PDF--></tt></td> | ||||
</tr> | ||||
</tbody> | ||||
</table> | ||||
<t> | ||||
Notes: | ||||
</t> | ||||
<ol spacing="normal" type="(%d)"> | ||||
<li> | ||||
For maximum compliance with the ECMAScript "JSON" object, | ||||
values that are to be interpreted as true integers | ||||
<bcp14>SHOULD</bcp14> be in the range -9007199254740991 to | ||||
9007199254740991. | ||||
However, how numbers are used in applications does not affect the | ||||
JCS algorithm. | ||||
</li> | ||||
<li> | ||||
Although a set of specific integers like 2**68 could be regarded as | ||||
having | ||||
extended precision, the JCS/ECMAScript number serialization | ||||
algorithm does not take this into consideration. | ||||
</li> | ||||
<li> | ||||
Values out of range are not permitted in JSON. | ||||
See <xref target="json.ser.number" format="default"/>. | ||||
</li> | ||||
<li> | ||||
This number is exactly 1424953923781206.25 but will, after the "Note | ||||
2" rule | ||||
mentioned in <xref target="json.ser.number" format="default"/>, be | ||||
truncated and | ||||
rounded to the closest even value. | ||||
</li> | ||||
</ol> | ||||
<t> | ||||
For a more exhaustive validation of a JCS number serializer, you may | ||||
test against a file (currently) available in the development portal | ||||
(see <xref target="json.development" format="default"/>) containing a | ||||
large set of sample values. Another option is running V8 <xref | ||||
target="V8" format="default"/> as a live reference together with a | ||||
program generating a substantial amount of random IEEE 754 values. | ||||
</t> | ||||
</section> | ||||
<section anchor="json.wireformat" numbered="true" toc="default"> | ||||
<name>Canonicalized JSON as "Wire Format"</name> | ||||
<t> | ||||
Since the result from the canonicalization process (see <xref | ||||
target="json.utf8" format="default"/>) is fully valid JSON, it can | ||||
also be used as "Wire Format". However, this is just an option since | ||||
cryptographic schemes based on JCS, in most cases, would not depend on | ||||
that externally supplied JSON data already being canonicalized. | ||||
</t> | ||||
<t> | ||||
In fact, the ECMAScript standard way of serializing objects using | ||||
"JSON.stringify()" produces a | ||||
more "logical" format, where properties are | ||||
kept in the order they were created or received. The | ||||
example below shows an address record that could benefit from | ||||
ECMAScript standard serialization: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{ | ||||
"name": "John Doe", | ||||
"address": "2000 Sunset Boulevard", | ||||
"city": "Los Angeles", | ||||
"zip": "90001", | ||||
"state": "CA" | ||||
} | ||||
</sourcecode> | ||||
<t> | ||||
Using canonicalization, the properties above would be output in the | ||||
order | ||||
"address", "city", "name", "state", and "zip", which adds fuzziness | ||||
to the data from a human (developer or technical support) perspective. | ||||
Canonicalization also converts JSON data into a single line of text, | ||||
which may | ||||
be less than ideal for debugging and logging. | ||||
</t> | ||||
</section> | ||||
<section anchor="json.bignumbers" numbered="true" toc="default"> | ||||
<name>Dealing with Big Numbers</name> | ||||
<t> | ||||
There are several issues associated with the | ||||
JSON number type, here illustrated by the following | ||||
sample object: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{ | ||||
"giantNumber": 1.4e+9999, | ||||
"payMeThis": 26000.33, | ||||
"int64Max": 9223372036854775807 | ||||
} | ||||
</sourcecode> | ||||
<t> | ||||
Although the sample above conforms to JSON <xref target="RFC8259" | ||||
format="default"/>, | ||||
applications would normally use different native data types for | ||||
storing | ||||
"giantNumber" and "int64Max". In addition, monetary data like | ||||
"payMeThis" would | ||||
presumably not rely on floating-point data types due to rounding | ||||
issues with respect | ||||
to decimal arithmetic. | ||||
</t> | ||||
<t> | ||||
The established way of handling this kind of "overloading" of the | ||||
JSON number type (at least in an extensible manner) is through | ||||
mapping mechanisms, instructing parsers what to do with different | ||||
properties | ||||
based on their name. However, this greatly limits the value of using | ||||
the | ||||
JSON number type outside of its original, somewhat constrained | ||||
JavaScript context. | ||||
The ECMAScript "JSON" object does not support mappings to the JSON | ||||
number type either. | ||||
</t> | ||||
<t> | ||||
Due to the above, numbers that do not have a natural place in the | ||||
current | ||||
JSON ecosystem <bcp14>MUST</bcp14> be wrapped using the JSON string | ||||
type. This is close to | ||||
a de facto standard for open systems. This is also applicable for | ||||
other data types that do not have direct support in JSON, like | ||||
"DateTime" | ||||
objects as described in <xref target="string.subtypes" | ||||
format="default"/>. | ||||
</t> | ||||
<t> | ||||
Aided by a system using the JSON string type, be it programmatic like | ||||
</t> | ||||
<sourcecode type=""> | ||||
var obj = JSON.parse('{"giantNumber": "1.4e+9999"}'); | ||||
var biggie = new BigNumber(obj.giantNumber); | ||||
</sourcecode> | ||||
<t> | ||||
or declarative schemes like OpenAPI <xref target="OPENAPI" | ||||
format="default"/>, | ||||
JCS imposes no limits on applications, including when using | ||||
ECMAScript. | ||||
</t> | ||||
</section> | ||||
<section anchor="string.subtypes" numbered="true" toc="default"> | ||||
<name>String Subtype Handling</name> | ||||
<t> | ||||
Due to the limited set of data types featured in JSON, the JSON string | ||||
type is commonly used for holding subtypes. This can, depending on | ||||
JSON parsing method, lead to interoperability problems, which | ||||
<bcp14>MUST</bcp14> be dealt with by JCS-compliant applications | ||||
targeting a wider audience. | ||||
</t> | ||||
<t> | ||||
Assume you want to parse a JSON object where the schema | ||||
designer assigned the property "big" for holding a "BigInt" subtype | ||||
and | ||||
"time" for holding a "DateTime" subtype, while "val" is supposed to be | ||||
a JSON number | ||||
compliant with JCS. The following example shows such an object: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{ | ||||
"time": "2019-01-28T07:45:10Z", | ||||
"big": "055", | ||||
"val": 3.5 | ||||
} | ||||
</sourcecode> | ||||
<t>Parsing of this object can be accomplished by the following | ||||
ECMAScript statement:</t> | ||||
<sourcecode type=""> | ||||
var object = JSON.parse(JSON_object_featured_as_a_string); | ||||
</sourcecode> | ||||
<t>After parsing, the actual data can be extracted, which for subtypes, | ||||
also involves a conversion step using the result of the parsing process | ||||
(an ECMAScript object) as input:</t> | ||||
<sourcecode type=""> | ||||
... = new Date(object.time); // Date object | ||||
... = BigInt(object.big); // Big integer | ||||
... = object.val; // JSON/JS number | ||||
</sourcecode> | ||||
<t> | ||||
Note that the "BigInt" data type is currently only natively supported | ||||
by V8 <xref target="V8" format="default"/>. | ||||
</t> | ||||
<t> | ||||
Canonicalization of "object" using the sample code in <xref | ||||
target="canonicalize.js" format="default"/> would return the | ||||
following string: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{"big":"055","time":"2019-01-28T07:45:10Z","val":3.5} | ||||
</sourcecode> | ||||
<t> | ||||
Although this is (with respect to JCS) technically correct, there is | ||||
another way of parsing JSON data, which also can be used with | ||||
ECMAScript as shown below: | ||||
</t> | ||||
<sourcecode type=""> | ||||
// "BigInt" requires the following code to become JSON serializable | ||||
BigInt.prototype.toJSON = function() { | ||||
return this.toString(); | ||||
}; | ||||
// JSON parsing using a "stream"-based method | ||||
var object = JSON.parse(JSON_object_featured_as_a_string, | ||||
(k,v) => k == 'time' ? new Date(v) : k == 'big' ? BigInt(v) : v | ||||
); | ||||
</sourcecode> | ||||
<t> | ||||
If you now apply the canonicalizer in <xref target="canonicalize.js" | ||||
format="default"/> to "object", the following string would be | ||||
generated: | ||||
</t> | ||||
<sourcecode type="json"> | ||||
{"big":"55","time":"2019-01-28T07:45:10.000Z","val":3.5} | ||||
</sourcecode> | ||||
<t> | ||||
In this case, the string arguments for "big" and "time" have changed | ||||
with respect to the original, | ||||
presumably making an application depending on JCS fail. | ||||
</t> | ||||
<t> | ||||
The reason for the deviation is that in stream- and schema-based JSON | ||||
parsers, | ||||
the original string argument is typically replaced on the fly | ||||
by the native subtype that, when serialized, may exhibit a different | ||||
and platform-dependent pattern. | ||||
</t> | ||||
<t> | ||||
That is, stream- and schema-based parsing <bcp14>MUST</bcp14> treat | ||||
subtypes as "pure" (immutable) JSON string types and perform the | ||||
actual conversion to the designated native type in a subsequent step. | ||||
In modern programming platforms like Go, Java, and C#, this can be | ||||
achieved with moderate efforts by combining annotations, getters, and | ||||
setters. Below is an example in C#/Json.NET showing a part of a class | ||||
that is serializable as a JSON object: | ||||
</t> | ||||
<sourcecode type=""> | ||||
// The "pure" string solution uses a local | ||||
// string variable for JSON serialization while | ||||
// exposing another type to the application | ||||
[JsonProperty("amount")] | ||||
private string _amount; | ||||
[JsonIgnore] | ||||
public decimal Amount { | ||||
get { return decimal.Parse(_amount); } | ||||
set { _amount = value.ToString(); } | ||||
} | ||||
</sourcecode> | ||||
<t> | ||||
In an application, "Amount" can be accessed as any other property | ||||
while it is actually represented by a quoted string in JSON contexts. | ||||
</t> | ||||
<t> | ||||
Note: The example above also addresses the constraints on numeric data | ||||
implied by I-JSON (the C# "decimal" data type has quite different | ||||
characteristics compared to IEEE 754 double precision). | ||||
</t> | ||||
<section anchor="string.subtypes.arrays" numbered="true" toc="default"> | ||||
<name>Subtypes in Arrays</name> | ||||
<t> | ||||
Since the JSON array construct permits mixing arbitrary JSON data | ||||
types, | ||||
custom parsing and serialization code may be required | ||||
to cope with subtypes anyway. | ||||
</t> | ||||
</section> | ||||
</section> | ||||
<section anchor="impl.guidelines" numbered="true" toc="default"> | ||||
<name>Implementation Guidelines</name> | ||||
<t> | ||||
The optimal solution is integrating support for JCS directly | ||||
in JSON serializers (parsers need no changes). | ||||
That is, canonicalization would just be an additional "mode" | ||||
for a JSON serializer. However, this is currently not the case. | ||||
Fortunately, JCS support can be introduced through externally supplied | ||||
canonicalizer software acting as a post processor to existing | ||||
JSON serializers. This arrangement also relieves the JCS implementer | ||||
from | ||||
having to deal with how underlying data is to be represented in JSON. | ||||
</t> | ||||
<t> | ||||
The post processor concept enables signature creation schemes like the | ||||
following: | ||||
</t> | ||||
<ol spacing="normal" type="1"> | ||||
<li> | ||||
Create the data to be signed. | ||||
</li> | ||||
<li> | ||||
Serialize the data using existing JSON tools. | ||||
</li> | ||||
<li> | ||||
Let the external canonicalizer process the serialized data and | ||||
return canonicalized result data. | ||||
</li> | ||||
<li> | ||||
Sign the canonicalized data. | ||||
</li> | ||||
<li> | ||||
Add the resulting signature value to the original JSON data | ||||
through a designated signature property. | ||||
</li> | ||||
<li> | ||||
Serialize the completed (now signed) JSON object using existing | ||||
JSON tools. | ||||
</li> | ||||
</ol> | ||||
<t> | ||||
A compatible signature verification scheme would then be as follows: | ||||
</t> | ||||
<ol spacing="normal" type="1"> | ||||
<li> | ||||
Parse the signed JSON data using existing JSON tools. | ||||
</li> | ||||
<li> | ||||
Read and save the signature value from the designated signature | ||||
property. | ||||
</li> | ||||
<li> | ||||
Remove the signature property from the parsed JSON object. | ||||
</li> | ||||
<li> | ||||
Serialize the remaining JSON data using existing JSON tools. | ||||
</li> | ||||
<li> | ||||
Let the external canonicalizer process the serialized data and | ||||
return canonicalized result data. | ||||
</li> | ||||
<li> | ||||
Verify that the canonicalized data matches the saved signature | ||||
value | ||||
using the algorithm and key used for creating the signature. | ||||
</li> | ||||
</ol> | ||||
<t> | ||||
A canonicalizer like above is effectively only a "filter", potentially | ||||
usable with | ||||
a multitude of quite different cryptographic schemes. | ||||
</t> | ||||
<t> | ||||
Using a JSON serializer with integrated JCS support, the serialization | ||||
performed | ||||
before the canonicalization step could be eliminated for both | ||||
processes. | ||||
</t> | ||||
</section> | ||||
<section anchor="open.source" numbered="true" toc="default"> | ||||
<name>Open-Source Implementations</name> | ||||
<t> | ||||
The following open-source implementations have been verified to be | ||||
compatible with JCS: | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
JavaScript: <eref | ||||
target="https://www.npmjs.com/package/canonicalize" | ||||
brackets="angle"/> | ||||
</li> | ||||
<li> | ||||
Java: <eref | ||||
target="https://github.com/erdtman/java-json-canonicalization" | ||||
brackets="angle"/> | ||||
</li> | ||||
<li> | ||||
Go: <eref | ||||
target="https://github.com/cyberphone/json-canonicalization/tree/mast | ||||
er/go" | ||||
brackets="angle"/> | ||||
</li> | ||||
<li> | ||||
.NET/C#: <eref | ||||
target="https://github.com/cyberphone/json-canonicalization/tree/mast | ||||
er/dotnet" | ||||
brackets="angle"/> | ||||
</li> | ||||
<li> | ||||
Python: <eref | ||||
target="https://github.com/cyberphone/json-canonicalization/tree/mast | ||||
er/python3" | ||||
brackets="angle"/> | ||||
</li> | ||||
</ul> | ||||
</section> | ||||
<section anchor="json.otherefforts" numbered="true" toc="default"> | ||||
<name>Other JSON Canonicalization Efforts</name> | ||||
<t> | ||||
There are (and have been) other efforts creating "Canonical JSON". | ||||
Below is a list of URLs to some of them: | ||||
</t> | ||||
<ul spacing="normal"> | ||||
<li> | ||||
<eref | ||||
target="https://tools.ietf.org/html/draft-staykov-hu-json-can | ||||
onical-form-00" | ||||
brackets="angle"/> | ||||
</li> | ||||
<li> | ||||
<eref target="https://gibson042.github.io/canonicaljson-spec/" | ||||
brackets="angle"/> | ||||
</li> | ||||
<li> | ||||
<eref target="http://wiki.laptop.org/go/Canonical_JSON" | ||||
brackets="angle"/> | ||||
</li> | ||||
</ul> | ||||
<t> | ||||
The listed efforts all build on text-level JSON-to-JSON | ||||
transformations. The primary feature of text-level canonicalization is | ||||
that it can be made neutral to the flavor of JSON used. However, such | ||||
schemes also imply major changes to the JSON parsing process, which is | ||||
a likely hurdle for adoption. Albeit at the expense of certain JSON | ||||
and application constraints, JCS was designed to be compatible with | ||||
existing JSON tools. | ||||
</t> | ||||
</section> | ||||
<section anchor="json.development" numbered="true" toc="default"> | ||||
<name>Development Portal</name> | ||||
<t> | ||||
The JCS specification is currently developed at: | ||||
<eref target="https://github.com/cyberphone/ietf-json-canon" | ||||
brackets="angle"/>. | ||||
</t> | ||||
<t> | ||||
JCS source code and extensive test data is available at: | ||||
<eref target="https://github.com/cyberphone/json-canonicalization" | ||||
brackets="angle"/>. | ||||
</t> | ||||
</section> | ||||
<section anchor="Acknowledgements" numbered="false" toc="default"> | ||||
<name>Acknowledgements</name> | ||||
<t> | ||||
Building on ECMAScript number serialization was | ||||
originally proposed by <contact fullname="James Manger"/>. This | ||||
ultimately led to the | ||||
adoption of the entire ECMAScript serialization scheme for JSON | ||||
primitives. | ||||
</t> | ||||
<t> | ||||
Other people who have contributed with valuable input to this | ||||
specification include | ||||
<contact fullname="Scott Ananian"/>, | ||||
<contact fullname="Tim Bray"/>, | ||||
<contact fullname="Ben Campbell"/>, | ||||
<contact fullname="Adrian Farell"/>, | ||||
<contact fullname="Richard Gibson"/>, | ||||
<contact fullname="Bron Gondwana"/>, | ||||
<contact fullname="John-Mark Gurney"/>, | ||||
<contact fullname="Mike Jones,"/> | ||||
<contact fullname="John Levine"/>, | ||||
<contact fullname="Mark Miller"/>, | ||||
<contact fullname="Matthew Miller"/>, | ||||
<contact fullname="Mark Nottingham"/>, | ||||
<contact fullname="Mike Samuel"/>, | ||||
<contact fullname="Jim Schaad"/>, | ||||
<contact fullname="Robert Tupelo-Schneck"/>, | ||||
and <contact fullname="Michal Wadas"/>. | ||||
</t> | ||||
<t> | ||||
For carrying out real-world concept verification, the software and | ||||
support for number serialization provided by | ||||
<contact fullname="Ulf Adams"/>, | ||||
<contact fullname="Tanner Gooding"/>, | ||||
and <contact fullname="Remy Oudompheng"/> | ||||
was very helpful. | ||||
</t> | ||||
</section> | ||||
</back> | ||||
</rfc> | ||||
End of changes. 1 change blocks. | ||||
lines changed or deleted | lines changed or added | |||
This html diff was produced by rfcdiff 1.45. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |