import crypto from 'crypto' ; import { radixHash } from '../common/RadixUtil' ; import { RadixSimpleIdentity , RadixAddress } from '../..' ; interface KeystoreData { crypto : { cipher : string ; cipherparams : { iv : string ; } ; ciphertext : string ; pbkdfparams : { iterations : number ; keylen : number ; digest : string ; salt : string ; } ; mac : string ; } ; id : string ; } export default class RadixKeyStore { param identity param password returns public static encryptSimpleIdentity ( identity , password ) { const privateKey = identity . address . keyPair . getPrivate ( 'hex' ) ; const id = identity . address . getUID ( ) . toString ( ) ; return this . encryptData ( privateKey , id , password ) ; } public static encryptData ( data , id , password ) { return new Promise ( ( resolve , reject ) => { const salt = crypto . randomBytes ( 32 ) . toString ( 'hex' ) ; const iterations = 100000 ; const keylen = 32 ; const digest = 'sha512' ; crypto . pbkdf2 ( password , salt , iterations , keylen , digest , ( error , derivedKey ) => { if ( error ) { reject ( error ) ; } const algorithm = 'aes-256-ctr' ; const iv = crypto . randomBytes ( 16 ) ; const cipher = crypto . createCipheriv ( algorithm , derivedKey , iv ) ; const ciphertext = Buffer . concat ( [ cipher . update ( data ) , cipher . final ( ) , ] ) ; const mac = this . calculateMac ( derivedKey , ciphertext ) ; const keystoreData = { crypto : { cipher : algorithm , cipherparams : { iv : iv . toString ( 'hex' ) , } , ciphertext : ciphertext . toString ( 'hex' ) , pbkdfparams : { iterations , keylen , digest , salt , } , mac : mac . toString ( 'hex' ) , } , id , } ; resolve ( keystoreData ) ; } ) ; } ) ; } param keystoreData param password returns public static decryptSimpleIdentity ( keystoreData , password ) { return this . decryptKeystore ( keystoreData , password ) . then ( ( data ) => { return new RadixSimpleIdentity ( RadixAddress . fromPrivate ( data ) ) ; } ) ; } public static decryptKeystore ( keystoreData , password ) { return new Promise < string > ( ( resolve , reject ) => { const salt = keystoreData . crypto . pbkdfparams . salt ; const iterations = keystoreData . crypto . pbkdfparams . iterations ; const keylen = keystoreData . crypto . pbkdfparams . keylen ; const digest = keystoreData . crypto . pbkdfparams . digest ; crypto . pbkdf2 ( password , salt , iterations , keylen , digest , ( error , derivedKey ) => { if ( error ) { return reject ( error ) ; } const algorithm = keystoreData . crypto . cipher ; const iv = Buffer . from ( keystoreData . crypto . cipherparams . iv , 'hex' ) ; const ciphertext = Buffer . from ( keystoreData . crypto . ciphertext , 'hex' ) ; const mac = Buffer . from ( keystoreData . crypto . mac , 'hex' ) ; const computedMAC = this . calculateMac ( derivedKey , ciphertext ) ; if ( ! computedMAC . equals ( mac ) ) { return reject ( 'mac mismatch' ) ; } const decipher = crypto . createDecipheriv ( algorithm , derivedKey , iv ) ; const data = Buffer . concat ( [ decipher . update ( ciphertext ) , decipher . final ( ) , ] ) . toString ( ) ; return resolve ( data ) ; } ) ; } ) ; } private static calculateMac ( derivedKey , ciphertext ) { const dataToMac = Buffer . concat ( [ derivedKey , ciphertext ] ) ; return radixHash ( dataToMac ) ; } }