diff --git a/package.json b/package.json index e1702c32..1896a7f7 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "lru-cache": "^4.1.3", "measure-scrollbar": "^1.1.0", "moment": "^2.22.2", + "openpgp": "^4.2.1", "qrcode": "^1.2.0", "qrloop": "0.8.1", "qs": "^6.5.1", diff --git a/src/config/errors.js b/src/config/errors.js index c9ea06a3..493f5fa8 100644 --- a/src/config/errors.js +++ b/src/config/errors.js @@ -51,3 +51,8 @@ export const FeeNotLoaded = createCustomErrorClass('FeeNotLoaded') export const NoDBPathGiven = createCustomErrorClass('NoDBPathGiven') export const DBWrongPassword = createCustomErrorClass('DBWrongPassword') export const DBNotReset = createCustomErrorClass('DBNotReset') + +// auto-update errors +export const UpdateIncorrectHash = createCustomErrorClass('UpdateIncorrectHash') +export const UpdateIncorrectSig = createCustomErrorClass('UpdateIncorrectSig') +export const UpdateFetchFileFail = createCustomErrorClass('UpdateFetchFileFail') diff --git a/src/main/autoUpdate.js b/src/main/autoUpdate.js deleted file mode 100644 index 1b50e078..00000000 --- a/src/main/autoUpdate.js +++ /dev/null @@ -1,32 +0,0 @@ -// @flow - -import { app, BrowserWindow } from 'electron' -import { autoUpdater } from 'electron-updater' - -type SendFunction = (type: string, data: *) => void - -export default (notify: SendFunction) => { - autoUpdater.on('checking-for-update', () => notify('checking')) - autoUpdater.on('update-available', info => notify('updateAvailable', info)) - autoUpdater.on('update-not-available', () => notify('updateNotAvailable')) - autoUpdater.on('error', err => notify('error', err)) - autoUpdater.on('download-progress', progress => notify('downloadProgress', progress)) - autoUpdater.on('update-downloaded', () => notify('downloaded')) - - autoUpdater.checkForUpdatesAndNotify() -} - -export function quitAndInstall() { - setImmediate(() => { - const browserWindows = BrowserWindow.getAllWindows() - - // Fixes quitAndInstall not quitting on macOS, as suggested on - // https://github.com/electron-userland/electron-builder/issues/1604#issuecomment-306709572 - app.removeAllListeners('window-all-closed') - browserWindows.forEach(browserWindow => { - browserWindow.removeAllListeners('close') - }) - - autoUpdater.quitAndInstall(false) - }) -} diff --git a/src/main/bridge.js b/src/main/bridge.js index e302d559..e6527fa4 100644 --- a/src/main/bridge.js +++ b/src/main/bridge.js @@ -12,7 +12,6 @@ import user from 'helpers/user' import { resolveLogsDirectory, cleanUpBeforeClosingSync } from 'helpers/log' import { deserializeError } from 'helpers/errors' -import setupAutoUpdater, { quitAndInstall } from './autoUpdate' import { setInternalProcessPID } from './terminator' import { getMainWindow } from './app' @@ -145,13 +144,3 @@ ipcMain.on('sentryLogsChanged', (event, payload) => { if (!p) return p.send({ type: 'sentryLogsChanged', payload }) }) - -// TODO move this to "command" pattern -ipcMain.on('updater', (event, { type, data }) => { - const handler = { - init: setupAutoUpdater, - quitAndInstall, - }[type] - const send = (type: string, data: *) => event.sender.send('updater', { type, data }) - handler(send, data, type) -}) diff --git a/src/main/updater/createAppUpdater.js b/src/main/updater/createAppUpdater.js new file mode 100644 index 00000000..1346040e --- /dev/null +++ b/src/main/updater/createAppUpdater.js @@ -0,0 +1,86 @@ +// @flow + +import { UpdateIncorrectHash, UpdateIncorrectSig } from 'config/errors' +import * as pgpHelper from './pgpHelper' + +type Opts = { + filename: string, + computeHash: () => Promise, + getNextKey: (?string) => Promise, + getNextKeySignature: string => Promise, + getHashFile: () => Promise, + getHashFileSignature: () => Promise, +} + +export default function createAppUpdater(opts: Opts): { verify: () => Promise } { + const { + filename, + computeHash, + getNextKey, + getNextKeySignature, + getHashFile, + getHashFileSignature, + } = opts + + // main logic: + // - fetch hashFile + its signature + // - verify signature + // - compare hash with update hash + // throw if any step fail. + async function verify() { + const [hashFile, hashFileSignature] = await Promise.all([getHashFile(), getHashFileSignature()]) + await verifyHashFileSignature(hashFile, hashFileSignature, await getNextKey()) + await compareHash(hashFile) + } + + // compute the update hash and compare it to the hash located in hash file + async function compareHash(hashFile) { + const computedHash = await computeHash() + const hashFromFile = extractHashFromHashFile(hashFile, filename) + if (hashFromFile !== computedHash) { + throw new UpdateIncorrectHash(computedHash) + } + } + + // verify hash signature with given pubkey + // if it fails, try to get next key and repeat + // if no more key, throw + async function verifyHashFileSignature(hash, sigContent, pubKey) { + try { + await pgpHelper.verify(hash, sigContent, pubKey) + } catch (err) { + try { + const nextPubKey = await getNextPubKey(pubKey) + await verifyHashFileSignature(hash, sigContent, nextPubKey) + } catch (err) { + throw new UpdateIncorrectSig() + } + } + } + + // fetch the next pubkey based on the previous key fingerprint + // also fetch signature, and verify against previous pubkey + async function getNextPubKey(pubKey) { + const fingerprint = await pgpHelper.getFingerprint(pubKey) + const nextPubKey = await getNextKey(fingerprint) + const nextPubKeySignature = await getNextKeySignature(fingerprint) + await pgpHelper.verify(nextPubKey, nextPubKeySignature, pubKey) + return nextPubKey + } + + return { + verify, + } +} + +// a hash file looks like: " \n " +// we only need the hash for the given filename +function extractHashFromHashFile(hashFile, filename) { + let hash + hashFile.split('\n').find(r => { + const row = r.split(/\s+/) + hash = row[1] === filename ? row[0] : '' + return !!hash + }) + return hash +} diff --git a/src/main/updater/createElectronAppUpdater.js b/src/main/updater/createElectronAppUpdater.js new file mode 100644 index 00000000..166b1057 --- /dev/null +++ b/src/main/updater/createElectronAppUpdater.js @@ -0,0 +1,62 @@ +// @flow + +import crypto from 'crypto' +import path from 'path' +import fs from 'fs' + +import network from 'api/network' +import { UpdateFetchFileFail } from 'config/errors' +import { fsReadFile } from 'helpers/fs' +import createAppUpdater from './createAppUpdater' + +import pubKey from './ledger-pubkey' + +export default async ({ feedURL, updateVersion }: { feedURL: string, updateVersion: string }) => { + const { app } = require('electron') + const updateFolder = path.resolve(app.getPath('userData'), '__update__') + const { fileName: filename } = await readUpdateInfos(updateFolder) + + const hashFileURL = `${feedURL}/ledger-live-desktop-${updateVersion}.sha512sum` + const hashSigFileURL = `${feedURL}/ledger-live-desktop-${updateVersion}.sha512sum.sig` + const keysURL = `${feedURL}/pubkeys` + + return createAppUpdater({ + filename, + computeHash: () => sha512sumPath(path.resolve(updateFolder, filename)), + getHashFile: () => getDistantFileContent(hashFileURL), + getHashFileSignature: () => getDistantFileContent(hashSigFileURL), + getNextKey: (fingerprint: ?string) => + fingerprint ? getDistantFileContent(`${keysURL}/${fingerprint}.asc`) : pubKey, + getNextKeySignature: async (fingerprint: string) => + getDistantFileContent(`${keysURL}/${fingerprint}.asc.sig`), + }) +} + +// read the electron-updater file. we basically only need the filename here, +// because the hash file contains hashes for all platforms (better to have +// only 1 file to sign lel) +export async function readUpdateInfos(updateFolder: string) { + const updateInfoPath = path.resolve(updateFolder, 'update-info.json') + const updateInfoContent = await fsReadFile(updateInfoPath) + return JSON.parse(updateInfoContent) +} + +// compute hash for given path. i guess we only need that here +export function sha512sumPath(filePath: string) { + return new Promise((resolve, reject) => { + const sum = crypto.createHash('sha512') + const stream = fs.createReadStream(filePath) + stream.on('data', data => sum.update(data)) + stream.on('end', () => resolve(sum.digest('hex'))) + stream.on('error', reject) + }) +} + +async function getDistantFileContent(url: string) { + try { + const res = await network({ method: 'GET', url }) + return res.data + } catch (err) { + throw new UpdateFetchFileFail(url) + } +} diff --git a/src/main/updater/createMockAppUpdater.js b/src/main/updater/createMockAppUpdater.js new file mode 100644 index 00000000..84b6045a --- /dev/null +++ b/src/main/updater/createMockAppUpdater.js @@ -0,0 +1,40 @@ +// @flow + +import createAppUpdater from './createAppUpdater' + +export default ({ + filename, + computedHash, + hashFile, + signature, + pubKey, + pubKeys, +}: { + filename: string, + computedHash: string, + hashFile: string, + signature: string, + pubKey: string, + pubKeys: Array<{ fingerprint: string, content: string, signature: string }>, +}) => + createAppUpdater({ + filename, + computeHash: () => Promise.resolve(computedHash), + getHashFile: () => Promise.resolve(hashFile), + getHashFileSignature: () => Promise.resolve(signature), + getNextKey: async (fingerprint: ?string) => { + if (!fingerprint) return Promise.resolve(pubKey) + const key = pubKeys.find(k => k.fingerprint === fingerprint) + if (!key) { + throw new Error(`Cannot find key for fingerprint ${fingerprint}`) + } + return key.content + }, + getNextKeySignature: async (fingerprint: string) => { + const key = pubKeys.find(k => k.fingerprint === fingerprint) + if (!key) { + throw new Error(`Cannot find signature for key ${fingerprint}`) + } + return key.signature + }, + }) diff --git a/src/main/updater/ledger-pubkey.js b/src/main/updater/ledger-pubkey.js new file mode 100644 index 00000000..f39f2859 --- /dev/null +++ b/src/main/updater/ledger-pubkey.js @@ -0,0 +1,111 @@ +// TODO: it's actually my key. replace with qorum key. + +export default `-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=97OE +-----END PGP PUBLIC KEY BLOCK-----` diff --git a/src/main/updater/mocks/mock-privkey-1.asc b/src/main/updater/mocks/mock-privkey-1.asc new file mode 100644 index 00000000..845fe737 --- /dev/null +++ b/src/main/updater/mocks/mock-privkey-1.asc @@ -0,0 +1,33 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQHYBFv8BAcBBADRLTFoRU3IUz37E70wy2jv7jg7QLSoHnJglcUYzq+RCBnQAfxQ +NOcQtJDKOJyo1v3819o5pxnrxWAutM/CNpjanzcWM1yLfHjrKo8BbHlmIbWBmV7w +8te+rm/VlyNi30D1eIIJy276nByW4rMLcfFe4T055xbEvRNvQzR+KfrCBwARAQAB +AAP8Dv3XMart5TKaGZmTkKCzd6ayHwUHLZlpByUUyC9gA9v5xZ+uzqzL9rWKPwQJ +rkwiTfHGcSVZxT0TJKXO0hOJpthgqbMnHokIhrYeNrDbU4l4tgE74Ik+8F3dGGns +C48Q+6lfACbpLMz7wq+60lLT7tzBKgYGA4G35W9+CY9jz8ECAOX3SpqFpOkoYcjh +GsmBSlTs9Kz4JyZDdigiM7xrroe9zkXtwuGUY2X14le/vCoZl3EP/WYV2CY8uODm +cMTYbaECAOjbZZPjudH9Ux234qdv/lMsiYwZ86aQmSUnaR2tNrTmlMiW60yKbsMC +smP0A+wZvpI9U6hMU8cbNtN7SW8WfqcCAOabL3NCCeQbDQBHI/0oKqrSIkMTQzKL +1MdUcvitzHBiKrOGF0PUgiZ0mNVXsSvLH0grULllAwfws3UeRmYGVwWfE7QiRm9v +IEJhciAoTW9jayBLZXkgMSkgPGZvb0BiYXIuY29tPojOBBMBCAA4FiEERZ/aCM7e +heCvgo2lv5b5SATkBG4FAlv8BAcCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AA +CgkQv5b5SATkBG5tlwP/V1nUnbcJloznFVZFQJtLdCd9IG15LYeexd0/B3sBZm5d +Jze29tkNWblHc+xBxAp6PuuRPyvBKZd4grPNCfD8tzFLRv3jeVXrrYjOzb4G6ug6 +15Zd0jFn6taCykCpgLcuKLDXm9lnCIR9cHD0IHhojdjdLjGbL1Kpu/N8xoOJlcOd +AdgEW/wEBwEEAOUUNwoEzjt/+YReQsPmQsKuBi9KxU1dvbcI0OG5xYdlp4ZgG9Gj +hJwvvH1aHiNU3qyRLF2PjL7dmM6xFIppTF8sakW2H34oE2nY9IS0HYdFNaIag0vk +G0isSrdRuVl38EClkvEEXnb/L7y/J3EDiBwW+4jWTd0SnS9C1IX+pzozABEBAAEA +A/9e21eNIP9K2Qu3uZZ46wSS+5+Y/qOjjSENRSvFhHjcP4Y2mN58GPNE/2lUGLhp +FfAc0gJmfZYgxL7NO7tPMxoJeFs7oo7duWua2rugLHUVH55ApiJXzOMlX4rpDNzR +eYsI4WOUgq1bMTjLoy/Zuz00IrItEuPqKFWot9CI+Rz8uQIA8bCtS1bn/2Tg9Bww +td3WQqERz9AOE3eSDynRrZ/APJrphMhEvL2YC03kS8U5x32cSq0vS8fMlUuGZXvf +Nxfq2wIA8qRjmyFCG+V3wUC6lymWj8DAh+BINCmuDRiXCzHQ1D1q+Nid1bO18dvd +tI81lmn7IIn9esKxU8ym/z9o22YRiQH/SHFD/b/KSZKKQnKOrcKtj2gLT0MW/gL2 +mUxenook+hEoZyBwIOEabz2o5rznmXCXnxlgZ2sIX4Ymx67b/0BtuqaSiLYEGAEI +ACAWIQRFn9oIzt6F4K+CjaW/lvlIBOQEbgUCW/wEBwIbDAAKCRC/lvlIBOQEbkeR +BACd1FWQvo1qNTuNC9IwHLLhBQbT8OEHn/TIP2daR0dNjsvcfvpmX4kXEHWFRW7G +CWaoWMNYtWFk8GrGXKDjZ50wLCw9rj/xJdjdz8vDjVZT3Si0MOrDI9WJLbBNrCBO +h52YTF89KXceUoBVqOgVta4ArbiagOK35ezlWjvg6H2slw== +=IP04 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/main/updater/mocks/mock-privkey-2.asc b/src/main/updater/mocks/mock-privkey-2.asc new file mode 100644 index 00000000..726a25d4 --- /dev/null +++ b/src/main/updater/mocks/mock-privkey-2.asc @@ -0,0 +1,33 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQHYBFv8BCEBBADA0YaD4Sa50kD9g2o/t6qkMMBZtmYt0kh/F+rAEBCYQL1H6dhA +FAUt7k9EYLAzUw03ByAB2WR097te1xEos9eUWz9YIUMJxE6Tk66kys0z+X1uZQcc +Gywxrg9EsMWQPwDSOJjFEKsMV81X7UjInh38+Dx/gy8sLUUClXDpo7HZiQARAQAB +AAP+ITy85ETOaR+uJkUd7ofZeV4R+iTv7hwedRIJXDwosCYQurG7nigVUHqQ3WeR +V/eMAp1OziLMJ0GA4fNOuOZvVY3ZuwcQFaejRMkItOgvuc4lZZ5IvZzoQrJ04NOO +mT6dugZpD3MYtnxoEftNvjEE/ezc1Wch5Nu06jo0fqb5tsUCANhuUemRQkqL+1sZ +HdhQT3vgxRae1WebH8pEjczLflNteydGE8+ND38tAYaQOqSj8Gs6v7uH9YUwHMMZ +vVT6/msCAOQSEFQ40mG9x+pTLWqfHFR6VeBKOuz+AnygYKIjKJ8eTgXay/2bT9Pg +qbeDYYTtfB0XFvUDd1b7xLBPZ1QTnNsCAI/8OTu958QxWoApsH4vEj7fuQXbOyYs +LERh5o7rMUeAacWStv5+i6sBLo63KQWLlfo/vjVJFQI1nxLApg8JKIubkbQmRml6 +eiBCYXJyIChNb2NrIEtleSAyKSA8Zml6ekBiYXJyLmNvbT6IzgQTAQgAOBYhBGcP +SZyL0UpLah6eU55NLhDLPtM/BQJb/AQhAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B +AheAAAoJEJ5NLhDLPtM/xMkD/2YwwDlKWS9vAe9Sl9dqU+Rz8OqNTrjhCDUSoGEr +83ltoGkl2unBOWXT8JQVBBokGexUdKhDS/2gEF+WxpAUORE9qVFkARMZgH19bIYf +mNmJY+0t0YttiOvx7GXUnynJ6V3iEX96uMbxfBdIbHF/thzK0yCG/pD9DoLJPB7z +nRA+nQHYBFv8BCEBBACkrayUHg6fK2dMI0gZKdR0WMyxwXXKg+kc1tlsrxqfb/bH +UVtCy+VlpIS2SGcEAfXSIPmeesj5OA4p6e6egPeVFoy6qWOTLR0U9x4GqW/uUsEz +FAiB6IRK1h2HMLsp5wOV8RTf2ENDiObRh3zqEEJ7zANHQm4sDoaRUIHo5RsbuQAR +AQABAAP6A/gywYRC9nfBnu53ybF+L0rHrl47NGU/HISaS5oh8mdsw6u0nzTShAN1 +9n3iy9AvSIAmfDk5+HXvM3fx2kzCCwJLv1Jxm7a9S92L6kzPQB/JdQ5c84cBespY +gzK8phGfCa2O0F+/mdBi5hKNuScM5g55UWpr17v0bbznEuhpF60CAMC4JKO32xUi +l2flVjhBR316i5IJmvUvN9Jw0sLtXuHS4tgaTWWPgThCEKc4iIy1Ngyy72mW4XTa +OzFNitInzw0CANrAbpzgKFawx+Kh2DrOUrj8IuXwf1NtajnfQfCk8nn4nzsXglq5 +A/z9pSHuTGck5PFJT9PGiNH1ohxRciFfdF0CAJZLcDj09dG1UbgF4JelXhIJFYAE +VJQTKLLZq9H2W2ClA07gKkfvN1r++5lbaIB5fNJkY3BKNzuuEKAICx3hMqajrYi2 +BBgBCAAgFiEEZw9JnIvRSktqHp5Tnk0uEMs+0z8FAlv8BCECGwwACgkQnk0uEMs+ +0z8XgAP/c75yAyOCLLKUjD2Ov3Xwn/4YR8/mayZNFGkJ28FRWyxrw4zWVE3TCJVK +8AKUpa8QA9FL0maahWV4EQzcOvO4MKwohbUZ6/XLLd9NhMa9L5pZzsGUT2JQQN/i +yk+I22jpK0+ILJwqwB28EBzwkqRJLSgfPwbeRJ9Ni7D9qf8T1VU= +=vt8y +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/main/updater/mocks/mock-pubkey-1.asc b/src/main/updater/mocks/mock-pubkey-1.asc new file mode 100644 index 00000000..9230034c --- /dev/null +++ b/src/main/updater/mocks/mock-pubkey-1.asc @@ -0,0 +1,19 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mI0EW/wEBwEEANEtMWhFTchTPfsTvTDLaO/uODtAtKgecmCVxRjOr5EIGdAB/FA0 +5xC0kMo4nKjW/fzX2jmnGevFYC60z8I2mNqfNxYzXIt8eOsqjwFseWYhtYGZXvDy +176ub9WXI2LfQPV4ggnLbvqcHJbiswtx8V7hPTnnFsS9E29DNH4p+sIHABEBAAG0 +IkZvbyBCYXIgKE1vY2sgS2V5IDEpIDxmb29AYmFyLmNvbT6IzgQTAQgAOBYhBEWf +2gjO3oXgr4KNpb+W+UgE5ARuBQJb/AQHAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B +AheAAAoJEL+W+UgE5ARubZcD/1dZ1J23CZaM5xVWRUCbS3QnfSBteS2HnsXdPwd7 +AWZuXSc3tvbZDVm5R3PsQcQKej7rkT8rwSmXeIKzzQnw/LcxS0b943lV662Izs2+ +BuroOteWXdIxZ+rWgspAqYC3Liiw15vZZwiEfXBw9CB4aI3Y3S4xmy9SqbvzfMaD +iZXDuI0EW/wEBwEEAOUUNwoEzjt/+YReQsPmQsKuBi9KxU1dvbcI0OG5xYdlp4Zg +G9GjhJwvvH1aHiNU3qyRLF2PjL7dmM6xFIppTF8sakW2H34oE2nY9IS0HYdFNaIa +g0vkG0isSrdRuVl38EClkvEEXnb/L7y/J3EDiBwW+4jWTd0SnS9C1IX+pzozABEB +AAGItgQYAQgAIBYhBEWf2gjO3oXgr4KNpb+W+UgE5ARuBQJb/AQHAhsMAAoJEL+W ++UgE5ARuR5EEAJ3UVZC+jWo1O40L0jAcsuEFBtPw4Qef9Mg/Z1pHR02Oy9x++mZf +iRcQdYVFbsYJZqhYw1i1YWTwasZcoONnnTAsLD2uP/El2N3Py8ONVlPdKLQw6sMj +1YktsE2sIE6HnZhMXz0pdx5SgFWo6BW1rgCtuJqA4rfl7OVaO+DofayX +=jimW +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/main/updater/mocks/mock-pubkey-2.asc b/src/main/updater/mocks/mock-pubkey-2.asc new file mode 100644 index 00000000..e0928360 --- /dev/null +++ b/src/main/updater/mocks/mock-pubkey-2.asc @@ -0,0 +1,19 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mI0EW/wEIQEEAMDRhoPhJrnSQP2Daj+3qqQwwFm2Zi3SSH8X6sAQEJhAvUfp2EAU +BS3uT0RgsDNTDTcHIAHZZHT3u17XESiz15RbP1ghQwnETpOTrqTKzTP5fW5lBxwb +LDGuD0SwxZA/ANI4mMUQqwxXzVftSMieHfz4PH+DLywtRQKVcOmjsdmJABEBAAG0 +JkZpenogQmFyciAoTW9jayBLZXkgMikgPGZpenpAYmFyci5jb20+iM4EEwEIADgW +IQRnD0mci9FKS2oenlOeTS4Qyz7TPwUCW/wEIQIbAwULCQgHAgYVCgkICwIEFgID +AQIeAQIXgAAKCRCeTS4Qyz7TP8TJA/9mMMA5SlkvbwHvUpfXalPkc/DqjU644Qg1 +EqBhK/N5baBpJdrpwTll0/CUFQQaJBnsVHSoQ0v9oBBflsaQFDkRPalRZAETGYB9 +fWyGH5jZiWPtLdGLbYjr8exl1J8pyeld4hF/erjG8XwXSGxxf7YcytMghv6Q/Q6C +yTwe850QPriNBFv8BCEBBACkrayUHg6fK2dMI0gZKdR0WMyxwXXKg+kc1tlsrxqf +b/bHUVtCy+VlpIS2SGcEAfXSIPmeesj5OA4p6e6egPeVFoy6qWOTLR0U9x4GqW/u +UsEzFAiB6IRK1h2HMLsp5wOV8RTf2ENDiObRh3zqEEJ7zANHQm4sDoaRUIHo5Rsb +uQARAQABiLYEGAEIACAWIQRnD0mci9FKS2oenlOeTS4Qyz7TPwUCW/wEIQIbDAAK +CRCeTS4Qyz7TPxeAA/9zvnIDI4IsspSMPY6/dfCf/hhHz+ZrJk0UaQnbwVFbLGvD +jNZUTdMIlUrwApSlrxAD0UvSZpqFZXgRDNw687gwrCiFtRnr9cst302Exr0vmlnO +wZRPYlBA3+LKT4jbaOkrT4gsnCrAHbwQHPCSpEktKB8/Bt5En02LsP2p/xPVVQ== +=Bk7g +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/main/updater/mocks/pubkey-1.asc b/src/main/updater/mocks/pubkey-1.asc new file mode 100644 index 00000000..fd38889b --- /dev/null +++ b/src/main/updater/mocks/pubkey-1.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +sfB6/eqK4Q== +=UKRx +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/main/updater/mocks/pubkey-2.asc b/src/main/updater/mocks/pubkey-2.asc new file mode 100644 index 00000000..44cd4ec0 --- /dev/null +++ b/src/main/updater/mocks/pubkey-2.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +sEQGv7WFrXcyYM38 +=/9tA +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage b/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage new file mode 100644 index 00000000..05b2f3e6 --- /dev/null +++ b/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage @@ -0,0 +1 @@ +sigko-shasumok diff --git a/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum b/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum new file mode 100644 index 00000000..b853635d --- /dev/null +++ b/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum @@ -0,0 +1 @@ +26ca4f4aa377a8cbe434463debe94d9ec923b930a3053d4fe7129440b48667ac66db03e0f6c417671e16ea9445148d2124df6d34184514f05b26aa14a0790f4b ledger-live-desktop-1.2.1-linux-x86_64.AppImage diff --git a/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig b/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig new file mode 100644 index 00000000..1e2574a1 --- /dev/null +++ b/src/main/updater/mocks/sigko-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEhhzv+bPVtMW9pE7JhKxNYN+ul88FAlvKAJIACgkQhKxNYN+u +l8/FJQ/+PvYCbEZB77BGBHPvoEjkTbp4lP9RYzIS1Ial0HUZ6CDsMOfFEVsfM7VQ +MTluvUERshVc5OX2Z4Y+G/eMYHInwZX+ytjWQ7/rXa4M/3na97ND/JwQjNmiIzjT +Fxaiis0BqwulOff9bvgu0AKrA6Bq/DiBKV2EuWwNKVADuUhoAru53YWtAg0mFU87 +XRSR6bww1/fDe9pD/PzWNJxonMHjZDz3KYQOh5RXqNE9sgI51hMVpqNW5O1H/N5P +GvqNDB0piyH85CbiZgvYhDVzVBhpuu1+gQLYMYRAsU8Grp5jJmnbTX81kJsqzyrY +1MNwH4v/nr1KIA/H/SwCXLgDX013VPc7U0rD74dZI8IJn2le/72U5jZknPM0B7vl +KBWgEOq3XYSqW5jlY4N4wdHmNvMiTureiLfZmOjV2XLk8aLX+/LpABHLBduGM3hZ +wsj7+av7jFce4w6bBNoew+P3OMS6aHyORTUkMKvGSACI4PchGHSgG6rmGM183o64 +opexXsnit5vIYQKbJATp2nuUmW9TD2kK6mDjk2Hio4wAR6KvawUf4prIkF1rR/M5 +4DrJ3Oq9ntvmApyE8o5t/uOFE/HSuS/+ZGn1nieSPJTME6WtSYWV9gxfLOgErpXD +GoibMFoInfjh/rPE5JwoCuIaBGYOuFS9ytwlYAPcA7sJQlb9vLE= +=KWhh +-----END PGP SIGNATURE----- diff --git a/src/main/updater/mocks/sigko-shasumok/update-info.json b/src/main/updater/mocks/sigko-shasumok/update-info.json new file mode 100644 index 00000000..fb193762 --- /dev/null +++ b/src/main/updater/mocks/sigko-shasumok/update-info.json @@ -0,0 +1,3 @@ +{ + "fileName": "ledger-live-desktop-1.2.1-linux-x86_64.AppImage" +} diff --git a/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage b/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage new file mode 100644 index 00000000..20008850 --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage @@ -0,0 +1 @@ +sigok-shasumok diff --git a/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum b/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum new file mode 100644 index 00000000..8ed6ec6e --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum @@ -0,0 +1 @@ +this_is_ko_26ca4f4aa377a8cbe434463debe94d9ec923b930a3053d4fe7129440b48667ac66db03e0f6c417671e16ea9445148d2124df6d34184514f05b26aa14a0790f4b ledger-live-desktop-1.2.1-linux-x86_64.AppImage diff --git a/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig b/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig new file mode 100644 index 00000000..c8da22d6 --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumko/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEESKQ3KFYSBLGhLWtAHS/CMF4ss5kFAlvKBL4ACgkQHS/CMF4s +s5lbKxAAndUSWP3tL+aBl/Jn1Fjr48bPlRHFpqUY1bW59uAEB0C1xkHCvExkH7Wh +oc1nT3DsE+khwfhPeruKiNBcgtLtPWBW0PaY7N1tcafzYVldBZYSRfZotv1wEuqF +vt4kgqzCTPLOLfaUxTP7esDv6oIdHY7bah/0QsgvPiOxtT+/Kb/6OIXGT6IyWJxX +6Z/oc2lzkxmIyALFp53YEejUeCayiATVcoR15MCOK2zofU9RBk0BI2R50cNlefpX +WttXOfFP5l2EecZA0S3vA8am66+nG3jKHA8cLIqc//X1pgFCNbZXxCt2kTiE4ri6 +8XW0ne9tGvHk+fG2/4ZhNK2dqzwSD5Yitd2g5HU3ZOUxVdmH5K2XyFF5Zhixca+c +ivFlH00LEagF8hhxkf2tZWSG8fLvffNmzPdkQ1HySq4FpT/K33twb+MYHAWtwEP6 +/Epyiq7S15aB2SRkdOnBv1VQWf0wGZO/ABKutgF8xrOh13prLLSmXFenIdXGsdnP +2Wlawn6GyvSOcK+s6Z6AuETXXVdGnUQDuwjUyqQXUWp4Fm0xInTTPcgruh7Ip3Ra +vBeQcYr4i/aB37X6fUVogzq4Sy2DvDUnG7uMmaVyDxK6RhQ9/M3Jql/6ffCJPXm5 +J7AqoM/QCHfVkXhvgKKwq8hxb6JLSykjB9h//AonCVRcKW/dqY8= +=kaOO +-----END PGP SIGNATURE----- diff --git a/src/main/updater/mocks/sigok-shasumko/update-info.json b/src/main/updater/mocks/sigok-shasumko/update-info.json new file mode 100644 index 00000000..fb193762 --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumko/update-info.json @@ -0,0 +1,3 @@ +{ + "fileName": "ledger-live-desktop-1.2.1-linux-x86_64.AppImage" +} diff --git a/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage b/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage new file mode 100644 index 00000000..20008850 --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage @@ -0,0 +1 @@ +sigok-shasumok diff --git a/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum b/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum new file mode 100644 index 00000000..b853635d --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum @@ -0,0 +1 @@ +26ca4f4aa377a8cbe434463debe94d9ec923b930a3053d4fe7129440b48667ac66db03e0f6c417671e16ea9445148d2124df6d34184514f05b26aa14a0790f4b ledger-live-desktop-1.2.1-linux-x86_64.AppImage diff --git a/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig b/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig new file mode 100644 index 00000000..d4a145f1 --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumok/ledger-live-desktop-1.2.1-linux-x86_64.AppImage.sha512sum.sig @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEESKQ3KFYSBLGhLWtAHS/CMF4ss5kFAlvJ+ysACgkQHS/CMF4s +s5mepQ/8DkHSiLEdpMx1GHRX7ElRbhefD7ZAbBCaf/1igpx9K/+SrKvVkgywX2us +D+MU6vmxcFLuMMb5NWpY2s7/0iC+fj36x33fCSvRkmmxxXWf+SzyFozX8sAc/vOo +2x1KTR5jvxspZS0/ERX2KH/88hIWH9kzLsrk/qvppWXpJvEVS5hEHKijLST1aw+c +4L01n9dBwTAsMLo8xm7ybcU86yKUsvbd96XJHuQoqJeg4TOqveUuEmh1J/9jaXnO +3ZcPOtOP42jKuIOFglGMTgtFwR/WN7kxb5eS7wDodvTJE0YzpxeugW4WEfJ2nspZ +3ZCW3G4DD38/uN5MGnPEM77pqKqDZJY7wPHTO2PbXa6w5+WkW9cvE38fBikdQsnX +nKIuvsqQRBBsIOmVe4m+VJ3wrBPpk7IrYSp54+M8XWf8EkkyZYxtMhE+1bqPhRqG +aTIw6qJXJ+yDowe8cFaEybs4xcUl2t4QWHIXM5Y09ZFcwhD5UQwtXz9x6jJogR2j +W8MLeNnAUhRxFY/hySq+wJasKMDKbWXGPk3TmMNn8P+6g6HEllM4KucJty2OH50t +nJbYt0da3Br2W8os1RPA71MmfW6nGhzcFhX2ShKLicyw46Tyc0GGaO29oDwgQkRc +wcbFJmZVMj9ZuZArmfhFL53ZQj9kbAF2qWPNt+L369jSmw5tAko= +=z2+Z +-----END PGP SIGNATURE----- diff --git a/src/main/updater/mocks/sigok-shasumok/update-info.json b/src/main/updater/mocks/sigok-shasumok/update-info.json new file mode 100644 index 00000000..fb193762 --- /dev/null +++ b/src/main/updater/mocks/sigok-shasumok/update-info.json @@ -0,0 +1,3 @@ +{ + "fileName": "ledger-live-desktop-1.2.1-linux-x86_64.AppImage" +} diff --git a/src/main/updater/pgpHelper.js b/src/main/updater/pgpHelper.js new file mode 100644 index 00000000..d31f41d6 --- /dev/null +++ b/src/main/updater/pgpHelper.js @@ -0,0 +1,40 @@ +// @flow + +import * as openpgp from 'openpgp' + +export async function getFingerprint(pubKey: string) { + const { keys } = await openpgp.key.readArmored(pubKey) + if (!keys.length) { + throw new Error('No key found in pubKey') + } + return keys[0].getFingerprint() +} + +export async function verify(msgContent: string, sigContent: string, pubKeyContent: string) { + const signature = await openpgp.signature.readArmored(sigContent) + const message = openpgp.message.fromText(msgContent) + const { keys: publicKeys } = await openpgp.key.readArmored(pubKeyContent) + const pgpOpts = { message, publicKeys, signature } + const verified = await openpgp.verify(pgpOpts) + + if (verified.signatures.length === 0) { + throw new Error('No signature found') + } + + if (!verified.signatures.every(sig => sig.valid)) { + throw new Error('Signature check failed') + } +} + +export async function sign(msgContent: string, privKeyContent: string) { + const privKey = (await openpgp.key.readArmored(privKeyContent)).keys[0] + + const options = { + message: openpgp.cleartext.fromText(msgContent), + privateKeys: [privKey], + detached: true, + } + + const signed = await openpgp.sign(options) + return signed.signature +} diff --git a/src/main/updater/updater.spec.js b/src/main/updater/updater.spec.js new file mode 100644 index 00000000..69c5801c --- /dev/null +++ b/src/main/updater/updater.spec.js @@ -0,0 +1,114 @@ +import path from 'path' +import crypto from 'crypto' + +import { fsReadFile } from 'helpers/fs' +import { UpdateIncorrectHash, UpdateIncorrectSig } from 'config/errors' +import createMockAppUpdater from './createMockAppUpdater' +import { sha512sumPath, readUpdateInfos } from './createElectronAppUpdater' +import * as pgpHelper from './pgpHelper' + +const base = path.resolve(__dirname, 'mocks') + +describe('AppUpdater', () => { + describe('simple cases', () => { + test('should not throw if correct hash & correct sig', async () => { + await verifyMockFolder('sigok-shasumok', 'pubkey-1.asc') + }) + + test('should throw if correct hash but incorrect pubkey', async () => { + const p = verifyMockFolder('sigok-shasumok', 'pubkey-2.asc') + await expectFail(p, UpdateIncorrectSig) + }) + + test('should throw if incorrect hash but correct sig', async () => { + const p = verifyMockFolder('sigok-shasumko', 'pubkey-1.asc') + await expectFail(p, UpdateIncorrectHash) + }) + + test('should throw if correct hash but incorrect sig', async () => { + const p = verifyMockFolder('sigko-shasumok', 'pubkey-1.asc') + await expectFail(p, UpdateIncorrectSig) + }) + }) + + describe('key invalidation', () => { + test('should verify successfully by using getNextKey()', async () => { + const filename = 'foo.exe' + const mockPrivKey1 = await fsReadFile(path.resolve(base, 'mock-privkey-1.asc'), 'ascii') + const mockPubKey1 = await fsReadFile(path.resolve(base, 'mock-pubkey-1.asc'), 'ascii') + const mockPrivKey2 = await fsReadFile(path.resolve(base, 'mock-privkey-2.asc'), 'ascii') + const mockPubKey2 = await fsReadFile(path.resolve(base, 'mock-pubkey-2.asc'), 'ascii') + + // This is the desired scenario: + // + // Let's simulate a release. + // + // 1) create binary (lol) and its hash. this is basically the app update + const update = '0010011-content-of-the-app-01110001010111' + + // 2) create hash of update + const hash = sha512sum(update) + const hashFile = `${hash} ${filename}` // yeah, the hash file contains also file name + + // 3) sign the hash with the *KEY 2* (the app will use the *KEY 1* first) + const signature = await pgpHelper.sign(hashFile, mockPrivKey2) + + // 4) sign *KEY 2* with *KEY 1* and get the *KEY 1* fingerprint, ye. + const mockPubKey2Signature = await pgpHelper.sign(mockPubKey2, mockPrivKey1) + const mockPubKey1Fingerprint = await pgpHelper.getFingerprint(mockPubKey1) + + // 5) app updater, with all thoses infos + const updater = await createMockAppUpdater({ + filename, + computedHash: hash, + hashFile, + signature, + pubKey: mockPubKey1, + pubKeys: [ + { + fingerprint: mockPubKey1Fingerprint, + content: mockPubKey2, + signature: mockPubKey2Signature, + }, + ], + }) + + await updater.verify() + }) + }) +}) + +async function expectFail(promise, errType) { + let err + try { + await promise + } catch (e) { + err = e + } + expect(err).toBeDefined() + expect(err).toBeInstanceOf(errType) +} + +async function verifyMockFolder(folderName, pubKeyName, pubKeys = []) { + const mock = path.resolve(base, folderName) + const { fileName: filename } = await readUpdateInfos(mock) + const computedHash = await sha512sumPath(path.resolve(mock, filename)) + const hashFile = await fsReadFile(path.resolve(mock, `${filename}.sha512sum`), 'ascii') + const signature = await fsReadFile(path.resolve(mock, `${filename}.sha512sum.sig`), 'ascii') + const pubKey = await fsReadFile(path.resolve(base, pubKeyName)) + const updater = await createMockAppUpdater({ + filename, + computedHash, + hashFile, + signature, + pubKey, + pubKeys, + }) + await updater.verify() +} + +export function sha512sum(content) { + const sum = crypto.createHash('sha512') + sum.update(content) + return sum.digest('hex') +} diff --git a/yarn.lock b/yarn.lock index 9afa1d4a..cf086e7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1791,6 +1791,13 @@ redux "^4.0.0" reselect "^3.0.1" +"@mattiasbuelens/web-streams-polyfill@0.1.0-alpha.4": + version "0.1.0-alpha.4" + resolved "https://registry.yarnpkg.com/@mattiasbuelens/web-streams-polyfill/-/web-streams-polyfill-0.1.0-alpha.4.tgz#fde9688fac83ff40e3be1a01973c867e12a74492" + integrity sha512-WAsiWLWc7ZNS1b0qFAoKSFLeqXesPa60YelVE3pPKc6pZ4iuSW9l6DBxY4hMPQj1dQCBDrUHJj/NDSjE85bTRQ== + dependencies: + "@types/whatwg-streams" "0.0.5" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -2112,6 +2119,11 @@ resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976" integrity sha512-5Th3OsZ4gTRdr9Mho83BQ23cex4sRhOR4XTG+m+cJc0FhtUBK9Vn62hBJ+pnQYnSxoPOsKoAPOx6FcphxBC8ng== +"@types/whatwg-streams@0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@types/whatwg-streams/-/whatwg-streams-0.0.5.tgz#66c3852b8e33f2a2ca225a6d8fd8096e34d17fe6" + integrity sha512-y1UgRuGO64x/v+UIerA2AMquW/qxaIUD95rbf8FYxtVG//D3381+JexnZfcEiZSqXErdxdPmXpz8srY7gs9Grw== + "@types/ws@^3.2.0": version "3.2.1" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-3.2.1.tgz#b0c1579e58e686f83ce0a97bb9463d29705827fb" @@ -2346,6 +2358,13 @@ acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0, acorn@^5.6.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" integrity sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ== +address-rfc2822@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/address-rfc2822/-/address-rfc2822-2.0.4.tgz#2dbd3b8d6c2de1e957c1a8549dc012d40bbc3431" + integrity sha1-Lb07jWwt4elXwahUncAS1Au8NDE= + dependencies: + email-addresses "^3.0.0" + address@1.0.3, address@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" @@ -2721,6 +2740,10 @@ asap@~2.0.3: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= +"asmcrypto.js@github:openpgpjs/asmcrypto": + version "2.3.0" + resolved "https://codeload.github.com/openpgpjs/asmcrypto/tar.gz/6e4e407b9b8ae317925a9e677cc7b4de3e447e83" + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -2730,6 +2753,15 @@ asn1.js@^4.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +asn1.js@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.0.1.tgz#7668b56416953f0ce3421adbb3893ace59c96f59" + integrity sha512-aO8EaEgbgqq77IEw+1jfx5c9zTbzvkfuRBuZsSsPnTHMkmd5AI4J6OtITLZFa381jReeaQL67J0GBTUu0+ZTVw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -4240,7 +4272,7 @@ bn.js@^3.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-3.3.0.tgz#1138e577889fdc97bbdab51844f2190dfc0ae3d7" integrity sha1-ETjld4if3Je72rUYRPIZDfwK49c= -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.4.0: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.8, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== @@ -4541,6 +4573,14 @@ buffer@^5.0.3: base64-js "^1.0.2" ieee754 "^1.1.4" +buffer@^5.0.8: + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + buffer@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.0.tgz#53cf98241100099e9eeae20ee6d51d21b16e541e" @@ -6939,6 +6979,23 @@ elliptic@^6.0.0, elliptic@^6.2.3: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +"elliptic@github:openpgpjs/elliptic": + version "6.4.0" + resolved "https://codeload.github.com/openpgpjs/elliptic/tar.gz/e187e706e11fa51bcd20e46e5119054be4e2a4a6" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +email-addresses@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-3.0.2.tgz#a31280d19baf86669840a0aa45be1d7f6e7df315" + integrity sha512-IMn9dnwLMsgZjdUHswB/UZ0S8LQ/u+2/qjnHJ9tCtp3QHZsIYwJCiJOo2FT0i3CwwK/dtSODYtxuvzV4D9MY5g== + emoji-regex@^6.1.0: version "6.5.1" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2" @@ -8705,6 +8762,14 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" +hash.js@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" + integrity sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -11123,7 +11188,7 @@ mini-css-extract-plugin@^0.4.0: loader-utils "^1.1.0" webpack-sources "^1.1.0" -minimalistic-assert@^1.0.0: +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== @@ -11363,7 +11428,7 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.1.1: +node-fetch@^2.1.1, node-fetch@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.2.0.tgz#4ee79bde909262f9775f731e3656d0db55ced5b5" integrity sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA== @@ -11439,6 +11504,13 @@ node-loader@^0.6.0: resolved "https://registry.yarnpkg.com/node-loader/-/node-loader-0.6.0.tgz#c797ef51095ed5859902b157f6384f6361e05ae8" integrity sha1-x5fvUQle1YWZArFX9jhPY2HgWug= +node-localstorage@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-localstorage/-/node-localstorage-1.3.1.tgz#3177ef42837f398aee5dd75e319b281e40704243" + integrity sha512-NMWCSWWc6JbHT5PyWlNT2i8r7PgGYXVntmKawY83k/M0UJScZ5jirb61TLnqKwd815DfBQu+lR3sRw08SPzIaQ== + dependencies: + write-file-atomic "^1.1.4" + node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" @@ -11783,6 +11855,25 @@ opener@^1.4.3: resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= +openpgp@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/openpgp/-/openpgp-4.2.1.tgz#5d693ca9d90f52f0826447a37596b6a9a5a725da" + integrity sha512-spO7D5LVS8H8ktpX55XDNzvIktMQA7CHZVCt3i4REeP3rbFlzvRbg0cR699yC/hwH0dx09/z+6C+fH4lmbyucw== + dependencies: + "@mattiasbuelens/web-streams-polyfill" "0.1.0-alpha.4" + address-rfc2822 "^2.0.3" + asmcrypto.js "github:openpgpjs/asmcrypto" + asn1.js "^5.0.0" + bn.js "^4.11.8" + buffer "^5.0.8" + elliptic "github:openpgpjs/elliptic" + hash.js "^1.1.3" + node-fetch "^2.1.2" + node-localstorage "~1.3.0" + pako "^1.0.6" + seek-bzip "github:openpgpjs/seek-bzip" + web-stream-tools "github:openpgpjs/web-stream-tools" + opn@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225" @@ -11976,7 +12067,7 @@ package-json@^4.0.0: registry-url "^3.0.3" semver "^5.1.0" -pako@~1.0.5: +pako@^1.0.6, pako@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg== @@ -14347,6 +14438,12 @@ secp256k1@^3.0.1: nan "^2.2.1" safe-buffer "^5.1.0" +"seek-bzip@github:openpgpjs/seek-bzip": + version "1.0.5-git" + resolved "https://codeload.github.com/openpgpjs/seek-bzip/tar.gz/3aca608ffedc055a1da1d898ecb244804ef32209" + dependencies: + commander "~2.8.1" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -16272,6 +16369,10 @@ wdio-dot-reporter@~0.0.8: resolved "https://registry.yarnpkg.com/wdio-dot-reporter/-/wdio-dot-reporter-0.0.10.tgz#facfb7c9c5984149951f59cbc3cd0752101cf0e0" integrity sha512-A0TCk2JdZEn3M1DSG9YYbNRcGdx/YRw19lTiRpgwzH4qqWkO/oRDZRmi3Snn4L2j54KKTfPalBhlOtc8fojVgg== +"web-stream-tools@github:openpgpjs/web-stream-tools": + version "0.0.1" + resolved "https://codeload.github.com/openpgpjs/web-stream-tools/tar.gz/9ab800d46add161db496506d67338202ad0114ce" + webdriverio@^4.8.0: version "4.13.1" resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-4.13.1.tgz#624ef4ca569f3c9a5e8e9b11302b4431eda1fb8a" @@ -16721,7 +16822,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^1.2.0: +write-file-atomic@^1.1.4, write-file-atomic@^1.2.0: version "1.3.4" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" integrity sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=