Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

feat: add decrypt function into ether ext #506

Merged
merged 1 commit into from
Oct 6, 2023

Conversation

Sotatek-HauTran3
Copy link
Contributor

@Sotatek-HauTran3 Sotatek-HauTran3 commented Sep 14, 2023

Additional decrypt function into ether-ext
Keystore v4 format support example

  • Parse multisig or rolebase key that includes 3 keys into multiple json for each key
  • Decrypt the keys
  • Took the code from Ethers to bypass the address comparison part.
  • Return Wallet instance

@kjeom
Copy link
Member

kjeom commented Sep 14, 2023

@Sotatek-HauTran3 Could you add some description for this PR?

@Sotatek-HauTran3 Sotatek-HauTran3 changed the title feat: add decrypt and encrypt function into ether ext feat: add decrypt function into ether ext Sep 14, 2023
@blukat29
Copy link
Contributor

blukat29 commented Sep 22, 2023

I found a way to make it much simpler by using the existing ethers.js decryptKeystoreSync function.
Async versions can be constructed similarly.

It works because decryptKeystoreSync accepts a JSON object without the .address field, where the address check is skipped.

@Sotatek-HauTran3 If this method makes sense to you, can you update the current pull request?

ethers/keystore.ts:

import * as _ from "lodash";
import { decryptKeystoreSync } from "@ethersproject/json-wallets";
import { Bytes } from "@ethersproject/bytes";

// Modified from KeystoreAccount at @ethersproject/json-wallets/src.ts/keystore.ts
interface KeystoreAccountList {
  address: string;
  privateKeys: string[];
}

function decryptKey(crypto: any, password: Bytes | string): string {
  const datav3 = {
    version: 3,
    crypto: crypto,
  }
  const jsonv3 = JSON.stringify(datav3);
  const ka = decryptKeystoreSync(jsonv3, password);
  return ka.privateKey;
}

// Decrypts the Klaytn KIP-3 (https://github.com/klaytn/kips/blob/main/KIPs/kip-3.md) JSON wallet.
//
// KIP-3 v4 wallet comes in one of three shapes:
// - An array of one key = [key]
// - An array of multiple keys = [key, key, key]
// - A nested array of keys = [[key], [], [key, key]]
//
// Regardless of how they are organized, each encrytped key object is decrypted
// then returned in a flattend array of private keys.
function decryptKeystoreV4(json: string, password: Bytes | string): KeystoreAccountList {
  const data = JSON.parse(json);
  let privateKeys: string[] = [];

  if (!_.has(data, "keyring")) {
    throw new Error("invalid JSON wallet v4 (KIP-3)");
  }
  if (!_.isArray(data.keyring)) {
    throw new Error("invalid JSON wallet v4 (KIP-3)");
  }

  _.each(data.keyring, (keyOrKeys: any) => {
    if (!_.isArray(keyOrKeys)) {
      let key: any = keyOrKeys;
      privateKeys.push(decryptKey(key, password));
    } else {
      let keys: any[] = keyOrKeys;
      _.each(keys, (key: any) => {
        privateKeys.push(decryptKey(key, password));
      });
    }
  });

  return {
    address: _.toString(data.address),
    privateKeys: privateKeys,
  };
}

// Decrypts a JSON keystore wallet and returns the account details containing
// the account address and the list of private keys.
// It accepts a Crowdsale wallet, V3 keystore, and KIP-3 V4 keystore wallets.
export function decryptKeystoreListSync(json: string, password: Bytes | string): KeystoreAccountList {
  const data = JSON.parse(json);
  const version = parseInt(data.version);

  // decryptKeystoreV4 handles v4 (KIP-3) wallets.
  if (version == 4) {
    return decryptKeystoreV4(json, password);
  }

  // decryptKeystoreSync handles v3 and Crowdsale wallets.
  const ka = decryptKeystoreSync(json, password);
  return {
    address: ka.address,
    privateKeys: [ka.privateKey],
  };
}

ethers/signer.ts:

import { decryptKeystoreListSync } from "./keystore";

export class Wallet extends EthersWallet {
  // ...
  
  static fromEncryptedJsonSync(json: string, password: string | Bytes): Wallet {
    const { address, privateKeys } = decryptKeystoreListSync(json, password);
    return new Wallet(address, privateKeys[0]);
  }

  static fromEncryptedJsonListSync(json: string, password: string | Bytes): Wallet[] {
    const { address, privateKeys } = decryptKeystoreListSync(json, password);
    return _.map(privateKeys, (privateKey) => new Wallet(address, privateKey));
  }
}
``

@Sotatek-HauTran3 Sotatek-HauTran3 force-pushed the task/add-encrypt-decrypt-into-ethers-ext branch from 55c3315 to 5b7c6db Compare September 22, 2023 10:36
@Sotatek-HauTran3 Sotatek-HauTran3 force-pushed the task/add-encrypt-decrypt-into-ethers-ext branch from 5b7c6db to bbe55a0 Compare September 22, 2023 10:40
@kjeom
Copy link
Member

kjeom commented Oct 4, 2023

@Sotatek-HauTran3 LGTM.
I think it's better to check the result more detail in the unit test. let's leave it another task.

@kjeom kjeom merged commit ffaf395 into dev Oct 6, 2023
@kjeom kjeom mentioned this pull request Oct 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants