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