Skip to content

Instantly share code, notes, and snippets.

@leplatrem leplatrem/1263602.patch
Last active Apr 11, 2016

Embed
What would you like to do?
Bug 1263602 - Untested code to verify signatures
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
You can’t perform that action at this time.