Skip to content

Commit

Permalink
feat(wallet-mobile): Add HW message signing support
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeljscript committed Jan 23, 2025
1 parent ce86c66 commit e3ef331
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 47 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {useConfirmHWConnectionModal} from './common/ConfirmHWConnectionModal'
import {userRejectedError} from './common/errors'
import {createDappConnector} from './common/helpers'
import {usePromptRootKey} from './common/hooks'
import {useShowHWNotSupportedModal} from './common/HWNotSupportedModal'
import {useOpenUnverifiedDappModal} from './common/UnverifiedDappModal'
import {useNavigateTo} from './common/useNavigateTo'
import {useStrings} from './common/useStrings'
Expand Down Expand Up @@ -155,23 +154,44 @@ const useSignData = () => {
}

const useSignDataWithHW = () => {
const {showHWNotSupportedModal, closeModal} = useShowHWNotSupportedModal()
const {confirmHWConnection, closeModal} = useConfirmHWConnectionModal()
const {wallet, meta} = useSelectedWallet()

return React.useCallback(() => {
return new Promise<{signature: string; key: string}>((_resolve, reject) => {
let shouldResolveOnClose = true
showHWNotSupportedModal({
onConfirm: () => {
closeModal()
shouldResolveOnClose = false
return reject(userRejectedError())
},
onClose: () => {
if (shouldResolveOnClose) reject(userRejectedError())
},
return React.useCallback(
(address: string, payload: string) => {
return new Promise<{signature: string; key: string}>((resolve, reject) => {
let isClosed = false
confirmHWConnection({
onConfirm: async ({transportType, deviceInfo}) => {
try {
const cip30 = cip30LedgerExtensionMaker(wallet, meta)
const result = await cip30.signData(address, payload, deviceInfo, transportType === 'USB')
resolve(result)
isClosed = true
closeModal()
} catch (error) {
if (error instanceof BaseLedgerError) {
throw error
}
reject(error)
isClosed = true
closeModal()
}
},
onCancel: () => {
reject(userRejectedError())
isClosed = true
closeModal()
},
onClose: () => {
if (isClosed) return
reject(userRejectedError())
},
})
})
})
}, [showHWNotSupportedModal, closeModal])
},
[confirmHWConnection, wallet, meta, closeModal],
)
}

const useConfirmConnection = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {MessageAddressFieldType, MessageData} from '@cardano-foundation/ledgerjs-hw-app-cardano'
import {Transaction} from '@emurgo/cross-csl-core'
import {createSignedLedgerTxFromCbor} from '@emurgo/yoroi-lib'
import {normalizeToAddress} from '@emurgo/yoroi-lib/dist/internals/utils/addresses'
import {HW, Wallet} from '@yoroi/types'

import {toLedgerSignRequest} from '../../../features/Discover/common/ledger'
import {cardanoConfig} from '../../../features/WalletManager/common/adapters/cardano/cardano-config'
import {assertHasAllSigners} from '../common/signatureUtils'
import {signTxWithLedger} from '../hw/hw'
import {signMessageWithLedger, signTxWithLedger} from '../hw/hw'
import {YoroiWallet} from '../types'
import {getAddressedUtxos, getHexAddressingMap} from '../utils'
import {wrappedCsl} from '../wrappedCsl'
Expand All @@ -17,6 +19,33 @@ export const cip30LedgerExtensionMaker = (wallet: YoroiWallet, meta: Wallet.Meta
class CIP30LedgerExtension {
constructor(private wallet: YoroiWallet, private meta: Wallet.Meta) {}

async signData(
address: string,
payload: string,
hwDeviceInfo: HW.DeviceInfo,
useUSB: boolean,
): Promise<{signature: string; key: string}> {
const {csl, release} = wrappedCsl()
try {
const normalizedAddress = await normalizeToAddress(csl, address)
if (!normalizedAddress) throw new Error('Invalid address')
const ledgerPayload: MessageData = {
messageHex: payload,
signingPath: this.wallet.getAddressing(await normalizedAddress.toBech32(undefined)).path,
hashPayload: false,
preferHexDisplay: false,
addressFieldType: MessageAddressFieldType.KEY_HASH,
}
const response = await signMessageWithLedger(ledgerPayload, hwDeviceInfo, useUSB)
return {
signature: response.signatureHex,
key: response.signingPublicKeyHex,
}
} finally {
release()
}
}

async signTx(cbor: string, partial: boolean, hwDeviceInfo: HW.DeviceInfo, useUSB: boolean): Promise<Transaction> {
const {csl, release} = wrappedCsl()
try {
Expand Down
17 changes: 17 additions & 0 deletions apps/wallet-mobile/src/yoroi-wallets/cardano/hw/hw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type {
GetExtendedPublicKeyResponse,
GetSerialResponse,
GetVersionResponse,
MessageData,
SignMessageResponse,
SignTransactionRequest,
SignTransactionResponse,
} from '@cardano-foundation/ledgerjs-hw-app-cardano'
Expand Down Expand Up @@ -283,3 +285,18 @@ export const signTxWithLedger = async (
throw mapLedgerError(e)
}
}

export const signMessageWithLedger = async (
signRequest: MessageData,
hwDeviceInfo: HW.DeviceInfo,
useUSB: boolean,
): Promise<SignMessageResponse> => {
try {
const appAda = await connectionHandler(hwDeviceInfo.hwFeatures.deviceId, hwDeviceInfo.hwFeatures.deviceObj, useUSB)
const ledgerSignature = await appAda.signMessage(signRequest)
await appAda.transport.close()
return ledgerSignature
} catch (e) {
throw mapLedgerError(e)
}
}

0 comments on commit e3ef331

Please sign in to comment.