From 5031c8ca74fefce069f3d84ef978900ecb0c359d Mon Sep 17 00:00:00 2001 From: Pavel <14926950+prichodko@users.noreply.github.com> Date: Tue, 14 Jun 2022 14:28:08 +0200 Subject: [PATCH] Use chat keys in the UI (#274) * add util for compressing public key * add chat keys to account and member * use compressPublicKey in color hash util * use chat keys in UI --- packages/status-js/src/account.ts | 5 ++-- packages/status-js/src/client/member.ts | 6 ++-- .../src/utils/compress-public-key.test.ts | 30 +++++++++++++++++++ .../src/utils/compress-public-key.ts | 10 +++++++ .../src/utils/public-key-to-color-hash.ts | 16 ++++------ .../member-sidebar/disconnect-dialog.tsx | 2 +- .../components/member-sidebar/member-item.tsx | 4 +-- .../components/member-sidebar/user-item.tsx | 2 +- .../components/user-profile-dialog/index.tsx | 16 ++++++---- 9 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 packages/status-js/src/utils/compress-public-key.test.ts create mode 100644 packages/status-js/src/utils/compress-public-key.ts diff --git a/packages/status-js/src/account.ts b/packages/status-js/src/account.ts index bd18a02e..399ef6c5 100644 --- a/packages/status-js/src/account.ts +++ b/packages/status-js/src/account.ts @@ -2,6 +2,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak' import { getPublicKey, sign, utils } from 'ethereum-cryptography/secp256k1' import { bytesToHex, concatBytes } from 'ethereum-cryptography/utils' +import { compressPublicKey } from './utils/compress-public-key' import { generateUsername } from './utils/generate-username' export class Account { @@ -13,12 +14,10 @@ export class Account { constructor() { const privateKey = utils.randomPrivateKey() const publicKey = getPublicKey(privateKey) - const chatKey = getPublicKey(privateKey, true) this.privateKey = bytesToHex(privateKey) this.publicKey = bytesToHex(publicKey) - // TODO?: add 0x prefix to public key - this.chatKey = bytesToHex(chatKey) + this.chatKey = '0x' + compressPublicKey(this.publicKey) this.username = generateUsername('0x' + this.publicKey) } diff --git a/packages/status-js/src/client/member.ts b/packages/status-js/src/client/member.ts index 7406da25..c9f13635 100644 --- a/packages/status-js/src/client/member.ts +++ b/packages/status-js/src/client/member.ts @@ -1,3 +1,4 @@ +import { compressPublicKey } from '../utils/compress-public-key' import { generateUsername } from '../utils/generate-username' import { publicKeyToColorHash } from '../utils/public-key-to-color-hash' @@ -5,13 +6,14 @@ import type { ColorHash } from '../utils/public-key-to-color-hash' export class Member { publicKey: string + chatKey: string username: string colorHash: ColorHash constructor(publicKey: string) { this.publicKey = publicKey + this.chatKey = '0x' + compressPublicKey(publicKey) this.username = generateUsername(publicKey) - // TODO: can it fail? - this.colorHash = publicKeyToColorHash(publicKey)! + this.colorHash = publicKeyToColorHash(publicKey) } } diff --git a/packages/status-js/src/utils/compress-public-key.test.ts b/packages/status-js/src/utils/compress-public-key.test.ts new file mode 100644 index 00000000..a5efa699 --- /dev/null +++ b/packages/status-js/src/utils/compress-public-key.test.ts @@ -0,0 +1,30 @@ +import { getPublicKey, utils } from 'ethereum-cryptography/secp256k1' +import { bytesToHex } from 'ethereum-cryptography/utils' + +import { compressPublicKey } from './compress-public-key' + +describe('compressPublicKey', () => { + it('should return compressed public key', () => { + const privateKey = utils.randomPrivateKey() + + const publicKey = bytesToHex(getPublicKey(privateKey)) + const compressedPublicKey = bytesToHex(getPublicKey(privateKey, true)) + + expect(compressPublicKey(publicKey)).toEqual(compressedPublicKey) + }) + + it('should accept public key with a base prefix', () => { + const privateKey = utils.randomPrivateKey() + + const publicKey = '0x' + bytesToHex(getPublicKey(privateKey)) + const compressedPublicKey = bytesToHex(getPublicKey(privateKey, true)) + + expect(compressPublicKey(publicKey)).toEqual(compressedPublicKey) + }) + + it('should throw error if public key is not a valid hex', () => { + expect(() => { + compressPublicKey('not a valid public key') + }).toThrowErrorMatchingInlineSnapshot(`"Invalid public key"`) + }) +}) diff --git a/packages/status-js/src/utils/compress-public-key.ts b/packages/status-js/src/utils/compress-public-key.ts new file mode 100644 index 00000000..9906b859 --- /dev/null +++ b/packages/status-js/src/utils/compress-public-key.ts @@ -0,0 +1,10 @@ +import * as secp from 'ethereum-cryptography/secp256k1' + +export function compressPublicKey(publicKey: string): string { + try { + const pk = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix" + return secp.Point.fromHex(pk).toHex(true) + } catch (error) { + throw new Error('Invalid public key') + } +} diff --git a/packages/status-js/src/utils/public-key-to-color-hash.ts b/packages/status-js/src/utils/public-key-to-color-hash.ts index 2b162ac3..41d30249 100644 --- a/packages/status-js/src/utils/public-key-to-color-hash.ts +++ b/packages/status-js/src/utils/public-key-to-color-hash.ts @@ -1,22 +1,16 @@ import * as secp256k1 from 'ethereum-cryptography/secp256k1' +import { compressPublicKey } from './compress-public-key' + export type ColorHash = number[][] const COLOR_HASH_COLORS_COUNT = 32 const COLOR_HASH_SEGMENT_MAX_LENGTH = 5 -export function publicKeyToColorHash(publicKey: string): ColorHash | undefined { - const publicKeyHex = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix" +export function publicKeyToColorHash(publicKey: string): ColorHash { + const compressedPublicKey = compressPublicKey(publicKey) - let compressedPublicKeyDigits: string - try { - compressedPublicKeyDigits = - secp256k1.Point.fromHex(publicKeyHex).toHex(true) // validates and adds "sign prefix" too - } catch (error) { - return undefined - } - - const colorHashHex = compressedPublicKeyDigits.slice(43, 63) + const colorHashHex = compressedPublicKey.slice(43, 63) const colorHash = hexToColorHash( colorHashHex, COLOR_HASH_COLORS_COUNT, diff --git a/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx b/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx index 7b167e9c..c29ac6b4 100644 --- a/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx +++ b/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx @@ -20,7 +20,7 @@ export const DisconnectDialog = (props: Props) => { {account.username} - Chatkey: {account.publicKey} + Chatkey: {account.chatKey} diff --git a/packages/status-react/src/components/member-sidebar/member-item.tsx b/packages/status-react/src/components/member-sidebar/member-item.tsx index a665ad6d..5b912857 100644 --- a/packages/status-react/src/components/member-sidebar/member-item.tsx +++ b/packages/status-react/src/components/member-sidebar/member-item.tsx @@ -14,7 +14,7 @@ interface Props { export const MemberItem = (props: Props) => { const { member, indicator, verified, untrustworthy } = props - const { publicKey, username, colorHash } = member + const { chatKey, username, colorHash } = member return ( @@ -65,7 +65,7 @@ export const MemberItem = (props: Props) => { )} - {publicKey} + {chatKey} diff --git a/packages/status-react/src/components/member-sidebar/user-item.tsx b/packages/status-react/src/components/member-sidebar/user-item.tsx index a6e0d9a3..a432445e 100644 --- a/packages/status-react/src/components/member-sidebar/user-item.tsx +++ b/packages/status-react/src/components/member-sidebar/user-item.tsx @@ -25,7 +25,7 @@ export const UserItem = (props: Props) => { - {account.publicKey} + {account.chatKey} diff --git a/packages/status-react/src/components/user-profile-dialog/index.tsx b/packages/status-react/src/components/user-profile-dialog/index.tsx index 466e806e..fc9446f6 100644 --- a/packages/status-react/src/components/user-profile-dialog/index.tsx +++ b/packages/status-react/src/components/user-profile-dialog/index.tsx @@ -2,20 +2,24 @@ import React from 'react' import { Avatar, Dialog, EmojiHash, Heading, Text } from '~/src/system' +import type { Member } from '~/src/protocol' + interface Props { - name: string + member: Member } // TODO: Add all states of contact, wait for desktop release export const UserProfileDialog = (props: Props) => { - const { name, ...dialogProps } = props + const { member, ...dialogProps } = props + + const { username, colorHash, chatKey } = member return ( - + - - {name} - Chatkey: 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377 + + {username} + Chatkey: {chatKey}