/**
 * ECIES encrypt/decrypt with Ethereum keys
 * A Typescript implementation of geth/crypto/ecies/ecies.go
 * Modified from https://github.com/LimelabsTech/eth-ecies/blob/master/index.js
 * At commit c858cbd021e9a99d8afa629de33c8c30d923b3e5.
 */ 'use strict' ; import { createCipheriv , createDecipheriv , createHash , createHmac , randomBytes } from 'crypto' ; import { ec as EC } from 'elliptic' ; const ec = new EC ( 'secp256k1' ) ; export const IV_LENGTH = 16 ; param { Buffer } ctr returns const IncCounter = ( ctr ) => { for ( let i = ctr . length - 1 ; i >= 0 ; i -- ) { ctr [ i ] ++ ; if ( ctr [ i ] !== 0 ) { return ctr ; } } return ctr ; } ; param { Buffer } px param { number } kdLen returns { Buffer } const ConcatKDF = ( px , kdLen ) => { const blockSize = 32 ; const reps = ( ( kdLen + 7 ) * 8 ) / ( blockSize * 8 ) ; let counter = Buffer . from ( '00000001' , 'hex' ) ; let k = Buffer . from ( '00' , 'hex' ) ; for ( let i = 0 ; i <= reps ; i ++ ) { const hash = createHash ( 'sha256' ) ; hash . update ( counter ) ; hash . update ( px ) ; k = Buffer . concat ( [ k , hash . digest ( ) ] ) ; counter = IncCounter ( counter ) ; } return k . slice ( 1 , kdLen + 1 ) ; } ; param { Buffer } encryptionKey param { Buffer } iv param { Buffer } plaintext returns { Buffer } export function AES128Encrypt ( encryptionKey , iv , plaintext ) { const cipher = createCipheriv ( 'aes-128-ctr' , encryptionKey , iv ) ; const firstChunk = cipher . update ( plaintext ) ; const secondChunk = cipher . final ( ) ; return Buffer . concat ( [ iv , firstChunk , secondChunk ] ) ; } param { Buffer } encryptionKey param { Buffer } macKey param { Buffer } plaintext returns { Buffer } export function AES128EncryptAndHMAC ( encryptionKey , macKey , plaintext ) { const iv = randomBytes ( IV_LENGTH ) ; const dataToMac = AES128Encrypt ( encryptionKey , iv , plaintext ) ; const mac = createHmac ( 'sha256' , macKey ) . update ( dataToMac ) . digest ( ) ; return Buffer . concat ( [ dataToMac , mac ] ) ; } param { Buffer } encryptionKey param { Buffer } iv param { Buffer } ciphertext returns { Buffer } export function AES128Decrypt ( encryptionKey , iv , ciphertext ) { const cipher = createDecipheriv ( 'aes-128-ctr' , encryptionKey , iv ) ; const firstChunk = cipher . update ( ciphertext ) ; const secondChunk = cipher . final ( ) ; return Buffer . concat ( [ firstChunk , secondChunk ] ) ; } param { Buffer } encryptionKey param { Buffer } macKey param { Buffer } ciphertext returns { Buffer } export function AES128DecryptAndHMAC ( encryptionKey , macKey , ciphertext ) { const iv = ciphertext . slice ( 0 , IV_LENGTH ) ; const message = ciphertext . slice ( IV_LENGTH , ciphertext . length - 32 ) ; const mac = ciphertext . slice ( ciphertext . length - 32 , ciphertext . length ) ; const dataToMac = Buffer . concat ( [ iv , message ] ) ; const computedMac = createHmac ( 'sha256' , macKey ) . update ( dataToMac ) . digest ( ) ; if ( ! mac . equals ( computedMac ) ) { throw new Error ( 'MAC mismatch' ) ; } return AES128Decrypt ( encryptionKey , iv , message ) ; } param { Buffer } pubKeyTo param { Buffer } plaintext returns { Buffer } export function Encrypt ( pubKeyTo , plaintext ) { const ephemPrivKey = ec . keyFromPrivate ( randomBytes ( 32 ) ) ; const ephemPubKey = ephemPrivKey . getPublic ( false , 'hex' ) ; const ephemPubKeyEncoded = Buffer . from ( ephemPubKey , 'hex' ) ; const px = ephemPrivKey . derive ( ec . keyFromPublic ( Buffer . concat ( [ Buffer . from ( [ 0x04 ] ) , pubKeyTo ] ) ) . getPublic ( ) ) ; const hash = ConcatKDF ( px . toArrayLike ( Buffer ) , 32 ) ; const encryptionKey = hash . slice ( 0 , 16 ) ; const macKey = createHash ( 'sha256' ) . update ( hash . slice ( 16 ) ) . digest ( ) ; const message = AES128EncryptAndHMAC ( encryptionKey , macKey , plaintext ) ; const serializedCiphertext = Buffer . concat ( [ ephemPubKeyEncoded , message , ] ) ; return serializedCiphertext ; } param { Buffer } privKey param { Buffer } encrypted returns { Buffer } export function Decrypt ( privKey , encrypted ) { const ephemPubKeyEncoded = encrypted . slice ( 0 , 65 ) ; const symmetricEncrypted = encrypted . slice ( 65 ) ; const ephemPubKey = ec . keyFromPublic ( ephemPubKeyEncoded ) . getPublic ( ) ; const px = ec . keyFromPrivate ( privKey ) . derive ( ephemPubKey ) ; const hash = ConcatKDF ( px . toBuffer ( ) , 32 ) ; const encryptionKey = hash . slice ( 0 , 16 ) ; const macKey = createHash ( 'sha256' ) . update ( hash . slice ( 16 ) ) . digest ( ) ; return AES128DecryptAndHMAC ( encryptionKey , macKey , symmetricEncrypted ) ; } export const ECIES = { Encrypt , Decrypt , AES128EncryptAndHMAC , AES128DecryptAndHMAC } ;