add web3j based test (incomplete)

This commit is contained in:
Michele Balistreri 2017-10-05 14:10:49 +03:00
parent 7c72c16578
commit fb24995cee
3 changed files with 127 additions and 12 deletions

View File

@ -33,11 +33,20 @@ repositories {
dependencies { dependencies {
testCompile(files("../jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar")) testCompile(files("../jcardsim/target/jcardsim-3.0.5-SNAPSHOT.jar"))
testCompile('org.web3j:core:2.3.1')
testCompile("org.bouncycastle:bcprov-jdk15on:1.58") testCompile("org.bouncycastle:bcprov-jdk15on:1.58")
testCompile("org.junit.jupiter:junit-jupiter-api:5.0.0") testCompile("org.junit.jupiter:junit-jupiter-api:5.0.0")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0") testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0")
} }
junitPlatform {
filters {
tags {
exclude 'manual'
}
}
}
sourceCompatibility = 1.3 sourceCompatibility = 1.3
targetCompatibility = 1.3 targetCompatibility = 1.3

View File

@ -4,6 +4,7 @@ import javacard.framework.ISO7816;
import org.bouncycastle.jce.interfaces.ECPrivateKey; import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey; import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
import org.web3j.crypto.ECKeyPair;
import javax.smartcardio.CardChannel; import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException; import javax.smartcardio.CardException;
@ -75,6 +76,40 @@ public class WalletAppletCommandSet {
return loadKey(data, WalletApplet.LOAD_KEY_EC); return loadKey(data, WalletApplet.LOAD_KEY_EC);
} }
public ResponseAPDU loadKey(ECKeyPair ecKeyPair) throws CardException {
byte[] publicKey = ecKeyPair.getPublicKey().toByteArray();
byte[] privateKey = ecKeyPair.getPrivateKey().toByteArray();
int privLen = privateKey.length;
int privOff = 0;
int pubLen = publicKey.length;
int pubOff = 0;
if(privateKey[0] == 0x00) {
privOff++;
privLen--;
}
if(publicKey[0] == 0x00) {
pubOff++;
pubLen--;
}
byte[] data = new byte[pubLen + privLen + 7];
data[0] = (byte) 0xA1;
data[1] = (byte) (pubLen + privLen + 5);
data[2] = (byte) 0x80;
data[3] = (byte) (pubLen + 1);
data[4] = (byte) 0x04;
System.arraycopy(publicKey, pubOff, data, 5, pubLen);
data[5 + pubLen] = (byte) 0x81;
data[6 + pubLen] = (byte) privLen;
System.arraycopy(privateKey, privOff, data, 7 + pubLen, privLen);
return loadKey(data, WalletApplet.LOAD_KEY_EC);
}
public ResponseAPDU loadKey(byte[] data, byte keyType) throws CardException { public ResponseAPDU loadKey(byte[] data, byte keyType) throws CardException {
CommandAPDU loadKey = new CommandAPDU(0x80, WalletApplet.INS_LOAD_KEY, keyType, 0, secureChannel.encryptAPDU(data)); CommandAPDU loadKey = new CommandAPDU(0x80, WalletApplet.INS_LOAD_KEY, keyType, 0, secureChannel.encryptAPDU(data));
return apduChannel.transmit(loadKey); return apduChannel.transmit(loadKey);

View File

@ -8,31 +8,48 @@ import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.RawTransaction;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import javax.smartcardio.*; import javax.smartcardio.*;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.*; import java.security.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@DisplayName("Test the Wallet Applet") @DisplayName("Test the Wallet Applet")
public class WalletAppletTest { public class WalletAppletTest {
private static CardTerminal cardTerminal; private static CardTerminal cardTerminal;
private static CardChannel apduChannel; private static CardChannel apduChannel;
private static CardSimulator simulator;
private SecureChannelSession secureChannel; private SecureChannelSession secureChannel;
private WalletAppletCommandSet cmdSet; private WalletAppletCommandSet cmdSet;
private static final boolean USE_SIMULATOR = true; private static final boolean USE_SIMULATOR;
static {
USE_SIMULATOR = !System.getProperty("im.status.wallet.test.simulated", "false").equals("false");
}
@BeforeAll @BeforeAll
static void initAll() throws CardException { static void initAll() throws CardException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
if (USE_SIMULATOR) { if (USE_SIMULATOR) {
CardSimulator simulator = new CardSimulator(); simulator = new CardSimulator();
AID appletAID = AIDUtil.create(WalletAppletCommandSet.APPLET_AID); AID appletAID = AIDUtil.create(WalletAppletCommandSet.APPLET_AID);
byte[] instParams = Hex.decode("0F53746174757357616C6C657441707001000C313233343536373839303132"); byte[] instParams = Hex.decode("0F53746174757357616C6C657441707001000C313233343536373839303132");
simulator.installApplet(appletAID, WalletApplet.class, instParams, (short) 0, (byte) instParams.length); simulator.installApplet(appletAID, WalletApplet.class, instParams, (short) 0, (byte) instParams.length);
@ -54,7 +71,7 @@ public class WalletAppletTest {
@BeforeEach @BeforeEach
void init() throws CardException { void init() throws CardException {
apduChannel.getCard().getATR(); reset();
cmdSet = new WalletAppletCommandSet(apduChannel); cmdSet = new WalletAppletCommandSet(apduChannel);
byte[] keyData = cmdSet.select().getData(); byte[] keyData = cmdSet.select().getData();
secureChannel = new SecureChannelSession(keyData); secureChannel = new SecureChannelSession(keyData);
@ -146,9 +163,7 @@ public class WalletAppletTest {
assertEquals(0x9000, response.getSW()); assertEquals(0x9000, response.getSW());
// Reset card and verify that the new PIN has really been set // Reset card and verify that the new PIN has really been set
apduChannel.getCard().getATR(); resetAndSelectAndOpenSC();
cmdSet.select();
cmdSet.openSecureChannel();
response = cmdSet.verifyPIN("654321"); response = cmdSet.verifyPIN("654321");
assertEquals(0x9000, response.getSW()); assertEquals(0x9000, response.getSW());
@ -207,9 +222,7 @@ public class WalletAppletTest {
assertEquals(0x9000, response.getSW()); assertEquals(0x9000, response.getSW());
// Check that PIN has been changed and unblocked // Check that PIN has been changed and unblocked
apduChannel.getCard().getATR(); resetAndSelectAndOpenSC();
cmdSet.select();
cmdSet.openSecureChannel();
response = cmdSet.verifyPIN("654321"); response = cmdSet.verifyPIN("654321");
assertEquals(0x9000, response.getSW()); assertEquals(0x9000, response.getSW());
@ -353,9 +366,7 @@ public class WalletAppletTest {
// Signing session is aborted on reselection // Signing session is aborted on reselection
response = cmdSet.sign(data, WalletApplet.SIGN_DATA,true, false); response = cmdSet.sign(data, WalletApplet.SIGN_DATA,true, false);
assertEquals(0x9000, response.getSW()); assertEquals(0x9000, response.getSW());
apduChannel.getCard().getATR(); resetAndSelectAndOpenSC();
cmdSet.select();
cmdSet.openSecureChannel();
response = cmdSet.verifyPIN("000000"); response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSW()); assertEquals(0x9000, response.getSW());
response = cmdSet.sign(smallData, WalletApplet.SIGN_DATA,false, true); response = cmdSet.sign(smallData, WalletApplet.SIGN_DATA,false, true);
@ -382,6 +393,52 @@ public class WalletAppletTest {
} }
@Test
@DisplayName("Sign actual Ethereum transaction")
@Tag("manual")
void signTransactionTest() throws Exception {
// Initialize credentials
Web3j web3j = Web3j.build(new HttpService());
Credentials wallet1 = WalletUtils.loadCredentials("testwallet", "testwallets/wallet1.json");
Credentials wallet2 = WalletUtils.loadCredentials("testwallet", "testwallets/wallet2.json");
// Load keys on card
cmdSet.openSecureChannel();
ResponseAPDU response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSW());
response = cmdSet.loadKey(wallet1.getEcKeyPair());
assertEquals(0x9000, response.getSW());
// Verify balance
System.out.println("Wallet 1 balance: " + web3j.ethGetBalance(wallet1.getAddress(), DefaultBlockParameterName.LATEST).send().getBalance());
System.out.println("Wallet 2 balance: " + web3j.ethGetBalance(wallet2.getAddress(), DefaultBlockParameterName.LATEST).send().getBalance());
BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice();
BigInteger weiValue = Convert.toWei(BigDecimal.valueOf(1.0), Convert.Unit.FINNEY).toBigIntegerExact();
BigInteger nonce = web3j.ethGetTransactionCount(wallet1.getAddress(), DefaultBlockParameterName.LATEST).send().getTransactionCount();
RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, Transfer.GAS_LIMIT, wallet2.getAddress(), weiValue);
byte[] txBytes = TransactionEncoder.encode(rawTransaction);
byte[] hash = Hash.sha3(txBytes);
response = cmdSet.sign(hash, WalletApplet.SIGN_PRECOMPUTED_HASH,true, true);
assertEquals(0x9000, response.getSW());
byte[] sig = secureChannel.decryptAPDU(response.getData());
Method encode = TransactionEncoder.class.getDeclaredMethod("encode", RawTransaction.class, Sign.SignatureData.class);
encode.setAccessible(true);
byte[] signedMessage = (byte[]) encode.invoke(null, rawTransaction, new Sign.SignatureData((byte) 0, null, null)); //TODO: generate signature data
String hexValue = "0x" + Hex.toHexString(signedMessage);
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
if (ethSendTransaction.hasError()) {
System.out.println("Transaction Error: " + ethSendTransaction.getError().getMessage());
}
assertFalse(ethSendTransaction.hasError());
}
private KeyPairGenerator keypairGenerator() throws Exception { private KeyPairGenerator keypairGenerator() throws Exception {
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1"); ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDH", "BC"); KeyPairGenerator g = KeyPairGenerator.getInstance("ECDH", "BC");
@ -389,4 +446,18 @@ public class WalletAppletTest {
return g; return g;
} }
private void reset() {
if (USE_SIMULATOR) {
simulator.reset();
} else {
apduChannel.getCard().getATR();
}
}
private void resetAndSelectAndOpenSC() throws CardException {
reset();
cmdSet.select();
cmdSet.openSecureChannel();
}
} }