From d057c8a5e3d03deb30875a122cde6b3971bf770a Mon Sep 17 00:00:00 2001 From: Ikemefuna Obioha Date: Tue, 27 Feb 2024 19:44:00 +0100 Subject: [PATCH] Set up claim message signing and feedback --- libs/wcm/constants/permissions.js | 1 + package.json | 1 + setup/react/app/MainRouter.js | 2 +- .../RequestSignMessageConfirmation/index.js | 24 +++++++++++------ .../RequestSignMessageDialog/index.js | 12 ++++++--- .../components/RequestSummary/index.js | 27 ++++++++++++++----- .../message/components/signedMessage/index.js | 2 +- src/modules/message/store/action.js | 23 +++++++++++++++- .../utils/signMessageWithPrivateKey.js | 9 +++++++ 9 files changed, 79 insertions(+), 22 deletions(-) diff --git a/libs/wcm/constants/permissions.js b/libs/wcm/constants/permissions.js index 52685009b4..2fae713ada 100644 --- a/libs/wcm/constants/permissions.js +++ b/libs/wcm/constants/permissions.js @@ -1,4 +1,5 @@ export const SIGNING_METHODS = { SIGN_TRANSACTION: { key: 'sign_transaction', title: 'Signature request' }, SIGN_MESSAGE: { key: 'sign_message', title: 'Sign message' }, + SIGN_CLAIM_MESSAGE: { key: 'sign_claim_message', title: 'Sign claim message' }, }; diff --git a/package.json b/package.json index ef50e89e48..7da29978b2 100644 --- a/package.json +++ b/package.json @@ -178,6 +178,7 @@ "socket.io-client": "4.7.0", "stream-browserify": "3.0.0", "swiper": "8.4.2", + "tweetnacl": "1.0.3", "usb": "2.9.0", "yup": "0.32.11" }, diff --git a/setup/react/app/MainRouter.js b/setup/react/app/MainRouter.js index ecee117326..ed1fd1fac0 100644 --- a/setup/react/app/MainRouter.js +++ b/setup/react/app/MainRouter.js @@ -28,7 +28,7 @@ const MainRouter = ({ history }) => { if (event.name === EVENTS.SESSION_REQUEST) { const method = event.meta?.params?.request?.method; - if (method === 'sign_message') { + if (method === 'sign_message' || method === 'sign_claim_message') { showRequestModal('requestSignMessageDialog', event); } else { showRequestModal('requestView', event); diff --git a/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/RequestSignMessageConfirmation/index.js b/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/RequestSignMessageConfirmation/index.js index e598b2b69e..fd26bb1048 100644 --- a/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/RequestSignMessageConfirmation/index.js +++ b/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/RequestSignMessageConfirmation/index.js @@ -3,23 +3,31 @@ import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { useCurrentAccount } from '@account/hooks'; import { useDispatch } from 'react-redux'; -import { signMessage } from '@message/store/action'; +import { signMessage, signClaimMessage } from '@message/store/action'; import CopyToClipboard from '@common/components/copyToClipboard'; import { PrimaryButton } from '@theme/buttons'; import styles from './RequestSignMessageConfirmation.css'; -export function RequestSignMessageConfirmation({ nextStep, address, message }) { +export function RequestSignMessageConfirmation({ nextStep, address, message, portalMessage }) { const { t } = useTranslation(); const [currentAccount] = useCurrentAccount(); const dispatch = useDispatch(); /* istanbul ignore next */ const onClick = () => { - nextStep({ - message, - actionFunction: (formProps, _, privateKey) => - dispatch(signMessage({ message, nextStep, privateKey, currentAccount })), - }); + if (message) { + nextStep({ + message, + actionFunction: (formProps, _, privateKey) => + dispatch(signMessage({ message, nextStep, privateKey, currentAccount })), + }); + } else { + nextStep({ + message: portalMessage, + actionFunction: (formProps, _, privateKey) => + dispatch(signClaimMessage({ portalMessage, nextStep, privateKey, currentAccount })), + }); + } }; return ( @@ -39,7 +47,7 @@ export function RequestSignMessageConfirmation({ nextStep, address, message }) { }} />

{t('Message')}

-
{message}
+
{message ?? portalMessage}
{t('Continue')} diff --git a/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/index.js b/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/index.js index 20dc352cb6..b8e2c8e5f2 100644 --- a/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/index.js +++ b/src/modules/blockchainApplication/connection/components/RequestSignMessageDialog/index.js @@ -16,7 +16,7 @@ import SignedMessage from '@message/components/signedMessage'; import { RequestSignMessageConfirmation } from '@blockchainApplication/connection/components/RequestSignMessageDialog/RequestSignMessageConfirmation'; import { USER_REJECT_ERROR } from '@libs/wcm/utils/jsonRPCFormat'; import styles from './RequestSignMessageDialog.css'; -import RequestSummary from '../RequestSummary'; +import RequestSummary, { getTitle } from '../RequestSummary'; // eslint-disable-next-line max-statements const RequestSignMessageDialog = () => { @@ -31,7 +31,8 @@ const RequestSignMessageDialog = () => { const { peer, requiredNamespaces } = sessionRequest || {}; const event = events?.find((e) => e.name === EVENTS.SESSION_REQUEST); - const { message, address } = event?.meta?.params?.request?.params || {}; + const { method, params: { message, address, portalMessage } = {} } = + event?.meta?.params?.request || {}; const { icons, name, url } = peer?.metadata || {}; /* istanbul ignore next */ @@ -73,7 +74,10 @@ const RequestSignMessageDialog = () => { {!isPasswordStep && !isErrorView && ( { })} onChange={onMultiStepChange} > - + +export const getTitle = (key, t) => Object.values(SIGNING_METHODS).find((item) => item.key === key)?.title ?? t('Method not found.'); const defaultToken = { symbol: 'LSK' }; // eslint-disable-next-line max-statements -const RequestSummary = ({ nextStep, history, message }) => { +const RequestSummary = ({ nextStep, history, message, portalMessage }) => { const { t } = useTranslation(); const { getAccountByAddress, accounts } = useAccounts(); const [currentAccount, setCurrentAccount] = useCurrentAccount(); @@ -86,6 +87,12 @@ const RequestSummary = ({ nextStep, history, message }) => { actionFunction: (formProps, _, privateKey) => reduxDispatch(signMessage({ message, nextStep, privateKey, currentAccount })), }); + } else if (portalMessage) { + nextStep({ + portalMessage, + actionFunction: (formProps, _, privateKey) => + reduxDispatch(signClaimMessage({ portalMessage, nextStep, privateKey, currentAccount })), + }); } else { const moduleCommand = joinModuleAndCommand(transaction); const transactionJSON = toTransactionJSON(transaction, request?.request?.params.schema); @@ -110,6 +117,7 @@ const RequestSummary = ({ nextStep, history, message }) => { }); } }; + const rejectHandler = async () => { await respond({ payload: USER_REJECT_ERROR }); removeSearchParamsFromUrl(history, ['modal', 'status', 'name', 'action']); @@ -144,14 +152,19 @@ const RequestSummary = ({ nextStep, history, message }) => { const { payload, schema, address: publicKey } = request.request.params; let transactionObj; - if (!message) { + // Validate portal message + if (portalMessage && sizeOfString(portalMessage) === 84) { + setErrorMessage(''); + } else if (portalMessage && sizeOfString(portalMessage) !== 84) { + setErrorMessage('Claim message of invalid size received.'); + } else if (!message) { validator.validator.validateSchema(schema); transactionObj = decodeTransaction(Buffer.from(payload, 'hex'), schema); validator.validator.validate(schema, transactionObj.params); setTransaction(transactionObj); } - - const senderPublicKey = !message ? transactionObj.senderPublicKey : publicKey; + const senderPublicKey = + !message && !portalMessage ? transactionObj.senderPublicKey : publicKey; const address = extractAddressFromPublicKey(senderPublicKey); const account = getAccountByAddress(address); setSenderAccount({ @@ -203,7 +216,7 @@ const RequestSummary = ({ nextStep, history, message }) => { return (
- {!message && ( + {!message && !portalMessage && ( { return ( Buffer.concat([ @@ -39,3 +42,21 @@ export const signMessage = return nextStep({ signature, message }); }; + +export const signClaimMessage = + ({ nextStep, portalMessage, privateKey, currentAccount }) => + async () => { + const signature = signClaimMessageWithPrivateKey({ + message: portalMessage, + privateKey, + }); + const portalSignature = { + data: { + pubKey: currentAccount.metadata.pubkey, + r: `0x${signature.substring(0, 64)}`, + s: `0x${signature.substring(64)}`, + }, + }; + + return nextStep({ signature: portalSignature, portalMessage }); + }; diff --git a/src/modules/message/utils/signMessageWithPrivateKey.js b/src/modules/message/utils/signMessageWithPrivateKey.js index 3ae38c03e2..81a43f39a2 100644 --- a/src/modules/message/utils/signMessageWithPrivateKey.js +++ b/src/modules/message/utils/signMessageWithPrivateKey.js @@ -1,7 +1,16 @@ import { cryptography } from '@liskhq/lisk-client'; +import { sign } from 'tweetnacl'; export const signMessageWithPrivateKey = ({ message, privateKey }) => { const result = cryptography.ed.signAndPrintMessage(message, Buffer.from(privateKey, 'hex')); return result; }; + +export const signClaimMessageWithPrivateKey = ({ message, privateKey }) => { + const result = Buffer.from( + sign.detached(Buffer.from(message.substring(2), 'hex'), Buffer.from(privateKey, 'hex')) + ).toString('hex'); + + return result; +};