Skip to content

Instantly share code, notes, and snippets.

@bluet
Last active June 18, 2023 02:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bluet/e944003d6d2069666799cb398d48bb01 to your computer and use it in GitHub Desktop.
Save bluet/e944003d6d2069666799cb398d48bb01 to your computer and use it in GitHub Desktop.
eth-crypto and web3, public key retrieving, sign, recover and encrypting test
/* eslint-disable no-console */
const EthCrypto = require("eth-crypto");
const sigUtil = require("@metamask/eth-sig-util");
const ethUtil = require("ethereumjs-util");
async function test (provider, selectedAccount) {
// eslint-disable-next-line no-undef
const web3 = new Web3(provider);
// const accounts = await web3.eth.getAccounts();
// selectedAccount = accounts[0];
console.log("Wallet is", web3.eth.accounts.wallet);
//
// Get signature
//
console.warn("Get signature\n================");
// Deprecated: This works must shows warning.
// https://ethereum.stackexchange.com/questions/41993/how-to-encrypt-a-message-using-only-the-public-key-from-web3-eth-sign/63983#63983
// let secretMsgSignature = await web3.eth.sign(
// web3.eth.accounts.hashMessage("Secret message"),
// selectedAccount
// );
// console.log("Signature is", secretMsgSignature);
const secretMsgSignature = await new Promise((resolve, reject) => {
window.ethereum.sendAsync({
"method": "personal_sign",
"params": [web3.utils.utf8ToHex("Secret message"), selectedAccount],
"from": selectedAccount,
}, (err, response) => {
if (err) {return reject(err);}
resolve(response.result);
});
});
console.log("Signature is", secretMsgSignature);
//
// Get signers address from signatures
//
console.warn("Get signers address from signatures\n================");
let signerAddress = EthCrypto.recover(
secretMsgSignature,
web3.eth.accounts.hashMessage("Secret message")
);
console.log("Signer address is", signerAddress);
//
// Get public key
//
console.warn("Get public key\n================");
const pubkey_ethcrypto = EthCrypto.recoverPublicKey(
secretMsgSignature,
web3.eth.accounts.hashMessage("Secret message")
);
console.log("EthCrypto: My public key is", pubkey_ethcrypto);
let pubkey_ethcrypto_compressed = EthCrypto.publicKey.compress(pubkey_ethcrypto);
console.log("EthCrypto: My public key compressed is", pubkey_ethcrypto_compressed);
console.log("EthCrypto: My public key compressed length is", pubkey_ethcrypto_compressed.length);
//
// Get Public Key from Ethereum (MetaMask)
//
console.warn("Get Public Key from Ethereum (MetaMask)\n================");
let pubkey_eth = await window.ethereum.request({
"method": "eth_getEncryptionPublicKey",
"params": [selectedAccount], // you must have access to the specified account
});
console.log("window.Ethereum: My public Key from Ethereum (MetaMask):", pubkey_eth);
console.log("window.Ethereum: My public key from Ethereum (MetaMask) is", Buffer.from(pubkey_eth, "base64").toString("hex"));
console.log("window.Ethereum: My public key from Ethereum (MetaMask) length is", Buffer.from(pubkey_eth, "base64").toString("hex").length);
let pubkey_eth_decompressed_prefix02 = EthCrypto.publicKey.decompress("02" + Buffer.from(pubkey_eth, "base64").toString("hex"));
console.log("window.Ethereum: My public key decompressed (prefix 02) from Ethereum (MetaMask) is", pubkey_eth_decompressed_prefix02);
console.log("window.Ethereum: My public key decompressed (prefix 02) length from Ethereum (MetaMask) is", pubkey_eth_decompressed_prefix02.length);
let pubkey_eth_decompressed_prefix03 = EthCrypto.publicKey.decompress("03" + Buffer.from(pubkey_eth, "base64").toString("hex"));
console.log("window.Ethereum: My public key decompressed (prefix 03) from Ethereum (MetaMask) is", pubkey_eth_decompressed_prefix03);
console.log("window.Ethereum: My public key decompressed (prefix 03) length from Ethereum (MetaMask) is", pubkey_eth_decompressed_prefix03.length);
//
// Encrypt message
//
console.warn("Encrypt message\n================");
// Encrypt message: Use EthCrypto with EthCrypto public key
console.info("Encrypt message: Use EthCrypto with EthCrypto public key");
const encmsg_ethcrypto_ethcryptokey_raw = await EthCrypto.encryptWithPublicKey(
pubkey_ethcrypto,
"Secret message"
);
const encmsg_ethcrypto_ethcryptokey = ethUtil.bufferToHex(
Buffer.from(
JSON.stringify(
encmsg_ethcrypto_ethcryptokey_raw
),
"utf8"
)
);
console.log("EthCrypto: Raw encrypted message (ethcrypto key) is", encmsg_ethcrypto_ethcryptokey_raw);
console.log("EthCrypto: Message encrypted (ethcrypto key) is", encmsg_ethcrypto_ethcryptokey);
console.log("EthCrypto: Message encrypted (ethcrypto key) EthCrypto.cipher.stringify is", EthCrypto.cipher.stringify(encmsg_ethcrypto_ethcryptokey_raw));
// Encrypt message: Use EthCrypto with Ethereum (MetaMask) public key
console.info("Encrypt message: Use EthCrypto with Ethereum (MetaMask) public key");
const encmsg_ethcrypto_ethkey_prefix02_raw = await EthCrypto.encryptWithPublicKey(
pubkey_eth_decompressed_prefix02, // pubkey_eth is the public key of the Ethereum (MetaMask) account
"Secret message"
);
const encmsg_ethcrypto_ethkey_prefix02 = ethUtil.bufferToHex(
Buffer.from(JSON.stringify(encmsg_ethcrypto_ethkey_prefix02_raw), "utf8")
);
console.log(
"EthCrypto: Raw encrypted message (eth key prefix 02) is",
encmsg_ethcrypto_ethkey_prefix02_raw
);
console.log("EthCrypto: Message encrypted (eth key prefix 02) is", encmsg_ethcrypto_ethkey_prefix02);
const encmsg_ethcrypto_ethkey_prefix03_raw = await EthCrypto.encryptWithPublicKey(
pubkey_eth_decompressed_prefix03, // pubkey_eth is the public key of the Ethereum (MetaMask) account
"Secret message"
);
const encmsg_ethcrypto_ethkey_prefix03 = ethUtil.bufferToHex(
Buffer.from(JSON.stringify(encmsg_ethcrypto_ethkey_prefix03_raw), "utf8")
);
console.log(
"EthCrypto: Raw encrypted message (eth key prefix 03) is",
encmsg_ethcrypto_ethkey_prefix03_raw
);
console.log("EthCrypto: Message encrypted (eth key prefix 03) is", encmsg_ethcrypto_ethkey_prefix03);
// Encrypt message: Use Ethereum with EthCrypto public key
console.info("Encrypt message: Use Ethereum with EthCrypto public key");
const encmsg_eth_ethcryptokey_raw = sigUtil.encrypt({
"publicKey": Buffer.from(pubkey_ethcrypto_compressed.substring(2), "hex").toString(
"base64"
),
"data": "Secret message",
"version": "x25519-xsalsa20-poly1305",
});
const encmsg_eth_ethcryptokey = ethUtil.bufferToHex(
Buffer.from(JSON.stringify(encmsg_eth_ethcryptokey_raw), "utf8")
);
console.log("sigUtil: Raw encrypted message (ethcrypto key) is", encmsg_eth_ethcryptokey_raw);
console.log("sigUtil: Message encrypted (ethcrypto key) is: ", encmsg_eth_ethcryptokey);
// Encrypt message: Use Ethereum with Ethereum (MetaMask) public key
console.info("Encrypt message: Use Ethereum with Ethereum (MetaMask) public key");
const encmsg_eth_ethkey_raw = sigUtil.encrypt({
"publicKey": pubkey_eth,
"data": "Secret message",
"version": "x25519-xsalsa20-poly1305",
});
const encmsg_eth_ethkey = ethUtil.bufferToHex(
Buffer.from(
JSON.stringify(
encmsg_eth_ethkey_raw
),
"utf8"
)
);
console.log("sigUtil: Raw encrypted message (eth key) is", encmsg_eth_ethkey_raw);
console.log("sigUtil: Message encrypted (eth key) is: ", encmsg_eth_ethkey);
//
// Decrypt message
//
console.warn("Decrypt messages\n================");
// Decrypt message: Use Ethereum with Ethereum public key
// console.info("Decrypt message: Use Ethereum with Ethereum public key");
// const decmsg_eth_byEth = await window.ethereum.request({
// "method": "eth_decrypt",
// "params": [encmsg_eth_ethkey, selectedAccount], // you must have access to the specified account
// });
// console.log("window.Ethereum: Decrypted message from Ethereum is: ", decmsg_eth_byEth);
}
@bluet
Copy link
Author

bluet commented Mar 14, 2022

Update:

public key recovered from web3 signed msg public key from eth_getEncryptionPublicKey
encrypt with eth-crypto
encrypt with sigUtil

But message decrypting doesn't work in public key from eth_getEncryptionPublicKey + eth_decrypt.

@bluet
Copy link
Author

bluet commented Mar 25, 2022

From @Ismael's comment and MetaMask github issue:

  1. The key from MetaMask's eth_getEncryptionPublicKey rpc is NOT the Public Key of my account, but a different key calculated from my private key with the nacl implementation of the X25519_XSalsa20_Poly1305 algorithm, which is different from Ethereum's default algorithm.
  2. MetaMask holds the private key, but it doesn't provide a function to decrypt messages with the private key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment