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 8, 2021
1 parent f40759c commit 97d6464
Show file tree
Hide file tree
Showing 10 changed files with 136 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 @@ -485,7 +485,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 @@ -337,3 +338,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,24 @@ 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) {
console.log(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 @@ -3,44 +3,31 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// 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 { EthereumSignedTx, HDNodeResponse } from 'trezor-connect/lib/typescript/trezor/protobuf'
import { Unsuccessful, EthereumSignTransaction, CommonParams, Success, EthereumSignMessage } from 'trezor-connect'
import { EthereumSignedTx, HDNodeResponse, MessageSignature } from 'trezor-connect/lib/typescript/trezor/protobuf'
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 @@ -54,12 +41,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 @@ -12,6 +12,7 @@ import {
} from '../../components/desktop/popup-modals/add-account-modal/hardware-wallet-connect/types'
import {
kTrezorHardwareVendor,
SignHardwareMessageOperationResult,
TransactionInfo
} from '../../constants/types'
import {
Expand All @@ -21,8 +22,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 @@ -81,6 +84,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 @@ -124,7 +148,9 @@ export default class TrezorBridgeKeyring extends EventEmitter {
private normalize (buf: any) {
return bufferToHex(buf).toString()
}

private prepareSignMessagePayload = (path: string, message: string): SignMessageCommandPayload => {
return { path: path, message: message }
}
private prepareTransactionPayload = (path: string, txInfo: TransactionInfo, chainId: string): SignTransactionCommandPayload => {
const txParams = {
nonce: txInfo.txData.baseData.nonce,
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 @@ -367,6 +367,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 @@ -29,7 +29,8 @@ import {
import {
findHardwareAccountInfo,
signTrezorTransaction,
signLedgerTransaction
signLedgerTransaction,
signMessageWithHardwareKeyring
} from '../../common/async/lib'

import { fetchSwapQuoteFactory } from '../../common/async/handlers'
Expand Down Expand Up @@ -210,32 +211,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,6 +39,6 @@ export type SignMessageProcessedPayload = {
export type SignMessageHardwareProcessedPayload = {
success: boolean,
id: number,
signature: string,
error: string
signature?: string,
error?: string
}
15 changes: 14 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,13 @@ addTrezorCommandHandler(TrezorCommand.SignTransaction, (command: SignTransaction
})
})
})

addTrezorCommandHandler(TrezorCommand.SignMessage, (command: SignMessageCommand, source: Window): Promise<SignMessageResponsePayload> => {
return new Promise(async (resolve) => {
console.log(command.payload)
TrezorConnect.ethereumSignMessage(command.payload).then((result: SignMessageResponse) => {
console.log(result)
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 @@ -332,4 +332,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 97d6464

Please sign in to comment.