From 29640c9b595c39fff17bcf04416d0c6773e0c280 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Sat, 27 Mar 2021 15:10:16 +0100 Subject: [PATCH] HTTP ECC Encryption implemented --- package.json | 3 +- services/gunDB/Mediator/index.js | 2 +- src/cors.js | 2 +- src/routes.js | 111 +++++++++++++++++- src/server.js | 73 ++++++++++-- utils/ECC/crypto.js | 72 ++++++++++++ utils/ECC/index.js | 115 +++++++++++++++++++ utils/fieldError.js | 11 ++ utils/protectedRoutes.js | 5 +- yarn.lock | 189 ++++++++++++++++++++++++++++++- 10 files changed, 555 insertions(+), 28 deletions(-) create mode 100644 utils/ECC/crypto.js create mode 100644 utils/ECC/index.js create mode 100644 utils/fieldError.js diff --git a/package.json b/package.json index 7955cca1..b8a4919b 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "cors": "^2.8.4", "debug": "^3.1.0", "dotenv": "^8.1.0", + "eccrypto": "^1.1.6", "express": "^4.14.1", "express-session": "^1.17.1", "google-proto-files": "^1.0.3", @@ -105,4 +106,4 @@ "pre-commit": "yarn lint && yarn typecheck && yarn lint-staged" } } -} \ No newline at end of file +} diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index eddb2466..5bb0ab99 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -462,7 +462,7 @@ const instantiateGun = () => { const _gun = /** @type {unknown} */ (new Gun({ axe: false, multicast: false, - peers: Config.PEERS + peers: ['https://gun.shock.network:8765/gun'] })) gun = /** @type {GUNNode} */ (_gun) diff --git a/src/cors.js b/src/cors.js index 7a0bd24b..f3617730 100644 --- a/src/cors.js +++ b/src/cors.js @@ -3,7 +3,7 @@ const setAccessControlHeaders = (req, res) => { res.header("Access-Control-Allow-Methods", "OPTIONS,POST,GET,PUT,DELETE") res.header( "Access-Control-Allow-Headers", - "Origin, X-Requested-With, Content-Type, Accept, Authorization, public-key-for-decryption" + "Origin, X-Requested-With, Content-Type, Accept, Authorization, public-key-for-decryption, encryption-device-id" ); }; diff --git a/src/routes.js b/src/routes.js index 11481455..7ddb0247 100644 --- a/src/routes.js +++ b/src/routes.js @@ -20,6 +20,7 @@ const getListPage = require('../utils/paginate') const auth = require('../services/auth/auth') const FS = require('../utils/fs') const Encryption = require('../utils/encryptionStore') +const ECC = require('../utils/ECC') const LightningServices = require('../utils/lightningServices') const lndErrorManager = require('../utils/lightningServices/errors') const GunDB = require('../services/gunDB/Mediator') @@ -212,17 +213,19 @@ module.exports = async ( }) app.use((req, res, next) => { - const deviceId = req.headers['x-shockwallet-device-id'] + const legacyDeviceId = req.headers['x-shockwallet-device-id'] + const deviceId = req.headers['encryption-device-id'] logger.debug('Decrypting route...') try { if ( nonEncryptedRoutes.includes(req.path) || - process.env.DISABLE_SHOCK_ENCRYPTION === 'true' + process.env.DISABLE_SHOCK_ENCRYPTION === 'true' || + (deviceId && !legacyDeviceId) ) { return next() } - if (!deviceId) { + if (!legacyDeviceId) { const error = { field: 'deviceId', message: 'Please specify a device ID' @@ -231,7 +234,7 @@ module.exports = async ( return res.status(401).json(error) } - if (!Encryption.isAuthorizedDevice({ deviceId })) { + if (!Encryption.isAuthorizedDevice({ deviceId: legacyDeviceId })) { const error = { field: 'deviceId', message: 'Please specify a device ID' @@ -263,7 +266,7 @@ module.exports = async ( reqData = req.body.data || req.body.encryptedData } const decryptedKey = Encryption.decryptKey({ - deviceId, + deviceId: legacyDeviceId, message: encryptedKey }) if (reqData) { @@ -294,6 +297,67 @@ module.exports = async ( } }) + app.use(async (req, res, next) => { + const legacyDeviceId = req.headers['x-shockwallet-device-id'] + const deviceId = req.headers['encryption-device-id'] + logger.info('Decrypting route...') + try { + if ( + nonEncryptedRoutes.includes(req.path) || + process.env.DISABLE_SHOCK_ENCRYPTION === 'true' || + (legacyDeviceId && !deviceId) + ) { + logger.info( + 'Unprotected route detected! ' + + req.path + + ' Legacy ID:' + + legacyDeviceId + + ' Device ID:' + + deviceId + ) + return next() + } + + if (!deviceId) { + const error = { + field: 'deviceId', + message: 'Please specify a device ID' + } + logger.error('Please specify a device ID') + return res.status(401).json(error) + } + + if (!ECC.isAuthorizedDevice({ deviceId })) { + const error = { + field: 'deviceId', + message: 'Please specify a device ID' + } + logger.error('Unknown Device') + return res.status(401).json(error) + } + + if (!ECC.isEncryptedMessage(req.body)) { + logger.warn('Message not encrypted!', req.body) + return next() + } + + logger.info('Decrypting ECC message...') + + const decryptedMessage = await ECC.decryptMessage({ + deviceId, + encryptedMessage: req.body + }) + + // eslint-disable-next-line + req.body = JSON.parse(decryptedMessage) + + return next() + } catch (err) { + logger.error(err) + return res.status(401).json(err) + } + }) + app.use(async (req, res, next) => { logger.info(`Route: ${req.path}`) if (unprotectedRoutes[req.method][req.path]) { @@ -470,6 +534,43 @@ module.exports = async ( } }) + app.post('/api/encryption/exchange', async (req, res) => { + try { + const { publicKey, deviceId } = req.body + + if (!publicKey) { + return res.status(400).json({ + field: 'publicKey', + message: 'Please provide a valid public key' + }) + } + + if ( + !deviceId || + !/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/iu.test( + deviceId + ) + ) { + return res.status(400).json({ + field: 'deviceId', + message: 'Please provide a valid device ID' + }) + } + + const authorizedDevice = await ECC.authorizeDevice({ + deviceId, + publicKey + }) + return res.json(authorizedDevice) + } catch (err) { + logger.error(err) + return res.status(401).json({ + field: 'unknown', + message: err + }) + } + }) + app.get('/api/lnd/wallet/status', async (req, res) => { try { const walletStatus = await walletExists() diff --git a/src/server.js b/src/server.js index 02f7c033..9f063c99 100644 --- a/src/server.js +++ b/src/server.js @@ -14,6 +14,7 @@ const server = program => { const Storage = require('node-persist') const Path = require('path') const { Logger: CommonLogger } = require('shock-common') + const ECC = require('../utils/ECC') const LightningServices = require('../utils/lightningServices') const Encryption = require('../utils/encryptionStore') const app = Express() @@ -119,10 +120,16 @@ const server = program => { * @param {(() => void)} next */ const modifyResponseBody = (req, res, next) => { - const deviceId = req.headers['x-shockwallet-device-id'] + const legacyDeviceId = req.headers['x-shockwallet-device-id'] + const deviceId = req.headers['encryption-device-id'] const oldSend = res.send - if (!nonEncryptedRoutes.includes(req.path)) { + if (nonEncryptedRoutes.includes(req.path)) { + next() + return + } + + if (legacyDeviceId) { res.send = (...args) => { if (args[0] && args[0].encryptedData && args[0].encryptionKey) { logger.warn('Response loop detected!') @@ -137,11 +144,13 @@ const server = program => { } // arguments[0] (or `data`) contains the response body - const authorized = Encryption.isAuthorizedDevice({ deviceId }) + const authorized = Encryption.isAuthorizedDevice({ + deviceId: legacyDeviceId + }) const encryptedMessage = authorized ? Encryption.encryptMessage({ message: args[0] ? args[0] : {}, - deviceId, + deviceId: legacyDeviceId, metadata: { hash } @@ -151,6 +160,38 @@ const server = program => { oldSend.apply(res, args) } } + + if (deviceId) { + res.send = (...args) => { + if (args[0] && args[0].ciphertext && args[0].iv) { + logger.warn('Response loop detected!') + oldSend.apply(res, args) + return + } + + const authorized = ECC.isAuthorizedDevice({ + deviceId + }) + + // Using classic promises syntax to avoid + // modifying res.send's return type + if (authorized) { + ECC.encryptMessage({ + deviceId, + message: args[0] + }).then(encryptedMessage => { + args[0] = JSON.stringify(encryptedMessage) + oldSend.apply(res, args) + }) + } + + if (!authorized) { + args[0] = JSON.stringify(args[0]) + oldSend.apply(res, args) + } + } + } + next() } @@ -271,19 +312,27 @@ const server = program => { } } - const getSessionSecret = async () => { - const sessionSecret = await Storage.getItem('config/sessionSecret') + const storePersistentRandomField = async ({ fieldName, length = 16 }) => { + const randomField = await Storage.getItem(fieldName) - if (sessionSecret) { - return sessionSecret + if (randomField) { + return randomField } - const newSecret = await Encryption.generateRandomString() - await Storage.setItem('config/sessionSecret', newSecret) - return newSecret + const newValue = await Encryption.generateRandomString() + await Storage.setItem(fieldName, newValue) + return newValue } - const sessionSecret = await getSessionSecret() + const [sessionSecret] = await Promise.all([ + storePersistentRandomField({ + fieldName: 'config/sessionSecret' + }), + storePersistentRandomField({ + fieldName: 'encryption/hostId', + length: 8 + }) + ]) app.use( session({ diff --git a/utils/ECC/crypto.js b/utils/ECC/crypto.js new file mode 100644 index 00000000..7f218432 --- /dev/null +++ b/utils/ECC/crypto.js @@ -0,0 +1,72 @@ +const { Buffer } = require("buffer"); +const FieldError = require("../fieldError") + +const convertUTF8ToBuffer = (value) => Buffer.from(value, 'utf-8'); + +const convertBase64ToBuffer = (value) => Buffer.from(value, 'base64'); + +const convertBufferToBase64 = (buffer) => buffer.toString("base64"); + +const processKey = (key) => { + if (Buffer.isBuffer(key)) { + return key; + } + const convertedKey = convertBase64ToBuffer(key); + return convertedKey; +}; + +const convertToEncryptedMessageResponse = (encryptedMessage) => { + if (typeof encryptedMessage.ciphertext === "string") { + return encryptedMessage; + } + + if (Buffer.isBuffer(encryptedMessage.ciphertext) && + Buffer.isBuffer(encryptedMessage.iv) && + Buffer.isBuffer(encryptedMessage.mac) && + Buffer.isBuffer(encryptedMessage.ephemPublicKey)) { + return { + ciphertext: convertBufferToBase64(encryptedMessage.ciphertext), + iv: convertBufferToBase64(encryptedMessage.iv), + mac: convertBufferToBase64(encryptedMessage.mac), + ephemPublicKey: convertBufferToBase64(encryptedMessage.ephemPublicKey) + }; + } + throw new FieldError({ + field: "encryptedMessage", + message: "Unknown encrypted message format" + }); +}; + +const convertToEncryptedMessage = (encryptedMessage) => { + if (encryptedMessage.ciphertext instanceof Buffer && + encryptedMessage.iv instanceof Buffer && + encryptedMessage.mac instanceof Buffer && + encryptedMessage.ephemPublicKey instanceof Buffer) { + // @ts-ignore + return encryptedMessage; + } + if (typeof encryptedMessage.ciphertext === "string" && + typeof encryptedMessage.iv === "string" && + typeof encryptedMessage.mac === "string" && + typeof encryptedMessage.ephemPublicKey === "string") { + return { + ciphertext: convertBase64ToBuffer(encryptedMessage.ciphertext), + iv: convertBase64ToBuffer(encryptedMessage.iv), + mac: convertBase64ToBuffer(encryptedMessage.mac), + ephemPublicKey: convertBase64ToBuffer(encryptedMessage.ephemPublicKey) + }; + } + throw new FieldError({ + field: "encryptedMessage", + message: "Unknown encrypted message format" + }); +}; + +module.exports = { + convertUTF8ToBuffer, + convertBase64ToBuffer, + convertBufferToBase64, + convertToEncryptedMessage, + convertToEncryptedMessageResponse, + processKey +} \ No newline at end of file diff --git a/utils/ECC/index.js b/utils/ECC/index.js new file mode 100644 index 00000000..7d3363bf --- /dev/null +++ b/utils/ECC/index.js @@ -0,0 +1,115 @@ +/** @format */ +const ECCrypto = require('eccrypto') +const Storage = require('node-persist') +const FieldError = require('../fieldError') +const { + convertBufferToBase64, + processKey, + convertToEncryptedMessageResponse, + convertUTF8ToBuffer, + convertToEncryptedMessage, + convertBase64ToBuffer +} = require('./crypto') + +const nodeKeyPairs = new Map() +const devicePublicKeys = new Map() + +const isEncryptedMessage = message => + message && + message.ciphertext && + message.iv && + message.mac && + message.ephemPublicKey + +const generateKeyPair = deviceId => { + const privateKey = ECCrypto.generatePrivate() + const publicKey = ECCrypto.getPublic(privateKey) + const privateKeyBase64 = convertBufferToBase64(privateKey) + const publicKeyBase64 = convertBufferToBase64(publicKey) + + nodeKeyPairs.set(deviceId, { + privateKey, + publicKey + }) + + return { + privateKey, + publicKey, + privateKeyBase64, + publicKeyBase64 + } +} + +const isAuthorizedDevice = ({ deviceId }) => devicePublicKeys.has(deviceId) + +const authorizeDevice = async ({ deviceId, publicKey }) => { + const hostId = await Storage.get('encryption/hostId') + devicePublicKeys.set(deviceId, convertBase64ToBuffer(publicKey)) + const keyPair = generateKeyPair(deviceId) + + return { + success: true, + APIPublicKey: keyPair.publicKeyBase64, + hostId + } +} + +const encryptMessage = async ({ message = '', deviceId }) => { + const publicKey = devicePublicKeys.get(deviceId) + + if (!publicKey) { + throw new FieldError({ + field: 'deviceId', + message: 'Unauthorized Device ID detected' + }) + } + + const processedPublicKey = processKey(publicKey) + const messageBuffer = convertUTF8ToBuffer(message) + const encryptedMessage = await ECCrypto.encrypt( + processedPublicKey, + messageBuffer + ) + const encryptedMessageResponse = { + ciphertext: encryptedMessage.ciphertext, + iv: encryptedMessage.iv, + mac: encryptedMessage.mac, + ephemPublicKey: encryptedMessage.ephemPublicKey + } + + return convertToEncryptedMessageResponse(encryptedMessageResponse) +} + +const decryptMessage = async ({ encryptedMessage, deviceId }) => { + try { + const keyPair = nodeKeyPairs.get(deviceId) + + if (!keyPair) { + throw new FieldError({ + field: 'deviceId', + message: 'Unauthorized Device ID detected' + }) + } + + const processedPrivateKey = processKey(keyPair.privateKey) + const processedPublicKey = processKey(keyPair.publicKey) + const decryptedMessage = await ECCrypto.decrypt( + processedPrivateKey, + convertToEncryptedMessage(encryptedMessage) + ) + const parsedMessage = decryptedMessage.toString('utf8') + return parsedMessage + } catch (err) { + console.error(err) + throw err + } +} + +module.exports = { + isAuthorizedDevice, + isEncryptedMessage, + generateKeyPair, + encryptMessage, + decryptMessage, + authorizeDevice +} diff --git a/utils/fieldError.js b/utils/fieldError.js new file mode 100644 index 00000000..4aae0cb2 --- /dev/null +++ b/utils/fieldError.js @@ -0,0 +1,11 @@ +class FieldError extends Error { + constructor(error) { + super(); + this.message = error?.message ?? "An unknown error has occurred"; + this.field = error?.field ?? "unknown"; + this.name = error?.name; + this.stack = error?.stack; + } +} + +module.exports = FieldError \ No newline at end of file diff --git a/utils/protectedRoutes.js b/utils/protectedRoutes.js index e3d53d44..44d6e061 100644 --- a/utils/protectedRoutes.js +++ b/utils/protectedRoutes.js @@ -16,7 +16,8 @@ module.exports = { "/api/lnd/wallet": true, "/api/lnd/wallet/existing": true, "/api/lnd/auth": true, - "/api/security/exchangeKeys": true + "/api/security/exchangeKeys": true, + "/api/encryption/exchange": true }, PUT: {}, DELETE: {} @@ -30,5 +31,5 @@ module.exports = { PUT: {}, DELETE: {} }, - nonEncryptedRoutes: ['/api/security/exchangeKeys', '/healthz', '/ping', '/api/lnd/wallet/status', '/api/gun/auth'] + nonEncryptedRoutes: ['/api/security/exchangeKeys', "/api/encryption/exchange", '/healthz', '/ping', '/api/lnd/wallet/status', '/api/gun/auth'] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 96a3b795..852f0cbe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -877,6 +877,11 @@ acorn-walk@^6.0.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== +acorn@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== + acorn@^5.5.3: version "5.7.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" @@ -1407,6 +1412,20 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI= + dependencies: + safe-buffer "^5.0.1" + bitcore-lib@^0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.15.0.tgz#f924be13869f2aab7e04aeec5642ad3359b6cec2" @@ -1434,6 +1453,11 @@ bn.js@=4.11.8, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + body-parser@1.19.0, body-parser@^1.16.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -1494,7 +1518,7 @@ braces@^3.0.1: dependencies: fill-range "^7.0.1" -brorand@^1.0.1: +brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= @@ -1511,6 +1535,18 @@ browser-resolve@^1.11.3: dependencies: resolve "1.1.7" +browserify-aes@^1.0.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + bs58@=4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -1545,6 +1581,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + buffer@^5.4.3: version "5.4.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" @@ -1702,6 +1743,14 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -2005,6 +2054,29 @@ create-error-class@^3.0.0: dependencies: capture-stack-trace "^1.0.0" +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -2244,6 +2316,15 @@ dotenv@^8.1.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.1.0.tgz#d811e178652bfb8a1e593c6dd704ec7e90d85ea2" integrity sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA== +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs= + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -2257,6 +2338,18 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +eccrypto@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/eccrypto/-/eccrypto-1.1.6.tgz#846bd1222323036f7a3515613704386399702bd3" + integrity sha512-d78ivVEzu7Tn0ZphUUaL43+jVPKTMPFGtmgtz1D0LrFn7cY3K8CdrvibuLz2AAkHBLKZtR8DMbB2ukRYFk987A== + dependencies: + acorn "7.1.1" + elliptic "6.5.4" + es6-promise "4.2.8" + nan "2.14.0" + optionalDependencies: + secp256k1 "3.7.1" + ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -2274,6 +2367,19 @@ elegant-spinner@^2.0.0: resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-2.0.0.tgz#f236378985ecd16da75488d166be4b688fd5af94" integrity sha512-5YRYHhvhYzV/FC4AiMdeSIg3jAYGq9xFvbhZMpPlJoBsfYgrw2DSCYeXfat6tYBu45PWiyRr3+flaCPPmviPaA== +elliptic@6.5.4, elliptic@^6.4.1: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + elliptic@=6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -2415,6 +2521,11 @@ es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-promise@4.2.8: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -2594,6 +2705,14 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + exec-sh@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" @@ -2848,6 +2967,11 @@ file-stream-rotator@^0.5.7: dependencies: moment "^2.11.2" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -3328,6 +3452,15 @@ has@^1.0.1, has@^1.0.3: dependencies: function-bind "^1.1.1" +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" @@ -3336,7 +3469,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hmac-drbg@^1.0.0: +hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= @@ -3503,7 +3636,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4727,6 +4860,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4913,11 +5055,16 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1, nan@^2.13.2: +nan@2.14.0, nan@^2.12.1, nan@^2.13.2: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nan@^2.14.0: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -5765,7 +5912,7 @@ readable-stream@^2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.4.0: +readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -6015,6 +6162,14 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -6051,7 +6206,7 @@ safe-buffer@5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6093,6 +6248,20 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +secp256k1@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.7.1.tgz#12e473e0e9a7c2f2d4d4818e722ad0e14cc1e2f1" + integrity sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.4.1" + nan "^2.14.0" + safe-buffer "^5.1.2" + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -6174,6 +6343,14 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"