github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/doc/json-signing/json-signing.txt (about) 1 JSON claim objects need to be signed. If I want to distribute a Camli 2 blob object publicly, declaring that I "favorite" or "star" a named 3 entity, it should be verifiable. 4 5 The properties we want in the JSON file, ideally, include: 6 7 GOAL #1) it's still a valid JSON file in its entirety. 8 9 This means no non-JSON compliant header or footer. 10 11 This implies that the data structure to be signed and the signature 12 metadata be separate, in an outer JSON wrapper. 13 14 This has been discussed and implemented in various ways. For example, 15 in jchris's canonical-json project, 16 17 http://github.com/jchris/canonical-json 18 19 ... the "signed-content" and the "signature" are parallel objects under the 20 same outer JSON object. 21 22 The problem then becomes that the verifier, after parsing the JSON 23 blob, needs to re-serialize the JSON "signed-content" object, 24 byte-for-byte as in the original, in order to verify the signature. 25 26 In jchris' strategy, the canonicalization is implemented by 27 referencing JavaScript code that serializes it. This has the 28 advantage that the serialization could change over time, but the 29 disadvantage that you have to embed a Rhino, V8, SpiderMonkey, or 30 similar into your parser, which is somewhat heavy. Considering that 31 canonical JSON serialization is something that should be relatively 32 static and could be defined once, I'm not sure that the flexibility is 33 worth the cost. 34 35 Overall, though, the jchris approach's structure of the JSON file is 36 good. 37 38 Notably, it satisfies on of my other goals: 39 40 GOAL #2) The document still be human-readable. 41 42 For instance, the laptop.org project is proposing this Canonical JSON 43 format: 44 45 http://wiki.laptop.org/go/Canonical_JSON 46 47 .. unfortunately, all whitespace is stripped. It's not a deal 48 breaker, but lacks human readableness. 49 50 You might say, "Bring your own serialization! Wrap the signed-content 51 in a string!" 52 53 But then you're back to the readable problem, because JSON strings 54 can't have embedded newline literals. 55 56 Further, the laptop.org proposal requires the use of a new JSON 57 serialization library and parser for each language which wants to 58 produce camli documents. This isn't a huge deal, but considering that 59 JSON libraries already exist and people are oddly passionate about 60 their favorites and inertia's not to be ignored, I state the next 61 goal: 62 63 GOAL #3) Don't require a new JSON library for parsing/serialization. 64 65 With the above goals in mind, Camli uses the following scheme to sign 66 and verify JSON documents: 67 68 SIGNING 69 ======= 70 71 -- Start with a JSON object (not an array) to be encoded and signed. 72 We'll call this data structure 'O'. While this signing technique 73 could be used for applications other than Camlistore, this document 74 is specifically about Camlistore, which requires that the JSON 75 object 'O' contain the following two key/value pairs: 76 "camliVersion": "1" 77 "camliSigner": "hashalg-xxxxxxxxxxx" (blobref of ASCII-armored public key) 78 79 -- To find your camliSigner value, you could use GPG like: 80 81 $ gpg --no-default-keyring --keyring=example/test-keyring.gpg --secret-keyring=example/test-secring.gpg \ 82 --export --armor 26F5ABDA > example/public-key.txt 83 84 $ sha1sum example/public-key.txt 85 8616ebc5143efe038528c2ab8fa6582353805a7a 86 87 ... so the blobref value for camliSigner is "sha1-8616ebc5143efe038528c2ab8fa6582353805a7a". 88 Clients will use this value in the future to find the public key to verify 89 signtures. 90 91 -- Serialize in-memory JSON object 'O' with whatever JSON 92 serialization library you have available. internal or trailing 93 whitespace doesn't matter. We'll call the JSON serialization of 94 'O' (defined in earlier step) 'J' 95 (e.g. doc/example/signing-before-J.camli) 96 97 -- Now remove any trailing whitespace and exactly and only one '}' 98 character from the end of string 'J'. We'll call this truncated, 99 trimmed string 'T'. 100 (e.g. doc/example/signing-before.camli) 101 102 -- Create an ASCII-armored detached signature of this document, 103 e.g.: 104 105 gpg --detach-sign --local-user=54F8A914 --armor \ 106 -o signing-before.camli.detachsig signing-before.camli 107 108 (The output file is in doc/example/signing-before.camli.detachsig) 109 110 -- Take just the base64 part of that ASCII detached signature 111 into a single line, and call that 'S'. 112 113 -- Append the following to 'T' above: 114 115 ,"camliSig":"<S>"}\n 116 117 ... where <S> is the single-line ASCII base64 detached signature. 118 Note that there are exactly 13 bytes before <S> and exactly 119 3 bytes after <S>. Those must match exactly. 120 121 -- The resulting string is 'C', the camli-signed JSON document. 122 123 (The output file is in doc/example/signing-after.camli) 124 125 In review: 126 127 O == the object to be signed 128 J == any valid JSON serialization of O 129 T == J, with 0+ trailing whitespace removed, and then 1 '}' character 130 removed 131 S == ascii-armored detached signature of T 132 C == CONCAT(T, ',"camliSig":"', S, '"}', '\n') 133 134 (strictly, the trailing newline and the exact JSON serialziation of 135 the camlisig element doesn't matter, but it'd be advised to follow 136 this recommendation for compatibility with other verification code) 137 138 VERIFYING 139 ========= 140 141 -- start with a byte array representing the JSON to be verified. 142 call this 'BA' ("bytes all") 143 144 -- given the byte array, find the last index in 'BA' of the 13 byte 145 substring: 146 ,"camliSig":" 147 148 Let's call the bytes before that 'BP' ("bytes payload") and the bytes 149 starting at that substring 'BS' ("bytes signature") 150 151 -- define 'BPJ' ("bytes payload JSON") as 'BP' + the single byte '}'. 152 153 -- parse 'BPJ', verifying that it's valid JSON object (dictionary). 154 verify that the object has a 'camliSigner' key with a string key 155 that's a valid blobref (e.g. "sha1-xxxxxxx") note the camliSigner. 156 157 -- replace the first byte of 'BS' (the ',') with an open brace ('{') 158 and parse it as JSON. verify that it's a valid JSON object with 159 exactly one key: "camliSig" 160 161 -- using 'camliSigner', a camli blobref, find the blob (cached, via 162 camli/web lookup, etc) that represents a GPG public key. 163 164 -- use GnuPG or equivalent libraries to verify that the ASCII-armored 165 GPG signature in "camliSig" signs the bytes in 'BP' using the 166 GPG public key found via the 'camliSigner' blobref