import * as base58 from 'base-58' ; import { createCipheriv , createDecipheriv } from 'crypto' ; import * as CryptoJS from 'crypto-js' ; import * as asyncScrypt from 'scrypt-async' ; import { DEFAULT_SCRYPT , OEP_FLAG , OEP_HEADER } from './consts' ; import { Address } from './crypto/address' ; import { PublicKey } from './crypto/PublicKey' ; import { ERROR_CODE } from './error' ; import { ab2hexstring , hexstring2ab , hexXor , isHexString , StringReader } from './utils' ; /**
 * Decribtes the structure of params for scrypt
 */ export interface ScryptParams { cost : number ; blockSize : number ; parallel : number ; size : number ; } param keyphrase param addressHash param params function scrypt ( keyphrase , addressHash , params ) { let derived = [ ] ; asyncScrypt ( keyphrase . normalize ( 'NFC' ) , hexstring2ab ( addressHash ) , { N : params . cost , r : params . blockSize , p : params . parallel , dkLen : params . size } , ( result ) => { derived = result as number [ ] ; } ) ; return new Buffer ( derived ) ; } /**
 * Encrypt with aes-ctr
 */ export function encryptWithCtr ( privateKey , publicKeyEncoded , keyphrase , scryptParams = DEFAULT_SCRYPT ) { const publicKey = PublicKey . deserializeHex ( new StringReader ( publicKeyEncoded ) ) ; const address = Address . fromPubKey ( publicKey ) ; const addresshash = address . getB58Checksum ( ) ; const derived = scrypt ( keyphrase , addresshash , scryptParams ) . toString ( 'hex' ) ; const derived1 = derived . slice ( 0 , 32 ) ; const derived2 = derived . slice ( 64 ) ; const iv = CryptoJS . enc . Hex . parse ( derived1 ) ; const encrypted = CryptoJS . AES . encrypt ( CryptoJS . enc . Hex . parse ( privateKey ) , CryptoJS . enc . Hex . parse ( derived2 ) , { mode : CryptoJS . mode . CTR , padding : CryptoJS . pad . NoPadding , iv } ) ; const assembled = encrypted . ciphertext . toString ( ) ; return new Buffer ( assembled , 'hex' ) . toString ( 'base64' ) ; } param encryptedKey param keyphrase param saltOrAddress export function decryptWithCtr ( encryptedKey , keyphrase , saltOrAddress , scryptParams = DEFAULT_SCRYPT ) { const encrypted = Buffer . from ( encryptedKey , 'base64' ) . toString ( 'hex' ) ; let salt = '' ; if ( typeof saltOrAddress === 'string' && saltOrAddress . length === 8 ) { salt = saltOrAddress ; } else if ( saltOrAddress instanceof Address ) { salt = saltOrAddress . getB58Checksum ( ) ; } else { throw ERROR_CODE . INVALID_PARAMS ; } const derived = scrypt ( keyphrase , salt , scryptParams ) . toString ( 'hex' ) ; const derived1 = derived . slice ( 0 , 32 ) ; const derived2 = derived . slice ( 64 ) ; const iv = CryptoJS . enc . Hex . parse ( derived1 ) ; const ciphertexts = { ciphertext : CryptoJS . enc . Hex . parse ( encrypted ) , salt : '' , iv : '' } ; const decrypted = CryptoJS . AES . decrypt ( ciphertexts , CryptoJS . enc . Hex . parse ( derived2 ) , { mode : CryptoJS . mode . CTR , padding : CryptoJS . pad . NoPadding , iv } ) ; const privateKey = decrypted . toString ( ) ; return privateKey ; } param saltOrAddress param publicKeyEncoded export function checkCtrDecrypted ( saltOrAddress , publicKeyEncoded ) { let salt = '' ; if ( typeof saltOrAddress === 'string' && saltOrAddress . length === 8 ) { salt = saltOrAddress ; } else if ( saltOrAddress instanceof Address ) { salt = saltOrAddress . getB58Checksum ( ) ; } else { throw ERROR_CODE . INVALID_PARAMS ; } const publicKey = PublicKey . deserializeHex ( new StringReader ( publicKeyEncoded ) ) ; const address = Address . fromPubKey ( publicKey ) ; const saltNew = address . getB58Checksum ( ) ; if ( saltNew !== salt ) { console . log ( 'keyphrase error.' ) ; throw ERROR_CODE . Decrypto_ERROR ; } } /**
 * Encrypt with aes-ecb
 */ export function encryptWithEcb ( privateKey , publicKeyEncoded , keyphrase , scryptParams = DEFAULT_SCRYPT ) { const publicKey = PublicKey . deserializeHex ( new StringReader ( publicKeyEncoded ) ) ; const address = Address . fromPubKey ( publicKey ) ; const addresshash = address . getB58Checksum ( ) ; const derived = scrypt ( keyphrase , addresshash , scryptParams ) . toString ( 'hex' ) ; const derived1 = derived . slice ( 0 , 64 ) ; const derived2 = derived . slice ( 64 ) ; const xor = hexXor ( privateKey , derived1 ) ; const encrypted = CryptoJS . AES . encrypt ( CryptoJS . enc . Hex . parse ( xor ) , CryptoJS . enc . Hex . parse ( derived2 ) , { mode : CryptoJS . mode . ECB , padding : CryptoJS . pad . NoPadding } ) ; const assembled = OEP_HEADER + OEP_FLAG + addresshash + encrypted . ciphertext . toString ( ) ; return base58 . encode ( Buffer . from ( assembled , 'hex' ) ) ; } /**
 * Decrypt with aes-ecb
 */ export function decryptWithEcb ( encryptedKey , keyphrase , scryptParams = DEFAULT_SCRYPT ) { const assembled = ab2hexstring ( base58 . decode ( encryptedKey ) ) ; const addressHash = assembled . substr ( 6 , 8 ) ; const encrypted = assembled . substr ( - 64 ) ; const derived = scrypt ( keyphrase , addressHash , scryptParams ) . toString ( 'hex' ) ; const derived1 = derived . slice ( 0 , 64 ) ; const derived2 = derived . slice ( 64 ) ; const ciphertexts = { ciphertext : CryptoJS . enc . Hex . parse ( encrypted ) , salt : '' , iv : '' } ; const decrypted = CryptoJS . AES . decrypt ( ciphertexts , CryptoJS . enc . Hex . parse ( derived2 ) , { mode : CryptoJS . mode . ECB , padding : CryptoJS . pad . NoPadding } ) ; const privateKey = hexXor ( decrypted . toString ( ) , derived1 ) ; return privateKey ; } param encryptedKey param decryptedKey param publicKeyEncoded export function checkEcbDecrypted ( encryptedKey , decryptedKey , publicKeyEncoded ) { const assembled = ab2hexstring ( base58 . decode ( encryptedKey ) ) ; const addressHash = assembled . substr ( 6 , 8 ) ; const publicKey = PublicKey . deserializeHex ( new StringReader ( publicKeyEncoded ) ) ; const address = Address . fromPubKey ( publicKey ) ; const addressHashNew = address . getB58Checksum ( ) ; if ( addressHashNew !== addressHash ) { console . log ( 'keyphrase error.' ) ; throw ERROR_CODE . Decrypto_ERROR ; } } param privateKey param address param salt param keyphrase param scryptParams export function encryptWithGcm ( privateKey , address , salt , keyphrase , scryptParams = DEFAULT_SCRYPT ) { if ( ! isHexString ( privateKey ) ) { throw new Error ( ERROR_CODE . INVALID_PARAMS + ', Invalid private key' ) ; } const derived = scrypt ( keyphrase , salt , scryptParams ) ; const derived1 = derived . slice ( 0 , 12 ) ; const derived2 = derived . slice ( 32 ) ; const key = derived2 ; const iv = derived1 ; const aad = new Buffer ( address . toBase58 ( ) ) ; const cipher = createCipheriv ( 'aes-256-gcm' , key , iv ) ; cipher . setAAD ( aad ) ; const plainText = Buffer . from ( privateKey , 'hex' ) ; let ciphertext = cipher . update ( plainText ) ; const final = cipher . final ( ) ; const authTag = cipher . getAuthTag ( ) ; ciphertext = Buffer . concat ( [ ciphertext , final ] ) ; const result = Buffer . concat ( [ ciphertext , authTag ] ) ; return result . toString ( 'base64' ) ; } param encrypted param address param salt param keyphrase param scryptParams export function decryptWithGcm ( encrypted , address , salt , keyphrase , scryptParams = DEFAULT_SCRYPT ) { if ( salt . length !== 32 ) { throw ERROR_CODE . INVALID_PARAMS ; } const result = Buffer . from ( encrypted , 'base64' ) ; const ciphertext = result . slice ( 0 , result . length - 16 ) ; const authTag = result . slice ( result . length - 16 ) ; const derived = scrypt ( keyphrase , salt , scryptParams ) ; const derived1 = derived . slice ( 0 , 12 ) ; const derived2 = derived . slice ( 32 ) ; const key = derived2 ; const iv = derived1 ; const aad = new Buffer ( address . toBase58 ( ) ) ; const decipher = createDecipheriv ( 'aes-256-gcm' , key , iv ) ; decipher . setAAD ( aad ) ; decipher . setAuthTag ( authTag ) ; let decrypted = decipher . update ( ciphertext ) . toString ( 'hex' ) ; try { decrypted += decipher . final ( ) . toString ( 'hex' ) ; } catch ( err ) { throw ERROR_CODE . Decrypto_ERROR ; } return decrypted ; }