Use EIP-712 to sign public key message

This commit is contained in:
Franck Royer 2021-08-12 16:36:28 +10:00
parent 01696d074c
commit 076192aa66
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
4 changed files with 84 additions and 31 deletions

View File

@ -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

View File

@ -193,6 +193,7 @@ function App() {
address={address}
encryptionPublicKey={encPublicKey}
waku={waku}
providerRequest={provider?.provider?.request}
/>
</fieldset>
<fieldset>

View File

@ -11,6 +11,9 @@ interface Props {
waku: Waku | undefined;
signer: Signer | undefined;
address: string | undefined;
providerRequest:
| ((request: { method: string; params?: Array<any> }) => Promise<any>)
| 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)

View File

@ -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<any>;
}) => Promise<any>
): Promise<PublicKeyMessage> {
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<any>;
from?: string;
}) => Promise<any>
): Promise<Uint8Array> {
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);
}