/*
*******************************************************************************
* Java Card Bitcoin Hardware Wallet
* (c) 2015 Ledger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/
package com.ledger.wallet.test;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import junit.framework.TestCase;
import com.licel.jcardsim.base.Simulator;
import com.licel.jcardsim.utils.AIDUtil;
import com.licel.jcardsim.utils.ByteUtil;
import javacard.framework.AID;
import javacard.framework.ISO7816;
import com.ledger.wallet.LedgerWalletApplet;
import com.btchip.BTChipDongle;
import com.btchip.BTChipConstants;
import com.btchip.BTChipException;
import com.btchip.BitcoinTransaction;
public class TestTransaction extends AbstractTest {
// fcabbad3a52ba1274c9a5e41f15df84cc79d1116499a2284025df0ff3737160d
public static final byte[] TXIN_1 = ByteUtil.byteArray("01000000011ce6fa2f5f4d49355821803039fcce4e3e3f7d0b448244222d98e38041637c73000000006a47304402202301a4e34c2e482624b89152ded4fe7624c4d0ff8627c5b8d03049812fd4559f0220531e00d2b004a3dcd0f4e8e57649ee6cddd269bb6c23972182b38c11d4cf910f012103f6e22ad16597a679f53b47f02a2473e0f4ed1c2c1dfe5b9c9115d54b94c37c41ffffffff022fc8a900000000001976a914bb42f9f29403fe18be77d5c71e1a7b11fb7933f988ace0930400000000001976a91442e73e3c17fadf8d5547d5ed07897cf7385302ef88ac00000000");
// 2da2361a2034615bd7788dd608f72ac2262f2ee0b764be2e301729be81d3cfa4
public static final byte[] TXOUT_1 = ByteUtil.byteArray("01000000010d163737fff05d0284229a4916119dc74cf85df1415e9a4c27a12ba5d3baabfc010000006b483045022100f2008bfb170266905f7de00390fa4bce4ad1bba4694a4a4bff8e57b90abdcc9b02202bf77725e58d59f52d848897ef9fb5769c39a5a434725b06e2b7642a7d55a63d01210372fd24bfbaa0017c61a56706f26782507a94233a65d83694aaab3ed5fefad0caffffffff01222b0400000000001976a914e585fe65b9beb6937fbd1d0b386966451417978d88ac00000000");
private static final String TXOUT_1_ADDRESS = "n2SZZ2n1dgnCEJYEJ6TDfKvt9tnRF7LNxf";
// 482cb7d6377332684c53fc7ce1ef8dffeb5666dae97a0053b640524a8db67f5c
public static final byte[] TXIN_2_1 = ByteUtil.byteArray("01000000019a3882d438d24e3ad7fe98180a952c2e8fea8acd86172d967b43659385b39bfd010000006b48304502210091b7b32d220102d9d5bd385d3fd6b957fdb902daca4fe398e0688b157860379a02202f2f148d9d0424c741bfb3dc89a6e3d32beb026153332cb72864d7d55d47220f0121024d1b2529fe22c463844e9c88f6b7ccb8f7fa6601862d58c5eb6dbad62beee0aaffffffff024f316101000000001976a914a7896ab8c92668021db756cba95795ec9c9bbe7088ac801a0600000000001976a91464d521453272dbd156fcf7844385748198dda27b88ac00000000");
// d26f11f27e5bfeca12e157b0e9f7c97b0a0f163a29e79ec406ba123f2174a871
public static final byte[] TXIN_2_2 = ByteUtil.byteArray("01000000022ce66cbc0f14cd91c4967fa7a3acb1214d32b9da0ed3fb9e862b7733b7791de6000000006b483045022100e614f109e23f78ae27abaa5188d325a8a5cdc8a9724d11a42b5362f7a1c4181d02204518d7a9e96b9e3ddce99a2ec80d2ad05ff04221f20332479d7c800ecceb10b80121026f2f5cf851d6cb1df93dc33df5d306ae1a1cd4f3dca1b9c34b8f3864db20c280ffffffffb8801068b6bbd0d157898211699d47c9de12a71c42e4863fabc06d8786f10ca6000000006b483045022100935d3f81f7c814b2e34203e821f682a6de61fae03b229d4f6631edd2c08487e302202d0740a90b9120b0fa344b8f581fc003033a50ccb66f3a7ace7c3fe5b5e4b9c60121021f5ccdbf1254e2f10052bdf0374554e7abc7e3214e951279cb1ff799c79ae709ffffffff02e0930400000000001976a91464d521453272dbd156fcf7844385748198dda27b88ac2b6d0300000000001976a914d4dd045ef4e98a28264301b463c57dd3106f565788ac00000000");
// b3b45c8768ca708f87696d08be6f17cb1199a603870287bb59ab8ca9903086c2
public static final byte[] TXOUT_2 = ByteUtil.byteArray("01000000025c7fb68d4a5240b653007ae9da6656ebff8defe17cfc534c68327337d6b72c48010000006a47304402205a5df97d31199317b6c26bac71942101236c5ab49b6ea19566fe775a5003c88e02207b976e7e19800fe3c26fb4e6e57015d3c395383e90b0b95082e2055d12c0a1b50121035ff80a17dbc573406c653718fb16c7400c43d6d07886ed37da0a0c30ade31179ffffffff71a874213f12ba06c49ee7293a160f0a7bc9f7e9b057e112cafe5b7ef2116fd2000000006a47304402200d50aa47541f30dcc47370916560114f0fe4b437f6e58c89af91254e970f8c1802202673babd4a920e72644294b53a4eebf293d771555f6d953109340bb5435473470121035ff80a17dbc573406c653718fb16c7400c43d6d07886ed37da0a0c30ade31179ffffffff0282a40200000000001976a914538abe38a9744c03dcc8122b787d976ab9f7a90c88ac20a10700000000001976a914643a30d6e2a664b11bee6f07471658d58f91ad4288ac00000000");
public static final String TXOUT_2_ADDRESS = "mpeuaQLpsGey3UbQ1xupe5FEwRMfCNMLh5";
// 7764aef5620cbe5fb4d95377c668e365affc43fa1878d881175ca00d82193ff1
public static final byte[] TXIN_3 = ByteUtil.byteArray("01000000016c9bd18d376d47995edbc623e4d57dfa6677e52ee72f3ff620a00b26d1372d81010000006a473044022025a3832dc0fbfbc7432146238339c35a39707cc7c5c2e5267e089976550b480702205734a02fb2b9490e365fd5d05e6367c2f0b032a8d43ee6eaf256570b21c571f90121024be65fee9e15a9d264516abe811286b0cf5107d72a2155f07978cae7af7f141bffffffff02801a06000000000017a914296d13ce7f304a815307cac4b2bb5830d5b019a2874bad2f00000000001976a91430a40bb2280255f92bea4f65c2ff9c408cfe906788ac00000000");
// 2dd0a8212e873222be1eb83da020cb3febd3cd12a0449af85e4b16b1614b8621
public static final byte[] TXOUT_3 = ByteUtil.byteArray("0100000001f13f19820da05c1781d87818fa43fcaf65e368c67753d9b45fbe0c62f5ae647700000000da0048304502210094e6ba564ee8dfda62fa97053142b9a71605b7d81a1badb3a3944cb1cb1540db02207bbace366553c76d9d33bdb4332a7daf8ba503ac0fe01437ca9d1f5069a10aa60147304402203d8c3803c4289e05a48350e5c107b591fc8e2f35504ccb25c7a3e0a22272c2f40220238454e6e70f355e66963e70feaafc8230637e10efc750310336353e347aaf170147522102a3cf97be30a15eede7165963e710207f989ec9d7cb717b44e03b6912b80ad7b22103a23b107c301f7bd4870453dda3dd86758693629690702832d3fae22cdd4df3cf52aeffffffff02905f01000000000017a91479b277f534a02f50fc7891d831460dd87c3aa4b187e0930400000000001976a9143b3c2312df153aeac34f19944fec0b5f2ec4e5d088ac00000000");
public static final byte[] TXIN_3_REDEEM_SCRIPT = ByteUtil.byteArray("522102a3cf97be30a15eede7165963e710207f989ec9d7cb717b44e03b6912b80ad7b22103a23b107c301f7bd4870453dda3dd86758693629690702832d3fae22cdd4df3cf52ae");
public static String EXPECTED_ADDRESS_1 = "n4CYzppnrJViRcUrezwcSdwwGERAcHScQZ";
public static byte[] EXPECTED_PUBLIC_KEY_1 = ByteUtil.byteArray("04a44e52606aaafa575c3d9c2d09819ce885ab4066bb7d1e8d61acae24986ab4579b9fb5742b49fa3dcf508242ba31f01ee889072159cd6aff27048d7ba4e9e3f0");
public static byte[] EXPECTED_CHAINCODE_1 = ByteUtil.byteArray("80ecfe04ceeabc5745b0eeeb5b5f36d0f40119e18c865cf8d5a407bcb6e8b88c");
public void testTX1ContactlessSticky() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_1));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_1));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs());
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
// Keycard validation is done while still in the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
byte[] pin = keycardHelper.getPIN(TXOUT_1_ADDRESS, keycardIndexes);
byte[] signature = dongle.untrustedHashSign("44'/0'/0'/0/0", pin);
signature = canonicalizeSignature(signature);
byte[] originalSignature = Arrays.copyOfRange(txout_1.getInputs().get(0).getScript(), 1, 1 + signature.length);
assertTrue(Arrays.equals(signature, originalSignature));
}
public void testTX1ContactlessNoPIN() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_1));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_1));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
try {
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
}
public void testTX1ContactlessUntrustedInput() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_1));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_1));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
byte[] prevout = Arrays.copyOfRange(input1.getValue(), 4, 4 + 36);
input1 = dongle.createInput(prevout, false);
try {
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_WRONG_DATA);
}
}
public void testTX1ContactlessDisconnect() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_1));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_1));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs());
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
reset();
// Card is removed from the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
byte[] pin = keycardHelper.getPIN(TXOUT_1_ADDRESS, keycardIndexes);
byte[] signature = null;
try {
dongle.untrustedHashSign("44'/0'/0'/0/0", pin);
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
}
public void testTX1ContactlessDisconnectReconnect() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_1));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_1));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs());
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
reset();
// Card is removed from the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
byte[] pin = keycardHelper.getPIN(TXOUT_1_ADDRESS, keycardIndexes);
// Reinitialize the transient parser
dongle.startUntrustedTransaction(
false,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
dongle.finalizeInputFull(txout_1.serializeOutputs());
byte[] signature = dongle.untrustedHashSign("44'/0'/0'/0/0", pin);
signature = canonicalizeSignature(signature);
byte[] originalSignature = Arrays.copyOfRange(txout_1.getInputs().get(0).getScript(), 1, 1 + signature.length);
assertTrue(Arrays.equals(signature, originalSignature));
}
public void testTX1ContactlessDisconnectReconnectFakeOutputAmount() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_1));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_1));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs());
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
reset();
// Card is removed from the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
byte[] pin = keycardHelper.getPIN(TXOUT_1_ADDRESS, keycardIndexes);
// Reinitialize the transient parser
dongle.startUntrustedTransaction(
false,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
byte[] fullOutput = txout_1.serializeOutputs();
fullOutput[4]++;
try {
dongle.finalizeInputFull(fullOutput);
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_WRONG_DATA);
}
}
public void testTX1ContactlessDisconnectReconnectFakeOutputDestination() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_1));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_1));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs());
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
reset();
// Card is removed from the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
byte[] pin = keycardHelper.getPIN(TXOUT_1_ADDRESS, keycardIndexes);
// Reinitialize the transient parser
dongle.startUntrustedTransaction(
false,
0,
new BTChipDongle.BTChipInput[] { input1 },
txin_1.getOutputs().get(1).getScript());
byte[] fullOutput = txout_1.serializeOutputs();
fullOutput[fullOutput.length - 5] ^= (byte)0x42;
try {
dongle.finalizeInputFull(fullOutput);
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_WRONG_DATA);
}
}
public void testTX2ContactlessSticky() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_1));
BitcoinTransaction txin_2 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_2));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_2));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
BTChipDongle.BTChipInput input2 = dongle.getTrustedInput(txin_2, 0);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
// Keycard validation is done while still in the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
byte[] pin = keycardHelper.getPIN(TXOUT_2_ADDRESS, keycardIndexes);
byte[] signature = dongle.untrustedHashSign("44'/0'/0'/0/1", pin);
signature = canonicalizeSignature(signature);
byte[] originalSignature = Arrays.copyOfRange(txout_1.getInputs().get(0).getScript(), 1, 1 + signature.length);
assertTrue(Arrays.equals(signature, originalSignature));
// Process second input
dongle.startUntrustedTransaction(
false,
1,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_2.getOutputs().get(0).getScript());
dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
signature = dongle.untrustedHashSign("44'/0'/0'/0/1", pin);
signature = canonicalizeSignature(signature);
originalSignature = Arrays.copyOfRange(txout_1.getInputs().get(1).getScript(), 1, 1 + signature.length);
assertTrue(Arrays.equals(signature, originalSignature));
}
public void testTX2ContactlessDisconnect() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_1));
BitcoinTransaction txin_2 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_2));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_2));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
BTChipDongle.BTChipInput input2 = dongle.getTrustedInput(txin_2, 0);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
reset();
// Card is removed from the field
byte[] pin = keycardHelper.getPIN(TXOUT_2_ADDRESS, keycardIndexes);
try {
dongle.untrustedHashSign("44'/0'/0'/0/1", pin);
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
}
public void testTX2ContactlessDisconnectReconnect() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_1));
BitcoinTransaction txin_2 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_2));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_2));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
BTChipDongle.BTChipInput input2 = dongle.getTrustedInput(txin_2, 0);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
// Keycard validation is done while still in the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
reset();
// Card is removed from the field
// Reinitialize the transient parser
dongle.startUntrustedTransaction(
false,
0,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_1.getOutputs().get(1).getScript());
dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
byte[] pin = keycardHelper.getPIN(TXOUT_2_ADDRESS, keycardIndexes);
byte[] signature = dongle.untrustedHashSign("44'/0'/0'/0/1", pin);
signature = canonicalizeSignature(signature);
byte[] originalSignature = Arrays.copyOfRange(txout_1.getInputs().get(0).getScript(), 1, 1 + signature.length);
assertTrue(Arrays.equals(signature, originalSignature));
// Process second input
dongle.startUntrustedTransaction(
false,
1,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_2.getOutputs().get(0).getScript());
dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
signature = dongle.untrustedHashSign("44'/0'/0'/0/1", pin);
signature = canonicalizeSignature(signature);
originalSignature = Arrays.copyOfRange(txout_1.getInputs().get(1).getScript(), 1, 1 + signature.length);
assertTrue(Arrays.equals(signature, originalSignature));
}
public void testTX2ContactlessDisconnectReconnectSwap() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_1));
BitcoinTransaction txin_2 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_2));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_2));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
BTChipDongle.BTChipInput input2 = dongle.getTrustedInput(txin_2, 0);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_1.getOutputs().get(1).getScript());
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.KEYCARD);
// Keycard validation is done while still in the field
byte[] keycardIndexes = ((BTChipDongle.BTChipOutputKeycard)output).getKeycardIndexes();
assertEquals(keycardIndexes.length, DEFAULT_KEYCARD_ADDRESS_SIZE);
reset();
// Card is removed from the field
// Reinitialize the transient parser
dongle.startUntrustedTransaction(
false,
0,
new BTChipDongle.BTChipInput[] { input2, input1 },
txin_2.getOutputs().get(0).getScript());
try {
dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/0");
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_WRONG_DATA);
}
}
public void testTX2ContactlessNoChange() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_1));
BitcoinTransaction txin_2 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_2));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_2));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
BTChipDongle.BTChipInput input2 = dongle.getTrustedInput(txin_2, 0);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_1.getOutputs().get(1).getScript());
try {
dongle.finalizeInputFull(txout_1.serializeOutputs());
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_WRONG_DATA);
}
}
public void testTX2ContactlessWrongChange() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_1));
BitcoinTransaction txin_2 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_2_2));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_2));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 1);
BTChipDongle.BTChipInput input2 = dongle.getTrustedInput(txin_2, 0);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1, input2 },
txin_1.getOutputs().get(1).getScript());
try {
dongle.finalizeInputFull(txout_1.serializeOutputs(), "44'/0'/0'/1/1");
fail();
}
catch(BTChipException e) {
assertEquals(e.getSW(), ISO7816.SW_WRONG_DATA);
}
}
public void testTX3Contactless() throws BTChipException {
KeycardHelper keycardHelper = new KeycardHelper(DEFAULT_KEYCARD);
BTChipDongle dongle = prepareDongleRestoreTestnet(true);
simulator.changeProtocol("T=CL,TYPE_A,T1");
dongle.verifyPin(DEFAULT_PIN);
BitcoinTransaction txin_1 = new BitcoinTransaction(new ByteArrayInputStream(TXIN_3));
BitcoinTransaction txout_1 = new BitcoinTransaction(new ByteArrayInputStream(TXOUT_3));
BTChipDongle.BTChipInput input1 = dongle.getTrustedInput(txin_1, 0);
byte[] prevout = Arrays.copyOfRange(input1.getValue(), 4, 4 + 36);
input1 = dongle.createInput(prevout, false);
dongle.startUntrustedTransaction(
true,
0,
new BTChipDongle.BTChipInput[] { input1 },
TXIN_3_REDEEM_SCRIPT);
BTChipDongle.BTChipOutput output = dongle.finalizeInputFull(txout_1.serializeOutputs());
assertEquals(output.getUserConfirmation(), BTChipDongle.UserConfirmation.NONE);
byte[] signature = dongle.untrustedHashSign("45'/2147483647/0/0", new byte[0]);
signature = canonicalizeSignature(signature);
byte[] originalSignature = Arrays.copyOfRange(txout_1.getInputs().get(0).getScript(), 2, 2 + signature.length);
assertTrue(Arrays.equals(signature, originalSignature));
}
}