Merge pull request #505 from shocknet/ecc-as-subprocess
Ecc as subprocess
This commit is contained in:
commit
25f7d2c325
6 changed files with 340 additions and 124 deletions
|
|
@ -95,10 +95,7 @@
|
||||||
"no-warning-comments": "off",
|
"no-warning-comments": "off",
|
||||||
|
|
||||||
// broken
|
// broken
|
||||||
"sort-imports": "off",
|
"sort-imports": "off"
|
||||||
|
|
||||||
// Would require to needlessly split code into too many files.
|
|
||||||
"mocha/max-top-level-suites": "off"
|
|
||||||
},
|
},
|
||||||
"parser": "babel-eslint",
|
"parser": "babel-eslint",
|
||||||
"env": {
|
"env": {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/** @format */
|
/** @format */
|
||||||
const ECCrypto = require('eccrypto')
|
|
||||||
const Storage = require('node-persist')
|
const Storage = require('node-persist')
|
||||||
|
const { fork } = require('child_process')
|
||||||
|
|
||||||
const FieldError = require('../fieldError')
|
const FieldError = require('../fieldError')
|
||||||
const logger = require('../../config/log')
|
const logger = require('../../config/log')
|
||||||
const {
|
const {
|
||||||
|
|
@ -12,6 +13,9 @@ const {
|
||||||
convertToEncryptedMessage,
|
convertToEncryptedMessage,
|
||||||
convertBase64ToBuffer
|
convertBase64ToBuffer
|
||||||
} = require('./crypto')
|
} = require('./crypto')
|
||||||
|
const { invoke } = require('./subprocess')
|
||||||
|
|
||||||
|
const cryptoSubprocess = fork('utils/ECC/subprocess')
|
||||||
|
|
||||||
const nodeKeyPairs = new Map()
|
const nodeKeyPairs = new Map()
|
||||||
const devicePublicKeys = new Map()
|
const devicePublicKeys = new Map()
|
||||||
|
|
@ -47,9 +51,9 @@ const isEncryptedMessage = message =>
|
||||||
* Generates a new encryption key pair that will be used
|
* Generates a new encryption key pair that will be used
|
||||||
* when communicating with the deviceId specified
|
* when communicating with the deviceId specified
|
||||||
* @param {string} deviceId
|
* @param {string} deviceId
|
||||||
* @returns {Pair}
|
* @returns {Promise<Pair>}
|
||||||
*/
|
*/
|
||||||
const generateKeyPair = deviceId => {
|
const generateKeyPair = async deviceId => {
|
||||||
try {
|
try {
|
||||||
const existingKey = nodeKeyPairs.get(deviceId)
|
const existingKey = nodeKeyPairs.get(deviceId)
|
||||||
|
|
||||||
|
|
@ -62,8 +66,8 @@ const generateKeyPair = deviceId => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const privateKey = ECCrypto.generatePrivate()
|
const privateKey = await invoke('generatePrivate', [], cryptoSubprocess)
|
||||||
const publicKey = ECCrypto.getPublic(privateKey)
|
const publicKey = await invoke('getPublic', [privateKey], cryptoSubprocess)
|
||||||
const privateKeyBase64 = convertBufferToBase64(privateKey)
|
const privateKeyBase64 = convertBufferToBase64(privateKey)
|
||||||
const publicKeyBase64 = convertBufferToBase64(publicKey)
|
const publicKeyBase64 = convertBufferToBase64(publicKey)
|
||||||
|
|
||||||
|
|
@ -107,7 +111,7 @@ const isAuthorizedDevice = ({ deviceId }) => devicePublicKeys.has(deviceId)
|
||||||
const authorizeDevice = async ({ deviceId, publicKey }) => {
|
const authorizeDevice = async ({ deviceId, publicKey }) => {
|
||||||
const hostId = await Storage.get('encryption/hostId')
|
const hostId = await Storage.get('encryption/hostId')
|
||||||
devicePublicKeys.set(deviceId, convertBase64ToBuffer(publicKey))
|
devicePublicKeys.set(deviceId, convertBase64ToBuffer(publicKey))
|
||||||
const keyPair = generateKeyPair(deviceId)
|
const keyPair = await generateKeyPair(deviceId)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -137,10 +141,12 @@ const encryptMessage = async ({ message = '', deviceId }) => {
|
||||||
|
|
||||||
const processedPublicKey = processKey(publicKey)
|
const processedPublicKey = processKey(publicKey)
|
||||||
const messageBuffer = convertUTF8ToBuffer(parsedMessage)
|
const messageBuffer = convertUTF8ToBuffer(parsedMessage)
|
||||||
const encryptedMessage = await ECCrypto.encrypt(
|
const encryptedMessage = await invoke(
|
||||||
processedPublicKey,
|
'encrypt',
|
||||||
messageBuffer
|
[processedPublicKey, messageBuffer],
|
||||||
|
cryptoSubprocess
|
||||||
)
|
)
|
||||||
|
|
||||||
const encryptedMessageResponse = {
|
const encryptedMessageResponse = {
|
||||||
ciphertext: encryptedMessage.ciphertext,
|
ciphertext: encryptedMessage.ciphertext,
|
||||||
iv: encryptedMessage.iv,
|
iv: encryptedMessage.iv,
|
||||||
|
|
@ -173,9 +179,10 @@ const decryptMessage = async ({ encryptedMessage, deviceId }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const processedPrivateKey = processKey(keyPair.privateKey)
|
const processedPrivateKey = processKey(keyPair.privateKey)
|
||||||
const decryptedMessage = await ECCrypto.decrypt(
|
const decryptedMessage = await invoke(
|
||||||
processedPrivateKey,
|
'decrypt',
|
||||||
convertToEncryptedMessage(encryptedMessage)
|
[processedPrivateKey, convertToEncryptedMessage(encryptedMessage)],
|
||||||
|
cryptoSubprocess
|
||||||
)
|
)
|
||||||
const parsedMessage = decryptedMessage.toString('utf8')
|
const parsedMessage = decryptedMessage.toString('utf8')
|
||||||
|
|
||||||
|
|
@ -203,5 +210,11 @@ module.exports = {
|
||||||
authorizeDevice,
|
authorizeDevice,
|
||||||
generateRandomString,
|
generateRandomString,
|
||||||
nodeKeyPairs,
|
nodeKeyPairs,
|
||||||
devicePublicKeys
|
devicePublicKeys,
|
||||||
|
/**
|
||||||
|
* Used for tests.
|
||||||
|
*/
|
||||||
|
killECCCryptoSubprocess() {
|
||||||
|
cryptoSubprocess.kill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,103 +12,113 @@ const {
|
||||||
decryptMessage,
|
decryptMessage,
|
||||||
encryptMessage,
|
encryptMessage,
|
||||||
generateKeyPair,
|
generateKeyPair,
|
||||||
isAuthorizedDevice
|
isAuthorizedDevice,
|
||||||
|
killECCCryptoSubprocess
|
||||||
} = require('./ECC')
|
} = require('./ECC')
|
||||||
|
|
||||||
const uuid = () => words({ exactly: 24 }).join('-')
|
const uuid = () => {
|
||||||
|
const arr = /** @type {string[]} */ (words({ exactly: 24 }))
|
||||||
|
return arr.join('-')
|
||||||
|
}
|
||||||
|
|
||||||
const storageDirectory = Path.resolve(__dirname, `./.test-storage`)
|
const storageDirectory = Path.resolve(__dirname, `./.test-storage`)
|
||||||
|
|
||||||
console.log(`Storage directory: ${storageDirectory}`)
|
console.log(`Storage directory: ${storageDirectory}`)
|
||||||
|
|
||||||
describe('generateKeyPair()', () => {
|
describe('ECC', () => {
|
||||||
it('generates a keypair', () => {
|
describe('generateKeyPair()', () => {
|
||||||
const pair = generateKeyPair(uuid())
|
it('generates a keypair', async () => {
|
||||||
|
expect.hasAssertions()
|
||||||
|
const pair = await generateKeyPair(uuid())
|
||||||
|
|
||||||
expect(pair.privateKey).toBeInstanceOf(Buffer)
|
expect(pair.privateKey).toBeInstanceOf(Buffer)
|
||||||
expect(typeof pair.privateKeyBase64 === 'string').toBeTruthy()
|
expect(typeof pair.privateKeyBase64 === 'string').toBeTruthy()
|
||||||
expect(pair.publicKey).toBeInstanceOf(Buffer)
|
expect(pair.publicKey).toBeInstanceOf(Buffer)
|
||||||
expect(typeof pair.publicKeyBase64 === 'string').toBeTruthy()
|
expect(typeof pair.publicKeyBase64 === 'string').toBeTruthy()
|
||||||
})
|
|
||||||
it('returns the same pair for the same device', () => {
|
|
||||||
const id = uuid()
|
|
||||||
const pair = generateKeyPair(id)
|
|
||||||
const pairAgain = generateKeyPair(id)
|
|
||||||
|
|
||||||
expect(pairAgain).toStrictEqual(pair)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('authorizeDevice()/isAuthorizedDevice()', () => {
|
|
||||||
it('authorizes a device given its ID', async () => {
|
|
||||||
expect.hasAssertions()
|
|
||||||
await Storage.init({
|
|
||||||
dir: storageDirectory
|
|
||||||
})
|
})
|
||||||
const deviceId = uuid()
|
it('returns the same pair for the same device', async () => {
|
||||||
const pair = generateKeyPair(deviceId)
|
expect.hasAssertions()
|
||||||
await authorizeDevice({ deviceId, publicKey: pair.publicKeyBase64 })
|
const id = uuid()
|
||||||
expect(isAuthorizedDevice({ deviceId })).toBeTruthy()
|
const pair = await generateKeyPair(id)
|
||||||
})
|
const pairAgain = await generateKeyPair(id)
|
||||||
})
|
|
||||||
|
|
||||||
describe('encryptMessage()/decryptMessage()', () => {
|
expect(pairAgain).toStrictEqual(pair)
|
||||||
before(() =>
|
|
||||||
Storage.init({
|
|
||||||
dir: storageDirectory
|
|
||||||
})
|
})
|
||||||
)
|
})
|
||||||
it('throws if provided with an unauthorized device id when encrypting', async () => {
|
|
||||||
expect.hasAssertions()
|
|
||||||
const deviceId = uuid()
|
|
||||||
|
|
||||||
try {
|
describe('authorizeDevice()/isAuthorizedDevice()', () => {
|
||||||
await encryptMessage({
|
it('authorizes a device given its ID', async () => {
|
||||||
message: uuid(),
|
expect.hasAssertions()
|
||||||
deviceId
|
await Storage.init({
|
||||||
|
dir: storageDirectory
|
||||||
})
|
})
|
||||||
throw new Error('encryptMessage() did not throw')
|
const deviceId = uuid()
|
||||||
} catch (_) {
|
const pair = await generateKeyPair(deviceId)
|
||||||
expect(true).toBeTruthy()
|
await authorizeDevice({ deviceId, publicKey: pair.publicKeyBase64 })
|
||||||
}
|
expect(isAuthorizedDevice({ deviceId })).toBeTruthy()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
it('throws if provided with an unknown device id when decrypting', async () => {
|
|
||||||
expect.hasAssertions()
|
|
||||||
const deviceId = uuid()
|
|
||||||
|
|
||||||
try {
|
describe('encryptMessage()/decryptMessage()', () => {
|
||||||
await decryptMessage({
|
before(() =>
|
||||||
|
Storage.init({
|
||||||
|
dir: storageDirectory
|
||||||
|
})
|
||||||
|
)
|
||||||
|
it('throws if provided with an unauthorized device id when encrypting', async () => {
|
||||||
|
expect.hasAssertions()
|
||||||
|
const deviceId = uuid()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await encryptMessage({
|
||||||
|
message: uuid(),
|
||||||
|
deviceId
|
||||||
|
})
|
||||||
|
throw new Error('encryptMessage() did not throw')
|
||||||
|
} catch (_) {
|
||||||
|
expect(true).toBeTruthy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it('throws if provided with an unknown device id when decrypting', async () => {
|
||||||
|
expect.hasAssertions()
|
||||||
|
const deviceId = uuid()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await decryptMessage({
|
||||||
|
deviceId,
|
||||||
|
encryptedMessage: {
|
||||||
|
ciphertext: uuid(),
|
||||||
|
ephemPublicKey: uuid(),
|
||||||
|
iv: uuid(),
|
||||||
|
mac: uuid(),
|
||||||
|
metadata: uuid()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
throw new Error('decryptMessage() did not throw')
|
||||||
|
} catch (_) {
|
||||||
|
expect(true).toBeTruthy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
it('encrypts and decrypts messages when given a known device id', async () => {
|
||||||
|
expect.hasAssertions()
|
||||||
|
const deviceId = uuid()
|
||||||
|
|
||||||
|
const pair = await generateKeyPair(deviceId)
|
||||||
|
|
||||||
|
await authorizeDevice({ deviceId, publicKey: pair.publicKeyBase64 })
|
||||||
|
|
||||||
|
const message = 'Bitcoin fixes this'
|
||||||
|
|
||||||
|
const encryptedMessage = await encryptMessage({ deviceId, message })
|
||||||
|
|
||||||
|
const decrypted = await decryptMessage({
|
||||||
deviceId,
|
deviceId,
|
||||||
encryptedMessage: {
|
encryptedMessage
|
||||||
ciphertext: uuid(),
|
|
||||||
ephemPublicKey: uuid(),
|
|
||||||
iv: uuid(),
|
|
||||||
mac: uuid(),
|
|
||||||
metadata: uuid()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
throw new Error('decryptMessage() did not throw')
|
|
||||||
} catch (_) {
|
|
||||||
expect(true).toBeTruthy()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
it('encrypts and decrypts messages when given a known device id', async () => {
|
|
||||||
expect.hasAssertions()
|
|
||||||
const deviceId = uuid()
|
|
||||||
|
|
||||||
const pair = generateKeyPair(deviceId)
|
expect(decrypted).toEqual(message)
|
||||||
|
|
||||||
await authorizeDevice({ deviceId, publicKey: pair.publicKeyBase64 })
|
|
||||||
|
|
||||||
const message = 'Bitcoin fixes this'
|
|
||||||
|
|
||||||
const encryptedMessage = await encryptMessage({ deviceId, message })
|
|
||||||
|
|
||||||
const decrypted = await decryptMessage({
|
|
||||||
deviceId,
|
|
||||||
encryptedMessage
|
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(decrypted).toEqual(message)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
after(killECCCryptoSubprocess)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,14 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
const { Buffer } = require('buffer')
|
const { Buffer } = require('buffer')
|
||||||
const Crypto = require('crypto')
|
const { fork } = require('child_process')
|
||||||
|
|
||||||
const FieldError = require('../fieldError')
|
const FieldError = require('../fieldError')
|
||||||
|
|
||||||
|
const { invoke } = require('./subprocess')
|
||||||
|
|
||||||
|
const cryptoSubprocess = fork('utils/ECC/subprocess')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} EncryptedMessageBuffer
|
* @typedef {object} EncryptedMessageBuffer
|
||||||
* @prop {Buffer} ciphertext
|
* @prop {Buffer} ciphertext
|
||||||
|
|
@ -23,19 +28,15 @@ const FieldError = require('../fieldError')
|
||||||
* @prop {any?} metadata
|
* @prop {any?} metadata
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const generateRandomString = (length = 16) =>
|
const generateRandomString = async (length = 16) => {
|
||||||
new Promise((resolve, reject) => {
|
if (length % 2 !== 0 || length < 2) {
|
||||||
// Gotta halve because randomBytes returns a sequence twice the size
|
throw new Error('Random string length must be an even number.')
|
||||||
Crypto.randomBytes(length / 2, (err, buffer) => {
|
}
|
||||||
if (err) {
|
|
||||||
reject(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = buffer.toString('hex')
|
const res = await invoke('generateRandomString', [length], cryptoSubprocess)
|
||||||
resolve(token)
|
|
||||||
})
|
return res
|
||||||
})
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
|
|
@ -135,5 +136,11 @@ module.exports = {
|
||||||
convertBufferToBase64,
|
convertBufferToBase64,
|
||||||
convertToEncryptedMessage,
|
convertToEncryptedMessage,
|
||||||
convertToEncryptedMessageResponse,
|
convertToEncryptedMessageResponse,
|
||||||
processKey
|
processKey,
|
||||||
|
/**
|
||||||
|
* Used for tests.
|
||||||
|
*/
|
||||||
|
killCryptoCryptoSubprocess() {
|
||||||
|
cryptoSubprocess.kill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,27 +7,33 @@ const expect = require('expect')
|
||||||
const {
|
const {
|
||||||
generateRandomString,
|
generateRandomString,
|
||||||
convertBase64ToBuffer,
|
convertBase64ToBuffer,
|
||||||
convertBufferToBase64
|
convertBufferToBase64,
|
||||||
|
killCryptoCryptoSubprocess
|
||||||
} = require('./crypto')
|
} = require('./crypto')
|
||||||
|
|
||||||
describe('generateRandomString()', () => {
|
describe('crypto', () => {
|
||||||
it('creates a random string of the specified length', async () => {
|
describe('generateRandomString()', () => {
|
||||||
expect.hasAssertions()
|
it('creates a random string of the specified length', async () => {
|
||||||
const len = Math.ceil(Math.random() * 100)
|
expect.hasAssertions()
|
||||||
const result = await generateRandomString(len)
|
const base = Math.ceil(Math.random() * 100)
|
||||||
|
const len = base % 2 !== 0 ? base + 1 : base
|
||||||
|
const result = await generateRandomString(len)
|
||||||
|
|
||||||
expect(result.length).toEqual(len)
|
expect(result.length).toEqual(len)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
describe('Buffer <> String <> Buffer', () => {
|
describe('Buffer <> String <> Buffer', () => {
|
||||||
it('preserves values', async () => {
|
it('preserves values', async () => {
|
||||||
const rnd = await generateRandomString(24)
|
const rnd = await generateRandomString(24)
|
||||||
|
|
||||||
const asBuffer = convertBase64ToBuffer(rnd)
|
const asBuffer = convertBase64ToBuffer(rnd)
|
||||||
|
|
||||||
const asStringAgain = convertBufferToBase64(asBuffer)
|
const asStringAgain = convertBufferToBase64(asBuffer)
|
||||||
|
|
||||||
expect(asStringAgain).toEqual(rnd)
|
expect(asStringAgain).toEqual(rnd)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
after(killCryptoCryptoSubprocess)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
183
utils/ECC/subprocess.js
Normal file
183
utils/ECC/subprocess.js
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
/**
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
const Crypto = require('crypto')
|
||||||
|
const ECCrypto = require('eccrypto')
|
||||||
|
const uuid = require('uuid/v1')
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
|
const mapValues = require('lodash/mapValues')
|
||||||
|
|
||||||
|
const logger = require('../../config/log')
|
||||||
|
|
||||||
|
logger.info('crypto subprocess invoked')
|
||||||
|
|
||||||
|
process.on('uncaughtException', e => {
|
||||||
|
logger.error('Uncaught exception inside crypto subprocess:')
|
||||||
|
logger.error(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
process.on('unhandledRejection', e => {
|
||||||
|
logger.error('Unhandled rejection inside crypto subprocess:')
|
||||||
|
logger.error(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {'generateRandomString' | 'convertUTF8ToBuffer'
|
||||||
|
* | 'convertBase64ToBuffer' | 'convertBufferToBase64' | 'generatePrivate'
|
||||||
|
* | 'getPublic' | 'encrypt' | 'decrypt'
|
||||||
|
* } Method
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} obj
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
const processBufferAfterSerialization = obj => {
|
||||||
|
if (typeof obj === 'object' && obj !== null) {
|
||||||
|
if (obj.type === 'Buffer') {
|
||||||
|
return Buffer.from(obj.data)
|
||||||
|
}
|
||||||
|
return mapValues(obj, processBufferAfterSerialization)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} Msg
|
||||||
|
* @prop {any[]} args
|
||||||
|
* @prop {string} id
|
||||||
|
* @prop {Method} method
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Msg} msg
|
||||||
|
*/
|
||||||
|
const handleMsg = async msg => {
|
||||||
|
if (typeof msg !== 'object' || msg === null) {
|
||||||
|
logger.error('Msg in crypto subprocess not an object')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id, method } = msg
|
||||||
|
const args = msg.args.map(processBufferAfterSerialization)
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (method === 'generateRandomString') {
|
||||||
|
const [length] = args
|
||||||
|
|
||||||
|
Crypto.randomBytes(length / 2, (err, buffer) => {
|
||||||
|
if (err) {
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
err: err.message
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = buffer.toString('hex')
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: token
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (method === 'convertUTF8ToBuffer') {
|
||||||
|
const [value] = args
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: Buffer.from(value, 'utf8')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (method === 'convertBase64ToBuffer') {
|
||||||
|
const [value] = args
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: Buffer.from(value, 'base64')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (method === 'convertBufferToBase64') {
|
||||||
|
const [buffer] = args
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: buffer.toString('base64')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (method === 'generatePrivate') {
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: ECCrypto.generatePrivate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (method === 'getPublic') {
|
||||||
|
const [privateKey] = args
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: ECCrypto.getPublic(privateKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (method === 'encrypt') {
|
||||||
|
const [processedPublicKey, messageBuffer] = args
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: await ECCrypto.encrypt(processedPublicKey, messageBuffer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (method === 'decrypt') {
|
||||||
|
const [processedPrivateKey, encryptedMessage] = args
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
id,
|
||||||
|
payload: await ECCrypto.decrypt(processedPrivateKey, encryptedMessage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// @ts-expect-error
|
||||||
|
process.send({
|
||||||
|
err: e.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('message', handleMsg)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Method} method
|
||||||
|
* @param {any[]} args
|
||||||
|
* @param {import('child_process').ChildProcess} cryptoSubprocess
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
const invoke = (method, args, cryptoSubprocess) =>
|
||||||
|
new Promise((res, rej) => {
|
||||||
|
const id = uuid()
|
||||||
|
/** @param {any} msg */
|
||||||
|
const listener = msg => {
|
||||||
|
if (msg.id === id) {
|
||||||
|
cryptoSubprocess.off('message', listener)
|
||||||
|
if (msg.err) {
|
||||||
|
rej(new Error(msg.err))
|
||||||
|
} else {
|
||||||
|
res(processBufferAfterSerialization(msg.payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cryptoSubprocess.on('message', listener)
|
||||||
|
cryptoSubprocess.send({
|
||||||
|
args,
|
||||||
|
id,
|
||||||
|
method
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
invoke
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue