2022-09-05 16:57:11 -04:00
"use strict" ;
2022-11-30 15:44:23 -05:00
/ * *
* The JSON Wallet formats allow a simple way to store the private
* keys needed in Ethereum along with related information and allows
* for extensible forms of encryption .
*
* These utilities facilitate decrypting and encrypting the most common
* JSON Wallet formats .
*
* @ _subsection : api / wallet : JSON Wallets [ json - wallets ]
* /
2022-09-05 16:57:11 -04:00
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2022-11-30 15:44:23 -05:00
exports . encryptKeystoreJson = exports . encryptKeystoreJsonSync = exports . decryptKeystoreJson = exports . decryptKeystoreJsonSync = exports . isKeystoreJson = void 0 ;
2022-09-05 16:57:11 -04:00
const aes _js _1 = require ( "aes-js" ) ;
const index _js _1 = require ( "../address/index.js" ) ;
const index _js _2 = require ( "../crypto/index.js" ) ;
const index _js _3 = require ( "../transaction/index.js" ) ;
const index _js _4 = require ( "../utils/index.js" ) ;
const utils _js _1 = require ( "./utils.js" ) ;
const _version _js _1 = require ( "../_version.js" ) ;
const defaultPath = "m/44'/60'/0'/0/0" ;
2022-11-30 15:44:23 -05:00
/ * *
* Returns true if % % json % % is a valid JSON Keystore Wallet .
* /
2022-09-05 16:57:11 -04:00
function isKeystoreJson ( json ) {
try {
const data = JSON . parse ( json ) ;
const version = ( ( data . version != null ) ? parseInt ( data . version ) : 0 ) ;
if ( version === 3 ) {
return true ;
}
}
catch ( error ) { }
return false ;
}
exports . isKeystoreJson = isKeystoreJson ;
function decrypt ( data , key , ciphertext ) {
const cipher = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.cipher:string" ) ;
if ( cipher === "aes-128-ctr" ) {
const iv = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.cipherparams.iv:data!" ) ;
const aesCtr = new aes _js _1 . CTR ( key , iv ) ;
return ( 0 , index _js _4 . hexlify ) ( aesCtr . decrypt ( ciphertext ) ) ;
}
2022-11-09 02:57:02 -05:00
( 0 , index _js _4 . assert ) ( false , "unsupported cipher" , "UNSUPPORTED_OPERATION" , {
2022-09-05 16:57:11 -04:00
operation : "decrypt"
} ) ;
}
function getAccount ( data , _key ) {
2022-09-15 22:58:45 -04:00
const key = ( 0 , index _js _4 . getBytes ) ( _key ) ;
2022-09-05 16:57:11 -04:00
const ciphertext = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.ciphertext:data!" ) ;
const computedMAC = ( 0 , index _js _4 . hexlify ) ( ( 0 , index _js _2 . keccak256 ) ( ( 0 , index _js _4 . concat ) ( [ key . slice ( 16 , 32 ) , ciphertext ] ) ) ) . substring ( 2 ) ;
2022-11-09 02:57:02 -05:00
( 0 , index _js _4 . assertArgument ) ( computedMAC === ( 0 , utils _js _1 . spelunk ) ( data , "crypto.mac:string!" ) . toLowerCase ( ) , "incorrect password" , "password" , "[ REDACTED ]" ) ;
2022-09-05 16:57:11 -04:00
const privateKey = decrypt ( data , key . slice ( 0 , 16 ) , ciphertext ) ;
const address = ( 0 , index _js _3 . computeAddress ) ( privateKey ) ;
if ( data . address ) {
let check = data . address . toLowerCase ( ) ;
2023-02-18 22:18:42 -05:00
if ( ! check . startsWith ( "0x" ) ) {
2022-09-05 16:57:11 -04:00
check = "0x" + check ;
}
2022-11-09 02:57:02 -05:00
( 0 , index _js _4 . assertArgument ) ( ( 0 , index _js _1 . getAddress ) ( check ) === address , "keystore address/privateKey mismatch" , "address" , data . address ) ;
2022-09-05 16:57:11 -04:00
}
const account = { address , privateKey } ;
// Version 0.1 x-ethers metadata must contain an encrypted mnemonic phrase
const version = ( 0 , utils _js _1 . spelunk ) ( data , "x-ethers.version:string" ) ;
if ( version === "0.1" ) {
const mnemonicKey = key . slice ( 32 , 64 ) ;
const mnemonicCiphertext = ( 0 , utils _js _1 . spelunk ) ( data , "x-ethers.mnemonicCiphertext:data!" ) ;
const mnemonicIv = ( 0 , utils _js _1 . spelunk ) ( data , "x-ethers.mnemonicCounter:data!" ) ;
const mnemonicAesCtr = new aes _js _1 . CTR ( mnemonicKey , mnemonicIv ) ;
account . mnemonic = {
path : ( ( 0 , utils _js _1 . spelunk ) ( data , "x-ethers.path:string" ) || defaultPath ) ,
locale : ( ( 0 , utils _js _1 . spelunk ) ( data , "x-ethers.locale:string" ) || "en" ) ,
2022-09-15 22:58:45 -04:00
entropy : ( 0 , index _js _4 . hexlify ) ( ( 0 , index _js _4 . getBytes ) ( mnemonicAesCtr . decrypt ( mnemonicCiphertext ) ) )
2022-09-05 16:57:11 -04:00
} ;
}
return account ;
}
2022-11-10 04:05:51 -05:00
function getDecryptKdfParams ( data ) {
2022-09-05 16:57:11 -04:00
const kdf = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdf:string" ) ;
if ( kdf && typeof ( kdf ) === "string" ) {
if ( kdf . toLowerCase ( ) === "scrypt" ) {
const salt = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.salt:data!" ) ;
const N = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.n:int!" ) ;
const r = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.r:int!" ) ;
const p = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.p:int!" ) ;
// Make sure N is a power of 2
2022-11-30 15:44:23 -05:00
( 0 , index _js _4 . assertArgument ) ( N > 0 && ( N & ( N - 1 ) ) === 0 , "invalid kdf.N" , "kdf.N" , N ) ;
( 0 , index _js _4 . assertArgument ) ( r > 0 && p > 0 , "invalid kdf" , "kdf" , kdf ) ;
2022-09-05 16:57:11 -04:00
const dkLen = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.dklen:int!" ) ;
2022-11-30 15:44:23 -05:00
( 0 , index _js _4 . assertArgument ) ( dkLen === 32 , "invalid kdf.dklen" , "kdf.dflen" , dkLen ) ;
2022-09-05 16:57:11 -04:00
return { name : "scrypt" , salt , N , r , p , dkLen : 64 } ;
}
else if ( kdf . toLowerCase ( ) === "pbkdf2" ) {
const salt = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.salt:data!" ) ;
const prf = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.prf:string!" ) ;
const algorithm = prf . split ( "-" ) . pop ( ) ;
2022-11-30 15:44:23 -05:00
( 0 , index _js _4 . assertArgument ) ( algorithm === "sha256" || algorithm === "sha512" , "invalid kdf.pdf" , "kdf.pdf" , prf ) ;
2022-09-05 16:57:11 -04:00
const count = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.c:int!" ) ;
const dkLen = ( 0 , utils _js _1 . spelunk ) ( data , "crypto.kdfparams.dklen:int!" ) ;
2022-11-30 15:44:23 -05:00
( 0 , index _js _4 . assertArgument ) ( dkLen === 32 , "invalid kdf.dklen" , "kdf.dklen" , dkLen ) ;
2022-09-05 16:57:11 -04:00
return { name : "pbkdf2" , salt , count , dkLen , algorithm } ;
}
}
2022-11-09 02:57:02 -05:00
( 0 , index _js _4 . assertArgument ) ( false , "unsupported key-derivation function" , "kdf" , kdf ) ;
2022-09-05 16:57:11 -04:00
}
2022-11-30 15:44:23 -05:00
/ * *
* Returns the account details for the JSON Keystore Wallet % % json % %
* using % % password % % .
*
* It is preferred to use the [ async version ] ( decryptKeystoreJson )
* instead , which allows a [ [ ProgressCallback ] ] to keep the user informed
* as to the decryption status .
*
* This method will block the event loop ( freezing all UI ) until decryption
* is complete , which can take quite some time , depending on the wallet
* paramters and platform .
* /
2022-09-05 16:57:11 -04:00
function decryptKeystoreJsonSync ( json , _password ) {
const data = JSON . parse ( json ) ;
const password = ( 0 , utils _js _1 . getPassword ) ( _password ) ;
2022-11-10 04:05:51 -05:00
const params = getDecryptKdfParams ( data ) ;
2022-09-05 16:57:11 -04:00
if ( params . name === "pbkdf2" ) {
const { salt , count , dkLen , algorithm } = params ;
const key = ( 0 , index _js _2 . pbkdf2 ) ( password , salt , count , dkLen , algorithm ) ;
return getAccount ( data , key ) ;
}
2022-11-10 04:05:51 -05:00
( 0 , index _js _4 . assert ) ( params . name === "scrypt" , "cannot be reached" , "UNKNOWN_ERROR" , { params } ) ;
const { salt , N , r , p , dkLen } = params ;
const key = ( 0 , index _js _2 . scryptSync ) ( password , salt , N , r , p , dkLen ) ;
return getAccount ( data , key ) ;
2022-09-05 16:57:11 -04:00
}
exports . decryptKeystoreJsonSync = decryptKeystoreJsonSync ;
function stall ( duration ) {
return new Promise ( ( resolve ) => { setTimeout ( ( ) => { resolve ( ) ; } , duration ) ; } ) ;
}
2022-11-30 15:44:23 -05:00
/ * *
* Resolves to the decrypted JSON Keystore Wallet % % json % % using the
* % % password % % .
*
* If provided , % % progress % % will be called periodically during the
* decrpytion to provide feedback , and if the function returns
* ` ` false ` ` will halt decryption .
*
* The % % progressCallback % % will * * always * * receive ` ` 0 ` ` before
* decryption begins and ` ` 1 ` ` when complete .
* /
2022-09-05 16:57:11 -04:00
async function decryptKeystoreJson ( json , _password , progress ) {
const data = JSON . parse ( json ) ;
const password = ( 0 , utils _js _1 . getPassword ) ( _password ) ;
2022-11-10 04:05:51 -05:00
const params = getDecryptKdfParams ( data ) ;
2022-09-05 16:57:11 -04:00
if ( params . name === "pbkdf2" ) {
if ( progress ) {
progress ( 0 ) ;
await stall ( 0 ) ;
}
const { salt , count , dkLen , algorithm } = params ;
const key = ( 0 , index _js _2 . pbkdf2 ) ( password , salt , count , dkLen , algorithm ) ;
if ( progress ) {
progress ( 1 ) ;
await stall ( 0 ) ;
}
return getAccount ( data , key ) ;
}
2022-11-10 04:05:51 -05:00
( 0 , index _js _4 . assert ) ( params . name === "scrypt" , "cannot be reached" , "UNKNOWN_ERROR" , { params } ) ;
const { salt , N , r , p , dkLen } = params ;
const key = await ( 0 , index _js _2 . scrypt ) ( password , salt , N , r , p , dkLen , progress ) ;
return getAccount ( data , key ) ;
2022-09-05 16:57:11 -04:00
}
exports . decryptKeystoreJson = decryptKeystoreJson ;
2022-11-10 04:05:51 -05:00
function getEncryptKdfParams ( options ) {
2022-09-05 16:57:11 -04:00
// Check/generate the salt
2022-11-10 04:05:51 -05:00
const salt = ( options . salt != null ) ? ( 0 , index _js _4 . getBytes ) ( options . salt , "options.salt" ) : ( 0 , index _js _2 . randomBytes ) ( 32 ) ;
2022-09-05 16:57:11 -04:00
// Override the scrypt password-based key derivation function parameters
let N = ( 1 << 17 ) , r = 8 , p = 1 ;
if ( options . scrypt ) {
if ( options . scrypt . N ) {
N = options . scrypt . N ;
}
if ( options . scrypt . r ) {
r = options . scrypt . r ;
}
if ( options . scrypt . p ) {
p = options . scrypt . p ;
}
}
2022-11-30 15:44:23 -05:00
( 0 , index _js _4 . assertArgument ) ( typeof ( N ) === "number" && N > 0 && Number . isSafeInteger ( N ) && ( BigInt ( N ) & BigInt ( N - 1 ) ) === BigInt ( 0 ) , "invalid scrypt N parameter" , "options.N" , N ) ;
( 0 , index _js _4 . assertArgument ) ( typeof ( r ) === "number" && r > 0 && Number . isSafeInteger ( r ) , "invalid scrypt r parameter" , "options.r" , r ) ;
( 0 , index _js _4 . assertArgument ) ( typeof ( p ) === "number" && p > 0 && Number . isSafeInteger ( p ) , "invalid scrypt p parameter" , "options.p" , p ) ;
2022-11-10 04:05:51 -05:00
return { name : "scrypt" , dkLen : 32 , salt , N , r , p } ;
}
function _encryptKeystore ( key , kdf , account , options ) {
const privateKey = ( 0 , index _js _4 . getBytes ) ( account . privateKey , "privateKey" ) ;
// Override initialization vector
const iv = ( options . iv != null ) ? ( 0 , index _js _4 . getBytes ) ( options . iv , "options.iv" ) : ( 0 , index _js _2 . randomBytes ) ( 16 ) ;
2022-11-30 15:44:23 -05:00
( 0 , index _js _4 . assertArgument ) ( iv . length === 16 , "invalid options.iv length" , "options.iv" , options . iv ) ;
2022-11-10 04:05:51 -05:00
// Override the uuid
const uuidRandom = ( options . uuid != null ) ? ( 0 , index _js _4 . getBytes ) ( options . uuid , "options.uuid" ) : ( 0 , index _js _2 . randomBytes ) ( 16 ) ;
2022-11-30 15:44:23 -05:00
( 0 , index _js _4 . assertArgument ) ( uuidRandom . length === 16 , "invalid options.uuid length" , "options.uuid" , options . iv ) ;
2022-09-05 16:57:11 -04:00
// This will be used to encrypt the wallet (as per Web3 secret storage)
2022-11-10 04:05:51 -05:00
// - 32 bytes As normal for the Web3 secret storage (derivedKey, macPrefix)
// - 32 bytes AES key to encrypt mnemonic with (required here to be Ethers Wallet)
2022-09-05 16:57:11 -04:00
const derivedKey = key . slice ( 0 , 16 ) ;
const macPrefix = key . slice ( 16 , 32 ) ;
// Encrypt the private key
const aesCtr = new aes _js _1 . CTR ( derivedKey , iv ) ;
2022-09-15 22:58:45 -04:00
const ciphertext = ( 0 , index _js _4 . getBytes ) ( aesCtr . encrypt ( privateKey ) ) ;
2022-09-05 16:57:11 -04:00
// Compute the message authentication code, used to check the password
const mac = ( 0 , index _js _2 . keccak256 ) ( ( 0 , index _js _4 . concat ) ( [ macPrefix , ciphertext ] ) ) ;
// See: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
const data = {
address : account . address . substring ( 2 ) . toLowerCase ( ) ,
2022-11-30 15:44:23 -05:00
id : ( 0 , index _js _4 . uuidV4 ) ( uuidRandom ) ,
2022-09-05 16:57:11 -04:00
version : 3 ,
Crypto : {
cipher : "aes-128-ctr" ,
cipherparams : {
iv : ( 0 , index _js _4 . hexlify ) ( iv ) . substring ( 2 ) ,
} ,
ciphertext : ( 0 , index _js _4 . hexlify ) ( ciphertext ) . substring ( 2 ) ,
kdf : "scrypt" ,
kdfparams : {
2022-11-10 04:05:51 -05:00
salt : ( 0 , index _js _4 . hexlify ) ( kdf . salt ) . substring ( 2 ) ,
n : kdf . N ,
2022-09-05 16:57:11 -04:00
dklen : 32 ,
2022-11-10 04:05:51 -05:00
p : kdf . p ,
r : kdf . r
2022-09-05 16:57:11 -04:00
} ,
mac : mac . substring ( 2 )
}
} ;
// If we have a mnemonic, encrypt it into the JSON wallet
if ( account . mnemonic ) {
const client = ( options . client != null ) ? options . client : ` ethers/ ${ _version _js _1 . version } ` ;
const path = account . mnemonic . path || defaultPath ;
const locale = account . mnemonic . locale || "en" ;
const mnemonicKey = key . slice ( 32 , 64 ) ;
2022-09-15 22:58:45 -04:00
const entropy = ( 0 , index _js _4 . getBytes ) ( account . mnemonic . entropy , "account.mnemonic.entropy" ) ;
2022-09-05 16:57:11 -04:00
const mnemonicIv = ( 0 , index _js _2 . randomBytes ) ( 16 ) ;
const mnemonicAesCtr = new aes _js _1 . CTR ( mnemonicKey , mnemonicIv ) ;
2022-09-15 22:58:45 -04:00
const mnemonicCiphertext = ( 0 , index _js _4 . getBytes ) ( mnemonicAesCtr . encrypt ( entropy ) ) ;
2022-09-05 16:57:11 -04:00
const now = new Date ( ) ;
const timestamp = ( now . getUTCFullYear ( ) + "-" +
( 0 , utils _js _1 . zpad ) ( now . getUTCMonth ( ) + 1 , 2 ) + "-" +
( 0 , utils _js _1 . zpad ) ( now . getUTCDate ( ) , 2 ) + "T" +
( 0 , utils _js _1 . zpad ) ( now . getUTCHours ( ) , 2 ) + "-" +
( 0 , utils _js _1 . zpad ) ( now . getUTCMinutes ( ) , 2 ) + "-" +
( 0 , utils _js _1 . zpad ) ( now . getUTCSeconds ( ) , 2 ) + ".0Z" ) ;
const gethFilename = ( "UTC--" + timestamp + "--" + data . address ) ;
data [ "x-ethers" ] = {
client , gethFilename , path , locale ,
mnemonicCounter : ( 0 , index _js _4 . hexlify ) ( mnemonicIv ) . substring ( 2 ) ,
mnemonicCiphertext : ( 0 , index _js _4 . hexlify ) ( mnemonicCiphertext ) . substring ( 2 ) ,
version : "0.1"
} ;
}
return JSON . stringify ( data ) ;
}
2022-11-30 15:44:23 -05:00
/ * *
* Return the JSON Keystore Wallet for % % account % % encrypted with
* % % password % % .
*
* The % % options % % can be used to tune the password - based key
* derivation function parameters , explicitly set the random values
* used . Any provided [ [ ProgressCallback ] ] is ignord .
* /
2022-11-10 04:05:51 -05:00
function encryptKeystoreJsonSync ( account , password , options ) {
if ( options == null ) {
options = { } ;
}
const passwordBytes = ( 0 , utils _js _1 . getPassword ) ( password ) ;
const kdf = getEncryptKdfParams ( options ) ;
const key = ( 0 , index _js _2 . scryptSync ) ( passwordBytes , kdf . salt , kdf . N , kdf . r , kdf . p , 64 ) ;
return _encryptKeystore ( ( 0 , index _js _4 . getBytes ) ( key ) , kdf , account , options ) ;
}
exports . encryptKeystoreJsonSync = encryptKeystoreJsonSync ;
2022-11-30 15:44:23 -05:00
/ * *
* Resolved to the JSON Keystore Wallet for % % account % % encrypted
* with % % password % % .
*
* The % % options % % can be used to tune the password - based key
* derivation function parameters , explicitly set the random values
* used and provide a [ [ ProgressCallback ] ] to receive periodic updates
* on the completion status . .
* /
2022-11-10 04:05:51 -05:00
async function encryptKeystoreJson ( account , password , options ) {
if ( options == null ) {
options = { } ;
}
const passwordBytes = ( 0 , utils _js _1 . getPassword ) ( password ) ;
const kdf = getEncryptKdfParams ( options ) ;
const key = await ( 0 , index _js _2 . scrypt ) ( passwordBytes , kdf . salt , kdf . N , kdf . r , kdf . p , 64 , options . progressCallback ) ;
return _encryptKeystore ( ( 0 , index _js _4 . getBytes ) ( key ) , kdf , account , options ) ;
}
2022-09-05 16:57:11 -04:00
exports . encryptKeystoreJson = encryptKeystoreJson ;
//# sourceMappingURL=json-keystore.js.map