{"action":"created","comment":{"url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/comments/38897852","id":38897852,"diff_hunk":"@@ -0,0 +1,348 @@\n+# FxSyncWebCrypto\n+Uses WebCrypto to decrypt data from [Firefox Sync's Global Storage format](http://docs.services.mozilla.com/sync/storageformat5.html).\n+\n+## Usage\n+The code in this folder relies on three things:\n+\n+* [WebCrypto](http://www.w3.org/TR/WebCryptoAPI/)\n+* A client for Firefox Accounts (for instance gaia's [FxAccountsClients](\n+    https://github.com/mozilla-b2g/gaia/blob/master/apps/system/js\n+    /fx_accounts_manager.js))\n+* A client for Firefox Sync (for instance [kinto.js](\n+    https://github.com/Kinto/kinto.js) + [syncto](\n+    https://github.com/mozilla-services/syncto), [straight XHR](\n+    http://mxr.mozilla.org/mozilla-central/source/services/sync/tests/unit\n+    /test_httpd_sync_server.js), or [a python-based one](\n+    https://github.com/mozilla-services/syncclient))\n+\n+The steps are as follows:\n+\n+### Get the kB key from FxA\n+\n+Use the [onepw protocol](\n+https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol) to retrieve a\n+pair of encryption keys (`kA` and `kB`) from the FxA service. Your\n+FxAccountsClient is hopefully able to do this for you. Discard `kA` and hold on\n+to `kB` (it should be a hex string of 64 characters).\n+\n+### Check the storage format\n+\n+This library currently only supports [storage format 5](\n+http://docs.services.mozilla.com/sync/storageformat5.html), so first retrieve\n+the [metaglobal record](\n+http://docs.services.mozilla.com/sync/storageformat5.html#metaglobal-record) to\n+make sure that the rest of the data on the FxSync account is in the right\n+format. Example using mozilla-services's syncclient:\n+\n+```bash\n+$ python sync/main.py email@example.com $PASSWORD get_record meta global\n+{u'id': u'global',\n+ u'modified': 1437655930.34,\n+ u'payload': u'{\"syncID\":\"35sY_luKUnYO\",\"storageVersion\":5,\"declined\":[ \\\n+     \"prefs\",\"bookmarks\",\"addons\"],\"engines\":{\"clients\":{\"version\":1,\"syncID\": \\\n+     \"VWMk-0KZ8aKh\"},\"tabs\":{\"version\":1,\"syncID\":\"eGExUapwMq0O\"},\"forms\":{ \\\n+     \"version\":1,\"syncID\":\"Tgd0wt_q7nQO\"},\"history\":{\"version\":1,\"syncID\": \\\n+     \"vAIUDLBox_g4\"},\"passwords\":{\"version\":1,\"syncID\":\"vNno7ecPn7P2\"}}}'}\n+````\n+\n+In this example we're good, because the value of `storageVersion` there is 5.\n+\n+### Retrieve the CryptoKeys object.\n+\n+Similar to how you just retrieved `meta/global`, retrieve `crypto/keys`:\n+\n+```bash\n+$ python sync/main.py email@example.com $PASSWORD get_record crypto keys\n+{u'id': u'keys',\n+ u'modified': 1439218393.69,\n+ u'payload': u'{\"ciphertext\":\"PP5yNUYwJJoLcsL5o85i6RZfvanYDrwtChDD/LdKTZ8JOLub \\\n+     Z9DyRv3HMetSkbhL3HLvVm/FJ1Z4F2Z6IKQCxAc5dNnLsBIUUxhOHLbT0x9/jfnqZ8fLtlbko \\\n+     gI3ZlNvbc8iUF1aX+boe0Pv43vM0VvzxrnJDYzZ2a6jm9nbzUn0ldV9sv6vuvGHE6dANnRkZ3 \\\n+     wA/q0q8UvjdwpzXBixAw==\",\"IV\":\"FmosM+XBNy81/9oEAgI4Uw==\",\"hmac\":\"01a816e45 \\\n+     77c6cf3f97b66b4382d0a3e7e9178c75a3d38ed9ac8ad6397c2ecce\"}'}\n+````\n+\n+Note that the payload is a JSON-encoded object, in which ciphertext and IV are\n+base64 strings, and hmac is a hex string.\n+\n+### Construct the FxSyncWebCrypto object\n+\n+Using `kB` and `cryptoKeys`, you can call the FxSyncWebCrypto constructor:\n+\n+````js\n+var kB = '85c4f8c1d8e3e2186824c127af786891dd03c6e05b1b45f28f7181211bf2affb';\n+var cryptoKeys = {\n+  ciphertext: 'PP5yNUYwJJoLcsL5o85i6RZfvanYDrwtChDD/LdKTZ8JOLubZ9DyRv3HMetSkb' +\n+      'hL3HLvVm/FJ1Z4F2Z6IKQCxAc5dNnLsBIUUxhOHLbT0x9/jfnqZ8fLtlbkogI3ZlNvbc8i' +\n+      'UF1aX+boe0Pv43vM0VvzxrnJDYzZ2a6jm9nbzUn0ldV9sv6vuvGHE6dANnRkZ3wA/q0q8U' +\n+      'vjdwpzXBixAw==',\n+  IV: 'FmosM+XBNy81/9oEAgI4Uw==',\n+  hmac: '01a816e4577c6cf3f97b66b4382d0a3e7e9178c75a3d38ed9ac8ad6397c2ecce'\n+};\n+var historyEntry = {\n+  payload: {\n+    ciphertext: 'o/VpkqMj1tlT8t2youwsS2FgvQeonoHxqjGsRTu1+4swfyBq/QsnKfgOOMmD' +\n+        'IXZiPC3hOCNUlf/NtQiEe55hzJZEKLBshaLfXotai6KrprwrmykfiXnwn73n+nYNs8BX' +\n+        'L5awDHoaJToyFgF4PYokl7mwN7YC2xFiPgwO7Z2u/8r5RfnPV9MoafqvlvUkW+Tqs+QH' +\n+        'eHS/iuSA0P2h/j5ynt9v4xDWLVfEMce0KOKHQ5Qj7BmEPAieWP1trkkDmTdVi2euWrs+' +\n+        'fuG4C6PgY4A2j2DbNLVIloqpDVkqM2fgh0YOM9L2NC/uiKEb1Ynr2Fos',\n+    IV: 'kXL3hb11ltD+Jl0YFk+PlQ==',\n+    hmac: 'cb727efe7a3f0307921cecbd1a97c03f06a4d75c42026089494d84fcf92dbff9'\n+  },\n+  collectionName: 'history'\n+};\n+\n+var fswc = new FxSyncWebCrypto();\n+fswc.setKeys(kB, cryptoKeys).then(function() {\n+  return fswc.decrypt(historyEntry.payload, historyEntry.collectionName);\n+}).then(function(recordObj) {\n+  console.log('Decrypted history entry', recordObj);\n+  // Should print this to the console:\n+  // Decrypted history entry Object { id: \"_9sCUbahs0ay\",\n+  //      histUri: \"https://developer.mozilla.org/en-US…\", title:\n+  //      \"Object.prototype.__proto__ - JavaSc…\", visits: Array[1] }\n+\n+  return fswc.encrypt({foo: 'bar'}, 'my collection');\n+}).then(function(fooBarEncrypted) {\n+  console.log('Encrypted', {foo: 'bar'}, fooBarEncrypted);\n+  // Should print this to the console:\n+  // Encrypted Object { foo: \"bar\" } Object { hmac:\n+  //     \"fc38ab05c809928fd602427c034db1acb81…\", ciphertext:\n+  //     \"QVqNZPUFyYxxGpfRaD5Tqg==\", IV: \"o82jk3gzy7ELxck9uTCLmg==\" }\n+  return fswc.decrypt(fooBarEncrypted, 'my collection');\n+}).then(function(decryptedBack) {\n+  console.log('Decrypted back:', decryptedBack);\n+  // Should print this to the console:\n+  // Decrypted back: { foo: \"bar\" }\n+}).then(function() {\n+  console.log('Done');\n+}, function(err) {\n+  console.log('error', err);\n+});\n+````\n+\n+Note how you always have to specify the collection name (e.g. 'history' or\n+'passwords'), so that FxSyncWebCrypto can make sure it uses the right collection\n+key bundle. This is because the CryptoKeys object potentially contains a\n+different key bundle for each collection.\n+\n+## Functions provided\n+### constructor FxSyncWebCrypto\n+Arguments: none\n+\n+### setKeys(kB, cryptoKeys)\n+This function is where all the exciting stuff happens. First, kB is stretched\n+using 64-bit HKDF over the string 'identity.mozilla.com/picl/v1/oldsync'. The\n+result is split in two, where the first half becomes the AES key for decrypting\n+cryptoKeysCiphertext (with initialization vector cryptoKeysIV), and the second\n+half becomes the HMAC key for verifying the cryptoKeysHmac signature.\n+\n+Arguments:\n+* kB - A 64-byte hex string representing the 1024-bit `kB` key described in [onepw](https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol)\n+* cryptoKeys - an object, containing:\n+  * ciphertext - A Base64 string representing the ciphertext of the [CryptoKeys\n+      record](http://docs.services.mozilla.com/sync/storageformat5.html\n+      #crypto-keys-record) for the FxSync account.\n+  * IV - A Base64 string representing the initialization vector for the\n+      [CryptoKeys record](http://docs.services.mozilla.com/sync/\n+      storageformat5.html#crypto-keys-record) for the FxSync account.\n+  * hmac - a 64-byte hex string representing the 1024-bit hmac signature for the\n+      [CryptoKeys record](http://docs.services.mozilla.com/sync/\n+      storageformat5.html#crypto-keys-record) for the FxSync account.\n+Returns a promise that resolves when initialization was successful, and rejects\n+if the CryptoKeys could not be decrypted with the stretched kB, or if WebCrypto\n+is not available.\n+\n+### encrypt\n+Arguments:\n+* record: The object to JSON-stringify, sign, and encrypt\n+* collectionName: The name of the FxSync collection for which to encrypt (see http://docs.services.mozilla.com/sync/storageformat5.html#encryption).\n+Returns:\n+A promise for an object with ciphertext, IV, and hmac, which can be\n+JSON-stringified to get the payload to be uploaded to the FxSync server.\n+\n+### decrypt\n+This function checks the payload.hmac signature, and if that matches, uses\n+AES-CBC to decrypt payload.ciphertext, given payload.IV.\n+\n+Arguments:\n+* payload: An object with fields ciphertext, IV, and hmac, presumably the\n+JSON-parsed payload of a download from the FxSync server.\n+* collectionName: The name of the FxSync collection for which to decrypt (see http://docs.services.mozilla.com/sync/storageformat5.html#decryption).\n+Returns:\n+A promise for an object (the record again that was originally JSON-stringified\n+and encrypted on this or on another FxSync client).\n+\n+## How it works\n+### Where kB and cryptoKeys come from\n+In previous versions of FxSync, the 'sync key' was generated on the client, and\n+could only leave the client where it was generated as a slightly modified Base32\n+string. This library does not support importing such strings.\n+\n+In the current version of FxSync, the sync key is stored on the FxAccounts\n+server as 'kB'. You can retrieve '(kA, kB)' from there using the onepw protocol.\n+The rest of this doc assumes you already have kB as a Base64 string.\n+\n+You can get the cryptoKeys by retrieving the crypto/keys record from the FxSync\n+account for which you are passing kB, and JSON-parsing the result. For instance\n+using [syncclient](https://github.com/mozilla-services/syncclient) like this:\n+\n+```bash\n+$ python sync/main.py email@example.com $PASSWORD get_record crypto keys\n+{u'id': u'keys',\n+ u'modified': 1439218393.69,\n+ u'payload': u'{\"ciphertext\":\"PP5yNUYwJJoLcsL5o85i6RZfvanYDrwtChDD/LdKTZ8JOLub \\\n+     Z9DyRv3HMetSkbhL3HLvVm/FJ1Z4F2Z6IKQCxAc5dNnLsBIUUxhOHLbT0x9/jfnqZ8fLtlbko \\\n+     gI3ZlNvbc8iUF1aX+boe0Pv43vM0VvzxrnJDYzZ2a6jm9nbzUn0ldV9sv6vuvGHE6dANnRkZ3 \\\n+     wA/q0q8UvjdwpzXBixAw==\",\"IV\":\"FmosM+XBNy81/9oEAgI4Uw==\",\"hmac\":\"01a816e45 \\\n+     77c6cf3f97b66b4382d0a3e7e9178c75a3d38ed9ac8ad6397c2ecce\"}'}\n+````\n+\n+### Constructor and setKeys\n+\n+The constructor does basically nothing. The first interesting crypto stuff comes\n+when you call setKeys:\n+\n+````js\n+var fswc = new FxSyncWebCrypto();\n+fswc.setKeys(kB, cryptoKeys);\n+````\n+\n+The things setKeys does are:\n+* Convert kB from Base64 to a raw ArrayBuffer.\n+* Take the HMAC digest of an all-zeroes string using kB as a SHA256 key.\n+* Import the result as a HMAC-SHA256 key.\n+* Two rounds of HKDF on an all-zeroes string, with info\n+      'identity.mozilla.com/picl/v1/oldsync'.\n+* Split the output in two 256-bit strings.\n+* Use the first one as the AES key and the second one as the HMAC key of the\n+       Sync Key Bundle, and store this as this.mainSyncKey.\n+* Take the ciphertext from there, and construct an ArrayBuffer with the ASCII\n+       characters of that Base64 string.\n+* Use the HMAC key of the Sync Key Bundle to calculate its SHA256 HMAC\n+       signature.\n+* Convert the result to a hex string.\n+* Check that it matches the HMAC signature of the Sync Key Bundle. Stop if it\n+       doesn't.\n+* Convert the initialization vector (IV in the cryptoKeys) from Base64 to a raw\n+       ArrayBuffer.\n+* Use the AES key of the Sync Key Bundle and the IV to decrypt the ciphertext\n+       of the cryptoKeys with AES-CBC (256 bits).\n+* On success, JSON-parse the cleartext and store it as this.bulkKeyBundle.\n+* Convert the default keys from Base64 to raw ArrayBuffer, import them as\n+       CryptoKey object, and store them on this.bulkKeyBundle.defaultAsKeyBundle\n+       (first one is the AES with purpose encrypt/decrypt, second one is the\n+       HMAC key with purpose sign/verify).\n+\n+The bulkKeyBundle look something like this:\n+\n+````js\n+{\"id\":\"keys\",\n+ \"collection\":\"crypto\",\n+ \"collections\":{},\n+ \"default:['dGhlc2UtYXJlLWV4YWN0bHktMzItY2hhcmFjdGVycy4=',\n+           'eWV0LWFub3RoZXItc2V0LW9mLTMyLWNoYXJhY3RlcnM=']}\n+\n+````\n+\n+### decrypt\n+\n+The decrypt function does the following:\n+\n+* JSON-parse the payload into an object with ciphertext (base64), IV (base64),\n+      and hmac (hex).\n+* Use this.bulkKeyBundle.defaultAsKeyBundle.HMAC to verify the hmac signature of\n+      the ciphertext's Base64 characters.\n+* Use this.bulkKeyBundle.defaultAsKeyBundle.AES and IV to decrypt the\n+      ciphertext.\n+* JSON-parse the result and return it.\n+\n+### encrypt\n+\n+The encrypt function does the following:\n+\n+* Generate a random initialization vector (IV).\n+* JSON-stringify the record to form the cleartext.\n+* Use this.bulkKeyBundle.defaultAsKeyBundle.AES and IV to encrypt the cleartext\n+      and obtain the ciphertext.\n+* Use this.bulkKeyBundle.defaultAsKeyBundle.HMAC to sign the ciphertext's Base64\n+      characters and obtain hmac.\n+* JSON-stringify an object with ciphertext (Base64), IV (Base64), and hmac (hex)\n+      to obtain the payload.\n+# Random collection of links:\n+\n+## Links from etherpad","path":"apps/sync/js/crypto/README.md","position":274,"original_position":274,"commit_id":"e3571b4cc7fa8e03f8d189f1fdd0a6ca7bf1261b","original_commit_id":"e3571b4cc7fa8e03f8d189f1fdd0a6ca7bf1261b","user":{"login":"ferjm","id":480202,"avatar_url":"https://avatars.githubusercontent.com/u/480202?v=3","gravatar_id":"","url":"https://api.github.com/users/ferjm","html_url":"https://github.com/ferjm","followers_url":"https://api.github.com/users/ferjm/followers","following_url":"https://api.github.com/users/ferjm/following{/other_user}","gists_url":"https://api.github.com/users/ferjm/gists{/gist_id}","starred_url":"https://api.github.com/users/ferjm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ferjm/subscriptions","organizations_url":"https://api.github.com/users/ferjm/orgs","repos_url":"https://api.github.com/users/ferjm/repos","events_url":"https://api.github.com/users/ferjm/events{/privacy}","received_events_url":"https://api.github.com/users/ferjm/received_events","type":"User","site_admin":false},"body":"Maybe this section can be cleaned up a little bit (i.e. some of these are not that useful links or there's no context for the reader to understand what this \"etherpad\" you mention is :P)","created_at":"2015-09-08T07:49:37Z","updated_at":"2015-09-08T07:49:37Z","html_url":"https://github.com/mozilla-b2g/gaia/pull/31567#discussion_r38897852","pull_request_url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567","_links":{"self":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/comments/38897852"},"html":{"href":"https://github.com/mozilla-b2g/gaia/pull/31567#discussion_r38897852"},"pull_request":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567"}}},"pull_request":{"url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567","id":43562575,"html_url":"https://github.com/mozilla-b2g/gaia/pull/31567","diff_url":"https://github.com/mozilla-b2g/gaia/pull/31567.diff","patch_url":"https://github.com/mozilla-b2g/gaia/pull/31567.patch","issue_url":"https://api.github.com/repos/mozilla-b2g/gaia/issues/31567","number":31567,"state":"open","locked":false,"title":"Bug 1191776 - FxSyncWebCrypto component of Sync app r=ferjm","user":{"login":"michielbdejong","id":408412,"avatar_url":"https://avatars.githubusercontent.com/u/408412?v=3","gravatar_id":"","url":"https://api.github.com/users/michielbdejong","html_url":"https://github.com/michielbdejong","followers_url":"https://api.github.com/users/michielbdejong/followers","following_url":"https://api.github.com/users/michielbdejong/following{/other_user}","gists_url":"https://api.github.com/users/michielbdejong/gists{/gist_id}","starred_url":"https://api.github.com/users/michielbdejong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michielbdejong/subscriptions","organizations_url":"https://api.github.com/users/michielbdejong/orgs","repos_url":"https://api.github.com/users/michielbdejong/repos","events_url":"https://api.github.com/users/michielbdejong/events{/privacy}","received_events_url":"https://api.github.com/users/michielbdejong/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-08-27T20:25:50Z","updated_at":"2015-09-08T07:49:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3457603b5acb6748d24a0681764bd085fa96f1bf","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567/commits","review_comments_url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567/comments","review_comment_url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mozilla-b2g/gaia/issues/31567/comments","statuses_url":"https://api.github.com/repos/mozilla-b2g/gaia/statuses/e3571b4cc7fa8e03f8d189f1fdd0a6ca7bf1261b","head":{"label":"michielbdejong:1191776-fxsyncwebcrypto","ref":"1191776-fxsyncwebcrypto","sha":"e3571b4cc7fa8e03f8d189f1fdd0a6ca7bf1261b","user":{"login":"michielbdejong","id":408412,"avatar_url":"https://avatars.githubusercontent.com/u/408412?v=3","gravatar_id":"","url":"https://api.github.com/users/michielbdejong","html_url":"https://github.com/michielbdejong","followers_url":"https://api.github.com/users/michielbdejong/followers","following_url":"https://api.github.com/users/michielbdejong/following{/other_user}","gists_url":"https://api.github.com/users/michielbdejong/gists{/gist_id}","starred_url":"https://api.github.com/users/michielbdejong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michielbdejong/subscriptions","organizations_url":"https://api.github.com/users/michielbdejong/orgs","repos_url":"https://api.github.com/users/michielbdejong/repos","events_url":"https://api.github.com/users/michielbdejong/events{/privacy}","received_events_url":"https://api.github.com/users/michielbdejong/received_events","type":"User","site_admin":false},"repo":{"id":32516991,"name":"gaia","full_name":"michielbdejong/gaia","owner":{"login":"michielbdejong","id":408412,"avatar_url":"https://avatars.githubusercontent.com/u/408412?v=3","gravatar_id":"","url":"https://api.github.com/users/michielbdejong","html_url":"https://github.com/michielbdejong","followers_url":"https://api.github.com/users/michielbdejong/followers","following_url":"https://api.github.com/users/michielbdejong/following{/other_user}","gists_url":"https://api.github.com/users/michielbdejong/gists{/gist_id}","starred_url":"https://api.github.com/users/michielbdejong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michielbdejong/subscriptions","organizations_url":"https://api.github.com/users/michielbdejong/orgs","repos_url":"https://api.github.com/users/michielbdejong/repos","events_url":"https://api.github.com/users/michielbdejong/events{/privacy}","received_events_url":"https://api.github.com/users/michielbdejong/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/michielbdejong/gaia","description":"Gaia is a HTML5-based Phone UI for the Boot 2 Gecko Project. NOTE: For details of what branches are used for what releases, see","fork":true,"url":"https://api.github.com/repos/michielbdejong/gaia","forks_url":"https://api.github.com/repos/michielbdejong/gaia/forks","keys_url":"https://api.github.com/repos/michielbdejong/gaia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/michielbdejong/gaia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/michielbdejong/gaia/teams","hooks_url":"https://api.github.com/repos/michielbdejong/gaia/hooks","issue_events_url":"https://api.github.com/repos/michielbdejong/gaia/issues/events{/number}","events_url":"https://api.github.com/repos/michielbdejong/gaia/events","assignees_url":"https://api.github.com/repos/michielbdejong/gaia/assignees{/user}","branches_url":"https://api.github.com/repos/michielbdejong/gaia/branches{/branch}","tags_url":"https://api.github.com/repos/michielbdejong/gaia/tags","blobs_url":"https://api.github.com/repos/michielbdejong/gaia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/michielbdejong/gaia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/michielbdejong/gaia/git/refs{/sha}","trees_url":"https://api.github.com/repos/michielbdejong/gaia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/michielbdejong/gaia/statuses/{sha}","languages_url":"https://api.github.com/repos/michielbdejong/gaia/languages","stargazers_url":"https://api.github.com/repos/michielbdejong/gaia/stargazers","contributors_url":"https://api.github.com/repos/michielbdejong/gaia/contributors","subscribers_url":"https://api.github.com/repos/michielbdejong/gaia/subscribers","subscription_url":"https://api.github.com/repos/michielbdejong/gaia/subscription","commits_url":"https://api.github.com/repos/michielbdejong/gaia/commits{/sha}","git_commits_url":"https://api.github.com/repos/michielbdejong/gaia/git/commits{/sha}","comments_url":"https://api.github.com/repos/michielbdejong/gaia/comments{/number}","issue_comment_url":"https://api.github.com/repos/michielbdejong/gaia/issues/comments{/number}","contents_url":"https://api.github.com/repos/michielbdejong/gaia/contents/{+path}","compare_url":"https://api.github.com/repos/michielbdejong/gaia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/michielbdejong/gaia/merges","archive_url":"https://api.github.com/repos/michielbdejong/gaia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/michielbdejong/gaia/downloads","issues_url":"https://api.github.com/repos/michielbdejong/gaia/issues{/number}","pulls_url":"https://api.github.com/repos/michielbdejong/gaia/pulls{/number}","milestones_url":"https://api.github.com/repos/michielbdejong/gaia/milestones{/number}","notifications_url":"https://api.github.com/repos/michielbdejong/gaia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/michielbdejong/gaia/labels{/name}","releases_url":"https://api.github.com/repos/michielbdejong/gaia/releases{/id}","created_at":"2015-03-19T11:15:53Z","updated_at":"2015-03-19T11:16:20Z","pushed_at":"2015-09-07T19:01:30Z","git_url":"git://github.com/michielbdejong/gaia.git","ssh_url":"git@github.com:michielbdejong/gaia.git","clone_url":"https://github.com/michielbdejong/gaia.git","svn_url":"https://github.com/michielbdejong/gaia","homepage":"https://wiki.mozilla.org/B2G","size":950299,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"mozilla-b2g:master","ref":"master","sha":"109cbbd3a6b480e06e184df72a5e4452c8e69bab","user":{"login":"mozilla-b2g","id":1261292,"avatar_url":"https://avatars.githubusercontent.com/u/1261292?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-b2g","html_url":"https://github.com/mozilla-b2g","followers_url":"https://api.github.com/users/mozilla-b2g/followers","following_url":"https://api.github.com/users/mozilla-b2g/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-b2g/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-b2g/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-b2g/subscriptions","organizations_url":"https://api.github.com/users/mozilla-b2g/orgs","repos_url":"https://api.github.com/users/mozilla-b2g/repos","events_url":"https://api.github.com/users/mozilla-b2g/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-b2g/received_events","type":"Organization","site_admin":false},"repo":{"id":2317369,"name":"gaia","full_name":"mozilla-b2g/gaia","owner":{"login":"mozilla-b2g","id":1261292,"avatar_url":"https://avatars.githubusercontent.com/u/1261292?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-b2g","html_url":"https://github.com/mozilla-b2g","followers_url":"https://api.github.com/users/mozilla-b2g/followers","following_url":"https://api.github.com/users/mozilla-b2g/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-b2g/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-b2g/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-b2g/subscriptions","organizations_url":"https://api.github.com/users/mozilla-b2g/orgs","repos_url":"https://api.github.com/users/mozilla-b2g/repos","events_url":"https://api.github.com/users/mozilla-b2g/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-b2g/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mozilla-b2g/gaia","description":"Gaia is a HTML5-based Phone UI for the Boot 2 Gecko Project. NOTE: For details of what branches are used for what releases, see","fork":false,"url":"https://api.github.com/repos/mozilla-b2g/gaia","forks_url":"https://api.github.com/repos/mozilla-b2g/gaia/forks","keys_url":"https://api.github.com/repos/mozilla-b2g/gaia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mozilla-b2g/gaia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mozilla-b2g/gaia/teams","hooks_url":"https://api.github.com/repos/mozilla-b2g/gaia/hooks","issue_events_url":"https://api.github.com/repos/mozilla-b2g/gaia/issues/events{/number}","events_url":"https://api.github.com/repos/mozilla-b2g/gaia/events","assignees_url":"https://api.github.com/repos/mozilla-b2g/gaia/assignees{/user}","branches_url":"https://api.github.com/repos/mozilla-b2g/gaia/branches{/branch}","tags_url":"https://api.github.com/repos/mozilla-b2g/gaia/tags","blobs_url":"https://api.github.com/repos/mozilla-b2g/gaia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mozilla-b2g/gaia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mozilla-b2g/gaia/git/refs{/sha}","trees_url":"https://api.github.com/repos/mozilla-b2g/gaia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mozilla-b2g/gaia/statuses/{sha}","languages_url":"https://api.github.com/repos/mozilla-b2g/gaia/languages","stargazers_url":"https://api.github.com/repos/mozilla-b2g/gaia/stargazers","contributors_url":"https://api.github.com/repos/mozilla-b2g/gaia/contributors","subscribers_url":"https://api.github.com/repos/mozilla-b2g/gaia/subscribers","subscription_url":"https://api.github.com/repos/mozilla-b2g/gaia/subscription","commits_url":"https://api.github.com/repos/mozilla-b2g/gaia/commits{/sha}","git_commits_url":"https://api.github.com/repos/mozilla-b2g/gaia/git/commits{/sha}","comments_url":"https://api.github.com/repos/mozilla-b2g/gaia/comments{/number}","issue_comment_url":"https://api.github.com/repos/mozilla-b2g/gaia/issues/comments{/number}","contents_url":"https://api.github.com/repos/mozilla-b2g/gaia/contents/{+path}","compare_url":"https://api.github.com/repos/mozilla-b2g/gaia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mozilla-b2g/gaia/merges","archive_url":"https://api.github.com/repos/mozilla-b2g/gaia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mozilla-b2g/gaia/downloads","issues_url":"https://api.github.com/repos/mozilla-b2g/gaia/issues{/number}","pulls_url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls{/number}","milestones_url":"https://api.github.com/repos/mozilla-b2g/gaia/milestones{/number}","notifications_url":"https://api.github.com/repos/mozilla-b2g/gaia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mozilla-b2g/gaia/labels{/name}","releases_url":"https://api.github.com/repos/mozilla-b2g/gaia/releases{/id}","created_at":"2011-09-03T01:38:46Z","updated_at":"2015-09-08T07:38:17Z","pushed_at":"2015-09-08T07:48:32Z","git_url":"git://github.com/mozilla-b2g/gaia.git","ssh_url":"git@github.com:mozilla-b2g/gaia.git","clone_url":"https://github.com/mozilla-b2g/gaia.git","svn_url":"https://github.com/mozilla-b2g/gaia","homepage":"https://wiki.mozilla.org/B2G","size":1622088,"stargazers_count":1976,"watchers_count":1976,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":2424,"mirror_url":null,"open_issues_count":1189,"forks":2424,"open_issues":1189,"watchers":1976,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567"},"html":{"href":"https://github.com/mozilla-b2g/gaia/pull/31567"},"issue":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/issues/31567"},"comments":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/issues/31567/comments"},"review_comments":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567/comments"},"review_comment":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/31567/commits"},"statuses":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/statuses/e3571b4cc7fa8e03f8d189f1fdd0a6ca7bf1261b"}}}}