plugin signature validation

This commit is contained in:
antelle 2017-06-06 20:40:27 +02:00
parent c86042b885
commit 31f5f2d88a
3 changed files with 57 additions and 40 deletions

View File

@ -46,9 +46,7 @@ const SettingsStore = {
return new Promise(resolve => {
if (Launcher) {
const settingsFile = Launcher.getUserDataPath(this.fileName(key));
if (typeof data !== 'string') {
data = JSON.stringify(data);
}
data = JSON.stringify(data);
Launcher.writeFile(settingsFile, data, err => {
if (err) {
logger.error(`Error saving ${key}`, err);

View File

@ -19,49 +19,58 @@ const PluginGallery = {
}
this.loading = true;
this.loadError = false;
const ts = this.logger.ts();
return new Promise(resolve => {
const ts = this.logger.ts();
this.logger.debug('Loading plugins...');
const xhr = new XMLHttpRequest();
xhr.open('GET', Links.Plugins + '/plugins.json?_=' + Date.now());
xhr.responseType = 'text';
xhr.responseType = 'json';
xhr.send();
xhr.addEventListener('load', () => {
const data = xhr.response;
const gallery = JSON.parse(data);
const dataToVerify = data.replace(gallery.signature, '');
SignatureVerifier.verify(
kdbxweb.ByteUtils.stringToBytes(dataToVerify),
gallery.signature
).then(isValid => {
if (!isValid) {
this.logger.error('JSON signature invalid');
this.galleryLoadError = true;
resolve();
return;
}
this.logger.debug(`Loaded ${gallery.plugins.length} plugins`, this.logger.ts(ts));
if (Launcher) {
this.saveGallery(data);
}
resolve(gallery);
}).catch(e => {
this.logger.error('Error verifying plugins signature', e);
resolve();
});
resolve(data);
});
xhr.addEventListener('error', () => {
this.logger.error('Network error loading plugins');
resolve();
});
}).then(gallery => {
this.loading = false;
this.loadError = !gallery;
if (gallery) {
this.gallery = gallery;
}).then(data => {
return this.verifySignature(data).then(gallery => {
this.loading = false;
this.loadError = !gallery;
if (gallery) {
this.logger.debug(`Loaded ${gallery.plugins.length} plugins`, this.logger.ts(ts));
this.gallery = gallery;
this.saveGallery(gallery);
}
Backbone.trigger('plugin-gallery-load-complete');
return gallery;
});
});
},
verifySignature(gallery) {
const dataToVerify = JSON.stringify(gallery, null, 2).replace(gallery.signature, '');
return SignatureVerifier.verify(
kdbxweb.ByteUtils.stringToBytes(dataToVerify),
gallery.signature
).then(isValid => {
if (isValid) {
return gallery;
}
Backbone.trigger('plugin-gallery-load-complete');
return gallery;
this.logger.error('JSON signature invalid');
}).catch(e => {
this.logger.error('Error verifying plugins signature', e);
});
},
getCachedGallery() {
const ts = this.logger.ts();
return SettingsStore.load('plugin-gallery').then(data => {
return this.verifySignature(data).then(gallery => {
this.logger.debug(`Loaded cached plugin gallery`, this.logger.ts(ts));
return gallery;
});
});
},

View File

@ -1,8 +1,10 @@
const Backbone = require('backbone');
const Plugin = require('./plugin');
const PluginCollection = require('./plugin-collection');
const PluginGallery = require('./plugin-gallery');
const SettingsStore = require('../comp/settings-store');
const RuntimeInfo = require('../comp/runtime-info');
const SignatureVerifier = require('../util/signature-verifier');
const Logger = require('../util/logger');
const PluginManager = Backbone.Model.extend({
@ -29,11 +31,13 @@ const PluginManager = Backbone.Model.extend({
if (!state || !state.plugins || !state.plugins.length) {
return;
}
const promises = state.plugins.map(plugin => this.loadPlugin(plugin));
return Promise.all(promises).then(loadedPlugins => {
const plugins = this.get('plugins');
plugins.add(loadedPlugins.filter(plugin => plugin));
this.logger.info(`Loaded ${plugins.length} plugins`, this.logger.ts(ts));
return PluginGallery.getCachedGallery().then(gallery => {
const promises = state.plugins.map(plugin => this.loadPlugin(plugin, gallery));
return Promise.all(promises).then(loadedPlugins => {
const plugins = this.get('plugins');
plugins.add(loadedPlugins.filter(plugin => plugin));
this.logger.info(`Loaded ${plugins.length} plugins`, this.logger.ts(ts));
});
});
});
},
@ -159,13 +163,19 @@ const PluginManager = Backbone.Model.extend({
return updateNext();
},
loadPlugin(desc) {
loadPlugin(desc, gallery) {
const plugin = new Plugin({
manifest: desc.manifest,
url: desc.url,
autoUpdate: desc.autoUpdate
});
return plugin.install(desc.enabled, true)
let enabled = desc.enabled;
if (enabled) {
const galleryPlugin = gallery ? gallery.plugins.find(pl => pl.manifest.name === desc.manifest.name) : null;
const expectedPublicKey = galleryPlugin ? galleryPlugin.manifest.publicKey : SignatureVerifier.getPublicKey();
enabled = desc.manifest.publicKey === expectedPublicKey;
}
return plugin.install(enabled, true)
.then(() => plugin)
.catch(() => plugin);
},