Skip to content

Commit

Permalink
CU-86ducwq4y - BSNeo3 - Ledger - Fix issue where sending assets from …
Browse files Browse the repository at this point in the history
…NEON3 is being incorrectly flagged as arbitrary contract invocation
  • Loading branch information
raulduartep committed Aug 14, 2024
1 parent 4b05249 commit 8e75b1b
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/bs-neo3",
"comment": "Fix issue where sending assets from NEON3 is being incorrectly flagged as arbitrary contract invocation",
"type": "patch"
}
],
"packageName": "@cityofzion/bs-neo3"
}
2 changes: 1 addition & 1 deletion packages/bs-neo3/src/BSNeo3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class BSNeo3<BSCustomName extends string = string>
? u.BigInteger.fromDecimal(intent.amount, intent.tokenDecimals).toString()
: intent.amount,
},
{ type: 'Any', value: '' },
{ type: 'Any', value: null },
],
}
})
Expand Down
88 changes: 64 additions & 24 deletions packages/bs-neo3/src/NeonDappKitLedgerServiceNeo3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ import { wallet, api, u } from '@cityofzion/neon-js'
import { NeonParser } from '@cityofzion/neon-dappkit'
import EventEmitter from 'events'

enum LedgerStatus {
OK = 0x9000,
}

enum LedgerCommand {
GET_PUBLIC_KEY = 0x04,
SIGN = 0x02,
}

enum LedgerSecondParameter {
MORE_DATA = 0x80,
LAST_DATA = 0x00,
}

export class NeonDappKitLedgerServiceNeo3 implements LedgerService {
emitter: LedgerServiceEmitter = new EventEmitter() as LedgerServiceEmitter

Expand Down Expand Up @@ -39,32 +53,42 @@ export class NeonDappKitLedgerServiceNeo3 implements LedgerService {
try {
this.emitter.emit('getSignatureStart')

const bip44Buffer = this.toBip44Buffer(addressIndex)
await transport.send(0x80, 0x02, 0, 0x80, bip44Buffer, [0x9000])
await transport.send(0x80, 0x02, 1, 0x80, Buffer.from(NeonParser.numToHex(networkMagic, 4, true), 'hex'), [
0x9000,
])
const bip44 = this.#toBip44(addressIndex)
// Send the BIP44 account as first chunk
await this.#sendChunk(transport, LedgerCommand.SIGN, 0, LedgerSecondParameter.MORE_DATA, bip44)

// Send the network magic as second chunk
await this.#sendChunk(
transport,
LedgerCommand.SIGN,
1,
LedgerSecondParameter.MORE_DATA,
NeonParser.numToHex(networkMagic, 4, true)
)

const chunks = serializedTransaction.match(/.{1,510}/g) || []

for (let i = 0; i < chunks.length - 1; i++) {
await transport.send(0x80, 0x02, 2 + i, 0x80, Buffer.from(chunks[i], 'hex'), [0x9000])
// We plus 2 because we already sent 2 chunks before
const commandIndex = 2 + i
await this.#sendChunk(transport, LedgerCommand.SIGN, commandIndex, LedgerSecondParameter.MORE_DATA, chunks[i])
}

const response = await transport.send(
0x80,
0x02,
2 + chunks.length,
0x00,
Buffer.from(chunks[chunks.length - 1], 'hex'),
[0x9000]
// Again we plus 2 because we already sent 2 chunks before getting the chunks
const lastChunkIndex = 2 + chunks.length
const response = await this.#sendChunk(
transport,
LedgerCommand.SIGN,
lastChunkIndex,
LedgerSecondParameter.LAST_DATA,
chunks[chunks.length - 1]
)

if (response.length <= 2) {
throw new Error(`No more data but Ledger did not return signature!`)
}

const signature = this.derSignatureToHex(response.toString('hex'))
const signature = this.#derSignatureToHex(response.toString('hex'))

return signature
} finally {
Expand All @@ -73,28 +97,44 @@ export class NeonDappKitLedgerServiceNeo3 implements LedgerService {
}

async getPublicKey(transport: Transport, addressIndex = 0): Promise<string> {
const bip44Buffer = this.toBip44Buffer(addressIndex)

const result = await transport.send(0x80, 0x04, 0x00, 0x00, bip44Buffer, [0x9000])
const bip44 = this.#toBip44(addressIndex)

const result = await this.#sendChunk(
transport,
LedgerCommand.GET_PUBLIC_KEY,
0x00,
LedgerSecondParameter.LAST_DATA,
bip44
)
const publicKey = result.toString('hex').substring(0, 130)

return publicKey
}

private toBip44Buffer(addressIndex = 0, changeIndex = 0, accountIndex = 0) {
const accountHex = this.to8BitHex(accountIndex + 0x80000000)
const changeHex = this.to8BitHex(changeIndex)
const addressHex = this.to8BitHex(addressIndex)
#sendChunk(
transport: Transport,
command: LedgerCommand,
commandIndex: number,
secondParameter: LedgerSecondParameter,
chunk: string
) {
return transport.send(0x80, command, commandIndex, secondParameter, Buffer.from(chunk, 'hex'), [LedgerStatus.OK])
}

#toBip44(addressIndex = 0, changeIndex = 0, accountIndex = 0) {
const accountHex = this.#to8BitHex(accountIndex + 0x80000000)
const changeHex = this.#to8BitHex(changeIndex)
const addressHex = this.#to8BitHex(addressIndex)

return Buffer.from('8000002C' + '80000378' + accountHex + changeHex + addressHex, 'hex')
return '8000002C' + '80000378' + accountHex + changeHex + addressHex
}

private to8BitHex(num: number): string {
#to8BitHex(num: number): string {
const hex = num.toString(16)
return '0'.repeat(8 - hex.length) + hex
}

private derSignatureToHex(response: string): string {
#derSignatureToHex(response: string): string {
const ss = new u.StringStream(response)
// The first byte is format. It is usually 0x30 (SEQ) or 0x31 (SET)
// The second byte represents the total length of the DER module.
Expand Down

0 comments on commit 8e75b1b

Please sign in to comment.