114 lines
3.3 KiB
TypeScript
Raw Normal View History

2022-06-17 10:48:15 +10:00
import "@ethersproject/shims";
import { PublicKeyMessage } from "./messaging/wire";
import { generatePrivateKey, getPublicKey, utils } from "js-waku";
import { PublicKeyContentTopic } from "./waku";
2022-08-20 18:10:05 +10:00
import { keccak256, _TypedDataEncoder, recoverAddress } from "ethers/lib/utils";
2022-06-17 10:48:15 +10:00
import { equals } from "uint8arrays/equals";
import type { TypedDataSigner } from "@ethersproject/abstract-signer";
2022-06-17 10:48:15 +10:00
export const PublicKeyMessageEncryptionKey = utils.hexToBytes(
2022-08-20 18:10:05 +10:00
keccak256(utils.utf8ToBytes(PublicKeyContentTopic))
2022-06-17 10:48:15 +10:00
);
export interface KeyPair {
privateKey: Uint8Array;
publicKey: Uint8Array;
}
/**
2022-08-08 12:08:54 +10:00
* Generate new encryption key pair.
2022-06-17 10:48:15 +10:00
*/
export async function generateEncryptionKeyPair(): Promise<KeyPair> {
const privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
return { privateKey, publicKey };
}
/**
* Sign the encryption public key with Web3. This can then be published to let other
* users know to use this encryption public key to encrypt messages for the
* Ethereum Address holder.
*/
export async function createPublicKeyMessage(
address: string,
encryptionPublicKey: Uint8Array,
signer: TypedDataSigner
2022-06-17 10:48:15 +10:00
): Promise<PublicKeyMessage> {
const signature = await signEncryptionKey(
encryptionPublicKey,
address,
signer
2022-06-17 10:48:15 +10:00
);
return new PublicKeyMessage({
encryptionPublicKey: encryptionPublicKey,
ethAddress: utils.hexToBytes(address),
signature: utils.hexToBytes(signature),
});
}
function buildMsgParams(encryptionPublicKey: Uint8Array, fromAddress: string) {
2022-08-20 18:10:05 +10:00
return {
2022-06-17 10:48:15 +10:00
domain: {
name: "Ethereum Private Message over Waku",
version: "1",
},
value: {
2022-06-17 10:48:15 +10:00
message:
"By signing this message you certify that messages addressed to `ownerAddress` must be encrypted with `encryptionPublicKey`",
encryptionPublicKey: utils.bytesToHex(encryptionPublicKey),
ownerAddress: fromAddress,
},
// Refers to the keys of the *types* object below.
primaryType: "PublishEncryptionPublicKey",
types: {
PublishEncryptionPublicKey: [
{ name: "message", type: "string" },
{ name: "encryptionPublicKey", type: "string" },
{ name: "ownerAddress", type: "string" },
],
},
2022-08-20 18:10:05 +10:00
};
2022-06-17 10:48:15 +10:00
}
export async function signEncryptionKey(
encryptionPublicKey: Uint8Array,
fromAddress: string,
signer: TypedDataSigner
2022-06-17 10:48:15 +10:00
): Promise<Uint8Array> {
const { domain, types, value } = buildMsgParams(
encryptionPublicKey,
fromAddress
2022-08-20 18:10:05 +10:00
);
2022-06-17 10:48:15 +10:00
const result = await signer._signTypedData(domain, types, value);
2022-06-17 10:48:15 +10:00
console.log("TYPED SIGNED:" + JSON.stringify(result));
return utils.hexToBytes(result);
}
/**
* Validate that the Encryption Public Key was signed by the holder of the given Ethereum address.
*/
export function validatePublicKeyMessage(msg: PublicKeyMessage): boolean {
const { domain, types, value } = buildMsgParams(
2022-08-20 18:10:05 +10:00
msg.encryptionPublicKey,
"0x" + utils.bytesToHex(msg.ethAddress)
);
try {
const hash = _TypedDataEncoder.hash(domain, types, value);
2022-08-20 18:10:05 +10:00
const recovered = recoverAddress(hash, msg.signature);
console.log("Recovered", recovered);
console.log("ethAddress", "0x" + utils.bytesToHex(msg.ethAddress));
2022-06-17 10:48:15 +10:00
return equals(utils.hexToBytes(recovered), msg.ethAddress);
2022-08-20 18:10:05 +10:00
} catch (e) {
console.error("Could not recover public key from signature", e);
2022-08-20 18:10:05 +10:00
return false;
}
2022-06-17 10:48:15 +10:00
}