Skip to content

Commit

Permalink
Sign messages by Trezor
Browse files Browse the repository at this point in the history
  • Loading branch information
spylogsster committed Nov 10, 2021
1 parent 636a2ad commit eaf1f21
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 63 deletions.
4 changes: 3 additions & 1 deletion components/brave_wallet/browser/brave_wallet_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,9 @@ constexpr webui::LocalizedString kLocalizedStrings[] = {
{"braveWalletSignOnDeviceError",
IDS_BRAVE_WALLET_HARDWARE_TRANSACTION_DEVICE_ERROR},
{"braveWalletNoMessageToSignError",
IDS_BRAVE_WALLET_HARDWARE_TRANSACTION_NO_MESSAGE_TO_SIGN_ERROR}};
IDS_BRAVE_WALLET_HARDWARE_TRANSACTION_NO_MESSAGE_TO_SIGN_ERROR},
{"braveWalletProcessMessageError",
IDS_BRAVE_WALLET_HARDWARE_SIGN_MESSAGE_ERROR}};

const char kRopstenSwapBaseAPIURL[] = "https://ropsten.api.0x.org/";
const char kRopstenBuyTokenPercentageFee[] = "0.00875";
Expand Down
12 changes: 11 additions & 1 deletion components/brave_wallet_ui/common/async/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
TransactionInfo,
kLedgerHardwareVendor,
kTrezorHardwareVendor,
APIProxyControllers
APIProxyControllers,
SignHardwareMessageOperationResult
} from '../../constants/types'
import * as WalletActions from '../actions/wallet_actions'
import { GetNetworkInfo } from '../../utils/network-utils'
Expand Down Expand Up @@ -333,3 +334,12 @@ export async function signLedgerTransaction (apiProxy: APIProxyControllers, path
}
return { success: result.status }
}

export async function signMessageWithHardwareKeyring (apiProxy: APIProxyControllers, vendor: string, path: string, address: string, message: string): Promise<SignHardwareMessageOperationResult> {
const deviceKeyring = await apiProxy.getKeyringsByType(vendor)
if (vendor === kLedgerHardwareVendor) {
return deviceKeyring.signPersonalMessage(path, address, message)
}

return deviceKeyring.signPersonalMessage(path, message)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
} from '../../components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/types'

import {
kLedgerHardwareVendor, SignatureVRS
kLedgerHardwareVendor, SignatureVRS,
SignHardwareMessageOperationResult
} from '../../constants/types'

import Eth from '@ledgerhq/hw-app-eth'
Expand Down Expand Up @@ -69,25 +70,23 @@ export default class LedgerBridgeKeyring extends EventEmitter {
return this.app.signTransaction(path, rawTxHex)
}

signPersonalMessage = async (path: string, address: string, message: string) => {
return new Promise(async (resolve, reject) => {
try {
if (!this.isUnlocked() && !(await this.unlock())) {
return new Error(getLocale('braveWalletUnlockError'))
}
return this.app.signPersonalMessage(path,
Buffer.from(message)).then((result: SignatureVRS) => {
const signature = this._createMessageSignature(result, message, address)
if (!signature) {
return reject(new Error(getLocale('braveWalletLedgerValidationError')))
}
resolve(signature)
}).catch(reject)
} catch (e) {
reject(e)
signPersonalMessage = async (path: string, address: string, message: string): Promise<SignHardwareMessageOperationResult> => {
if (!this.isUnlocked() && !(await this.unlock())) {
return { success: false, error: getLocale('braveWalletUnlockError') }
}
try {
const data = await this.app.signPersonalMessage(path,
Buffer.from(message))
const signature = this._createMessageSignature(data, message, address)
if (!signature) {
return { success: false, error: getLocale('braveWalletLedgerValidationError') }
}
})
return { success: true, payload: signature }
} catch (e) {
return { success: false, error: e.message }
}
}

_createMessageSignature = (result: SignatureVRS, message: string, address: string) => {
let v = (result.v - 27).toString()
if (v.length < 2) {
Expand Down
50 changes: 29 additions & 21 deletions components/brave_wallet_ui/common/trezor/trezor-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,31 @@
// you can obtain one at http://mozilla.org/MPL/2.0/.
import { loadTimeData } from '../../../common/loadTimeData'
import { Unsuccessful, EthereumSignTransaction, CommonParams, Success } from 'trezor-connect'
import { HDNodeResponse } from 'trezor-connect/lib/typescript'
import { EthereumSignedTx } from 'trezor-connect/lib/typescript/networks/ethereum'
import { HDNodeResponse, MessageSignature } from 'trezor-connect/lib/typescript'
import { EthereumSignedTx, EthereumSignMessage } from 'trezor-connect/lib/typescript/networks/ethereum'
export const kTrezorBridgeUrl = loadTimeData.getString('braveWalletTrezorBridgeUrl')

export enum TrezorCommand {
Unlock = 'trezor-unlock',
GetAccounts = 'trezor-get-accounts',
SignTransaction = 'trezor-sign-treansaction'
SignTransaction = 'trezor-sign-treansaction',
SignMessage = 'trezor-sign-message'
}
export type CommandMessage = {
command: TrezorCommand
id: string
origin: string
}
export type TrezorAccountPath = {
path: string
}
export type GetAccountsCommand = CommandMessage & {
command: TrezorCommand.GetAccounts,
paths: TrezorAccountPath[]
}
export type UnlockCommand = CommandMessage & {
command: TrezorCommand.Unlock
}
export type UnlockResponse = CommandMessage & {
result: Boolean,
error?: Unsuccessful
}

export type SignTransactionCommandPayload = CommonParams & EthereumSignTransaction

export type SignTransactionCommand = CommandMessage & {
command: TrezorCommand.SignTransaction
payload: SignTransactionCommandPayload
export type TrezorAccountPath = {
path: string
}

export type SignTransactionResponse = Unsuccessful | Success<EthereumSignedTx>

export type TrezorAccount = {
publicKey: string
serializedPath: string,
Expand All @@ -55,12 +42,33 @@ export type TrezorGetPublicKeyResponse = Unsuccessful | Success<HDNodeResponse[]
export type GetAccountsResponsePayload = CommandMessage & {
payload: TrezorGetPublicKeyResponse
}
export type GetAccountsCommand = CommandMessage & {
command: TrezorCommand.GetAccounts,
paths: TrezorAccountPath[]
}

export type SignTransactionCommandPayload = CommonParams & EthereumSignTransaction
export type SignTransactionCommand = CommandMessage & {
command: TrezorCommand.SignTransaction
payload: SignTransactionCommandPayload
}
export type SignTransactionResponse = Unsuccessful | Success<EthereumSignedTx>
export type SignTransactionResponsePayload = CommandMessage & {
payload: SignTransactionResponse
}
export type TrezorFrameCommand = GetAccountsCommand | UnlockCommand | SignTransactionCommand
export type TrezorFrameResponse = UnlockResponse | GetAccountsResponsePayload | SignTransactionResponsePayload

export type SignMessageCommandPayload = CommonParams & EthereumSignMessage
export type SignMessageCommand = CommandMessage & {
command: TrezorCommand.SignMessage
payload: SignMessageCommandPayload
}
export type SignMessageResponse = Unsuccessful | Success<MessageSignature>
export type SignMessageResponsePayload = CommandMessage & {
payload: SignMessageResponse
}

export type TrezorFrameCommand = GetAccountsCommand | UnlockCommand | SignTransactionCommand | SignMessageCommand
export type TrezorFrameResponse = UnlockResponse | GetAccountsResponsePayload | SignTransactionResponsePayload | SignMessageResponsePayload

// Trezor library is loaded inside the chrome-untrusted webui page
// and communication is going through posting messages between parent window
Expand Down
30 changes: 28 additions & 2 deletions components/brave_wallet_ui/common/trezor/trezor_bridge_keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '../../components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/types'
import {
kTrezorHardwareVendor,
SignHardwareMessageOperationResult,
TransactionInfo
} from '../../constants/types'
import {
Expand All @@ -20,8 +21,10 @@ import {
TrezorAccount,
SignTransactionCommandPayload,
SignTransactionResponse,
TrezorError,
TrezorFrameCommand,
TrezorError
SignMessageCommandPayload,
SignMessageResponsePayload
} from '../../common/trezor/trezor-messages'
import { sendTrezorCommand } from '../../common/trezor/trezor-bridge-transport'
import { getLocale } from '../../../common/locale'
Expand Down Expand Up @@ -80,6 +83,27 @@ export default class TrezorBridgeKeyring extends EventEmitter {
return data.payload
}

signPersonalMessage = async (path: string, message: string): Promise<SignHardwareMessageOperationResult> => {
if (!this.isUnlocked() && !(await this.unlock())) {
return { success: false, error: getLocale('braveWalletUnlockError') }
}
const data = await this.sendTrezorCommand<SignMessageResponsePayload>({
command: TrezorCommand.SignMessage,
// @ts-ignore
id: crypto.randomUUID(),
payload: this.prepareSignMessagePayload(path, message),
origin: window.origin
})
if (!data) {
return { success: false, error: getLocale('braveWalletProcessMessageError') }
}
if (!data.payload.success) {
const unsuccess = data.payload
return { success: false, error: unsuccess.payload.error, code: unsuccess.payload.code }
}
return { success: true, payload: data.payload.payload.signature }
}

isUnlocked = () => {
return this.unlocked_
}
Expand Down Expand Up @@ -158,7 +182,9 @@ export default class TrezorBridgeKeyring extends EventEmitter {
}
}
}

private prepareSignMessagePayload = (path: string, message: string): SignMessageCommandPayload => {
return { path: path, message: message }
}
private publicKeyToAddress = (key: string) => {
const buffer = Buffer.from(key, 'hex')
const address = publicToAddress(buffer, true).toString('hex')
Expand Down
10 changes: 10 additions & 0 deletions components/brave_wallet_ui/constants/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,16 @@ export interface SignHardwareTransactionType {
error?: string
}

export type OperationResult = {
success: boolean
error?: string
code?: string | number
}

export type SignHardwareMessageOperationResult = OperationResult & {
payload?: string
}

export type SwapValidationErrorType =
| 'insufficientBalance'
| 'insufficientEthBalance'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import {
import {
findHardwareAccountInfo,
signTrezorTransaction,
signLedgerTransaction
signLedgerTransaction,
signMessageWithHardwareKeyring
} from '../../common/async/lib'

import { fetchSwapQuoteFactory } from '../../common/async/handlers'
Expand Down Expand Up @@ -247,32 +248,34 @@ handler.on(PanelActions.signMessageProcessed.getType(), async (store: Store, pay

handler.on(PanelActions.signMessageHardware.getType(), async (store, messageData: SignMessageData) => {
const apiProxy = await getAPIProxy()
const braveWalletService = apiProxy.braveWalletService
const hardwareAccount = await findHardwareAccountInfo(messageData.address)
if (hardwareAccount && hardwareAccount.hardware) {
let deviceKeyring = await apiProxy.getKeyringsByType(hardwareAccount.hardware.vendor)
deviceKeyring.signPersonalMessage(hardwareAccount.hardware.path, hardwareAccount.address, messageData.message).
then(async (signature: string) => {
store.dispatch(PanelActions.signMessageHardwareProcessed({ success: true, id: messageData.id, signature: signature, error: '' }))
}).catch(async (error: any) => {
store.dispatch(PanelActions.signMessageHardwareProcessed({ success: false, id: messageData.id, signature: '', error: error.message }))
})
if (!hardwareAccount || !hardwareAccount.hardware) {
const braveWalletService = apiProxy.braveWalletService
await braveWalletService.notifySignMessageHardwareRequestProcessed(false, messageData.id,
'', getLocale('braveWalletHardwareAccountNotFound'))
const signMessageRequest = await getPendingSignMessageRequest()
if (signMessageRequest) {
store.dispatch(PanelActions.signMessage(signMessageRequest))
return
}
apiProxy.closeUI()
return
}
await braveWalletService.notifySignMessageHardwareRequestProcessed(false, messageData.id,
'', getLocale('braveWalletHardwareAccountNotFound'))
const signMessageRequest = await getPendingSignMessageRequest()
if (signMessageRequest) {
store.dispatch(PanelActions.signMessage(signMessageRequest))
const info = hardwareAccount.hardware
apiProxy.closePanelOnDeactivate(false)
const signature = await signMessageWithHardwareKeyring(apiProxy, info.vendor, info.path, messageData.address, messageData.message)
apiProxy.closePanelOnDeactivate(true)
if (!signature || !signature.success) {
store.dispatch(PanelActions.signMessageHardwareProcessed({ success: false, id: messageData.id, error: signature.error }))
return
}
apiProxy.closeUI()
store.dispatch(PanelActions.signMessageHardwareProcessed({ success: true, id: messageData.id, signature: signature.payload }))
})

handler.on(PanelActions.signMessageHardwareProcessed.getType(), async (store, payload: SignMessageHardwareProcessedPayload) => {
const apiProxy = await getAPIProxy()
const braveWalletService = apiProxy.braveWalletService
await braveWalletService.notifySignMessageHardwareRequestProcessed(payload.success, payload.id, payload.signature, payload.error)
await braveWalletService.notifySignMessageHardwareRequestProcessed(payload.success, payload.id, payload.signature || '', payload.error || '')
const signMessageRequest = await getPendingSignMessageRequest()
if (signMessageRequest) {
store.dispatch(PanelActions.signMessage(signMessageRequest))
Expand Down
4 changes: 2 additions & 2 deletions components/brave_wallet_ui/panel/constants/action_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export type SignMessageProcessedPayload = {
export type SignMessageHardwareProcessedPayload = {
success: boolean,
id: number,
signature: string,
error: string
signature?: string,
error?: string
}

export type SwitchEthereumChainProcessedPayload = {
Expand Down
13 changes: 12 additions & 1 deletion components/brave_wallet_ui/trezor/trezor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
GetAccountsResponsePayload,
TrezorGetPublicKeyResponse,
SignTransactionCommand,
SignTransactionResponsePayload
SignTransactionResponsePayload,
SignMessageCommand,
SignMessageResponsePayload,
SignMessageResponse
} from '../common/trezor/trezor-messages'

import { addTrezorCommandHandler } from '../common/trezor/trezor-command-handler'
Expand Down Expand Up @@ -58,3 +61,11 @@ addTrezorCommandHandler(TrezorCommand.SignTransaction, (command: SignTransaction
})
})
})

addTrezorCommandHandler(TrezorCommand.SignMessage, (command: SignMessageCommand, source: Window): Promise<SignMessageResponsePayload> => {
return new Promise(async (resolve) => {
TrezorConnect.ethereumSignMessage(command.payload).then((result: SignMessageResponse) => {
resolve({ id: command.id, command: command.command, payload: result, origin: command.origin })
})
})
})
1 change: 1 addition & 0 deletions components/resources/wallet_strings.grdp
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,5 @@
<message name="IDS_BRAVE_WALLET_HARDWARE_TRANSACTION_NOT_FOUND_ERROR" desc="Unable to find hardware transaction during signing process">Unable to find hardware transaction, please try again</message>
<message name="IDS_BRAVE_WALLET_HARDWARE_TRANSACTION_DEVICE_ERROR" desc="Received error from hardware device during signing process">Received error from hardware device, probably you need to confirm the transaction on your device</message>
<message name="IDS_BRAVE_WALLET_HARDWARE_TRANSACTION_NO_MESSAGE_TO_SIGN_ERROR" desc="Unable to get raw messagse to sign for hardware transaction during signing process">Error processing the transaction, please try again</message>
<message name="IDS_BRAVE_WALLET_HARDWARE_SIGN_MESSAGE_ERROR" desc="Unable to sign personal messagse on hardware device">Error signing message, please try again</message>
</grit-part>

0 comments on commit eaf1f21

Please sign in to comment.