read private keys from external device

This commit is contained in:
antelle 2017-06-11 00:32:51 +02:00
parent 94df9fbe30
commit 5460848ea9
5 changed files with 207 additions and 166 deletions

View File

@ -7,9 +7,9 @@
"doc": "docs"
},
"scripts": {
"start": "npm run-script translations && npm run-script update",
"update": "node scripts/update-plugins.js",
"translations": "node scripts/download-translations.js"
"start": "node --harmony-async-await scripts/start.js",
"update": "node --harmony-async-await scripts/update-plugins.js",
"translations": "node --harmony-async-await scripts/download-translations.js"
},
"repository": {
"type": "git",
@ -20,5 +20,9 @@
"bugs": {
"url": "https://github.com/keeweb/keeweb-plugins/issues"
},
"homepage": "https://github.com/keeweb/keeweb-plugins#readme"
"homepage": "https://github.com/keeweb/keeweb-plugins#readme",
"devDependencies": {
"keychain": "^1.3.0",
"pkcs15-smartcard-sign": "^1.0.0"
}
}

View File

@ -3,6 +3,7 @@
const https = require('https');
const crypto = require('crypto');
const fs = require('fs');
const sign = require('./sign');
const keys = require('../keys/onesky.json');
@ -26,19 +27,21 @@ const urlParams = {
const pluginManifest = fs.readFileSync('tmpl/manifest.json', 'utf8');
const pluginIndexPage = fs.readFileSync('tmpl/language-index.html', 'utf8');
const privateKey = fs.readFileSync('keys/private-key.pem', 'binary');
const publicKey = fs.readFileSync('keys/public-key.pem', 'utf8')
.match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1]
.replace(/\n/g, '');
const defaultCountries = { 'SE': 'sv' };
module.exports = function() {
return new Promise(resolve => {
loadLanguages(languages =>
loadTranslations(translations =>
processData(languages, translations)
resolve(processData(languages, translations))
)
);
});
function loadLanguages(callback) {
async function loadLanguages(callback) {
if (USE_FILES) {
return callback(JSON.parse(fs.readFileSync('./data/languages.json', 'utf8')));
}
@ -63,7 +66,7 @@ function loadLanguages(callback) {
});
}
function loadTranslations(callback) {
async function loadTranslations (callback) {
if (USE_FILES) {
return callback(JSON.parse(fs.readFileSync('./data/translations.json', 'utf8')));
}
@ -88,14 +91,14 @@ function loadTranslations(callback) {
});
}
function processData(languages, translations) {
async function processData (languages, translations) {
let langCount = 0;
let skipCount = 0;
const enUs = translations['en-US'].translation;
const totalPhraseCount = Object.keys(enUs).length;
let errors = 0;
const meta = {};
Object.keys(translations).forEach(lang => {
for (const lang of Object.keys(translations)) {
const languageTranslations = translations[lang].translation;
if (lang === 'en-US' || !languageTranslations) {
return;
@ -150,10 +153,12 @@ function processData(languages, translations) {
}
}
const languageJson = JSON.stringify(languageTranslations, null, 2);
const sign = crypto.createSign('RSA-SHA256');
sign.write(Buffer.from(languageJson));
sign.end();
const signature = sign.sign(privateKey).toString('base64');
const data = Buffer.from(languageJson);
const signature = await sign(data).catch(e => {
console.log('Sign error', e);
process.exit(1);
});
const langInfo = languages.data.filter(x => x.code === lang)[0];
const region = (defaultCountries[langInfo.region] || langInfo.region).toLowerCase();
@ -191,7 +196,7 @@ function processData(languages, translations) {
} else {
skipCount++;
}
});
}
console.log(`Done: ${langCount} written, ${skipCount} skipped, ${errors} errors`);
if (errors) {
console.error('There were errors, please check the output.');
@ -199,3 +204,8 @@ function processData(languages, translations) {
}
fs.writeFileSync('docs/translations/meta.json', JSON.stringify(meta, null, 2));
}
};
if (require.main === module) {
module.exports();
}

25
scripts/sign.js Normal file
View File

@ -0,0 +1,25 @@
const fs = require('fs');
const signer = require('pkcs15-smartcard-sign');
const keychain = require('keychain');
const verifyKey = fs.readFileSync('keys/public-key.pem');
function getPin() {
if (getPin.pin) {
return Promise.resolve(getPin.pin);
}
return new Promise((resolve, reject) => {
keychain.getPassword({ account: 'keeweb', service: 'keeweb.pin', type: 'generic' }, (err, pass) => {
if (err) {
reject(err);
} else {
getPin.pin = pass;
resolve(pass);
}
});
});
}
module.exports = function sign(data) {
return getPin().then(pin => signer.sign({ data, verifyKey, pin }).then(data => data.toString('base64')));
};

4
scripts/start.js Normal file
View File

@ -0,0 +1,4 @@
(async function() {
await require('./download-translations')();
require('./update-plugins');
})();

View File

@ -1,14 +1,13 @@
/* eslint-disable no-console */
const fs = require('fs');
const crypto = require('crypto');
const sign = require('./sign');
console.log('Welcome to plugins updater');
console.log('Loading...');
const data = JSON.parse(fs.readFileSync('docs/plugins.json', 'utf8'));
const privateKey = fs.readFileSync('keys/private-key.pem', 'binary');
data.signature = '';
data.date = '';
@ -54,14 +53,13 @@ data.date = new Date().toISOString();
console.log('Signing...');
const dataToSign = JSON.stringify(data, null, 2);
const sign = crypto.createSign('RSA-SHA256');
sign.write(new Buffer(dataToSign));
sign.end();
data.signature = sign.sign(privateKey).toString('base64');
const dataToSign = Buffer.from(JSON.stringify(data, null, 2));
sign(dataToSign).then(signature => {
data.signature = signature.toString('base64');
fs.writeFileSync('docs/plugins.json', JSON.stringify(data, null, 2));
console.log('Done');
}).catch(err => {
console.error('Sign error', err);
process.exit(1);
});