idnits 2.17.1 draft-rundgren-json-canonicalization-scheme-05.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** The abstract seems to contain references ([RFC7493], [RFC8259], [ES6]), which it shouldn't. Please replace those with straight textual mentions of the documents in question. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year -- The document date (February 16, 2019) is 1896 days in the past. Is this intentional? Checking references for intended status: Proposed Standard ---------------------------------------------------------------------------- (See RFCs 3967 and 4897 for information about using normative references to lower-maturity documents in RFCs) == Missing Reference: 'RFC4648' is mentioned on line 436, but not defined == Missing Reference: 'RFC7515' is mentioned on line 440, but not defined == Missing Reference: 'XMLDSIG' is mentioned on line 454, but not defined == Missing Reference: 'NODEJS' is mentioned on line 432, but not defined == Missing Reference: 'RFC7638' is mentioned on line 444, but not defined == Missing Reference: 'KEYBASE' is mentioned on line 429, but not defined == Missing Reference: 'JSONCOMP' is mentioned on line 424, but not defined -- Looks like a reference, but probably isn't: '1' on line 459 == Missing Reference: 'V8' is mentioned on line 451, but not defined == Missing Reference: 'RYU' is mentioned on line 448, but not defined == Missing Reference: 'OPENAPI' is mentioned on line 694, but not defined -- Looks like a reference, but probably isn't: '2' on line 851 -- Looks like a reference, but probably isn't: '3' on line 853 -- Looks like a reference, but probably isn't: '4' on line 856 -- Looks like a reference, but probably isn't: '5' on line 859 -- Looks like a reference, but probably isn't: '6' on line 862 -- Looks like a reference, but probably isn't: '7' on line 870 -- Looks like a reference, but probably isn't: '8' on line 872 -- Looks like a reference, but probably isn't: '9' on line 874 -- Looks like a reference, but probably isn't: '10' on line 882 -- Looks like a reference, but probably isn't: '11' on line 885 -- Looks like a reference, but probably isn't: '12' on line 888 == Missing Reference: 'JsonIgnore' is mentioned on line 777, but not defined -- Possible downref: Non-RFC (?) normative reference: ref. 'ES6' -- Possible downref: Non-RFC (?) normative reference: ref. 'IEEE754' -- Possible downref: Non-RFC (?) normative reference: ref. 'UNICODE' Summary: 1 error (**), 0 flaws (~~), 12 warnings (==), 16 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 Network Working Group A. Rundgren 3 Internet-Draft Independent 4 Intended status: Standards Track B. Jordan 5 Expires: August 20, 2019 Symantec Corporation 6 S. Erdtman 7 Spotify AB 8 February 16, 2019 10 JSON Canonicalization Scheme (JCS) 11 draft-rundgren-json-canonicalization-scheme-05 13 Abstract 15 Cryptographic operations like hashing and signing requires that the 16 original data does not change during serialization or parsing. By 17 applying the rules defined by the JSON Canonicalization Scheme (JCS), 18 data provided in JSON [RFC8259] format can be exchanged "as is", 19 while still being usable by secure cryptographic operations. JCS 20 achieves this by building on the strict serialization formats for 21 JSON primitives defined by ECMAScript [ES6], constraining JSON data 22 to the I-JSON [RFC7493] subset, and through a platform independent 23 property sorting scheme. 25 The intended audiences of this document are JSON tool vendors, as 26 well as designers of JSON based cryptographic solutions. 28 Status of This Memo 30 This Internet-Draft is submitted in full conformance with the 31 provisions of BCP 78 and BCP 79. 33 Internet-Drafts are working documents of the Internet Engineering 34 Task Force (IETF). Note that other groups may also distribute 35 working documents as Internet-Drafts. The list of current Internet- 36 Drafts is at https://datatracker.ietf.org/drafts/current/. 38 Internet-Drafts are draft documents valid for a maximum of six months 39 and may be updated, replaced, or obsoleted by other documents at any 40 time. It is inappropriate to use Internet-Drafts as reference 41 material or to cite them other than as "work in progress." 43 This Internet-Draft will expire on August 20, 2019. 45 Copyright Notice 47 Copyright (c) 2019 IETF Trust and the persons identified as the 48 document authors. All rights reserved. 50 This document is subject to BCP 78 and the IETF Trust's Legal 51 Provisions Relating to IETF Documents 52 (https://trustee.ietf.org/license-info) in effect on the date of 53 publication of this document. Please review these documents 54 carefully, as they describe your rights and restrictions with respect 55 to this document. Code Components extracted from this document must 56 include Simplified BSD License text as described in Section 4.e of 57 the Trust Legal Provisions and are provided without warranty as 58 described in the Simplified BSD License. 60 Table of Contents 62 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 63 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4 64 3. Detailed Operation . . . . . . . . . . . . . . . . . . . . . 4 65 3.1. Creation of Input Data . . . . . . . . . . . . . . . . . 4 66 3.2. Generation of Canonical JSON Data . . . . . . . . . . . . 5 67 3.2.1. Whitespace . . . . . . . . . . . . . . . . . . . . . 5 68 3.2.2. Serialization of Primitive Data Types . . . . . . . . 5 69 3.2.2.1. Serialization of Literals . . . . . . . . . . . . 6 70 3.2.2.2. Serialization of Strings . . . . . . . . . . . . 6 71 3.2.2.3. Serialization of Numbers . . . . . . . . . . . . 6 72 3.2.3. Sorting of Object Properties . . . . . . . . . . . . 7 73 3.2.4. UTF-8 Generation . . . . . . . . . . . . . . . . . . 8 74 4. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 9 75 5. Security Considerations . . . . . . . . . . . . . . . . . . . 9 76 6. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 9 77 7. References . . . . . . . . . . . . . . . . . . . . . . . . . 9 78 7.1. Normative References . . . . . . . . . . . . . . . . . . 9 79 7.2. Informal References . . . . . . . . . . . . . . . . . . . 10 80 7.3. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 11 81 Appendix A. ES6 Sample Canonicalizer . . . . . . . . . . . . . . 11 82 Appendix B. Number Serialization Samples . . . . . . . . . . . . 13 83 Appendix C. Canonicalized JSON as "Wire Format" . . . . . . . . 14 84 Appendix D. Dealing with Big Numbers . . . . . . . . . . . . . . 15 85 Appendix E. String Subtype Handling . . . . . . . . . . . . . . 16 86 E.1. Subtypes in Arrays . . . . . . . . . . . . . . . . . . . 18 87 Appendix F. Implementation Guidelines . . . . . . . . . . . . . 18 88 Appendix G. Open Source Implementations . . . . . . . . . . . . 19 89 Appendix H. Other JSON Canonicalization Efforts . . . . . . . . 19 90 Appendix I. Development Portal . . . . . . . . . . . . . . . . . 20 91 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 20 93 1. Introduction 95 Cryptographic operations like hashing and signing requires that the 96 original data does not change during serialization or parsing. One 97 way of accomplishing this is converting the data into a format that 98 has a simple and fixed representation like Base64Url [RFC4648], which 99 is how JWS [RFC7515] addressed this issue. 101 Another solution is to create a canonical version of the data, 102 similar to what was done for the XML Signature [XMLDSIG] standard. 103 The primary advantage with a canonicalizing scheme is that data can 104 be kept in its original form. This is the core rationale behind JCS. 105 Put another way: by using canonicalization a JSON Object may remain a 106 JSON Object even after being signed which simplifies system design, 107 documentation and logging. 109 To avoid "reinventing the wheel", JCS relies on serialization of JSON 110 primitives compatible with ECMAScript (aka JavaScript) beginning with 111 version 6 [ES6], hereafter referred to as "ES6". 113 Seasoned XML developers recalling difficulties getting signatures to 114 validate (usually due to different interpretations of the quite 115 intricate XML canonicalization rules as well as of the equally 116 extensive Web Services security standards), may rightfully wonder why 117 JCS would not suffer from similar issues. The reasons are twofold: 119 o The absence of a namespace concept and default values, as well as 120 constraining data to the I-JSON subset eliminate the need for 121 specific parsers for dealing with canonicalization. 123 o JCS compatible serialization of JSON primitives is supported by 124 most current Web browsers and as well as by Node.js [NODEJS], 125 while the full JCS specification is supported by multiple Open 126 Source implementations (see Appendix G). See also Appendix F. 128 In summary the JCS specification describes how serialization of JSON 129 primitives compliant with ES6 combined with a deterministic property 130 sorting scheme can be used for creating "Hashable" representations of 131 JSON data intended for consumption by cryptographic methods. 133 JCS is compatible with some existing systems relying on JSON 134 canonicalization such as JWK Thumbprint [RFC7638] and Keybase 135 [KEYBASE]. 137 For potential uses outside of cryptography see [JSONCOMP]. 139 2. Terminology 141 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 142 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 143 "OPTIONAL" in this document are to be interpreted as described in BCP 144 14 [RFC2119] [RFC8174] when, and only when, they appear in all 145 capitals, as shown here. 147 3. Detailed Operation 149 This section describes the different issues related to creating a 150 canonical JSON representation, and how they are addressed by JCS. 152 3.1. Creation of Input Data 154 In order to serialize JSON data, one needs data that is adapted for 155 JSON serialization. This is usually achieved by: 157 o Parsing previously generated JSON data. 159 o Programmatically creating data. 161 Irrespective of the method used, the data to be serialized MUST be 162 compatible with I-JSON [RFC7493], which implies the following: 164 o JSON Objects MUST NOT exhibit duplicate property names. 166 o JSON String data MUST be expressible as Unicode [UNICODE]. 168 o JSON Number data MUST be expressible as IEEE-754 [IEEE754] double 169 precision values. For applications needing higher precision or 170 longer integers than offered by IEEE-754 double precision, 171 Appendix D outlines how such requirements can be supported in an 172 interoperable and extensible way. 174 An additional constraint is that parsed JSON String data MUST NOT be 175 altered during subsequent serializations. For more information see 176 Appendix E. 178 Note: although the Unicode standard offers a possibility combining 179 certain characters into one, referred to as "Unicode Normalization" 180 (https://www.unicode.org/reports/tr15/ [1]), such functionality MUST 181 be delegated to the application layer. 183 3.2. Generation of Canonical JSON Data 185 The following subsections describe the steps required for creating a 186 canonical JSON representation of the data elaborated on in the 187 previous section. 189 Appendix A shows sample code for an ES6 based canonicalizer, matching 190 the JCS specification. 192 3.2.1. Whitespace 194 Whitespace between JSON elements MUST NOT be emitted. 196 3.2.2. Serialization of Primitive Data Types 198 Assume that you parse a JSON object like the following: 200 { 201 "numbers": [333333333.33333329, 1E30, 4.50, 202 2e-3, 0.000000000000000000000000001], 203 "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", 204 "literals": [null, true, false] 205 } 207 If you subsequently serialize the parsed data using a serializer 208 compliant with ES6's "JSON.stringify()", the result would (with a 209 line wrap added for display purposes only), be rather divergent with 210 respect to representation of data: 212 {"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string": 213 "EURO$\u000f\nA'B\"\\\\\"/","literals":[null,true,false]} 215 Note: EURO denotes a single Euro character (Unicode: U+20AC), 216 which not being ASCII, is currently not displayable in RFCs. 218 The reason for the difference between the parsed data and its 219 serialized counterpart, is due to a wide tolerance on input data (as 220 defined by JSON [RFC8259]), while output data (as defined by ES6), 221 has a fixed representation. As can be seen by the example, numbers 222 are subject to rounding as well. 224 The following subsections describe serialization of primitive JSON 225 data types according to JCS. This part is identical to that of ES6. 227 3.2.2.1. Serialization of Literals 229 The JSON literals "null", "true", and "false" present no challenge 230 since they already have a fixed definition in JSON [RFC8259]. 232 3.2.2.2. Serialization of Strings 234 For JSON String data (which includes JSON Object property names as 235 well), each Unicode code point MUST be serialized as described below 236 (also matching Section 24.3.2.2 of [ES6]): 238 o If the Unicode value falls within the traditional ASCII control 239 character range (U+0000 through U+001F), it MUST be serialized 240 using lowercase hexadecimal Unicode notation (\uhhhh) unless it is 241 in the set of predefined JSON control characters U+0008, U+0009, 242 U+000A, U+000C or U+000D which MUST be serialized as \b, \t, \n, 243 \f and \r respectively. 245 o If the Unicode value is outside of the ASCII control character 246 range, it MUST be serialized "as is" unless it is equivalent to 247 U+005C (\) or U+0022 (") which MUST be serialized as \\ and \" 248 respectively. 250 Finally, the resulting sequence of Unicode code points MUST be 251 enclosed in double quotes ("). 253 Note: some JSON systems permit the use of invalid Unicode data 254 including "lone surrogates" (e.g. U+DEAD). Since this leads to 255 interoperability issues including broken signatures, occurrences of 256 such data MUST cause the JCS algorithm to terminate with an error 257 indication. 259 3.2.2.3. Serialization of Numbers 261 JSON Number data MUST be serialized according to Section 7.1.12.1 of 262 [ES6] including the "Note 2" enhancement. 264 Due to the relative complexity of this part, the algorithm itself is 265 not included in this document. However, the specification is fully 266 implemented by for example Google's V8 [V8]. The open source Java 267 implementation mentioned in Appendix G uses a recently developed 268 number serialization algorithm called Ryu [RYU]. 270 ES6 builds on the IEEE-754 [IEEE754] double precision standard for 271 representing JSON Number data. Appendix B holds a set of IEEE-754 272 sample values and their corresponding JSON serialization. 274 Note: since NaN (Not a Number) and Infinity are not permitted in 275 JSON, occurrences of such values MUST cause the JCS algorithm to 276 terminate with an error indication. 278 3.2.3. Sorting of Object Properties 280 Although the previous step indeed normalized the representation of 281 primitive JSON data types, the result would not qualify as 282 "canonical" since JSON Object properties are not in lexicographic 283 (alphabetical) order. 285 Applied to the sample in Section 3.2.2, a properly canonicalized 286 version should (with a line wrap added for display purposes only), 287 read as: 289 {"literals":[null,true,false],"numbers":[333333333.3333333, 290 1e+30,4.5,0.002,1e-27],"string":"EURO$\u000f\nA'B\"\\\\\"/"} 292 Note: EURO denotes a single Euro character (Unicode: U+20AC), 293 which not being ASCII, is currently not displayable in RFCs. 295 The rules for lexicographic sorting of JSON Object properties 296 according to JCS are as follows: 298 o JSON Object properties MUST be sorted in a recursive manner which 299 means that possible JSON child Objects MUST have their properties 300 sorted as well. 302 o JSON Array data MUST also be scanned for presence of JSON Objects 303 (and applying associated property sorting), but array element 304 order MUST NOT be changed. 306 When a JSON Object is about to have its properties sorted, the 307 following measures MUST be adhered to: 309 o The sorting process is applied to property name strings in their 310 "raw" (unescaped) form. That is, a newline character is treated 311 as U+000A. 313 o Property name strings to be sorted are formatted as arrays of 314 UTF-16 [UNICODE] code units. The sorting is based on pure value 315 comparisons, where code units are treated as unsigned integers, 316 independent of locale settings. 318 o Property name strings either have different values at some index 319 that is a valid index for both strings, or their lengths are 320 different, or both. If they have different values at one or more 321 index positions, let k be the smallest such index; then the string 322 whose value at position k has the smaller value, as determined by 323 using the < operator, lexicographically precedes the other string. 324 If there is no index position at which they differ, then the 325 shorter string lexicographically precedes the longer string. 327 In plain English this means that property names are sorted in 328 ascending order like the following: 330 "" 331 "a" 332 "aa" 333 "ab" 335 The rationale for basing the sorting algorithm on UTF-16 code units 336 is that it maps directly to the string type in ECMAScript (featured 337 in Web browsers and Node.js), Java and .NET. Systems using another 338 internal representation of string data will need to convert JSON 339 property name strings into arrays of UTF-16 code units before 340 sorting. The conversion from UTF-8 or UTF-32 to UTF-16 is defined by 341 the Unicode [UNICODE] standard. 343 Note: for the purpose of obtaining a deterministic property order, 344 sorting on UTF-8 or UTF-32 encoded data would also work, but the 345 result would differ and thus be incompatible with this specification. 346 However, in practice property names rarely go outside of 7-bit ASCII 347 making it possible sorting on the UTF-8 byte level and still be 348 compatible with JCS. If this is a viable option or not depends on 349 the environment JCS is supposed to be used in. 351 3.2.4. UTF-8 Generation 353 Finally, in order to create a platform independent representation, 354 the result of the preceding step MUST be encoded in UTF-8. 356 Applied to the sample in Section 3.2.3 this should yield the 357 following bytes here shown in hexadecimal notation: 359 7b 22 6c 69 74 65 72 61 6c 73 22 3a 5b 6e 75 6c 6c 2c 74 72 360 75 65 2c 66 61 6c 73 65 5d 2c 22 6e 75 6d 62 65 72 73 22 3a 361 5b 33 33 33 33 33 33 33 33 33 2e 33 33 33 33 33 33 33 2c 31 362 65 2b 33 30 2c 34 2e 35 2c 30 2e 30 30 32 2c 31 65 2d 32 37 363 5d 2c 22 73 74 72 69 6e 67 22 3a 22 e2 82 ac 24 5c 75 30 30 364 30 66 5c 6e 41 27 42 5c 22 5c 5c 5c 5c 5c 22 2f 22 7d 366 This data is intended to be usable as input to cryptographic methods. 368 4. IANA Considerations 370 This document has no IANA actions. 372 5. Security Considerations 374 It is vital performing "sanity" checks on input data to avoid 375 overflowing buffers and similar things that could affect the 376 integrity of the system. 378 6. Acknowledgements 380 Building on ES6 Number serialization was originally proposed by 381 James Manger. This ultimately led to the adoption of the entire ES6 382 serialization scheme for JSON primitives. 384 Other people who have contributed with valuable input to this 385 specification include Scott Ananian, Richard Gibson, Bron Gondwana, 386 John-Mark Gurney, Mike Jones, Mike Miller, Mark Nottingham, 387 Mike Samuel, Jim Schaad, Robert Tupelo-Schneck and Michal Wadas. 389 7. References 391 7.1. Normative References 393 [ES6] Ecma International, "ECMAScript 2015 Language 394 Specification", . 397 [IEEE754] IEEE, "IEEE Standard for Floating-Point Arithmetic", 398 August 2008, . 400 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 401 Requirement Levels", BCP 14, RFC 2119, 402 DOI 10.17487/RFC2119, March 1997, 403 . 405 [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, 406 DOI 10.17487/RFC7493, March 2015, 407 . 409 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 410 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 411 May 2017, . 413 [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data 414 Interchange Format", STD 90, RFC 8259, 415 DOI 10.17487/RFC8259, December 2017, 416 . 418 [UNICODE] The Unicode Consortium, "The Unicode Standard, Version 419 10.0.0", 420 . 422 7.2. Informal References 424 [JSONCOMP] 425 A. Rundgren, ""Comparable" JSON - Work in progress", 426 . 429 [KEYBASE] "Keybase", 430 . 432 [NODEJS] "Node.js", . 434 [OPENAPI] "The OpenAPI Initiative", . 436 [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data 437 Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, 438 . 440 [RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web 441 Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 442 2015, . 444 [RFC7638] Jones, M. and N. Sakimura, "JSON Web Key (JWK) 445 Thumbprint", RFC 7638, DOI 10.17487/RFC7638, September 446 2015, . 448 [RYU] Ulf Adams, "Ryu floating point number serializing 449 algorithm", . 451 [V8] Google LLC, "Chrome V8 Open Source JavaScript Engine", 452 . 454 [XMLDSIG] W3C, "XML Signature Syntax and Processing Version 1.1", 455 . 457 7.3. URIs 459 [1] https://www.unicode.org/reports/tr15/ 461 [2] https://www.npmjs.com/package/canonicalize 463 [3] https://github.com/erdtman/java-json-canonicalization 465 [4] https://github.com/cyberphone/json-canonicalization/tree/master/ 466 go 468 [5] https://github.com/cyberphone/json-canonicalization/tree/master/ 469 dotnet 471 [6] https://github.com/cyberphone/json-canonicalization/tree/master/ 472 python3 474 [7] https://tools.ietf.org/html/draft-staykov-hu-json-canonical- 475 form-00 477 [8] https://gibson042.github.io/canonicaljson-spec/ 479 [9] http://wiki.laptop.org/go/Canonical_JSON 481 [10] https://github.com/cyberphone/ietf-json-canon 483 [11] https://cyberphone.github.io/ietf-json-canon 485 [12] https://github.com/cyberphone/json-canonicalization 487 Appendix A. ES6 Sample Canonicalizer 489 Below is an example of a JCS canonicalizer for usage with ES6 based 490 systems: 492 //////////////////////////////////////////////////////////// 493 // Since the primary purpose of this code is highlighting // 494 // the core of the JCS algorithm, error handling and // 495 // UTF-8 generation were not implemented // 496 //////////////////////////////////////////////////////////// 497 var canonicalize = function(object) { 499 var buffer = ''; 500 serialize(object); 501 return buffer; 503 function serialize(object) { 504 if (object === null || typeof object !== 'object' || 505 object.toJSON != null) { 506 ///////////////////////////////////////////////// 507 // Primitive type or toJSON - Use ES6/JSON // 508 ///////////////////////////////////////////////// 509 buffer += JSON.stringify(object); 511 } else if (Array.isArray(object)) { 512 ///////////////////////////////////////////////// 513 // Array - Maintain element order // 514 ///////////////////////////////////////////////// 515 buffer += '['; 516 let next = false; 517 object.forEach((element) => { 518 if (next) { 519 buffer += ','; 520 } 521 next = true; 522 ///////////////////////////////////////// 523 // Array element - Recursive expansion // 524 ///////////////////////////////////////// 525 serialize(element); 526 }); 527 buffer += ']'; 529 } else { 530 ///////////////////////////////////////////////// 531 // Object - Sort properties before serializing // 532 ///////////////////////////////////////////////// 533 buffer += '{'; 534 let next = false; 535 Object.keys(object).sort().forEach((property) => { 536 if (next) { 537 buffer += ','; 538 } 539 next = true; 540 /////////////////////////////////////////////// 541 // Property names are strings - Use ES6/JSON // 542 /////////////////////////////////////////////// 543 buffer += JSON.stringify(property); 544 buffer += ':'; 545 ////////////////////////////////////////// 546 // Property value - Recursive expansion // 547 ////////////////////////////////////////// 548 serialize(object[property]); 549 }); 550 buffer += '}'; 551 } 552 } 554 }; 556 Appendix B. Number Serialization Samples 558 The following table holds a set of ES6 compatible Number 559 serialization samples, including some edge cases. The column 560 "IEEE-754" refers to the internal ES6 representation of the Number 561 data type which is based on the IEEE-754 [IEEE754] standard using 562 64-bit (double precision) values, here expressed in hexadecimal. 564 |====================================================================| 565 | IEEE-754 | JSON Representation | Comment | 566 |====================================================================| 567 | 0000000000000000 | 0 | Zero | 568 |--------------------------------------------------------------------| 569 | 8000000000000000 | 0 | Minus zero | 570 |--------------------------------------------------------------------| 571 | 0000000000000001 | 5e-324 | Min pos number | 572 |--------------------------------------------------------------------| 573 | 8000000000000001 | -5e-324 | Min neg number | 574 |--------------------------------------------------------------------| 575 | 7fefffffffffffff | 1.7976931348623157e+308 | Max pos number | 576 |--------------------------------------------------------------------| 577 | ffefffffffffffff | -1.7976931348623157e+308 | Max neg number | 578 |--------------------------------------------------------------------| 579 | 4340000000000000 | 9007199254740992 | Max pos integer (1) | 580 |--------------------------------------------------------------------| 581 | c340000000000000 | -9007199254740992 | Max neg integer (1) | 582 |--------------------------------------------------------------------| 583 | 4430000000000000 | 295147905179352830000 | ~2**68 (2) | 584 |--------------------------------------------------------------------| 585 | 7fffffffffffffff | | NaN (3) | 586 |--------------------------------------------------------------------| 587 | 7ff0000000000000 | | Infinity (3) | 588 |--------------------------------------------------------------------| 589 | 44b52d02c7e14af5 | 9.999999999999997e+22 | | 590 |--------------------------------------------------------------------| 591 | 44b52d02c7e14af6 | 1e+23 | | 592 |--------------------------------------------------------------------| 593 | 44b52d02c7e14af7 | 1.0000000000000001e+23 | | 594 |--------------------------------------------------------------------| 595 | 444b1ae4d6e2ef4e | 999999999999999700000 | | 596 |--------------------------------------------------------------------| 597 | 444b1ae4d6e2ef4f | 999999999999999900000 | | 598 |--------------------------------------------------------------------| 599 | 444b1ae4d6e2ef50 | 1e+21 | | 600 |--------------------------------------------------------------------| 601 | 3eb0c6f7a0b5ed8c | 9.999999999999997e-7 | | 602 |--------------------------------------------------------------------| 603 | 3eb0c6f7a0b5ed8d | 0.000001 | | 604 |--------------------------------------------------------------------| 605 | 41b3de4355555553 | 333333333.3333332 | | 606 |--------------------------------------------------------------------| 607 | 41b3de4355555554 | 333333333.33333325 | | 608 |--------------------------------------------------------------------| 609 | 41b3de4355555555 | 333333333.3333333 | | 610 |--------------------------------------------------------------------| 611 | 41b3de4355555556 | 333333333.3333334 | | 612 |--------------------------------------------------------------------| 613 | 41b3de4355555557 | 333333333.33333343 | | 614 |--------------------------------------------------------------------| 615 | becbf647612f3696 | -0.0000033333333333333333 | | 616 |--------------------------------------------------------------------| 618 Notes: 620 (1) For maximum compliance with the ES6 "JSON" object values that 621 are to be interpreted as true integers SHOULD be in the range 622 -9007199254740991 to 9007199254740991. However, how numbers are 623 used in applications do not affect the JCS algorithm. 625 (2) Although a set of specific integers like 2**68 could be regarded 626 as having extended precision, the JCS/ES6 number serialization 627 algorithm does not take this in consideration. 629 (3) Invalid. See Section 3.2.2.3. 631 Appendix C. Canonicalized JSON as "Wire Format" 633 Since the result from the canonicalization process (see 634 Section 3.2.4), is fully valid JSON, it can also be used as 635 "Wire Format". However, this is just an option since cryptographic 636 schemes based on JCS, in most cases would not depend on that 637 externally supplied JSON data already is canonicalized. 639 In fact, the ES6 standard way of serializing objects using 640 "JSON.stringify()" produces a more "logical" format, where properties 641 are kept in the order they were created or received. The example 642 below shows an address record which could benefit from ES6 standard 643 serialization: 645 { 646 "name": "John Doe", 647 "address": "2000 Sunset Boulevard", 648 "city": "Los Angeles", 649 "zip": "90001", 650 "state": "CA" 651 } 653 Using canonicalization the properties above would be output in the 654 order "address", "city", "name", "state" and "zip", which adds 655 fuzziness to the data from a human (developer or technical support), 656 perspective. Canonicalization also converts JSON data into a single 657 line of text, which may be less than ideal for debugging and logging. 659 Appendix D. Dealing with Big Numbers 661 There are several issues associated with the JSON Number type, here 662 illustrated by the following sample object: 664 { 665 "giantNumber": 1.4e+9999, 666 "payMeThis": 26000.33, 667 "int64Max": 9223372036854775807 668 } 670 Although the sample above conforms to JSON [RFC8259], applications 671 would normally use different native data types for storing 672 "giantNumber" and "int64Max". In addition, monetary data like 673 "payMeThis" would presumably not rely on floating point data types 674 due to rounding issues with respect to decimal arithmetic. 676 The established way handling this kind of "overloading" of the JSON 677 Number type (at least in an extensible manner), is through mapping 678 mechanisms, instructing parsers what to do with different properties 679 based on their name. However, this greatly limits the value of using 680 the JSON Number type outside of its original somewhat constrained, 681 JavaScript context. The ES6 "JSON" object does not support mappings 682 to JSON Number either. 684 Due to the above, numbers that do not have a natural place in the 685 current JSON ecosystem MUST be wrapped using the JSON String type. 686 This is close to a de-facto standard for open systems. This is also 687 applicable for other data types that do not have direct support in 688 JSON, like "DateTime" objects as described in Appendix E. 690 Aided by a system using the JSON String type; be it programmatic like 691 var obj = JSON.parse('{"giantNumber": "1.4e+9999"}'); 692 var biggie = new BigNumber(obj.giantNumber); 694 or declarative schemes like OpenAPI [OPENAPI], JCS imposes no limits 695 on applications, including when using ES6. 697 Appendix E. String Subtype Handling 699 Due to the limited set of data types featured in JSON, the JSON 700 String type is commonly used for holding subtypes. This can 701 depending on JSON parsing method lead to interoperability problems 702 which MUST be dealt with by JCS compliant applications targeting a 703 wider audience. 705 Assume you want parse a JSON object where the schema designer 706 assigned the property "big" for holding a "BigInteger" subtype and 707 "time" for holding a "DateTime" subtype, while "val" is supposed to 708 be a JSON Number compliant with JCS. The following example shows 709 such an object: 711 { 712 "time": "2019-01-28T07:45:10Z", 713 "big": "055", 714 "val": 3.5 715 } 717 Parsing of this object can accomplished by the following ES6 718 statement: 720 var object = JSON.parse(JSON-data-featured-as-a-string); 722 After parsing the actual data can be extracted which for subtypes 723 also involve a conversion step using the result of the parsing 724 process (an ECMAScript object) as input: 726 ... = new Date(object.time); // Date object 727 ... = BigInt(object.big); // Big integer 728 ... = object.val; // JSON/JS number 730 Canonicalization of "object" using the sample code in Appendix A 731 would return the following string: 733 {"big":"055","time":"2019-01-28T07:45:10Z",val:3.5} 735 Although this is (with respect to JCS) technically correct, there is 736 another way parsing JSON data which also can be used with ES6 as 737 shown below: 739 // Currently required to make BigInt JSON serializable 740 BigInt.prototype.toJSON = function() { 741 return this.toString(); 742 }; 744 // JSON parsing using a "stream" based method 745 var object = JSON.parse(JSON-data-featured-as-a-string, 746 (k,v) => k == 'time' ? new Date(v) : k == 'big' ? BigInt(v) : v 747 ); 749 If you now apply the canonicalizer in Appendix A to "object", the 750 following string would be generated: 752 {"big":"55","time":"2019-01-28T07:45:10.000Z","val":3.5} 754 In this case the string arguments for "big" and "time" have changed 755 with respect to the original, presumable making an application 756 depending on JCS fail. 758 The reason for the deviation is that in stream and schema based JSON 759 parsers, the original "string" argument is typically replaced on-the- 760 fly by the native subtype which when serialized, may exhibit a 761 different and platform dependent pattern. 763 That is, stream and schema based parsing MUST treat subtypes as 764 "pure" (immutable) JSON String types, and perform the actual 765 conversion to the designated native type in a subsequent step. In 766 modern programming platforms like Go, Java and C# this can be 767 achieved with moderate efforts by combining annotations, getters and 768 setters. Below is an example in C#/Json.NET showing a part of a 769 class that is serializable as a JSON Object: 771 // The "pure" string solution uses a local 772 // string variable for JSON serialization while 773 // exposing another type to the application 774 [JsonProperty("amount")] 775 private string _amount; 777 [JsonIgnore] 778 public decimal Amount { 779 get { return decimal.Parse(_amount); } 780 set { _amount = value.ToString(); } 781 } 783 In an application "Amount" can be accessed as any other property 784 while it is actually represented by a quoted string in JSON contexts. 786 Note: the example above also addresses the constraints on numeric 787 data implied by I-JSON (the C# "decimal" data type has quite 788 different characteristics compared to IEEE-754 double precision). 790 E.1. Subtypes in Arrays 792 Since the JSON Array construct permits mixing arbitrary JSON 793 elements, custom parsing and serialization code must normally be used 794 to cope with subtypes anyway. 796 Appendix F. Implementation Guidelines 798 The optimal solution is integrating support for JCS directly in JSON 799 serializers (parsers need no changes). That is, canonicalization 800 would just be an additional "mode" for a JSON serializer. However, 801 this is currently not the case. Fortunately JCS support can be 802 performed through externally supplied canonicalizer software, 803 enabling signature creation schemes like the following: 805 1. Create the data to be signed. 807 2. Serialize the data using existing JSON tools. 809 3. Let the external canonicalizer process the serialized data and 810 return canonicalized result data. 812 4. Sign the canonicalized data. 814 5. Add the resulting signature value to the original JSON data 815 through a designated signature property. 817 6. Serialize the completed (now signed) JSON object using existing 818 JSON tools. 820 A compatible signature verification scheme would then be as follows: 822 1. Parse the signed JSON data using existing JSON tools. 824 2. Read and save the signature value from the designated signature 825 property. 827 3. Remove the signature property from the parsed JSON object. 829 4. Serialize the remaining JSON data using existing JSON tools. 831 5. Let the external canonicalizer process the serialized data and 832 return canonicalized result data. 834 6. Verify that the canonicalized data matches the saved signature 835 value using the algorithm and key used for creating the 836 signature. 838 A canonicalizer like above is effectively only a "filter", 839 potentially usable with a multitude of quite different cryptographic 840 schemes. 842 Using a JSON serializer with integrated JCS support, the 843 serialization performed before the canonicalization step could be 844 eliminated for both processes. 846 Appendix G. Open Source Implementations 848 The following Open Source implementations have been verified to be 849 compatible with JCS: 851 o JavaScript: https://www.npmjs.com/package/canonicalize [2] 853 o Java: https://github.com/erdtman/java-json-canonicalization [3] 855 o Go: https://github.com/cyberphone/json- 856 canonicalization/tree/master/go [4] 858 o .NET/C#: https://github.com/cyberphone/json- 859 canonicalization/tree/master/dotnet [5] 861 o Python: https://github.com/cyberphone/json- 862 canonicalization/tree/master/python3 [6] 864 Appendix H. Other JSON Canonicalization Efforts 866 There are (and have been) other efforts creating "Canonical JSON". 867 Below is a list of URLs to some of them: 869 o https://tools.ietf.org/html/draft-staykov-hu-json-canonical- 870 form-00 [7] 872 o https://gibson042.github.io/canonicaljson-spec/ [8] 874 o http://wiki.laptop.org/go/Canonical_JSON [9] 876 In contrast to JCS which is a serialization scheme, the listed 877 efforts build on text level JSON to JSON transformations. 879 Appendix I. Development Portal 881 The JCS specification is currently developed at: 882 https://github.com/cyberphone/ietf-json-canon [10]. 884 The most recent "editors' copy" can be found at: 885 https://cyberphone.github.io/ietf-json-canon [11]. 887 JCS source code and test data is available at: 888 https://github.com/cyberphone/json-canonicalization [12] 890 Authors' Addresses 892 Anders Rundgren 893 Independent 894 Montpellier 895 France 897 Email: anders.rundgren.net@gmail.com 898 URI: https://www.linkedin.com/in/andersrundgren/ 900 Bret Jordan 901 Symantec Corporation 902 350 Ellis Street 903 Mountain View CA 94043 904 USA 906 Email: bret_jordan@symantec.com 908 Samuel Erdtman 909 Spotify AB 910 Birger Jarlsgatan 61, 4tr 911 Stockholm 113 56 912 Sweden 914 Email: erdtman@spotify.com