json-canonicalize
v2.0.1
Published
JSON canonicalize function
Maintainers
Readme
json-canonicalize
JSON canonicalize function
JSON Canonicalization
Cryptographic operations like hashing and signing depend on that the target data does not change during serialization, transport, or parsing. By applying the rules defined by JCS (JSON Canonicalization Scheme), data provided in the JSON [RFC8259] format can be exchanged "as is", while still being subject to secure cryptographic operations. JCS achieves this by building on the serialization formats for JSON primitives as defined by ECMAScript [ES6], constraining JSON data to the I-JSON [RFC7493] subset, and through a platform independent property sorting scheme.
- Working document: https://cyberphone.github.io/ietf-json-canon
- Published IETF Draft: https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-05
- Published RFC 8785: https://www.rfc-editor.org/rfc/rfc8785
✨ Features
The JSON Canonicalization Scheme concept in a nutshell:
- Serialization of primitive JSON data types using methods compatible with ECMAScript's
JSON.stringify() - Lexicographic sorting of JSON
Objectproperties in a recursive process - JSON
Arraydata is also subject to canonicalization, but element order remains untouched
RFC 8785 Compatibility
This implementation is compatible with JCS / RFC 8785, with a couple of key differences in the default canonicalize function:
- Handling of
undefinedin arrays: When a value in an array isundefined, thecanonicalizefunction treats it asnull. RFC 8785 specifies that it should be treated asundefined, which can lead to different outputs. - Recursive References: This implementation supports recursive object references, which is an enhancement not covered by the standard.
To be fully compatible with RFC 8785, you can use the canonicalizeEx function with the undefinedInArrayToNull option set to false:
canonicalizeEx(obj, { undefinedInArrayToNull: false });Non-finite numbers
Per RFC 8785 §3.2.2.3, NaN and Infinity are not permitted in JSON: both canonicalize and canonicalizeEx throw an error when they encounter a non-finite number, instead of silently serializing it as null.
Note that this also covers overflowing literals: JSON.parse('{"v":1e400}') overflows 1e400 to Infinity at parse time (there is no way for the serializer to recover the original literal), so canonicalizing such a document throws as well rather than producing a digest that collides with other overflowing values.
Intercepting overflow at parse time
Since the overflow happens inside JSON.parse, the original literal is already lost by the time a value reaches the serializer. If you want to reject overflowing numbers at the boundary — before they enter your pipeline — parse with a reviver that checks Number.isFinite:
function parseRejectingNonFinite(text: string) {
return JSON.parse(text, (_key, value) => {
if (typeof value === 'number' && !Number.isFinite(value)) {
throw new Error(`Non-finite number (${value}) is not permitted in JSON (RFC 8785 §3.2.2.3)`);
}
return value;
});
}
parseRejectingNonFinite('{"v":1e400}'); // throws: Non-finite number (Infinity) is not permitted in JSON
canonicalize(parseRejectingNonFinite('{"v":1e400}')); // never reaches canonicalize🔧 Installation
yarn add json-canonicalize🎬 Getting started
Let's demonstrate simple usage with ... example:
import { canonicalize, canonicalizeEx } from 'json-canonicalize';
canonicalize(obj)
// Add `include` and `exclude` options to `canonicalizeEx`.
canonicalizeEx(obj, {exclude:['num', 'dt']})
// add canonicalize to JSON directly.
// which means
// JSON.canonicalize = canonicalize;
import from 'json-canonicalize/src/global';
JSON.canonicalize(obj)API
canonicalize(obj, allowCircular)
This is the main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation.
obj(any): The JavaScript object to canonicalize.allowCircular(boolean, optional): Iftrue, the function will handle circular references in the object by replacing them with a special identifier (e.g.,[Circular:$.ref]) that includes therefitem's origin, allowing for correct restoration of the object from the string. Defaults tofalse.
canonicalizeEx(obj, options)
This is the extended canonicalization function, offering more granular control over the serialization process.
obj(any): The JavaScript object to canonicalize.options(ISerializeOptions, optional): An object with the following properties:allowCircular(boolean, optional): Same as incanonicalize.filterUndefined(boolean, optional): Iftrue,undefinedvalues in objects will be filtered out. Defaults totrue.undefinedInArrayToNull(boolean, optional): Iftrue,undefinedvalues in arrays will be converted tonull. Defaults totrue.include(string[], optional): An array of property names to include in the canonicalization.exclude(string[], optional): An array of property names to exclude from the canonicalization.
🥂 License
MIT as always
Refs
- (JSON Canonicalization)[https://github.com/cyberphone/json-canonicalization]
