From 076192aa665b6ec5153663665ee84e3d41b4f78c Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 12 Aug 2021 16:36:28 +1000 Subject: [PATCH] Use EIP-712 to sign public key message --- examples/eth-pm-wallet-encryption/README.md | 2 +- examples/eth-pm-wallet-encryption/src/App.tsx | 1 + .../src/BroadcastPublicKey.tsx | 12 ++- .../eth-pm-wallet-encryption/src/crypto.ts | 100 +++++++++++++----- 4 files changed, 84 insertions(+), 31 deletions(-) diff --git a/examples/eth-pm-wallet-encryption/README.md b/examples/eth-pm-wallet-encryption/README.md index bc48722f33..be86eb5271 100644 --- a/examples/eth-pm-wallet-encryption/README.md +++ b/examples/eth-pm-wallet-encryption/README.md @@ -5,7 +5,7 @@ - Private Messaging - React/TypeScript - Waku Light Push -- Signature with Web3 +- Signature with Web3 using [EIP-712 v4: `eth_signTypedData_v4`](https://eips.ethereum.org/EIPS/eip-712) - Asymmetric Encryption - Usage of [`eth_decrypt`](https://docs.metamask.io/guide/rpc-api.html#eth-decrypt) Wallet API diff --git a/examples/eth-pm-wallet-encryption/src/App.tsx b/examples/eth-pm-wallet-encryption/src/App.tsx index 27e45cf000..fc431e4f2d 100644 --- a/examples/eth-pm-wallet-encryption/src/App.tsx +++ b/examples/eth-pm-wallet-encryption/src/App.tsx @@ -193,6 +193,7 @@ function App() { address={address} encryptionPublicKey={encPublicKey} waku={waku} + providerRequest={provider?.provider?.request} />
diff --git a/examples/eth-pm-wallet-encryption/src/BroadcastPublicKey.tsx b/examples/eth-pm-wallet-encryption/src/BroadcastPublicKey.tsx index 69303c319f..433ce65215 100644 --- a/examples/eth-pm-wallet-encryption/src/BroadcastPublicKey.tsx +++ b/examples/eth-pm-wallet-encryption/src/BroadcastPublicKey.tsx @@ -11,6 +11,9 @@ interface Props { waku: Waku | undefined; signer: Signer | undefined; address: string | undefined; + providerRequest: + | ((request: { method: string; params?: Array }) => Promise) + | undefined; } export default function BroadcastPublicKey({ @@ -18,15 +21,22 @@ export default function BroadcastPublicKey({ encryptionPublicKey, address, waku, + providerRequest, }: Props) { const broadcastPublicKey = () => { if (!encryptionPublicKey) return; if (!signer) return; if (!address) return; if (!waku) return; + if (!providerRequest) return; console.log('Creating Public Key Message'); - createPublicKeyMessage(signer, address, encryptionPublicKey) + createPublicKeyMessage( + signer, + address, + encryptionPublicKey, + providerRequest + ) .then((msg) => { console.log('Public Key Message created'); encodePublicKeyWakuMessage(msg) diff --git a/examples/eth-pm-wallet-encryption/src/crypto.ts b/examples/eth-pm-wallet-encryption/src/crypto.ts index 4c24eb6593..6461f48679 100644 --- a/examples/eth-pm-wallet-encryption/src/crypto.ts +++ b/examples/eth-pm-wallet-encryption/src/crypto.ts @@ -1,9 +1,9 @@ import '@ethersproject/shims'; -import { ethers } from 'ethers'; import { Signer } from '@ethersproject/abstract-signer'; import { PublicKeyMessage } from './messaging/wire'; import { hexToBuf, equalByteArrays, bufToHex } from 'js-waku/lib/utils'; +import * as sigUtil from 'eth-sig-util'; /** * Sign the Eth-DM public key with Web3. This can then be published to let other @@ -13,12 +13,19 @@ import { hexToBuf, equalByteArrays, bufToHex } from 'js-waku/lib/utils'; export async function createPublicKeyMessage( web3Signer: Signer, address: string, - encryptionPublicKey: Uint8Array + encryptionPublicKey: Uint8Array, + providerRequest: (request: { + method: string; + params?: Array; + }) => Promise ): Promise { - console.log('Asking wallet to sign Public Key Message'); - const signature = await web3Signer.signMessage( - formatPublicKeyForSignature(encryptionPublicKey) + const signature = await signEncryptionKey( + encryptionPublicKey, + address, + providerRequest ); + + console.log('Asking wallet to sign Public Key Message'); console.log('Public Key Message signed'); return new PublicKeyMessage({ @@ -28,33 +35,68 @@ export async function createPublicKeyMessage( }); } +function buildMsgParams(encryptionPublicKey: Uint8Array, fromAddress: string) { + return JSON.stringify({ + domain: { + chainId: 1, + name: 'Ethereum Private Message over Waku', + version: '1', + }, + message: { + encryptionPublicKey: bufToHex(encryptionPublicKey), + ownerAddress: fromAddress, + }, + // Refers to the keys of the *types* object below. + primaryType: 'PublishEncryptionPublicKey', + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + ], + PublishEncryptionPublicKey: [ + { name: 'encryptionPublicKey', type: 'string' }, + { name: 'ownerAddress', type: 'string' }, + ], + }, + }); +} + +export async function signEncryptionKey( + encryptionPublicKey: Uint8Array, + fromAddress: string, + providerRequest: (request: { + method: string; + params?: Array; + from?: string; + }) => Promise +): Promise { + const msgParams = buildMsgParams(encryptionPublicKey, fromAddress); + + const result = await providerRequest({ + method: 'eth_signTypedData_v4', + params: [fromAddress, msgParams], + from: fromAddress, + }); + + console.log('TYPED SIGNED:' + JSON.stringify(result)); + + return hexToBuf(result); +} + /** * Validate that the Encryption Public Key was signed by the holder of the given Ethereum address. */ export function validatePublicKeyMessage(msg: PublicKeyMessage): boolean { - const formattedMsg = formatPublicKeyForSignature(msg.encryptionPublicKey); - try { - const sigAddress = ethers.utils.verifyMessage(formattedMsg, msg.signature); - return equalByteArrays(sigAddress, msg.ethAddress); - } catch (e) { - console.log( - 'Failed to verify signature for Public Key Message', - formattedMsg, - msg - ); - return false; - } -} - -/** - * Prepare Eth-Dm Public key to be signed for publication. - * The public key is set in on Object `{ encryptionPublicKey: string; }`, converted - * to JSON and then hashed with Keccak256. - * The usage of the object helps ensure the signature is only used in an Eth-DM - * context. - */ -function formatPublicKeyForSignature(encryptionPublicKey: Uint8Array): string { - return JSON.stringify({ - encryptionPublicKey: bufToHex(encryptionPublicKey), + const recovered = sigUtil.recoverTypedSignature_v4({ + data: JSON.parse( + buildMsgParams(msg.encryptionPublicKey, '0x' + bufToHex(msg.ethAddress)) + ), + sig: '0x' + bufToHex(msg.signature), }); + + console.log('Recovered', recovered); + console.log('ethAddress', '0x' + bufToHex(msg.ethAddress)); + + return equalByteArrays(recovered, msg.ethAddress); }