mirror of
https://github.com/luxfi/ledger.git
synced 2026-10-30 16:21:35 +00:00
214 lines
7.2 KiB
TypeScript
214 lines
7.2 KiB
TypeScript
/** ******************************************************************************
|
|
* (c) 2020 Zondax GmbH
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
******************************************************************************* */
|
|
|
|
import Zemu from '@zondax/zemu'
|
|
import { ETH_DERIVATION, defaultOptions, models } from './common'
|
|
import Eth from '@ledgerhq/hw-app-eth'
|
|
import LuxApp from '@zondax/ledger-lux-app'
|
|
import { Transaction, FeeMarketEIP1559Transaction } from "@ethereumjs/tx";
|
|
import Common from '@ethereumjs/common'
|
|
import { bnToRlp, rlp, bufArrToArr} from "ethereumjs-util";
|
|
import { ec } from 'elliptic'
|
|
const BN = require('bn.js');
|
|
|
|
|
|
type NftInfo = {
|
|
token_address: string,
|
|
token_name: string,
|
|
chain_id: number,
|
|
}
|
|
|
|
type TestData = {
|
|
name: string,
|
|
op: Buffer,
|
|
nft_info: NftInfo | undefined,
|
|
chainId: number | undefined
|
|
}
|
|
const SIGN_TEST_DATA = [
|
|
{
|
|
name: 'basic_transfer',
|
|
op: {
|
|
value: 'abcdef00',
|
|
to: 'df073477da421520cf03af261b782282c304ad66',
|
|
},
|
|
chainId: 9867,
|
|
},
|
|
{
|
|
name: 'legacy_contract_deploy',
|
|
op: {
|
|
value: 'abcdef00',
|
|
data: '1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
|
},
|
|
chainId: 5,
|
|
},
|
|
{
|
|
name: 'legacy_contract_call',
|
|
op: {
|
|
to: '62650ae5c5777d1660cc17fcd4f48f6a66b9a4c2',
|
|
value: 'abcdef01',
|
|
data: 'ee919d500000000000000000000000000000000000000000000000000000000000000001',
|
|
},
|
|
chainId: 689,
|
|
},
|
|
{
|
|
name: 'erc20_transfer',
|
|
op: {
|
|
// this is not probably the contract address but lets use it
|
|
to: '62650ae5c5777d1660cc17fcd4f48f6a66b9a4c2',
|
|
value: '0',
|
|
data: 'a9059cbb0000000000000000000000005f658a6d1928c39b286b48192fea8d46d87ad07700000000000000000000000000000000000000000000000000000000000f4240',
|
|
},
|
|
chainId: 65089,
|
|
},
|
|
{
|
|
name: 'pangolin_contract_call',
|
|
op: {
|
|
// Pangolin LUX/DAI swap 2
|
|
to: '62650ae5c5777d1660cc17fcd4f550000eacdfa0',
|
|
value: '0',
|
|
data: '8a657e670000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000c7b9b39ab3081ac34fc4324e3f648b55528871970000000000000000000000000000000000000000000000000000017938e114be0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000ba7deebbfc5fa1100fb055a87773e1e99cd3507a',
|
|
},
|
|
chainId: 100,
|
|
},
|
|
{
|
|
name: 'erc721_approve',
|
|
op: {
|
|
// this is not probably the contract address but lets use it
|
|
to: '62650ae5c5777d1660cc17fcd4f48f6a66b9a4c2',
|
|
value: '0',
|
|
data: '095ea7b30000000000000000000000005f658a6d1928c39b286b48192fea8d46d87ad07700000000000000000000000000000000000000000000000000000000000f4240',
|
|
},
|
|
chainId: 43114,
|
|
nft_info: {
|
|
token_address: '62650ae5c5777d1660cc17fcd4f48f6a66b9a4c2',
|
|
token_name: 'Unknown',
|
|
chain_id: 43114,
|
|
},
|
|
},
|
|
{
|
|
name: 'basic_transfer_no_eip155',
|
|
op: {
|
|
value: 'a1bcd400',
|
|
to: 'df073477da421520cf03af261b782282c304ad66',
|
|
},
|
|
},
|
|
{
|
|
name: 'contract_deploy_no_eip155',
|
|
op: {
|
|
value: '1',
|
|
data: '1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
|
},
|
|
},
|
|
]
|
|
|
|
const rawUnsignedLegacyTransaction = (params: any, chainId: number | undefined) => {
|
|
|
|
const txParams = {
|
|
nonce: '0x00',
|
|
gasPrice: '0x6d6e2edc00',
|
|
gasLimit: '0x2dc6c0',
|
|
to: params.to !== undefined? '0x' + params.to: undefined,
|
|
value: '0x' + params.value,
|
|
data: params.data !== undefined? '0x' + params.data: undefined,
|
|
}
|
|
|
|
const chain = Common.forCustomChain(1, { name: 'lux', networkId: 1, chainId })
|
|
const options = chainId !== undefined? {common: chain}: undefined
|
|
|
|
// legacy
|
|
const tx = Transaction.fromTxData(txParams, options)
|
|
|
|
let unsignedTx: Buffer[] | Buffer
|
|
unsignedTx = tx.getMessageToSign(false)
|
|
unsignedTx = Buffer.from(rlp.encode(bufArrToArr(unsignedTx)))
|
|
|
|
return unsignedTx
|
|
|
|
};
|
|
|
|
// an alternative verification method for legacy transactions, taken from obsidian
|
|
// which uses the ethereumIS library
|
|
function check_legacy_signature(hexTx: string, signature: any, chainId: number | undefined) {
|
|
const ethTx = Buffer.from(hexTx, 'hex');
|
|
|
|
const chain = Common.forCustomChain(1, { name: 'lux', networkId: 1, chainId })
|
|
const tx_options = chainId !== undefined? {common: chain}: undefined
|
|
|
|
const txnBufsDecoded: any = rlp.decode(ethTx).slice(0,6);
|
|
const txnBufsMap = [signature.v, signature.r, signature.s].map(a=>Buffer.from(((a.length%2==1)?'0'+a:a),'hex'));
|
|
|
|
const txnBufs = txnBufsDecoded.concat(txnBufsMap);
|
|
|
|
const ethTxObj = Transaction.fromValuesArray(txnBufs, tx_options);
|
|
|
|
return ethTxObj.verifySignature()
|
|
}
|
|
|
|
describe.each(models)('EthereumLegacy [%s]; sign', function (m) {
|
|
test.each(SIGN_TEST_DATA)('sign legacy: $name', async function (data) {
|
|
const sim = new Zemu(m.path)
|
|
try {
|
|
await sim.start({ ...defaultOptions, model: m.name })
|
|
const app = new LuxApp(sim.getTransport())
|
|
|
|
const testcase = `${m.prefix.toLowerCase()}-eth-sign-${data.name}`
|
|
|
|
const currentScreen = sim.snapshot()
|
|
const msg = rawUnsignedLegacyTransaction(data.op, data.chainId);
|
|
|
|
const nft = data.nft_info
|
|
if (nft !== undefined) {
|
|
const provide_resp = await app.provideNftInfo(nft.token_address, nft.token_name, nft.chain_id)
|
|
expect(provide_resp.returnCode).toEqual(0x9000)
|
|
}
|
|
|
|
const respReq = app.signEVMTransaction(ETH_DERIVATION, msg.toString('hex'), null)
|
|
await sim.waitUntilScreenIsNot(currentScreen, 20000)
|
|
await sim.compareSnapshotsAndApprove('.', testcase)
|
|
|
|
const resp = await respReq
|
|
|
|
console.log(resp, m.name, data.name)
|
|
|
|
expect(resp).toHaveProperty('s')
|
|
expect(resp).toHaveProperty('r')
|
|
expect(resp).toHaveProperty('v')
|
|
|
|
//Verify signature
|
|
const resp_addr = await app.getETHAddress(ETH_DERIVATION, false)
|
|
|
|
const EC = new ec("secp256k1");
|
|
const sha3 = require('js-sha3');
|
|
const msgHash = sha3.keccak256(msg);
|
|
|
|
const pubKey = Buffer.from(resp_addr.publicKey, 'hex')
|
|
const signature_obj = {
|
|
r: Buffer.from(resp.r, 'hex'),
|
|
s: Buffer.from(resp.s, 'hex'),
|
|
}
|
|
|
|
const signatureOK = EC.verify(msgHash, signature_obj, pubKey, 'hex')
|
|
expect(signatureOK).toEqual(true)
|
|
|
|
// alternative verification to be safe
|
|
const test = await check_legacy_signature(msg.toString('hex'),resp, data.chainId)
|
|
expect(test).toEqual(true)
|
|
} finally {
|
|
await sim.close()
|
|
}
|
|
})
|
|
})
|