Last active
April 11, 2016 15:09
-
-
Save leplatrem/c2877cee0bb6c3f5e5612b165eb5ff30 to your computer and use it in GitHub Desktop.
Bug 1263602 - Untested code to verify signatures
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/services/common/KintoCertificateBlocklist.js b/services/common/KintoCertificateBlocklist.js | |
--- a/services/common/KintoCertificateBlocklist.js | |
+++ b/services/common/KintoCertificateBlocklist.js | |
@@ -13,16 +13,20 @@ Cu.import("resource://services-common/mo | |
Cu.import("resource://gre/modules/Services.jsm"); | |
Cu.import("resource://gre/modules/Task.jsm"); | |
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); | |
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", | |
"@mozilla.org/uuid-generator;1", | |
"nsIUUIDGenerator"); | |
+ | |
+const { CanonicalJSON } = Cu.import("resource://services-common/canonical-json.js"); | |
+ | |
+ | |
const PREF_KINTO_BASE = "services.kinto.base"; | |
const PREF_KINTO_BUCKET = "services.kinto.bucket"; | |
const PREF_KINTO_ONECRL_COLLECTION = "services.kinto.onecrl.collection"; | |
const PREF_KINTO_ONECRL_CHECKED_SECONDS = "services.kinto.onecrl.checked"; | |
const RE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; | |
// Kinto.js assumes version 4 UUIDs but allows you to specify custom | |
@@ -34,16 +38,75 @@ function makeIDSchema() { | |
return { | |
validate: RE_UUID.test.bind(RE_UUID), | |
generate: function() { | |
return uuidgen.generateUUID().toString(); | |
} | |
}; | |
} | |
+ | |
+function mergeChanges(localRecords, changes) { | |
+ const records = {}; | |
+ // Kinto.js adds attributes to local records that aren't present on server. | |
+ // (e.g. _status) | |
+ const stripPrivateProps = (obj) => { | |
+ return Object.keys(obj).reduce((current, key) => { | |
+ if (key.indexOf("_") !== 0) { | |
+ current[key] = obj[key]; | |
+ } | |
+ }, {}); | |
+ }; | |
+ // Local records by id. | |
+ localRecords.forEach((record) => records[record.id] = stripPrivateProps(record)); | |
+ // All existing records are replaced by the version from the server. | |
+ changes.forEach((record) => records[record.id] = record); | |
+ return records.values() | |
+ // Filter out deleted records. | |
+ .filter((record) => record.deleted === true) | |
+ // Sort list by record id. | |
+ .sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0); | |
+} | |
+ | |
+ | |
+function fetchCollectionMetadata(collection) { | |
+ // XXX: use kinto-http-client instead. | |
+ const collectionURL = collection.api.endpoints().collection( | |
+ collection.bucket, | |
+ collection.name | |
+ ); | |
+ return fetch(collectionURL) | |
+ .then(function(resp) { | |
+ // XXX: check status ... | |
+ return resp.json(); | |
+ }) | |
+ .then(function(json) { | |
+ // XXX: if json.data.signature publicKey... | |
+ return json.data; | |
+ }); | |
+} | |
+ | |
+ | |
+function validateCollectionSignature(payload, collection) { | |
+ return Task.spawn(function* () { | |
+ const {signature, publicKey} = yield fetchCollectionMetadata(collection); | |
+ const localRecords = (yield collection.list()).data; | |
+ const merged = mergeChanges(localRecords, payload.changes); | |
+ const serialized = CanonicalJSON.stringify(merged); | |
+ | |
+ // XXX Use the NSS exposed code to do the verification. | |
+ if (verifySignature(publicKey, signature, serialized)) { | |
+ // In case the hash is valid, apply the changes locally. | |
+ return payload; | |
+ } | |
+ throw new Error("Invalid content/signature"); | |
+ }); | |
+} | |
+ | |
+ | |
// A Kinto based client to keep the OneCRL certificate blocklist up to date. | |
function CertBlocklistClient() { | |
// maybe sync the collection of certificates with remote data. | |
// lastModified - the lastModified date (on the server, milliseconds since | |
// epoch) of data in the remote collection | |
// serverTime - the time on the server (milliseconds since epoch) | |
// returns a promise which rejects on sync failure | |
this.maybeSync = function(lastModified, serverTime) { | |
@@ -64,18 +127,21 @@ function CertBlocklistClient() { | |
remote: base, | |
bucket: bucket, | |
adapter: FirefoxAdapter, | |
}; | |
let db = new Kinto(config); | |
let collectionName = Services.prefs.getCharPref(PREF_KINTO_ONECRL_COLLECTION, | |
"certificates"); | |
- let blocklist = db.collection(collectionName, | |
- { idSchema: makeIDSchema() }); | |
+ let blocklist = db.collection(collectionName, { | |
+ idSchema: makeIDSchema(), | |
+ hooks: { | |
+ "incoming-changes": [validateCollectionSignature] | |
+ }}); | |
let updateLastCheck = function() { | |
let checkedServerTimeInSeconds = Math.round(serverTime / 1000); | |
Services.prefs.setIntPref(PREF_KINTO_ONECRL_CHECKED_SECONDS, | |
checkedServerTimeInSeconds); | |
} | |
return Task.spawn(function* () { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment