UNPKG

ecies-lite

Version:

A lightweight ECIES tool implemented in pure Node.JS

93 lines (86 loc) 4.22 kB
const crypto = require('crypto'); const config = { curveName: 'secp256k1', cipherAlgorithm: 'aes-256-cbc', hmacAlgorithm: 'sha256', ivSize: 16, cipherKeyGen: (bytes) => bytes.slice(0, 32), hmacKeyGen: (bytes) => bytes.slice(16) }; /** * Config the default parameters for ecies-lite * @param curveName: string | object - the elliptic curve to use | the config object contains config items * @param cipherAlgorithm?: string - the cipher algorithm to use * @param hmacAlgorithm?: string - the hmac algorithm to use * @param ivSize?: number - the size (in bytes) of initialization vector (for cipher) * @param cipherKeyGen?: (Buffer) -> Buffer - the cipher key generator * @param hmacKeyGen?: (Buffer) -> Buffer - the hmac key generator * @return none */ exports.config = (curveName, cipherAlgorithm, hmacAlgorithm, ivSize, cipherKeyGen, hmacKeyGen) => { if (typeof curveName === 'string') { config.curveName = curveName || config.curveName; config.cipherAlgorithm = cipherAlgorithm || config.cipherAlgorithm; config.hmacAlgorithm = hmacAlgorithm || config.hmacAlgorithm; config.ivSize = ivSize || config.ivSize; config.cipherKeyGen = cipherKeyGen || config.cipherKeyGen; config.hmacKeyGen = hmacKeyGen || config.hmacKeyGen; } else if (curveName instanceof Object) { Object.assign(config, curveName); } }; /** * Encrypt a message using the recepient's public key * @param pk: Buffer - The recipient's public key * @param msg: Buffer - The message to encrypt * @param opts?: the same structure as the config object - you can use it to specify advanced options * @return {epk: Buffer, iv: Buffer, ct: Buffer, mac: Buffer} - the ecies-lite structured object with fields correspondingly stands for ephemeral public key, initialization vector, cipher text, mac code for above data, etc. */ exports.encrypt = (pk, msg, opts) => { let ctx = Object.assign({}, config); ctx = Object.assign(ctx, opts || {}); const ecdh = crypto.createECDH(ctx.curveName); if (ctx.esk) { ecdh.setPrivateKey(ctx.esk); } else { ecdh.generateKeys(); } const epk = ecdh.getPublicKey(null, ctx.compressEpk ? 'compressed' : 'uncompressed'); with (ctx) { const hash = crypto.createHash('sha256').update(ecdh.computeSecret(pk)).digest(); const cipherKey = cipherKeyGen(hash), macKey = hmacKeyGen(hash); const iv = ctx.iv || crypto.randomBytes(ivSize); const cipher = crypto.createCipheriv(cipherAlgorithm, cipherKey, iv); let ct = cipher.update(msg); ct = Buffer.concat([ct, cipher.final()]); const mac = crypto.createHmac(hmacAlgorithm, macKey).update(Buffer.concat([epk, iv, ct])).digest(); return {epk, iv, ct, mac}; } }; /** * Decrypt a message in ecies-lite defined format using the recipient's private key * @param sk: Buffer - the recepient's private key * @param body: ecies-lite structured object - the ecies-lite body (seen format in encrypt) to decrypt * @param opts?: the same structure as the config object - you can use it to specify advanced options * @return Buffer - the plain text decrypted from the Ecies-lite body * @throws when mac value is unmatched, it throws 'Corrupted Ecies-lite body: unmatched authentication code' error */ exports.decrypt = (sk, body, opts) => { let ctx = Object.assign({}, config); ctx = Object.assign(ctx, opts || {}); ctx = Object.assign(ctx, body); with (ctx) { const ecdh = crypto.createECDH(curveName); ecdh.setPrivateKey(sk); const hash = crypto.createHash('sha256').update(ecdh.computeSecret(epk)).digest(); const cipherKey = cipherKeyGen(hash), macKey = hmacKeyGen(hash); const m = crypto.createHmac(hmacAlgorithm, macKey).update(Buffer.concat([epk, iv, ct])).digest(); if (m.compare(mac) !== 0 || mac.compare(m) !== 0) { throw new Error('Corrupted Ecies-lite body: unmatched authentication code'); } const decipher = crypto.createDecipheriv(cipherAlgorithm, cipherKey, iv); let pt = decipher.update(ct); return Buffer.concat([pt, decipher.final()]); } };