tornado-nova/src/keypair.js

120 lines
3.3 KiB
JavaScript
Raw Normal View History

2021-06-15 14:48:26 +02:00
const { encrypt, decrypt, getEncryptionPublicKey } = require('eth-sig-util')
const { ethers } = require('hardhat')
const { BigNumber } = ethers
const { poseidonHash, toFixedHex } = require('./utils')
function packEncryptedMessage(encryptedMessage) {
const nonceBuf = Buffer.from(encryptedMessage.nonce, 'base64')
const ephemPublicKeyBuf = Buffer.from(encryptedMessage.ephemPublicKey, 'base64')
const ciphertextBuf = Buffer.from(encryptedMessage.ciphertext, 'base64')
const messageBuff = Buffer.concat([
Buffer.alloc(24 - nonceBuf.length),
nonceBuf,
Buffer.alloc(32 - ephemPublicKeyBuf.length),
ephemPublicKeyBuf,
ciphertextBuf,
])
return '0x' + messageBuff.toString('hex')
}
function unpackEncryptedMessage(encryptedMessage) {
if (encryptedMessage.slice(0, 2) === '0x') {
encryptedMessage = encryptedMessage.slice(2)
}
const messageBuff = Buffer.from(encryptedMessage, 'hex')
const nonceBuf = messageBuff.slice(0, 24)
const ephemPublicKeyBuf = messageBuff.slice(24, 56)
const ciphertextBuf = messageBuff.slice(56)
return {
version: 'x25519-xsalsa20-poly1305',
nonce: nonceBuf.toString('base64'),
ephemPublicKey: ephemPublicKeyBuf.toString('base64'),
ciphertext: ciphertextBuf.toString('base64'),
}
}
2021-06-15 14:48:26 +02:00
class Keypair {
2021-06-16 02:07:32 +02:00
/**
* Initialize a new keypair. Generates a random private key if not defined
*
* @param {string} privkey
*/
2021-06-15 14:48:26 +02:00
constructor(privkey = ethers.Wallet.createRandom().privateKey) {
this.privkey = privkey
this.pubkey = poseidonHash([this.privkey])
this.encryptionKey = getEncryptionPublicKey(privkey.slice(2))
}
toString() {
2021-06-15 22:02:59 +02:00
return toFixedHex(this.pubkey) + Buffer.from(this.encryptionKey, 'base64').toString('hex')
}
2021-06-16 02:07:32 +02:00
/**
* Key address for this keypair, alias to {@link toString}
*
* @returns {string}
*/
2021-06-15 22:02:59 +02:00
address() {
return this.toString()
2021-06-15 14:48:26 +02:00
}
2021-06-16 02:07:32 +02:00
/**
* Initialize new keypair from address string
*
* @param str
* @returns {Keypair}
*/
2021-06-15 14:48:26 +02:00
static fromString(str) {
if (str.length === 130) {
str = str.slice(2)
}
if (str.length !== 128) {
throw new Error('Invalid key length')
}
return Object.assign(new Keypair(), {
privkey: null,
pubkey: BigNumber.from('0x' + str.slice(0, 64)),
2021-06-15 22:02:59 +02:00
encryptionKey: Buffer.from(str.slice(64, 128), 'hex').toString('base64'),
2021-06-15 14:48:26 +02:00
})
}
2021-10-30 21:35:35 +02:00
/**
* Sign a message using keypair private key
*
* @param {string|number|BigNumber} commitment a hex string with commitment
* @param {string|number|BigNumber} merklePath a hex string with merkle path
* @returns {BigNumber} a hex string with signature
*/
2021-11-08 16:05:57 +01:00
sign(commitment, merklePath) {
return poseidonHash([this.privkey, commitment, merklePath])
2021-10-30 21:35:35 +02:00
}
2021-06-16 02:07:32 +02:00
/**
* Encrypt data using keypair encryption key
*
* @param {Buffer} bytes
* @returns {string} a hex string with encrypted data
*/
encrypt(bytes) {
2021-06-16 02:31:31 +02:00
return packEncryptedMessage(
encrypt(this.encryptionKey, { data: bytes.toString('base64') }, 'x25519-xsalsa20-poly1305'),
)
2021-06-15 14:48:26 +02:00
}
2021-06-16 02:07:32 +02:00
/**
* Decrypt data using keypair private key
*
* @param {string} data a hex string with data
* @returns {Buffer}
*/
2021-06-15 14:48:26 +02:00
decrypt(data) {
return Buffer.from(decrypt(unpackEncryptedMessage(data), this.privkey.slice(2)), 'base64')
2021-06-15 14:48:26 +02:00
}
}
2021-07-22 16:01:22 +02:00
module.exports = {
Keypair,
packEncryptedMessage,
unpackEncryptedMessage,
}