import * as crypto from 'crypto' ; import * as elliptic from 'elliptic' ; import * as pkcs7 from 'pkcs7' ; export class Ecies { /**
     * Algorithm used for encryption.
     */ encAlg : string ; /**
     * Hash Algorithm used for kdf.
     */ hashAlg : string ; /**
     * Hash digest byte size.
     */ digestSize : number ; /**
     * Key input and out put format.
     */ keyFormat : string ; /**
     * Initialized Vector
     */ iv : Buffer ; /**
     * Elliptic Curve instance
     */ ec : any ; /**
     * key pair from
     * ec instance
     */ keyPair : any ; /**
     * if true, the pubkey will be
     * in compressed format, begin with '02' or '03'.
     * if false, begin with '04'
     */ compact : boolean ; /**
     * for curve name,
     * go https://github.com/indutny/elliptic
     * for reference
     */ constructor ( Curve ? ) { this . encAlg = 'aes-256-cbc' ; this . hashAlg = 'sha256' ; this . digestSize = 32 ; this . keyFormat = 'hex' ; this . compact = true ; const curve = Curve || 'p256' ; this . ec = new elliptic . ec ( curve ) ; this . keyPair = this . ec . genKeyPair ( ) ; this . iv = Buffer . alloc ( 0 ) ; } /**
     * generate random key pair
     */ generateKeyPair ( ) { this . keyPair = this . ec . genKeyPair ( ) ; return { priv : this . keyPair . getPrivate ( 'hex' ) , pub : this . keyPair . getPublic ( this . compact , 'hex' ) } ; } param privHex setKeyPair ( privHex ) { this . keyPair = this . ec . keyFromPrivate ( privHex , 'hex' ) ; } return getKeyPair ( ) { return { priv : this . keyPair . getPrivate ( 'hex' ) , pub : this . keyPair . getPublic ( this . compact , 'hex' ) } ; } param pubkey param msg param keylen enc ( pubkey , msg , keylen , _iv ? ) { const publicB = this . ec . keyFromPublic ( pubkey , 'hex' ) . getPublic ( ) ; const tmpKP = this . ec . genKeyPair ( ) ; const r = tmpKP . getPrivate ( ) ; const gTilde = tmpKP . getPublic ( ) ; const hTilde = publicB . mul ( r ) ; const out = gTilde . encode ( 'hex' ) ; const PEH = hTilde . getX ( ) . toString ( 'hex' ) ; const seed = Buffer . from ( out + PEH , 'hex' ) ; const derivedKeyArray = this . kdf2 ( seed , keylen * 8 , this . digestSize , this . hashAlg ) ; if ( ! derivedKeyArray ) { return ; } const derivedKey = Buffer . concat ( derivedKeyArray ) ; let iv = Buffer . alloc ( 16 ) ; if ( ! _iv ) { crypto . randomFillSync ( iv ) ; } else { iv = Buffer . from ( _iv , 'hex' ) ; } const algorithm = this . encAlg ; const cipher = crypto . createCipheriv ( algorithm , derivedKey , iv ) ; cipher . setAutoPadding ( false ) ; const msgCipher = cipher . update ( pkcs7 . pad ( msg ) , 'binary' , 'hex' ) + cipher . final ( 'hex' ) ; return { iv : iv . toString ( 'hex' ) , out , msgCipher } ; } param msgCipher param out param iv param keylen dec ( msgCipher , out , iv , keylen ) { const gTilde = this . ec . keyFromPublic ( out , 'hex' ) . getPublic ( ) ; const hTilde = gTilde . mul ( this . keyPair . getPrivate ( ) ) ; const PEH = hTilde . getX ( ) . toString ( 'hex' ) ; const seed = Buffer . from ( out + PEH , 'hex' ) ; const derivedKeyArray = this . kdf2 ( seed , keylen * 8 , this . digestSize , this . hashAlg ) ; if ( ! derivedKeyArray ) { return Buffer . alloc ( 0 ) ; } const derivedKey = Buffer . concat ( derivedKeyArray ) ; const iv2 = Buffer . from ( iv , 'hex' ) ; const algorithm = this . encAlg ; const decipher = crypto . createDecipheriv ( algorithm , derivedKey , iv2 ) ; decipher . setAutoPadding ( false ) ; const plain = decipher . update ( msgCipher , 'hex' , 'binary' ) + decipher . final ( 'binary' ) ; const unpad = pkcs7 . unpad ( Buffer . from ( plain , 'binary' ) ) ; return Buffer . from ( unpad ) ; } kdf2 ( seed , len , digestSize , hashFunc ) { if ( len < 0 ) { return [ ] ; } const byteLen = Math . ceil ( len / 8 ) ; const b = Math . ceil ( byteLen / digestSize ) ; const key = [ ] ; const offset = byteLen - ( b - 1 ) * digestSize ; let counter = 1 ; let hashIns ; while ( counter < b ) { hashIns = crypto . createHash ( hashFunc ) ; const h = hashIns . update ( Buffer . concat ( [ seed , this . I2OSP ( counter , 4 ) ] ) ) . digest ( ) ; key [ counter - 1 ] = Buffer . alloc ( offset ) ; key [ counter - 1 ] = h ; counter ++ ; } hashIns = crypto . createHash ( hashFunc ) ; const hEnd = hashIns . update ( Buffer . concat ( [ seed , this . I2OSP ( counter , 4 ) ] ) ) . digest ( ) ; key [ counter - 1 ] = Buffer . alloc ( offset ) ; key [ counter - 1 ] = hEnd ; return key ; } I2OSP ( num , len ) { const buf = Buffer . allocUnsafe ( len ) ; buf . writeUIntBE ( num , 0 , len ) ; return buf ; } }