Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check pubkey after signing #176

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions src/crypto-providers/ledgerCryptoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
PathTypes,
_AddressParameters,
stakeHashFromBaseAddress,
verifyIntendedPubKeySignatureMatch,
} from './util'

const {bech32, getShelleyAddressNetworkId} = require('cardano-crypto.js')
Expand Down Expand Up @@ -1047,6 +1048,7 @@ export const LedgerCryptoProvider: (
const createWitnesses = (
ledgerWitnesses: LedgerTypes.Witness[],
signingFiles: HwSigningData[],
txBodyHashHex: string,
): TxWitnesses => {
const getSigningFileDataByPath = (path: BIP32Path): HwSigningData => {
const hwSigningData = signingFiles.find((signingFile) =>
Expand All @@ -1056,8 +1058,14 @@ export const LedgerCryptoProvider: (
throw Error(Errors.MissingHwSigningDataAtPathError)
}
const witnessesWithKeys = ledgerWitnesses.map((witness) => {
const signingFile = getSigningFileDataByPath(witness.path as BIP32Path)
const {pubKey, chainCode} = splitXPubKeyCborHex(
getSigningFileDataByPath(witness.path as BIP32Path).cborXPubKeyHex,
signingFile.cborXPubKeyHex,
)
verifyIntendedPubKeySignatureMatch(
txBodyHashHex,
signingFile,
witness.witnessSignatureHex,
)
return {
path: witness.path as BIP32Path,
Expand Down Expand Up @@ -1213,12 +1221,17 @@ export const LedgerCryptoProvider: (
params: TxSigningParameters,
changeOutputFiles: HwSigningData[],
): Promise<TxWitnesses> => {
let ledgerWitnesses: LedgerTypes.Witness[]
try {
const ledgerWitnesses = await ledgerSignTx(params, changeOutputFiles)
return createWitnesses(ledgerWitnesses, params.hwSigningFileData)
ledgerWitnesses = await ledgerSignTx(params, changeOutputFiles)
} catch (err) {
throw Error(failedMsg(err))
}
return createWitnesses(
ledgerWitnesses,
params.hwSigningFileData,
params.txBodyHashHex,
)
}

const prepareCVoteDelegations = (
Expand Down Expand Up @@ -1503,17 +1516,32 @@ export const LedgerCryptoProvider: (
addressFieldType: LedgerTypes.MessageAddressFieldType.KEY_HASH,
}
}
try {
const response = await ledger.signMessage(ledgerArgs)

return {
signatureHex: response.signatureHex,
signingPublicKeyHex: response.signingPublicKeyHex,
addressFieldHex: response.addressFieldHex,
} as SignedMessageData
let response: LedgerTypes.SignMessageResponse
try {
response = await ledger.signMessage(ledgerArgs)
} catch (err) {
throw Error(failedMsg(err))
}

if (args.address !== undefined) {
janmazak marked this conversation as resolved.
Show resolved Hide resolved
const address = bech32.decode(args.address).data as Buffer
if (address.toString('hex') !== response.addressFieldHex) {
throw Error(Errors.MessageAddressMismatchError)
}
}
const pubKey = splitXPubKeyCborHex(
args.hwSigningFileData.cborXPubKeyHex,
).pubKey
if (pubKey.toString('hex') !== response.signingPublicKeyHex) {
throw Error(Errors.SigningPubKeyMismatchError)
}

return {
signatureHex: response.signatureHex,
signingPublicKeyHex: response.signingPublicKeyHex,
addressFieldHex: response.addressFieldHex,
} as SignedMessageData
}

const nativeScriptToLedgerTypes = (
Expand Down
28 changes: 27 additions & 1 deletion src/crypto-providers/trezorCryptoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
areAddressParamsAllowed,
_AddressParameters,
stakeHashFromBaseAddress,
verifyIntendedPubKeySignatureMatch,
} from './util'
import {Errors} from '../errors'
import {partition} from '../util'
Expand Down Expand Up @@ -652,6 +653,7 @@ export const TrezorCryptoProvider: () => Promise<CryptoProvider> = async () => {
const createWitnesses = (
trezorWitnesses: TrezorTypes.CardanoSignedTxWitness[],
signingFiles: HwSigningData[],
txBodyHashHex: string,
): TxWitnesses => {
const getSigningFileDataByXPubKey = (pubKey: PubKeyHex): HwSigningData => {
const hwSigningData = signingFiles.find(
Expand All @@ -668,6 +670,11 @@ export const TrezorCryptoProvider: () => Promise<CryptoProvider> = async () => {
const signingFile = getSigningFileDataByXPubKey(
witness.pubKey as PubKeyHex,
)
verifyIntendedPubKeySignatureMatch(
txBodyHashHex,
signingFile,
witness.signature,
)
return {
...witness,
path: signingFile.path,
Expand Down Expand Up @@ -845,7 +852,11 @@ export const TrezorCryptoProvider: () => Promise<CryptoProvider> = async () => {
changeOutputFiles: HwSigningData[],
): Promise<TxWitnesses> => {
const trezorWitnesses = await trezorSignTx(params, changeOutputFiles)
return createWitnesses(trezorWitnesses, params.hwSigningFileData)
return createWitnesses(
trezorWitnesses,
params.hwSigningFileData,
params.txBodyHashHex,
)
}

const prepareVoteDelegations = (
Expand Down Expand Up @@ -1098,6 +1109,21 @@ export const TrezorCryptoProvider: () => Promise<CryptoProvider> = async () => {
if (!response.success) {
throw Error(response.payload.error)
}

if (args.address !== undefined) {
const addressCli = bech32.decode(args.address).data as Buffer
const addressFieldHex = response.payload.headers.protected.address
if (addressCli.toString('hex') !== addressFieldHex) {
throw Error(Errors.MessageAddressMismatchError)
}
}
const pubKey = splitXPubKeyCborHex(
args.hwSigningFileData.cborXPubKeyHex,
).pubKey
if (pubKey.toString('hex') !== response.payload.pubKey) {
throw Error(Errors.SigningPubKeyMismatchError)
}

return {
addressFieldHex: response.payload.headers.protected.address as HexString,
signatureHex: response.payload.signature as HexString,
Expand Down
21 changes: 21 additions & 0 deletions src/crypto-providers/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const {
base58,
bech32,
blake2b,
verify,
} = require('cardano-crypto.js')

export type _AddressParameters = {
Expand Down Expand Up @@ -705,6 +706,25 @@ const validateCIP36RegistrationAddressType = (addressType: number): void => {
const getTxBodyHash = (txBody: TransactionBody): string =>
blake2b(encodeTxBody(txBody), 32).toString('hex')

const verifySignature = (
message: Buffer,
pubKey: Buffer,
signature: Buffer,
): boolean => verify(message, pubKey, signature)

const verifyIntendedPubKeySignatureMatch = (
messageHex: string,
hwSigningFile: HwSigningData,
signatureHex: string,
): void => {
const hash = Buffer.from(messageHex, 'hex')
const pubKey = splitXPubKeyCborHex(hwSigningFile.cborXPubKeyHex).pubKey
const signature = Buffer.from(signatureHex, 'hex')
if (!verifySignature(hash, pubKey, signature)) {
throw Error(Errors.SigningPubKeyMismatchError)
}
}

export {
PathTypes,
classifyPath,
Expand All @@ -730,4 +750,5 @@ export {
hasMultisigSigningFile,
determineSigningMode,
getTxBodyHash,
verifyIntendedPubKeySignatureMatch,
}
2 changes: 2 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const enum Errors {
InvalidNodeKeyGenInputsError = 'Invalid node key-gen inputs',
InvalidDerivationTypeError = 'Invalid derivation type',
TxSerializationMismatchError = 'Tx serialization mismatch',
SigningPubKeyMismatchError = 'Signing public key mismatch: likely because an incorrect signing file was used',
MetadataSerializationMismatchError = 'Metadata serialization mismatch',
MissingHwSigningDataAtPathError = 'Can not find hw signing data by path',
MissingHwSigningDataAtXPubKeyError = 'Can not find hw signing data by extended public key',
Expand Down Expand Up @@ -76,4 +77,5 @@ export const enum Errors {
InvalidMessageAddressError = 'Cannot derive address parameters in message signing: likely because address signing files are missing',
InvalidMessageAddressTypeError = 'Invalid or unsupported address type in message signing',
InvalidMessageAddressSigningFilesError = 'Missing address signing files in message signing',
MessageAddressMismatchError = 'Message address mismatch: likely because incorrect address signing files were used',
}