ethers.js icon indicating copy to clipboard operation
ethers.js copied to clipboard

Fix for prepended empty bytes on KeystoreJson decrypt

Open kevtechi opened this issue 1 year ago • 3 comments

In certain scenarios, decrypting a KeystoreJson can result in 00 being prepended to a private key in the getAccount function.

Here is an example with a random key

Json:

{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"2e9dc1c889a994f29f84501a88ee28df"},"ciphertext":"9b46c206388bc115b8ac811d288c68f7ae04a7501d5ae5a67dfb230525bbfb61","kdf":"scrypt","kdfparams":{"dklen":32,"n":512,"r":8,"p":1,"salt":"4e87b121893d3082cd70212cbb4c0114e0d858e7d91092c8af3f6d599a6d2072"},"mac":"46ec1e91ec891c0fd018ec4d098036e7f41d5871278631c590349f2c094161dd"},"id":"f04398b6-d60b-4d6b-88b2-6fb9aaa69487","version":3}

Password:

1edw8fROhOix-1QcaXQxXeoA2aCFvpHPeZxESqx-hKLHIxzVBhk8bLb8DMALuclUlbhq-arWJdMVuLM3l3uMsA==

The resulting private key should be:

0x54fc8d2c19216406f12e587253d4a52c20c6af9e1d98248381fb6133d732798f

But internally, the decrypt function returns:

0x0054fc8d2c19216406f12e587253d4a52c20c6af9e1d98248381fb6133d732798f

Additional Context

This KeystoreJson and private key was generated using web3dart

kevtechi avatar Jun 25 '24 18:06 kevtechi

Can you include a demo of how you are using the decrypt functions that generated the invalid key? I use the following code and get the correct (non-prefixed) values:

const json = '{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"2e9dc1c889a994f29f84501a88ee28df"},"ciphertext":"9b46c206388bc115b8ac811d288c68f7ae04a7501d5ae5a67dfb230525bbfb61","kdf":"scrypt","kdfparams":{"dklen":32,"n":512,"r":8,"p":1,"salt":"4e87b121893d3082cd70212cbb4c0114e0d858e7d91092c8af3f6d599a6d2072"},"mac":"46ec1e91ec891c0fd018ec4d098036e7f41d5871278631c590349f2c094161dd"},"id":"f04398b6-d60b-4d6b-88b2-6fb9aaa69487","version":3}';
const password = "1edw8fROhOix-1QcaXQxXeoA2aCFvpHPeZxESqx-hKLHIxzVBhk8bLb8DMALuclUlbhq-arWJdMVuLM3l3uMsA==";

const pk = "0x54fc8d2c19216406f12e587253d4a52c20c6af9e1d98248381fb6133d732798f";

console.log(await ethers.decryptKeystoreJsonSync(json, password));
// { 
//  address: '0x18e38dA42233e0c4D6107d129A9C953800e590F3',
//  privateKey: '0x54fc8d2c19216406f12e587253d4a52c20c6af9e1d98248381fb6133d732798f'
// }

(async function() {
  console.log(await ethers.decryptKeystoreJson(json, password));
  // { 
  //  address: '0x18e38dA42233e0c4D6107d129A9C953800e590F3',
  //  privateKey: '0x54fc8d2c19216406f12e587253d4a52c20c6af9e1d98248381fb6133d732798f'
  // }
})();

ricmoo avatar Jun 25 '24 19:06 ricmoo

Sorry about that, web3dart seems to only sometimes generate a key that will fail.

web3dart can always decrypt, it's ethers that can't.

Here is one that will fail in ethers:

{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"71b1f67ddc295c9f2a46e34ebd8f5910"},"ciphertext":"e3a9b0d39e65c9a521b3b4326600c9d715c4c712536e47443c2be83b876d995629","kdf":"scrypt","kdfparams":{"dklen":32,"n":512,"r":8,"p":1,"salt":"bb0463b7407242a131c7c8d78239c315d85006e8801efcce2004525761e31bc2"},"mac":"30556000e56b1d17cc31d4b3f288b7e73a7ff4104b1c3e71ae8b9ffea8fbb971"},"id":"dea3ce15-75ef-48b8-b0d8-2eb5fbe324f0","version":3}

Password:

1edw8fROhOix-1QcaXQxXeoA2aCFvpHPeZxESqx-hKLHIxzVBhk8bLb8DMALuclUlbhq-arWJdMVuLM3l3uMsA==

Error:

Error: Point of length 33 was invalid. Expected 33 compressed bytes or 65 uncompressed bytes
    at fromBytes (weierstrass.js:599:23)
    at Point.fromHex (weierstrass.js:233:40)
    at SigningKey.computePublicKey (signing-key.js:129:102)
    at computeAddress (address.js:19:75)
    at getAccount (json-keystore.js:70:90)
    at decryptKeystoreJsonSync (json-keystore.js:146:12)

kevtechi avatar Jun 26 '24 07:06 kevtechi

Decryption succeeds, but there's an extra 00 which makes address computation fail.

kevtechi avatar Jun 26 '24 07:06 kevtechi