idnits 2.17.1 draft-rundgren-json-canonicalization-scheme-04.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 6, 2019) is 1907 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 428, but not defined == Missing Reference: 'RFC7515' is mentioned on line 432, but not defined == Missing Reference: 'XMLDSIG' is mentioned on line 446, but not defined == Missing Reference: 'NODEJS' is mentioned on line 424, but not defined == Missing Reference: 'RFC7638' is mentioned on line 436, but not defined == Missing Reference: 'KEYBASE' is mentioned on line 421, but not defined -- Looks like a reference, but probably isn't: '1' on line 451 == Missing Reference: 'V8' is mentioned on line 443, but not defined == Missing Reference: 'RYU' is mentioned on line 440, but not defined == Missing Reference: 'OPENAPI' is mentioned on line 688, but not defined -- Looks like a reference, but probably isn't: '2' on line 890 -- Looks like a reference, but probably isn't: '3' on line 891 -- Looks like a reference, but probably isn't: '4' on line 894 -- Looks like a reference, but probably isn't: '5' on line 897 -- Looks like a reference, but probably isn't: '6' on line 900 -- Looks like a reference, but probably isn't: '7' on line 908 -- Looks like a reference, but probably isn't: '8' on line 910 -- Looks like a reference, but probably isn't: '9' on line 912 -- Looks like a reference, but probably isn't: '10' on line 917 -- Looks like a reference, but probably isn't: '11' on line 920 -- Looks like a reference, but probably isn't: '12' on line 923 == Missing Reference: 'JsonIgnore' is mentioned on line 771, 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 (~~), 11 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 10, 2019 Symantec Corporation 6 S. Erdtman 7 Spotify AB 8 February 6, 2019 10 JSON Canonicalization Scheme (JCS) 11 draft-rundgren-json-canonicalization-scheme-04 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 serialization formats for JSON 21 primitives as defined by ECMAScript [ES6], constraining JSON data to 22 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 10, 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 . . . . . . . . . . . . . . . . . . . . . 8 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 . . . . . . . . . . . . . . . . . . . . . . . . . . 10 81 Appendix A. ES6 Sample Canonicalizer . . . . . . . . . . . . . . 11 82 Appendix B. Number Serialization Samples . . . . . . . . . . . . 12 83 Appendix C. Canonicalized JSON as "Wire Format" . . . . . . . . 14 84 Appendix D. Dealing with Big Numbers . . . . . . . . . . . . . . 15 85 Appendix E. String Subtype Handling . . . . . . . . . . . . . . 15 86 E.1. Immutable String Method . . . . . . . . . . . . . . . . . 17 87 E.2. Data Normalization Method . . . . . . . . . . . . . . . . 17 88 E.3. Subtypes in Arrays . . . . . . . . . . . . . . . . . . . 18 89 Appendix F. Implementation Guidelines . . . . . . . . . . . . . 18 90 Appendix G. Open Source Implementations . . . . . . . . . . . . 19 91 Appendix H. Other JSON Canonicalization Efforts . . . . . . . . 20 92 Appendix I. Development Portal . . . . . . . . . . . . . . . . . 20 93 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 20 95 1. Introduction 97 Cryptographic operations like hashing and signing requires that the 98 original data does not change during serialization or parsing. One 99 way of accomplishing this is converting the data into a format that 100 has a simple and fixed representation like Base64Url [RFC4648], which 101 is how JWS [RFC7515] addressed this issue. 103 Another solution is to create a canonical version of the data, 104 similar to what was done for the XML Signature [XMLDSIG] standard. 105 The primary advantage with a canonicalizing scheme is that data can 106 be kept in its original form. This is the core rationale behind JCS. 107 Put another way: by using canonicalization a JSON Object may remain a 108 JSON Object even after being signed which simplifies system design, 109 documentation and logging. 111 To avoid "reinventing the wheel", JCS relies on serialization of JSON 112 primitives compatible with ECMAScript (aka JavaScript) beginning with 113 version 6 [ES6], hereafter referred to as "ES6". 115 Seasoned XML developers recalling difficulties getting signatures to 116 validate (usually due to different interpretations of the quite 117 intricate XML canonicalization rules as well as of the equally 118 extensive Web Services security standards), may rightfully wonder why 119 JCS would not suffer from similar issues. The reasons are twofold: 121 o The absence of a namespace concept and default values, as well as 122 constraining data to the I-JSON subset eliminate the need for 123 specific number parsing schemes for dealing with canonicalization. 125 o JCS compatible serialization of JSON primitives is supported by 126 most current Web browsers and as well as by Node.js [NODEJS], 127 while the full JCS specification is supported by multiple Open 128 Source implementations (see Appendix G). See also Appendix F. 130 In summary, the JCS specification describes how serialization of JSON 131 primitives compliant with ES6, combined with an elementary property 132 sorting scheme, can be used for supporting "Hashable" JSON. Note 133 that "true" canonicalization is an optional JCS feature, usually not 134 needed for cryptographic operations. See Appendix E for details. 136 JCS is compatible with some existing systems relying on JSON 137 canonicalization such as JWK Thumbprint [RFC7638] and Keybase 138 [KEYBASE]. 140 2. Terminology 142 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 143 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 144 "OPTIONAL" in this document are to be interpreted as described in BCP 145 14 [RFC2119] [RFC8174] when, and only when, they appear in all 146 capitals, as shown here. 148 3. Detailed Operation 150 This section describes the different issues related to creating a 151 canonical JSON representation, and how they are addressed by JCS. 153 3.1. Creation of Input Data 155 In order to serialize JSON data, one needs data that is adapted for 156 JSON serialization. This is usually achieved by: 158 o Parsing previously generated JSON data. 160 o Programmatically creating data. 162 Irrespective of the method used, the data to be serialized MUST be 163 compatible both with ES6 and I-JSON [RFC7493], which implies the 164 following: 166 o There MUST NOT be any duplicate property names within an element 167 to be serialized as a JSON Object. 169 o String data MUST be expressible as Unicode [UNICODE]. 171 o Numeric data MUST be expressible as IEEE-754 [IEEE754] double 172 precision values. For applications needing higher precision or 173 longer integers than offered by IEEE-754 double precision, 174 Appendix D outlines how such requirements can be supported in an 175 interoperable and extensible way. 177 Note: parsed String data MUST NOT be altered during subsequent 178 serializations. For more information see Appendix E. 180 Note: although the Unicode standard offers a possibility combining 181 certain characters into one, referred to as "Unicode Normalization" 182 (https://www.unicode.org/reports/tr15/ [1]), such functionality MUST 183 be delegated to the application layer. 185 3.2. Generation of Canonical JSON Data 187 The following subsections describe the steps required for creating a 188 canonical JSON representation of the data elaborated on in the 189 previous section. 191 Appendix A shows sample code for an ES6 based canonicalizer, matching 192 the JCS specification. 194 3.2.1. Whitespace 196 Whitespace between JSON elements MUST NOT be emitted. 198 3.2.2. Serialization of Primitive Data Types 200 Assume that you parse a JSON object like the following: 202 { 203 "numbers": [333333333.33333329, 1E30, 4.50, 204 2e-3, 0.000000000000000000000000001], 205 "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", 206 "literals": [null, true, false] 207 } 209 If you subsequently serialize the parsed data using a serializer 210 compliant with ES6's "JSON.stringify()", the result would (with a 211 line wrap added for display purposes only), be rather divergent with 212 respect to representation of data: 214 {"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string": 215 "\u20ac$\u000f\nA'B\"\\\\\"/","literals":[null,true,false]} 217 Note: \u20ac denotes the Euro character, which not 218 being ASCII, is currently not displayable in RFCs. 220 The reason for the difference between the parsed data and its 221 serialized counterpart, is due to a wide tolerance on input data (as 222 defined by JSON [RFC8259]), while output data (as defined by ES6), 223 has a fixed representation. As can be seen by the example, numbers 224 are subject to rounding as well. 226 The following subsections describe serialization of primitive JSON 227 data types according to JCS. This part is identical to that of ES6. 229 3.2.2.1. Serialization of Literals 231 The JSON literals "null", "true", and "false" present no challenge 232 since they already have a fixed definition in JSON [RFC8259]. 234 3.2.2.2. Serialization of Strings 236 For JSON String data (which includes JSON Object property names as 237 well), each character MUST be serialized as described below (also 238 matching Section 24.3.2.2 of [ES6]): 240 o If the Unicode value falls within the traditional ASCII control 241 character range (U+0000 through U+001F), it MUST be serialized 242 using lowercase hexadecimal Unicode notation (\uhhhh) unless it is 243 in the set of predefined JSON control characters U+0008, U+0009, 244 U+000A, U+000C or U+000D which MUST be serialized as \b, \t, \n, 245 \f and \r respectively. 247 o If the Unicode value is outside of the ASCII control character 248 range, it MUST be serialized "as is" unless it is equivalent to 249 U+005C (\) or U+0022 (") which MUST be serialized as \\ and \" 250 respectively. 252 Finally, the serialized string value MUST be enclosed in double 253 quotes ("). 255 Note: some JSON systems permit the use of invalid Unicode data 256 including "lone surrogates" (e.g. U+DEAD). Since this leads to 257 interoperability issues including broken signatures, occurrences of 258 such data MUST cause the JCS algorithm to terminate with an error 259 indication. 261 3.2.2.3. Serialization of Numbers 263 JSON data of type Number MUST be serialized according to 264 Section 7.1.12.1 of [ES6] including the "Note 2" enhancement. 266 Due to the relative complexity of this part, the algorithm itself is 267 not included in this document. However, the specification is fully 268 implemented by for example Google's V8 [V8]. The open source Java 269 implementation mentioned in Appendix G uses a recently developed 270 number serialization algorithm called Ryu [RYU]. 272 ES6 builds on the IEEE-754 [IEEE754] double precision standard for 273 representing JSON Number data. Appendix B holds a set of IEEE-754 274 sample values and their corresponding JSON serialization. 276 3.2.3. Sorting of Object Properties 278 Although the previous step indeed normalized the representation of 279 primitive JSON data types, the result would not qualify as 280 "canonical" since JSON Object properties are not in lexicographic 281 (alphabetical) order. 283 Applied to the sample in Section 3.2.2, a properly canonicalized 284 version should (with a line wrap added for display purposes only), 285 read as: 287 {"literals":[null,true,false],"numbers":[333333333.3333333, 288 1e+30,4.5,0.002,1e-27],"string":"\u20ac$\u000f\nA'B\"\\\\\"/"} 290 Note: \u20ac denotes the Euro character, which not 291 being ASCII, is currently not displayable in RFCs. 293 The rules for lexicographic sorting of JSON Object properties 294 according to JCS are as follows: 296 o JSON Object properties MUST be sorted in a recursive manner which 297 means that possible JSON child Objects MUST have their properties 298 sorted as well. 300 o JSON Array data MUST also be scanned for presence of JSON Objects 301 (and applying associated property sorting), but array element 302 order MUST NOT be changed. 304 When a JSON Object is about to have its properties sorted, the 305 following measures MUST be adhered to: 307 o The sorting process is applied to property strings in their "raw" 308 (unescaped) form. That is, a newline character is treated as 309 U+000A. 311 o Property strings to be sorted are formatted as arrays of UTF-16 312 [UNICODE] code units. The sorting is based on pure value 313 comparisons, where code units are treated as unsigned integers, 314 independent of locale settings. 316 o Property strings either have different values at some index that 317 is a valid index for both strings, or their lengths are different, 318 or both. If they have different values at one or more index 319 positions, let k be the smallest such index; then the string whose 320 value at position k has the smaller value, as determined by using 321 the < operator, lexicographically precedes the other string. If 322 there is no index position at which they differ, then the shorter 323 string lexicographically precedes the longer string. 325 o In plain English this means that property names are sorted in 326 ascending order like the following: 328 "" 329 "a" 330 "aa" 331 "ab" 333 The rationale for basing the sort algorithm on UTF-16 code units is 334 that it maps directly to the string type in ECMAScript (includes Web 335 browsers), Java and .NET. Systems using another internal 336 representation of string data will need to convert JSON property 337 strings into arrays of UTF-16 code units before sorting. The 338 conversion from UTF-8 or UTF-32 to UTF-16 is defined by the Unicode 339 [UNICODE] standard. 341 Note: for the purpose of obtaining a deterministic property order, 342 sorting on UTF-8 or UTF-32 encoded data would also work, but the 343 result would differ (and thus be incompatible with this 344 specification). 346 3.2.4. UTF-8 Generation 348 Finally, in order to create a platform independent representation, 349 the resulting JSON string data MUST be encoded in UTF-8. 351 Applied to the sample in Section 3.2.3 this should yield the 352 following bytes here shown in hexadecimal notation: 354 7b 22 6c 69 74 65 72 61 6c 73 22 3a 5b 6e 75 6c 6c 2c 74 72 355 75 65 2c 66 61 6c 73 65 5d 2c 22 6e 75 6d 62 65 72 73 22 3a 356 5b 33 33 33 33 33 33 33 33 33 2e 33 33 33 33 33 33 33 2c 31 357 65 2b 33 30 2c 34 2e 35 2c 30 2e 30 30 32 2c 31 65 2d 32 37 358 5d 2c 22 73 74 72 69 6e 67 22 3a 22 e2 82 ac 24 5c 75 30 30 359 30 66 5c 6e 41 27 42 5c 22 5c 5c 5c 5c 5c 22 2f 22 7d 361 This data is intended to be usable as input to cryptographic methods. 363 For other uses see Appendix C. 365 4. IANA Considerations 367 This document has no IANA actions. 369 5. Security Considerations 371 It is vital performing "sanity" checks on input data to avoid 372 overflowing buffers and similar things that could affect the 373 integrity of the system. 375 6. Acknowledgements 377 Building on ES6 Number serialization was originally proposed by James 378 Manger. This ultimately led to the adoption of the entire ES6 379 serialization scheme for JSON primitives. 381 Other people who have contributed with valuable input to this 382 specification include Bron Gondwana, Jim Schaad, John-Mark Gurney, 383 Mark Nottingham, Mike Jones, Mike Miller, Mike Samuel, Michal Wadas, 384 Richard Gibson, Robert Tupelo-Schneck and Scott Ananian. 386 7. References 388 7.1. Normative References 390 [ES6] Ecma International, "ECMAScript 2015 Language 391 Specification", . 394 [IEEE754] IEEE, "IEEE Standard for Floating-Point Arithmetic", 395 August 2008, . 397 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 398 Requirement Levels", BCP 14, RFC 2119, 399 DOI 10.17487/RFC2119, March 1997, 400 . 402 [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, 403 DOI 10.17487/RFC7493, March 2015, 404 . 406 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 407 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 408 May 2017, . 410 [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data 411 Interchange Format", STD 90, RFC 8259, 412 DOI 10.17487/RFC8259, December 2017, 413 . 415 [UNICODE] The Unicode Consortium, "The Unicode Standard, Version 416 10.0.0", 417 . 419 7.2. Informal References 421 [KEYBASE] "Keybase", 422 . 424 [NODEJS] "Node.js", . 426 [OPENAPI] "The OpenAPI Initiative", . 428 [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data 429 Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, 430 . 432 [RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web 433 Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 434 2015, . 436 [RFC7638] Jones, M. and N. Sakimura, "JSON Web Key (JWK) 437 Thumbprint", RFC 7638, DOI 10.17487/RFC7638, September 438 2015, . 440 [RYU] Ulf Adams, "Ryu floating point number serializing 441 algorithm", . 443 [V8] Google LLC, "Chrome V8 Open Source JavaScript Engine", 444 . 446 [XMLDSIG] W3C, "XML Signature Syntax and Processing Version 1.1", 447 . 449 7.3. URIs 451 [1] https://www.unicode.org/reports/tr15/ 453 [2] https://www.npmjs.com/package/canonicalize 455 [3] https://github.com/erdtman/java-json-canonicalization 457 [4] https://github.com/cyberphone/json-canonicalization/tree/master/ 458 go 460 [5] https://github.com/cyberphone/json-canonicalization/tree/master/ 461 dotnet 463 [6] https://github.com/cyberphone/json-canonicalization/tree/master/ 464 python3 466 [7] https://tools.ietf.org/html/draft-staykov-hu-json-canonical- 467 form-00 469 [8] https://gibson042.github.io/canonicaljson-spec/ 471 [9] http://wiki.laptop.org/go/Canonical_JSON 473 [10] https://github.com/cyberphone/ietf-json-canon 475 [11] https://cyberphone.github.io/ietf-json-canon 477 [12] https://github.com/cyberphone/json-canonicalization 479 Appendix A. ES6 Sample Canonicalizer 481 Below is a functionally complete example of a JCS compliant 482 canonicalizer for usage with ES6 based systems. 484 Note: the primary purpose of this code is highlighting the 485 canonicalization algorithm. Using the full power of ES6 would reduce 486 the code size considerably but would also be more difficult to follow 487 by non-experts. 489 var canonicalize = function(object) { 491 var buffer = ''; 492 serialize(object); 493 return buffer; 495 function serialize(object) { 496 if (object === null || typeof object !== 'object' || 497 object.toJSON != null) { 498 ///////////////////////////////////////////////// 499 // Primitive type or toJSON - Use ES6/JSON // 500 ///////////////////////////////////////////////// 501 buffer += JSON.stringify(object); 503 } else if (Array.isArray(object)) { 504 ///////////////////////////////////////////////// 505 // Array - Maintain element order // 506 ///////////////////////////////////////////////// 507 buffer += '['; 508 let next = false; 509 object.forEach((element) => { 510 if (next) { 511 buffer += ','; 512 } 513 next = true; 514 ///////////////////////////////////////// 515 // Array element - Recursive expansion // 516 ///////////////////////////////////////// 517 serialize(element); 518 }); 519 buffer += ']'; 521 } else { 522 ///////////////////////////////////////////////// 523 // Object - Sort properties before serializing // 524 ///////////////////////////////////////////////// 525 buffer += '{'; 526 let next = false; 527 Object.keys(object).sort().forEach((property) => { 528 if (next) { 529 buffer += ','; 530 } 531 next = true; 532 /////////////////////////////////////////////// 533 // Property names are strings - Use ES6/JSON // 534 /////////////////////////////////////////////// 535 buffer += JSON.stringify(property); 536 buffer += ':'; 537 ////////////////////////////////////////// 538 // Property value - Recursive expansion // 539 ////////////////////////////////////////// 540 serialize(object[property]); 541 }); 542 buffer += '}'; 543 } 544 } 545 }; 547 Appendix B. Number Serialization Samples 549 The following table holds a set of ES6 compatible Number 550 serialization samples, including some edge cases. The column "IEEE- 551 754" refers to the internal ES6 representation of the Number data 552 type which is based on the IEEE-754 [IEEE754] standard using 64-bit 553 (double precision) values, here expressed in hexadecimal. 555 |====================================================================| 556 | IEEE-754 | JSON Representation | Comment | 557 |====================================================================| 558 | 0000000000000000 | 0 | Zero | 559 |--------------------------------------------------------------------| 560 | 8000000000000000 | 0 | Minus zero | 561 |--------------------------------------------------------------------| 562 | 0000000000000001 | 5e-324 | Smallest pos number | 563 |--------------------------------------------------------------------| 564 | 8000000000000001 | -5e-324 | Smallest neg number | 565 |--------------------------------------------------------------------| 566 | 7fefffffffffffff | 1.7976931348623157e+308 | Largest pos number | 567 |--------------------------------------------------------------------| 568 | ffefffffffffffff | -1.7976931348623157e+308 | Largest neg number | 569 |--------------------------------------------------------------------| 570 | 4340000000000000 | 9007199254740992 | Largest pos integer | 571 |--------------------------------------------------------------------| 572 | c340000000000000 | -9007199254740992 | Largest neg integer | 573 |--------------------------------------------------------------------| 574 | 7fffffffffffffff | | NaN - Invalid | 575 |--------------------------------------------------------------------| 576 | 7ff0000000000000 | | Infinity - Invalid | 577 |--------------------------------------------------------------------| 578 | 44b52d02c7e14af5 | 9.999999999999997e+22 | | 579 |--------------------------------------------------------------------| 580 | 44b52d02c7e14af6 | 1e+23 | | 581 |--------------------------------------------------------------------| 582 | 44b52d02c7e14af7 | 1.0000000000000001e+23 | | 583 |--------------------------------------------------------------------| 584 | 444b1ae4d6e2ef4e | 999999999999999700000 | | 585 |--------------------------------------------------------------------| 586 | 444b1ae4d6e2ef4f | 999999999999999900000 | | 587 |--------------------------------------------------------------------| 588 | 3eb0c6f7a0b5ed8d | 0.000001 | | 589 |--------------------------------------------------------------------| 590 | 3eb0c6f7a0b5ed8c | 9.999999999999997e-7 | | 591 |--------------------------------------------------------------------| 592 | 41b3de4355555553 | 333333333.3333332 | | 593 |--------------------------------------------------------------------| 594 | 41b3de4355555554 | 333333333.33333325 | | 595 |--------------------------------------------------------------------| 596 | 41b3de4355555555 | 333333333.3333333 | | 597 |--------------------------------------------------------------------| 598 | 41b3de4355555556 | 333333333.3333334 | | 599 |--------------------------------------------------------------------| 600 | 41b3de4355555557 | 333333333.33333343 | | 601 |--------------------------------------------------------------------| 602 | becbf647612f3696 | -0.0000033333333333333333 | | 603 |--------------------------------------------------------------------| 604 Note: for maximum compliance with ECMAScript's "JSON" object, values 605 that are to be interpreted as true integers, SHOULD be in the range 606 -9007199254740991 to 9007199254740991. 608 Note: since NaN (Not a Number) and Infinity are not permitted in 609 JSON, occurrences of such values MUST cause the JCS algorithm to 610 terminate with an error indication. 612 Note: although a set of specific integers like 2**68 613 (4430000000000000 in IEEE-754 format) could be regarded as having 614 extended precision, the JCS/ES6 number serialization algorithm does 615 not take this in consideration. 617 Appendix C. Canonicalized JSON as "Wire Format" 619 Since the result from the canonicalization process (see 620 Section 3.2.4), is fully valid JSON, it can also be used as 621 "Wire Format". However, this is just an option since cryptographic 622 schemes based on JCS, in most cases would not depend on that 623 externally supplied JSON data already is canonicalized. 625 In fact, the ES6 standard way of serializing objects using 626 "JSON.stringify()" produces a more "logical" format, where properties 627 are kept in the order they were created or received. The example 628 below shows an address record which could benefit from ES6 standard 629 serialization: 631 { 632 "name": "John Doe", 633 "address": "2000 Sunset Boulevard", 634 "city": "Los Angeles", 635 "zip": "90001", 636 "state": "CA" 637 } 639 Using canonicalization the properties above would be output in the 640 order "address", "city", "name", "state" and "zip", which adds 641 fuzziness to the data from a human (developer or technical support), 642 perspective. 644 That is, for many applications, canonicalization would only be used 645 internally for creating a "hashable" representation of the data 646 needed for cryptographic operations. 648 Note: if message size is not a concern, you may even send 649 "Pretty Printed" JSON data on the wire (since whitespace always is 650 ignored by the canonicalization process). 652 Appendix D. Dealing with Big Numbers 654 There are several issues associated with the JSON Number type, here 655 illustrated by the following sample object: 657 { 658 "giantNumber": 1.4e+9999, 659 "payMeThis": 26000.33, 660 "int64Max": 9223372036854775807 661 } 663 Although the sample above conforms to JSON (according to [RFC8259]), 664 applications would normally use different native data types for 665 storing "giantNumber" and "int64Max". In addition, monetary data 666 like "payMeThis" would presumably not rely on floating point data 667 types due to rounding issues with respect to decimal arithmetic. 669 The established way handling this kind of "overloading" of the JSON 670 Number type (at least in an extensible manner), is through mapping 671 mechanisms, instructing parsers what to do with different properties 672 based on their name. However, this greatly limits the value of using 673 the Number type outside of its original somewhat constrained, 674 JavaScript context. The ES6 JSON object does not support mappings to 675 JSON Number either. 677 Due to the above, numbers that do not have a natural place in the 678 current JSON ecosystem MUST be wrapped using the JSON String type. 679 This is close to a de-facto standard for open systems. This is also 680 applicable for other data types that do not have direct support in 681 JSON, like "DateTime" objects. Also see Appendix E. 683 Aided by a system using the JSON String type; be it programmatic like 685 var obj = JSON.parse('{"giantNumber": "1.4e+9999"}'); 686 var biggie = new BigNumber(obj.giantNumber); 688 or declarative schemes like OpenAPI [OPENAPI], JCS imposes no limits 689 on applications, including when using ES6. 691 Appendix E. String Subtype Handling 693 Due to the limited set of data types featured in JSON, the JSON 694 String type is commonly used for holding subtypes. This appendix 695 shows that this can lead to interoperability problems depending on 696 JSON parsing method, which MUST be dealt with by JCS compliant 697 applications targeting a wider audience. 699 Assume you want to parse the following JSON string holding two 700 subtypes and a JSON number: 702 const jstring = 703 '{"time": "2019-01-28T07:45:10Z", "big": "055", "val": 3.5}'; 705 This can accomplished by the following ES6 statement: 707 var object = JSON.parse(jstring); 709 After parsing the actual data can be extracted which for subtypes 710 also involve a conversion step using the parsed string argument as 711 input: 713 ... = new Date(object.time); // Date object 714 ... = BigInt(object.big); // Big integer 715 ... = object.val; // JSON/JS number 717 Canonicalization of "object" using the sample code in Appendix A 718 would return the following string: 720 {"big":"055","time":"2019-01-28T07:45:10Z",val:3.5} 722 Although this is (with respect to JCS) technically correct, there is 723 another way parsing JSON data which also can be used with ES6 as 724 shown below: 726 // Currently required to make BigInt JSON serializable 727 BigInt.prototype.toJSON = function() { 728 return this.toString(); 729 }; 731 // JSON parsing using a "stream" based method 732 var object = JSON.parse(jstring, 733 (k,v) => k == 'time' ? new Date(v) : k == 'big' ? BigInt(v) : v 734 ); 736 If you now apply the canonicalizer in Appendix A to "object", the 737 following string would be generated: 739 {"big":"55","time":"2019-01-28T07:45:10.000Z","val":3.5} 741 In this case the string arguments for "big" and "time" have changed 742 with respect to the original, presumable making an application 743 depending on JCS fail. 745 The reason for the deviation is that in stream and schema based JSON 746 parsers, the original "string" argument is typically replaced on-the- 747 fly by the native subtype which when serialized, may exhibit a 748 different and platform dependent pattern. 750 The above may at first look like an insurmountable obstacle. 751 However, there are two fairly straightforward ways making subtype 752 serialization independent of parsing scheme elaborated on in the 753 following subsections. 755 E.1. Immutable String Method 757 One method for coping with stream and schema based parsers is 758 treating subtypes as "pure" strings with respect to JSON. To 759 accomplish this, subtype conversions are performed outside of the 760 core parsing process. In modern programming platforms like Go, Java 761 and C# this can be achieved with moderate efforts by combining 762 annotations, getters and setters. Below is an example in C#/Json.NET 763 showing a part of a class that is serializable as a JSON Object: 765 // The immutable string solution uses a local 766 // string variable for JSON serialization while 767 // exposing another type to the application 768 [JsonProperty("amount")] 769 private string _amount; 771 [JsonIgnore] 772 public decimal Amount { 773 get { return decimal.Parse(_amount); } 774 set { _amount = value.ToString(); } 775 } 777 In an application "Amount" can be accessed as any other property 778 while it is actually represented by a quoted string in JSON contexts. 780 Note: the example above also addresses the constraints on numeric 781 data implied by I-JSON (the C# "decimal" data type has quite 782 different characteristics compared to IEEE-754 double precision). 784 Note: the intial example in this appendix showing parsing using ES6, 785 faithfully implements the immutable string method. 787 E.2. Data Normalization Method 789 Another method for handling stream and schema based parsing is to 790 normalize subtype data. This requires that the following measures 791 are adhered to: 793 o The community or standard utilizing a specific JSON schema defines 794 a strict normalized form for each of the used subtypes. 796 o Compatible serializers are created for each subtype. 798 By adding a dedicated serializer for the "Date" subtype, the sample 799 JSON schema becomes compatible with JCS: 801 Date.prototype.toJSON = function() { 802 let date = this.toISOString(); 803 // In this particular case we selected a UTC notation 804 // yyyy-mm-ddThh:mm:ssZ 805 return date.substring(0, date.indexOf('.')) + 'Z'; 806 }; 808 The canonical string will after this upgrade read as: 810 {"big":"55","time":"2019-01-28T07:45:10Z","val":3.5} 812 The observant reader will note that "big" still is in error but that 813 is to be expected since the "055" argument in the input data clearly 814 is not a normalized form for a big integer. That is, the originator 815 of the JSON data did not follow the "contract". 817 A positive side effect of this arrangement is that it enforces strict 818 definitions of subtypes which improves interoperability in general as 819 well. 821 Defining specific subtypes and their normalized form is out of scope 822 for this specification. 824 Unlike the immutable string method, the data normalization method 825 accomplishes "true" canonicalization which may be usable by a wider 826 range of applications than "Hashable" JSON. 828 E.3. Subtypes in Arrays 830 Since the JSON Array construct permits mixing arbitrary JSON 831 elements, custom parsing and serialization code MUST be used to 832 support one (or both) of the methods described in the previous 833 subsections. 835 Appendix F. Implementation Guidelines 837 The optimal solution is integrating support for JCS directly in JSON 838 serializers (parsers need no changes). That is, canonicalization 839 would just be an additional "mode" for a JSON serializer. However, 840 this is currently not the case. Fortunately JCS support can be 841 performed through externally supplied canonicalizer software, 842 enabling signature creation schemes like the following: 844 1. Create the data to be signed. 846 2. Serialize the data using existing JSON tools. 848 3. Let the external canonicalizer process the serialized data and 849 return canonicalized result data. 851 4. Sign the canonicalized data. 853 5. Add the resulting signature value to the original JSON data 854 through a designated signature property. 856 6. Serialize the completed (now signed) JSON object using existing 857 JSON tools. 859 A compatible signature verification scheme would then be as follows: 861 1. Parse the signed JSON data using existing JSON tools. 863 2. Read and save the signature value from the designated signature 864 property. 866 3. Remove the signature property from the parsed JSON object. 868 4. Serialize the remaining JSON data using existing JSON tools. 870 5. Let the external canonicalizer process the serialized data and 871 return canonicalized result data. 873 6. Verify that the canonicalized data matches the saved signature 874 value using the algorithm and key used for creating the 875 signature. 877 A canonicalizer like above is effectively only a "filter", 878 potentially usable with a multitude of quite different cryptographic 879 schemes. 881 Using a JSON serializer with integrated JCS support, the 882 serialization performed before the canonicalization step could be 883 eliminated for both processes. 885 Appendix G. Open Source Implementations 887 The following Open Source implementations have been verified to be 888 compatible with JCS: 890 o JavaScript: https://www.npmjs.com/package/canonicalize [2] 891 o Java: https://github.com/erdtman/java-json-canonicalization [3] 893 o Go: https://github.com/cyberphone/json- 894 canonicalization/tree/master/go [4] 896 o .NET/C#: https://github.com/cyberphone/json- 897 canonicalization/tree/master/dotnet [5] 899 o Python: https://github.com/cyberphone/json- 900 canonicalization/tree/master/python3 [6] 902 Appendix H. Other JSON Canonicalization Efforts 904 There are (and have been) other efforts creating "Canonical JSON". 905 Below is a list of URLs to some of them: 907 o https://tools.ietf.org/html/draft-staykov-hu-json-canonical- 908 form-00 [7] 910 o https://gibson042.github.io/canonicaljson-spec/ [8] 912 o http://wiki.laptop.org/go/Canonical_JSON [9] 914 Appendix I. Development Portal 916 The JCS specification is currently developed at: 917 https://github.com/cyberphone/ietf-json-canon [10]. 919 The most recent "editors' copy" can be found at: 920 https://cyberphone.github.io/ietf-json-canon [11]. 922 JCS source code and test data is available at: 923 https://github.com/cyberphone/json-canonicalization [12] 925 Authors' Addresses 927 Anders Rundgren 928 Independent 929 Montpellier 930 France 932 Email: anders.rundgren.net@gmail.com 933 URI: https://www.linkedin.com/in/andersrundgren/ 934 Bret Jordan 935 Symantec Corporation 936 350 Ellis Street 937 Mountain View CA 94043 938 USA 940 Email: bret_jordan@symantec.com 942 Samuel Erdtman 943 Spotify AB 944 Birger Jarlsgatan 61, 4tr 945 Stockholm 113 56 946 Sweden 948 Email: erdtman@spotify.com