You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
My claim is A is correct, but others claim B is correct. Though in some sense it doesn't really matter which is used so long as all clients use the same (assuming both are deterministic).
Whitespace is not permitted between tokens in Canonical JSON. I don't think CJSON modifies the actual data in the dictionary values; they are permitted to contain anything.
Assuming that "bar\nbaz" is the literal string for the "foo" key, I think B is correct.
I imagined this being a writer that streams bytes out, and if you pass it a String in language X (not a JSON string value), it would write the literal newline and not \n.
This means the CJSON is not a subset of JSON (despite the docs claiming it is) since a literal tab may be present in a string as with a literal newline (not \t and \n).
So the question is, when you run a string through the encode_canonical (or whatever an impl calls it) function, are you stream the bytes as the appear in a JSON string or the bytes as they appear in the memory representation?
This means the CJSON is not a subset of JSON (despite the docs claiming it is) since a literal tab may be present in a string as with a literal newline (not \t and \n).
I don't quite follow your objection to the use of the word "subset." CJSON is designed to take valid JSON as input, and modify it so that its output (canonical JSON) produces repeatable hashes. To quote the laptop.org page:
A "canonical JSON" format is provided in order to provide meaningful and repeatable hashes of JSON-encoded data.
If CJSON is given invalid JSON input, then you should expect invalid JSON as output.
Which docs? The linked page at laptop.org simply says, "The grammar for canonical JSON closely matches the grammar presented at json.org: ..." We do use the word "subset" in the TUF specification, so is this the objection? We can change that to use the wording used by laptop.org. Perhaps "restricted dialect" would be a better term to use.
So the question is, when you run a string through the encode_canonical (or whatever an impl calls it) function, are you stream the bytes as the appear in a JSON string or the bytes as they appear in the memory representation?
If there are 8 bytes in a string, the streamer should read 8 uninterpreted bytes out of it and escape any hex 22 (") or hex 5C () that it encounters. You are free to dump JSON in whatever format you wish, to "pretty print" it for example. However, when the hashes are generated, you should run the JSON through CJSON to produce repeatable hashes.
Here's an example using the reference implementation's encode_canonical() and Python's json module, where the " is escaped, whitespace is removed, tokens are lexicographically sorted, etc.
It would be very useful for embedded implementations if the bytes that are fed into the checksum routine can* exactly match the bytes that are sent over the wire.
There would be two advantages:
Less code touches the untrusted data. The secure json parser now only has to be able to pull out the start and end positions of the 'signed' section and extract the signature. The full JSON parser only gets invoked after the signature check, and can be removed from the TCB.
The checksum can be calculated without requiring RAM for a second copy of the data (or building a complex and security-critical streaming JSON -> cjson converter).
(*) CAN not MUST: this requires cooperation from the server to generate the JSON in canonical form
The CJSON definition grammar (not the human friendly text that says "CJSON is parseable with a JSON parser") is not compatible with JSON.
Further, the CJSON definition doesn't allow the sequence \n since the first char \ isn't allow by rule 1, and rules 2 and 3 only explicitly allow \\ and \".
(edit: the quote definition of CJSON isn't really even a grammar and should have just used ABNF, and as a result my above claim is wrong)
If we do CJSON the way its done in securesystemslib, then the spec should redefine canonical JSON and stop referencing the OLPC wiki.
Proposed Definition
CJSON is a subset of JSON with these restrictions:
No whitespace is allowed outside strings
No floats, only ints
Keys must be sorted according to the bytes that make up the strings (not English / alphabetical)
So it turns out that in #457 (comment), the choice A actually is what securesystemslib does, and therefore the output of CJSON is definitely not JSON. See the above related ticket @cajun-rat linked to.
encode_canonical() appears to generate valid JSON that can be loaded by json.loads() In addition, the format of this example matches choice B.
The behaviour of encode_canonical() should exactly match what is stated on the OLPC wiki. If it does not, please do point out any discrepancies. Ideally, we'd like to follow the OLPC specification and not invent out own format.
The CJSON definition grammar (not the human friendly text that says "CJSON is parseable with a JSON parser") is not compatible with JSON.
How so? Feel free to link to the JSON specification for the specific violation(s), so I can better understand your objection.
Further, the CJSON definition doesn't allow the sequence \n since the first char \ isn't allow by rule 1, and rules 2 and 3 only explicitly allow \ and ".
I don't follow your reasoning. Is this Rule 1? "Strings are uninterpreted bytes, with only two escaped byte values."
Hmm, I understand this to mean that there are only two bytes that are escaped by CJSON: hex 22 (") and hex 5C (). Not that the backslash is disallowed. In other words, whenever CJSON encounters hex 5C, it escapes it (\\).
Ok, so that's super non-intuitive. When you do a json.dumps on a dict, it does the escaping for you, so that puts the burden on doing JSON escaping on the caller of the function so that one must JSON-escape strings, then pass them into encode_canonical.
This also doesn't disprove the point that the output of canonical JSON isn't parseable as JSON.
any byte except hex 22 (") or hex 5C (\)
That means that 0x0a (newline) is a valid byte in CJSON. This is contradictory to the JSON RFC which says:
Even still, the encode_canonical function should accept any python object that json.dumps accepts so long as it doesn't contain any floats. Preprocessing the input shouldn't be necessary.
If CJSON is a subset of JSON, then the following should hold:
assert json.loads(encode_canonical(some_json)) == json.dumps(some_json)
Which is not the case.
@vladimir-v-diaz Yeah, I typo'd that and dropped one of the json.loads because I was pseudocoding and not doing it in the terminal. Anyway.
You're still double escaping things. If I want to the following text to display:
Hello
world
I needs a Python dict like this:
{ 'message': 'Hello\nworld'}
Which can be represented as JSON like this:
{ "message": "Hello\nworld" }
So if I want to canonicalize that JSON, I would do this:
importjsonfromsecuresystemslib.formatsimportencode_canonical# double escaped because this is a string representing a JSON stringjson_string="{ \"message\": \"Hello\\nworld\" }"# single escaped because this is just a string json_dict_ref= {'message': 'Hello\nworld'}
json_loaded=json.loads(json_string)
assertjson_loaded==json_dict_refprint(json_dict_ref['message'])
#Hello#worldprint(json_loaded['message'])
#Hello#worldjson.loads(encode_canonical(json_loaded))
# raises json.decoder.JSONDecodeError: Invalid control character at: line 1 column 18 (char 17)
FWIW: this issue is apparently a result from our home-brewed implementation of canonical json:
importjsonfromcanonicaljsonimportencode_canonical_jsonasencode_canonical# double escaped because this is a string representing a JSON stringjson_string="{ \"message\": \"Hello\\nworld\" }"# single escaped because this is just a string json_dict_ref= {'message': 'Hello\nworld'}
json_loaded=json.loads(json_string)
assertjson_loaded==json_dict_refprint(json_dict_ref['message'])
#Hello#worldprint(json_loaded['message'])
#Hello#worldjson.loads(encode_canonical(json_loaded))
# {'message': 'Hello\nworld'}
constQU:u8 = b'"';// \x22constBS:u8 = b'\\';// \x5C// Lookup table of escape sequences. A value of b'x' at index i means that byte// i is escaped as "\x" in JSON. A value of 0 means that byte i is not escaped.#[cfg_attr(rustfmt, rustfmt_skip)]staticESCAPE:[u8;256] = [// 1 2 3 4 5 6 7 8 9 A B C D E F0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 10,0,QU,0,0,0,0,0,0,0,0,0,0,0,0,0,// 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 40,0,0,0,0,0,0,0,0,0,0,0,BS,0,0,0,// 50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// 90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// A0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// B0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// C0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// D0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,// F];
There are exactly two escapes, and you are allowed to have completely arbitrary input strings.
There are two answers.
Answer 1
If we ignore the incorrect sentence in the OLPC definition and follow the grammar that is defined on the wiki, then the output is not JSON, and we continue using the OLPC definition regardless.
Answer 2
We define CJSON as:
Canonical JSON is the subset of JSON where:
Floats are not allowed
There is no whitespace outside of strings
Keys in objects are ordered by their byte representations
There is no way to say that the output of the OLPC grammar is JSON. It is not. You are allowed to have arbitrary strings as input to the encode function and (among other things) newlines are not escaped in the output. JSON escapes unicode as \uXXX, but CJSON says "any byte except \ and " which actually means that the output of CJSON doesn't even have to be unicode at all (which itself makes for a really terrible spec since the assumption is that it has strings as input).
If we pick Answer 2 (which is the most sane) then all references to the OLPC wiki need to be removed and the securesystemslib have it's function changed to match. In fact, you can do this with json.dumps(my_json, sort_keys=True, separators=(':', ',')) plus a recursive function that errors on floats.
I don't care if Answer 1 or Answer 2 becomes the spec. I just care that whatever is being used now is not fully understood by everyone and relies on (the OLPC) ambiguous grammar that is in direct contradiction to the first sentence of it's own spec.
That means that 0x0a (newline) is a valid byte in CJSON. This is contradictory to the JSON RFC which says:
Thanks for the explanation! OLPC's canonical JSON does allow arbitrary bytes in strings for compatibility reasons: "A previous version of this specification required strings to be valid unicode, and relied on JSON's \u escape. This was abandoned as it doesn't allow representing arbitrary binary data in a string, and it doesn't preserve the identity of non-canonical unicode strings."
Although the grammar for OLPC's canonical JSON can produce invalid JSON data if non-unicode bytes are present in strings, it nevertheless still produces valid JSON if only unicode characters are used, and the final JSON is encoded properly (e.g., UTF-8 in our case).
Please note that TUF metadata is in JSON object format, and we canonicalize the valid JSON to calculate digests and signatures.
From the TUF spec:
4.1. Metaformat
All documents use a subset of the JSON object format, with
floating-point numbers omitted. When calculating the digest of an
object, we use the "canonical JSON" subdialect as described at http://wiki.laptop.org/go/Canonical_JSON
Yes, both json.dumps() and the canonicaljson package appear to escape \n in strings, whereas CJSON preserves characters, with the exception of the single quote and backslash characters.
Since TUF metadata is not written in canonical JSON nor needs to load CJSON with json.dumps() or json.loads(), this is not an issue in the reference implementation. We only use CJSON to calculate digests and signatures.
The canonicaljson module unfortunately doesn't follow all of the restrictions outlined in the OLPC Wiki.
Hey @vladimir-v-diaz. Slow response here. Since it seems that the OLPC grammar is what we want, can we change the TUF spec to not reference it and use a real ABNF to describe the CJSON grammar? This seems like the best way to avoid other people getting as bogged down with this as we did. :)
Also, good point on the unicode normalization. I hadn't thought about that and need to bring this up with the team here.
@heartsucker
We can indeed change the TUF specification to include a description. However, is a real ABNF necessary? I don't see why we'd need to mention the grammar. Can't we simply say that its standard JSON with the following restrictions:
Floating point numbers are not allowed in JSON. Neither are leading zeros or "minus 0" for integers.
All map keys must be quoted, and must appear in sorted order.
All whitespace is eliminated.
Trailing commas in members and elements are not allowed.
Only one 'escape' sequence is defined for strings, and its use is mandatory for quote and backslash.
Trailing commas in members and elements are not allowed.
This already is neither allowed by the JSON nor the OLPC CJSON grammars.
its standard JSON...
Only one 'escape' sequence is defined for strings, and its use is mandatory for quote and backslash.
We should use a formal grammar because from these sentences I can't tell if you've settled on the "JSON-subset" approach of the "OLPC Canonical JSON" approach. A formal grammar makes it unambiguous how things are encoded and escaped.
The JSON restrictions listed come from the OLPC page. I want to list the OLPC restrictions in the specification, and to clear up any confusion about certain parts of it (e.g., strings are unicode, etc.)
The OLPC page might explicitly mention trailing commas because certain parsers, ECMAScript 5 objects/functions, Python objects, etc. do allow it. It might be better to make it clear that trailing commas are disallowed.
If you think a formal grammar is better, please share it so we can judge. I personally think saying that we use standard JSON with certain restrictions is simple and clear. If an implementor wants a grammar, they can just visit json.org.
Note: We might not even discuss JSON in the specification if we generalize the format of Metadata documents. This might just be documentation in the reference implementation if we decide to continue using this format.
object = begin-object [ member *( value-separator member ) ]
end-object
member = string name-separator value
Trailing commas are not allowed.
Python objects, etc. do allow it
In both cases, there is not a 1-to-1 mapping between Python dicts/arrays/etc. and JSON. It just happens to be that JS/Python simple types look a whole awful lot like JSON, and that's more an artifact of the convenience of representing data as maps, lists, strings, numbers, booleans, and nulls.
x= {
"foo": lambdax: x+1,
}
Could be "mapped" to JS
x={foo: function(x){returnx+1},}
But both of these are not JSON, and there is no meaningful way to convert either to JSON. And in fact, the trailing commas are stripped by the interpreters and don't even exist in memory.
Anyway.
When the doc changes away from JSON only, it should just include a note that the signing format needs to be deterministic with respect to ordering of elements. The implementation details don't actually matter.
When the doc changes away from JSON only, it should just include a note that the signing format needs to be deterministic with respect to ordering of elements.
Yup, this will a be helpful note to have for implementors. In practice, some formats don't always match up exactly across different applications. Sometimes a missing trailing newline can break things.
if you go the “JSON subset” route, you might need to define keys not only be sorted, but to be unique as well: the JSON spec is deliberately vague about duplicate keys in objects—in particular, it does not prohibit them;
trailing newlines: note that POSIX defines a “text file” to end with a newline, and e.g. every standard POSIX utility (grep, sed, ...) is allowed to break on files that don’t end with one—if you write CJSON to files, you might want to specify it to include a single final newline.
I'm going to close this: I think the discussion is valuable but there does not seem to be an action we can take within python-tuf. There are securesystemslib and the specification issues linked above, both seem like better places to continue the discussion.
Please open a new issue if there is a possible python-tuf improvement in this area.
Activity
heartsucker commentedon Jun 7, 2017
Posting this publicly to serve as documentation should anyone else hit this issue.
vladimir-v-diaz commentedon Jun 7, 2017
Whitespace is not permitted between tokens in Canonical JSON. I don't think CJSON modifies the actual data in the dictionary values; they are permitted to contain anything.
Assuming that "bar\nbaz" is the literal string for the "foo" key, I think B is correct.
I'm curious, why do you claim A is correct?
heartsucker commentedon Jun 7, 2017
I imagined this being a writer that streams bytes out, and if you pass it a
Stringin language X (not a JSON string value), it would write the literal newline and not\n.Also, from the definition here:
and also
This means the CJSON is not a subset of JSON (despite the docs claiming it is) since a literal tab may be present in a string as with a literal newline (not
\tand\n).So the question is, when you run a string through the
encode_canonical(or whatever an impl calls it) function, are you stream the bytes as the appear in a JSON string or the bytes as they appear in the memory representation?vladimir-v-diaz commentedon Jun 7, 2017
I don't quite follow your objection to the use of the word "subset." CJSON is designed to take valid JSON as input, and modify it so that its output (canonical JSON) produces repeatable hashes. To quote the laptop.org page:
If CJSON is given invalid JSON input, then you should expect invalid JSON as output.
Which docs? The linked page at laptop.org simply says, "The grammar for canonical JSON closely matches the grammar presented at json.org: ..." We do use the word "subset" in the TUF specification, so is this the objection? We can change that to use the wording used by laptop.org. Perhaps "restricted dialect" would be a better term to use.
If there are 8 bytes in a string, the streamer should read 8 uninterpreted bytes out of it and escape any hex 22 (") or hex 5C () that it encounters. You are free to dump JSON in whatever format you wish, to "pretty print" it for example. However, when the hashes are generated, you should run the JSON through CJSON to produce repeatable hashes.
Here's an example using the reference implementation's encode_canonical() and Python's
jsonmodule, where the"is escaped, whitespace is removed, tokens are lexicographically sorted, etc.cajun-rat commentedon Jun 8, 2017
It would be very useful for embedded implementations if the bytes that are fed into the checksum routine can* exactly match the bytes that are sent over the wire.
There would be two advantages:
(*) CAN not MUST: this requires cooperation from the server to generate the JSON in canonical form
heartsucker commentedon Jun 8, 2017
The CJSON definition grammar (not the human friendly text that says "CJSON is parseable with a JSON parser") is not compatible with JSON.
Further, the CJSON definition doesn't allow the sequence
\nsince the first char\isn't allow by rule 1, and rules 2 and 3 only explicitly allow\\and\".(edit: the quote definition of CJSON isn't really even a grammar and should have just used ABNF, and as a result my above claim is wrong)
If we do CJSON the way its done in
securesystemslib, then the spec should redefine canonical JSON and stop referencing the OLPC wiki.Proposed Definition
CJSON is a subset of JSON with these restrictions:
cajun-rat commentedon Jun 8, 2017
Related ticket: advancedtelematic/tuf-test-vectors#42
heartsucker commentedon Jun 8, 2017
So it turns out that in #457 (comment), the choice A actually is what
securesystemslibdoes, and therefore the output of CJSON is definitely not JSON. See the above related ticket @cajun-rat linked to.heartsucker commentedon Jun 8, 2017
Additional information:
vladimir-v-diaz commentedon Jun 8, 2017
The input to
encode_canonical()must be raw JSON, which is just a dictionary in Python.encode_canonical()appears to generate valid JSON that can be loaded byjson.loads()In addition, the format of this example matches choice B.The behaviour of
encode_canonical()should exactly match what is stated on the OLPC wiki. If it does not, please do point out any discrepancies. Ideally, we'd like to follow the OLPC specification and not invent out own format.How so? Feel free to link to the JSON specification for the specific violation(s), so I can better understand your objection.
I don't follow your reasoning. Is this Rule 1? "Strings are uninterpreted bytes, with only two escaped byte values."
Hmm, I understand this to mean that there are only two bytes that are escaped by CJSON: hex 22 (") and hex 5C (). Not that the backslash is disallowed. In other words, whenever CJSON encounters hex 5C, it escapes it (
\\).heartsucker commentedon Jun 8, 2017
Ok, so that's super non-intuitive. When you do a
json.dumpson a dict, it does the escaping for you, so that puts the burden on doing JSON escaping on the caller of the function so that one must JSON-escape strings, then pass them intoencode_canonical.This also doesn't disprove the point that the output of canonical JSON isn't parseable as JSON.
That means that
0x0a(newline) is a valid byte in CJSON. This is contradictory to the JSON RFC which says:If inside the TUF lib wherever you're calling
encode_canonicalyou are first encoding each string value as JSON, then you are double escaping.If CJSON is a subset of JSON, then the following should hold:
Which is not the case.
Even still, the
encode_canonicalfunction should accept any python object thatjson.dumpsaccepts so long as it doesn't contain anyfloats. Preprocessing the input shouldn't be necessary.heartsucker commentedon Jun 8, 2017
This also might explain why I couldn't get the same key IDs as you guys in this comment: https://github.com/theupdateframework/tuf/issues/362#issuecomment-295322447
vladimir-v-diaz commentedon Jun 9, 2017
You appear to have mixed up the loads(str) and dumps(obj) functions.
json.loads(str)deserializes str (a str instance containing a JSON document) to a Python object, andjson.dumps(obj)serializes obj to a JSON formatted str.https://docs.python.org/3.3/library/json.html#json.loads
https://docs.python.org/3.3/library/json.html#json.dumps
In your example, you are comparing a dictionary to a string. I think the following code snippet is the example that you wanted:
Here is the full code:
heartsucker commentedon Jun 9, 2017
@vladimir-v-diaz Yeah, I typo'd that and dropped one of the
json.loadsbecause I was pseudocoding and not doing it in the terminal. Anyway.You're still double escaping things. If I want to the following text to display:
I needs a Python dict like this:
{ 'message': 'Hello\nworld'}Which can be represented as JSON like this:
{ "message": "Hello\nworld" }So if I want to canonicalize that JSON, I would do this:
SantiagoTorres commentedon Jun 9, 2017
FWIW: this issue is apparently a result from our home-brewed implementation of canonical json:
heartsucker commentedon Jun 9, 2017
The PyPI package
canonical_jsonis wrong. The code insecuresystemslibis (I haven't fully audited it) correct.Here is a Rust implementation:
https://vtllf.org/rustdoc/canonical_json/src/canonical_json/src/ser.rs.html#1-828
There are exactly two escapes, and you are allowed to have completely arbitrary input strings.
There are two answers.
Answer 1
If we ignore the incorrect sentence in the OLPC definition and follow the grammar that is defined on the wiki, then the output is not JSON, and we continue using the OLPC definition regardless.
Answer 2
We define CJSON as:
Canonical JSON is the subset of JSON where:
There is no way to say that the output of the OLPC grammar is JSON. It is not. You are allowed to have arbitrary strings as input to the encode function and (among other things) newlines are not escaped in the output. JSON escapes unicode as
\uXXX, but CJSON says "any byte except\and"which actually means that the output of CJSON doesn't even have to be unicode at all (which itself makes for a really terrible spec since the assumption is that it has strings as input).If we pick Answer 2 (which is the most sane) then all references to the OLPC wiki need to be removed and the
securesystemslibhave it's function changed to match. In fact, you can do this withjson.dumps(my_json, sort_keys=True, separators=(':', ','))plus a recursive function that errors on floats.I don't care if Answer 1 or Answer 2 becomes the spec. I just care that whatever is being used now is not fully understood by everyone and relies on (the OLPC) ambiguous grammar that is in direct contradiction to the first sentence of it's own spec.
vladimir-v-diaz commentedon Jun 12, 2017
@heartsucker
Thanks for the explanation! OLPC's canonical JSON does allow arbitrary bytes in strings for compatibility reasons: "A previous version of this specification required strings to be valid unicode, and relied on JSON's \u escape. This was abandoned as it doesn't allow representing arbitrary binary data in a string, and it doesn't preserve the identity of non-canonical unicode strings."
Although the grammar for OLPC's canonical JSON can produce invalid JSON data if non-unicode bytes are present in strings, it nevertheless still produces valid JSON if only unicode characters are used, and the final JSON is encoded properly (e.g., UTF-8 in our case).
Please note that TUF metadata is in JSON object format, and we canonicalize the valid JSON to calculate digests and signatures.
From the TUF spec:
vladimir-v-diaz commentedon Jun 12, 2017
Yes, both
json.dumps()and thecanonicaljsonpackage appear to escape\nin strings, whereas CJSON preserves characters, with the exception of the single quote and backslash characters.Since TUF metadata is not written in canonical JSON nor needs to load CJSON with json.dumps() or
json.loads(), this is not an issue in the reference implementation. We only use CJSON to calculate digests and signatures.The
canonicaljsonmodule unfortunately doesn't follow all of the restrictions outlined in the OLPC Wiki.heartsucker commentedon Jun 22, 2017
Hey @vladimir-v-diaz. Slow response here. Since it seems that the OLPC grammar is what we want, can we change the TUF spec to not reference it and use a real ABNF to describe the CJSON grammar? This seems like the best way to avoid other people getting as bogged down with this as we did. :)
Also, good point on the unicode normalization. I hadn't thought about that and need to bring this up with the team here.
vladimir-v-diaz commentedon Jun 28, 2017
@heartsucker
We can indeed change the TUF specification to include a description. However, is a real ABNF necessary? I don't see why we'd need to mention the grammar. Can't we simply say that its standard JSON with the following restrictions:
heartsucker commentedon Jun 29, 2017
This already is neither allowed by the JSON nor the OLPC CJSON grammars.
We should use a formal grammar because from these sentences I can't tell if you've settled on the "JSON-subset" approach of the "OLPC Canonical JSON" approach. A formal grammar makes it unambiguous how things are encoded and escaped.
vladimir-v-diaz commentedon Jun 29, 2017
The JSON restrictions listed come from the OLPC page. I want to list the OLPC restrictions in the specification, and to clear up any confusion about certain parts of it (e.g., strings are unicode, etc.)
The OLPC page might explicitly mention trailing commas because certain parsers, ECMAScript 5 objects/functions, Python objects, etc. do allow it. It might be better to make it clear that trailing commas are disallowed.
The following two JSON linters seem to think that
"{'hello': 'world',}"is valid JSON:https://codebeautify.org/jsonvalidator
https://jsonlint.com/
If you think a formal grammar is better, please share it so we can judge. I personally think saying that we use standard JSON with certain restrictions is simple and clear. If an implementor wants a grammar, they can just visit json.org.
vladimir-v-diaz commentedon Jun 29, 2017
Note: We might not even discuss JSON in the specification if we generalize the format of Metadata documents. This might just be documentation in the reference implementation if we decide to continue using this format.
heartsucker commentedon Jun 30, 2017
From RFC-7159 section 4:
Trailing commas are not allowed.
In both cases, there is not a 1-to-1 mapping between Python dicts/arrays/etc. and JSON. It just happens to be that JS/Python simple types look a whole awful lot like JSON, and that's more an artifact of the convenience of representing data as maps, lists, strings, numbers, booleans, and nulls.
Could be "mapped" to JS
But both of these are not JSON, and there is no meaningful way to convert either to JSON. And in fact, the trailing commas are stripped by the interpreters and don't even exist in memory.
Anyway.
When the doc changes away from JSON only, it should just include a note that the signing format needs to be deterministic with respect to ordering of elements. The implementation details don't actually matter.
vladimir-v-diaz commentedon Jun 30, 2017
Yup, this will a be helpful note to have for implementors. In practice, some formats don't always match up exactly across different applications. Sometimes a missing trailing newline can break things.
alexshpilkin commentedon Feb 11, 2018
Two nits:
jku commentedon Feb 17, 2022
I'm going to close this: I think the discussion is valuable but there does not seem to be an action we can take within python-tuf. There are securesystemslib and the specification issues linked above, both seem like better places to continue the discussion.
Please open a new issue if there is a possible python-tuf improvement in this area.