Skip to content

Instantly share code, notes, and snippets.

@almet
Created March 10, 2016 13:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save almet/f4fec4d259f320e52a64 to your computer and use it in GitHub Desktop.
Save almet/f4fec4d259f320e52a64 to your computer and use it in GitHub Desktop.
Kinto signature validation in Firefox chrome.
Cu.import("resource://services-common/canonical-json.js");
function mergeChanges(localRecords, changes) {
const records = {};
localRecords.data.forEach(function(record) {
records[record.id] = record;
});
// Apply remote changes in the "records" variable.
// All records that exists already are replaced by the version from the
// server.
changes.forEach(function(record) {
records[record.id] = record;
});
// Filter out protected properties of the records.
return Object.keys(records).map(function(recordID){
const newRecord = {};
Object.keys(records[recordID]).forEach(function(key) {
if (key.indexOf("_") !== 0) {
newRecord[key] = records[recordID][key];
}
});
return newRecord;
})
// Remove all deleted records.
.filter(function(record) {
return !(record.hasOwnProperty("deleted") &&
record.deleted === true);
});
}
function sortByID(a, b) {
return a.id - b.id;
if (a.id < b.id)
return -1;
if (a.id > b.id)
return 1;
return 0;
};
function signatureVerifierFactory(collection) {
return function validateCollectionSignature(payload, collection) {
console.log("validate collection signature called");
return collection.list()
.then(function(localRecords) {
// XXX payload.changes.changes?
var records = mergeChanges(localRecords, payload.changes.changes);
console.log("merged records", records);
return getCanonicalJSON(records, sortByID);
})
.then(function(canonicalJSON) {
console.log("CanonicalJSON", canonicalJSON);
const collectionURL = collection.api.endpoints().collection(
collection._bucket,
collection.name
);
return fetch(collectionURL).then(function(resp) {
return resp.json();
})
.then(function(json) {
console.log("json", json);
return json.data;
})
.then(function(data) {
// XXX Use the NSS exposed code to do the verification.
return verifySignature(data.publicKey, data.signature, canonicalJSON);
});
}).then(function(validated) {
console.log("verified", validated);
// In case the hash is valid, we don't want to alter the payload,
// so just return the same payload we've got in the input.
return payload;
});
}
}
function kintoStuff() {
Cu.import("resource://services-common/moz-kinto-client.js");
Cu.import("resource://gre/modules/Task.jsm");
let Kinto = loadKinto();
let FirefoxAdapter = Kinto.adapters.FirefoxAdapter;
let certList = Cc["@mozilla.org/security/certblocklist;1"]
.getService(Ci.nsICertBlocklist);
// Future blocklist clients can extract the sync-if-stale logic. For
// now, since this is currently the only client, we'll do this here.
let config = {
remote: "https://firefox.settings.services.mozilla.com/v1",
bucket: "blocklists",
adapter: FirefoxAdapter,
};
let db = new Kinto(config);
let collectionName = "certificates";
let blocklist = db.collection(collectionName, {
"hooks": {
"incoming-changes": [signatureVerifierFactory(collectionName)]
}
});
return Task.spawn(function* () {
try {
yield blocklist.db.open();
// do your kinto operations here
yield blocklist.sync();
} finally {
}
});
}
kintoStuff().then(() => {
console.log("done!")
dump("all done!");
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment