Updated on 2026-08-14
This commit is contained in:
parent
8c17a8d4f0
commit
b19c306250
70 changed files with 6149 additions and 6206 deletions
951
app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java
Normal file
951
app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java
Normal file
|
|
@ -0,0 +1,951 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.TagLostException;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.wallet.CoinEngine;
|
||||
import com.tangem.wallet.CoinEngineFactory;
|
||||
import com.tangem.wallet.Issuer;
|
||||
import com.tangem.wallet.Tangem_Card;
|
||||
import com.tangem.wallet.Manufacturer;
|
||||
|
||||
import org.spongycastle.jce.ECNamedCurveTable;
|
||||
import org.spongycastle.jce.interfaces.ECPublicKey;
|
||||
import org.spongycastle.jce.spec.ECNamedCurveParameterSpec;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Calendar;
|
||||
|
||||
import javax.crypto.KeyAgreement;
|
||||
|
||||
/**
|
||||
* Created by dvol on 14.07.2017.
|
||||
*/
|
||||
|
||||
public class CardProtocol {
|
||||
|
||||
public interface Notifications {
|
||||
void OnReadStart(CardProtocol cardProtocol);
|
||||
|
||||
void OnReadProgress(CardProtocol cardProtocol, int progress);
|
||||
|
||||
void OnReadFinish(CardProtocol cardProtocol);
|
||||
|
||||
void OnReadCancel();
|
||||
|
||||
void OnReadWait(int msec);
|
||||
|
||||
void OnReadBeforeRequest(int timeout);
|
||||
|
||||
void OnReadAfterRequest();
|
||||
}
|
||||
|
||||
public void setError(Exception error) {
|
||||
mError = error;
|
||||
}
|
||||
|
||||
public Tangem_Card getCard() {
|
||||
return mCard;
|
||||
}
|
||||
|
||||
public Exception getError() {
|
||||
return mError;
|
||||
}
|
||||
|
||||
private static final String logTag = "CardProtocol";
|
||||
public static final int SW_PIN_ERROR = SW.INVALID_PARAMS;
|
||||
|
||||
public static final String DefaultPIN = "000000";
|
||||
public static final String DefaultPIN2 = "000";
|
||||
|
||||
protected IsoDep mIsoDep;
|
||||
|
||||
public Tag getTag() {
|
||||
return mIsoDep.getTag();
|
||||
}
|
||||
|
||||
protected String mPIN;
|
||||
protected Notifications mNotifications;
|
||||
|
||||
public void setPIN(String PIN) {
|
||||
mPIN = PIN;
|
||||
if (mCard != null) {
|
||||
mCard.setPIN(PIN);
|
||||
}
|
||||
}
|
||||
|
||||
protected Tangem_Card mCard;
|
||||
protected Exception mError;
|
||||
protected Context mContext;
|
||||
|
||||
static {
|
||||
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
|
||||
}
|
||||
|
||||
public CardProtocol(Context context, IsoDep isoDep, Notifications notifications) {
|
||||
mContext = context;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
mPIN = null;
|
||||
mCard = new Tangem_Card(Util.byteArrayToHexString(mIsoDep.getTag().getId()));
|
||||
}
|
||||
|
||||
public CardProtocol(Context context, IsoDep isoDep, Tangem_Card card, Notifications notifications) {
|
||||
mContext = context;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
mPIN = card.getPIN();
|
||||
mCard = card;
|
||||
}
|
||||
|
||||
public static class TangemException extends Exception {
|
||||
public TangemException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TangemException_InvalidPIN extends TangemException {
|
||||
public TangemException_InvalidPIN(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TangemException_NeedPause extends TangemException {
|
||||
public TangemException_NeedPause(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TangemException_ExtendedLengthNotSupported extends TangemException {
|
||||
public TangemException_ExtendedLengthNotSupported(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] GetUID() {
|
||||
if (mIsoDep == null || mIsoDep.getTag() == null) return null;
|
||||
return mIsoDep.getTag().getId();
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
if (mIsoDep == null || mIsoDep.getTag() == null) return 60000;
|
||||
return mIsoDep.getTimeout();
|
||||
}
|
||||
|
||||
|
||||
protected byte[] protocolKey;
|
||||
|
||||
public void resetProtocolKey() {
|
||||
protocolKey = null;
|
||||
}
|
||||
|
||||
public void CreateProtocolKey() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
|
||||
protocolKey = CardCrypto.pbkdf2(Util.calculateSHA256(mPIN), mIsoDep.getTag().getId(), 50);
|
||||
//Log.e("Reader", String.format("PIN: %s, Protocol key: %s", mPIN, Util.bytesToHex(protocolKey)));
|
||||
if (sessionKey != null) {
|
||||
sessionKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] sessionKey = null;
|
||||
|
||||
public void run_OpenSession(Tangem_Card.EncryptionMode encryptionMode) throws Exception {
|
||||
sessionKey = null;
|
||||
try {
|
||||
CommandApdu cmdApdu = new CommandApdu(CommandApdu.ISO_CLA, INS.OpenSession.Code, 0, encryptionMode.getP());
|
||||
|
||||
switch (encryptionMode) {
|
||||
case Fast: {
|
||||
byte[] baMyChallenge = Util.generateRandomBytes(16);
|
||||
|
||||
cmdApdu.addTLV(TLV.Tag.TAG_Session_Key_A, baMyChallenge);
|
||||
Log.i(logTag, cmdApdu.getCommandName());
|
||||
ResponseApdu rspApdu = null;
|
||||
|
||||
try {
|
||||
if (mIsoDep == null) {
|
||||
throw new TagLostException();
|
||||
}
|
||||
byte[] cmdBytes = cmdApdu.toBytes();
|
||||
String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc());
|
||||
Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr));
|
||||
|
||||
byte[] rsp = mIsoDep.transceive(cmdBytes);
|
||||
rspApdu = new ResponseApdu(rsp);
|
||||
|
||||
Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp)));
|
||||
|
||||
if (rspApdu.isParsedWithError()) {
|
||||
throw new Exception("Can't parse answer");
|
||||
}
|
||||
} catch (Exception E) {
|
||||
sessionKey = null;
|
||||
throw E;
|
||||
}
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
byte[] baTheirsChallenge = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Session_Key_B).Value;
|
||||
if (protocolKey == null) CreateProtocolKey();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(baMyChallenge);
|
||||
outputStream.write(baTheirsChallenge);
|
||||
outputStream.write(protocolKey);
|
||||
sessionKey = Util.calculateSHA256(outputStream.toByteArray());
|
||||
//Log.i(logTag, String.format("Session key: %s", Util.bytesToHex(sessionKey)));
|
||||
} else {
|
||||
Log.e(logTag, String.format("Failed: %04X - %s", rspApdu.getSW1SW2(), rspApdu.getSW1SW2Description()));
|
||||
throw new Exception(String.format("Can't open session: SW - %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Strong: {
|
||||
KeyPairGenerator kpgen = KeyPairGenerator.getInstance("ECDH", "SC");
|
||||
kpgen.initialize(new ECGenParameterSpec("secp256k1"), new SecureRandom());
|
||||
KeyPair KP = kpgen.generateKeyPair();
|
||||
KeyAgreement ka = KeyAgreement.getInstance("ECDH", "SC");
|
||||
ka.init(KP.getPrivate());
|
||||
|
||||
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
//return spec.getG().multiply(new BigInteger((ECPrivateKey) )).getEncoded(false);
|
||||
ECPublicKey eckey = (ECPublicKey) KP.getPublic();
|
||||
byte[] baMyPublicKey = eckey.getQ().getEncoded(false);
|
||||
|
||||
cmdApdu.addTLV(TLV.Tag.TAG_Session_Key_A, baMyPublicKey);
|
||||
Log.i(logTag, cmdApdu.getCommandName());
|
||||
ResponseApdu rspApdu = null;
|
||||
|
||||
try {
|
||||
if (mIsoDep == null) {
|
||||
throw new TagLostException();
|
||||
}
|
||||
//mIsoDep.setTimeout(msTimeout);
|
||||
|
||||
byte[] cmdBytes = cmdApdu.toBytes();
|
||||
String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc());
|
||||
Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr));
|
||||
byte[] rsp = mIsoDep.transceive(cmdBytes);
|
||||
rspApdu = new ResponseApdu(rsp);
|
||||
Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp)));
|
||||
|
||||
if (rspApdu.isParsedWithError()) {
|
||||
throw new Exception("Can't parse answer");
|
||||
}
|
||||
} catch (Exception E) {
|
||||
sessionKey = null;
|
||||
throw E;
|
||||
}
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
byte[] baTheirsPublicKey = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Session_Key_B).Value;
|
||||
ka.doPhase(CardCrypto.LoadPublicKey(baTheirsPublicKey), true);
|
||||
if (protocolKey == null) CreateProtocolKey();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(ka.generateSecret());
|
||||
outputStream.write(protocolKey);
|
||||
sessionKey = Util.calculateSHA256(outputStream.toByteArray());
|
||||
// Log.i(logTag, String.format("Session key: %s", Util.bytesToHex(sessionKey)));
|
||||
} else {
|
||||
Log.i(logTag, String.format("Failed: %04X - %s", rspApdu.getSW1SW2(), rspApdu.getSW1SW2Description()));
|
||||
throw new Exception(String.format("Can't open session: SW - %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown encryption mode");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(logTag, String.format("Exception: %s", e.getMessage()));
|
||||
throw new Exception("Can't open session: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// send command APDU, get response APDU, and display HEX data to user
|
||||
private ResponseApdu SendAndReceive(CommandApdu cmdApdu, boolean breakOnNeedPause) throws Exception {
|
||||
if (mCard.encryptionMode != Tangem_Card.EncryptionMode.None) {
|
||||
if (sessionKey == null) {
|
||||
run_OpenSession(mCard.encryptionMode);
|
||||
}
|
||||
cmdApdu.Crypt(sessionKey);
|
||||
}
|
||||
cmdApdu.setP1(mCard.encryptionMode.getP());
|
||||
byte[] cmdBytes = cmdApdu.toBytes();
|
||||
String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc());
|
||||
Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr));
|
||||
byte[] rsp;
|
||||
ResponseApdu rspApdu;
|
||||
try {
|
||||
do {
|
||||
try {
|
||||
mNotifications.OnReadBeforeRequest(mIsoDep.getTimeout());
|
||||
try {
|
||||
rsp = mIsoDep.transceive(cmdBytes);
|
||||
} finally {
|
||||
mNotifications.OnReadAfterRequest();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().contains("length exceeds supported maximum") && mIsoDep.isExtendedLengthApduSupported()) {
|
||||
throw new TangemException_ExtendedLengthNotSupported(e.getMessage());
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
if (mCard.encryptionMode != Tangem_Card.EncryptionMode.None) {
|
||||
rspApdu = ResponseApdu.Decrypt(rsp, sessionKey);
|
||||
} else {
|
||||
rspApdu = new ResponseApdu(rsp);
|
||||
}
|
||||
|
||||
if (rspApdu.isParsedWithError()) {
|
||||
Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp)));
|
||||
throw new TangemException(rspApdu.getParseErroMessage());
|
||||
} else if (rspApdu.getSW1SW2() == SW.NEED_PAUSE && mNotifications != null) {
|
||||
int remainingPause = rspApdu.getTLVs().getTagAsInt(TLV.Tag.TAG_Pause) * 10;
|
||||
Log.v("NFC", String.format(">> Security delay, remaining %f s", remainingPause / 1000.0));
|
||||
if (breakOnNeedPause) {
|
||||
break;
|
||||
} else {
|
||||
mNotifications.OnReadWait(remainingPause);
|
||||
}
|
||||
} else {
|
||||
Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp)));
|
||||
}
|
||||
} while (rspApdu.getSW1SW2() == SW.NEED_PAUSE);
|
||||
} finally {
|
||||
mNotifications.OnReadWait(0);
|
||||
}
|
||||
|
||||
return rspApdu;
|
||||
}
|
||||
|
||||
private CommandApdu StartPrepareCommand(INS ins) throws NoSuchAlgorithmException {
|
||||
CommandApdu Apdu = new CommandApdu(ins);
|
||||
byte[] baPIN = Util.calculateSHA256(mPIN);
|
||||
Apdu.addTLV(TLV.Tag.TAG_PIN, baPIN);
|
||||
if (ins != INS.Read) {
|
||||
Apdu.addTLV(TLV.Tag.TAG_CardID, mCard.getCID());
|
||||
}
|
||||
return Apdu;
|
||||
}
|
||||
|
||||
public void run_Read() throws Exception {
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.Read);
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
|
||||
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
|
||||
readResult = rspApdu.getTLVs();
|
||||
|
||||
parseReadResult();
|
||||
|
||||
run_ReadOrWriteIssuerDataAndDefineOfflineBalance();
|
||||
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible PIN is invalid!\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
throw new TangemException(String.format("FAILED: [%04X]\n", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
public void run_ReadOrWriteIssuerDataAndDefineOfflineBalance() throws Exception {
|
||||
TLVList tlvIssuerData;
|
||||
if (mCard.getNeedWriteIssuerData()) {
|
||||
run_WriteIssuerData(mCard.getIssuerData(), mCard.getIssuerDataSignature());
|
||||
try {
|
||||
tlvIssuerData = TLVList.fromBytes(mCard.getIssuerData());
|
||||
} catch (TLVException e) {
|
||||
e.printStackTrace();
|
||||
tlvIssuerData = null;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
tlvIssuerData = run_ReadIssuerData();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
tlvIssuerData = null;
|
||||
}
|
||||
}
|
||||
if (mCard.getStatus() == Tangem_Card.Status.Loaded) {
|
||||
// try read offline balance data
|
||||
if (tlvIssuerData != null && tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination) != null && mCard.getMaxSignatures() == mCard.getRemainingSignatures()) {
|
||||
mCard.setOfflineBalance(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value);
|
||||
} else {
|
||||
mCard.clearOfflineBalance();
|
||||
}
|
||||
} else {
|
||||
mCard.clearOfflineBalance();
|
||||
}
|
||||
}
|
||||
|
||||
private TLVList readResult = null;
|
||||
|
||||
public void clearReadResult() {
|
||||
sessionKey = null;
|
||||
readResult = null;
|
||||
}
|
||||
|
||||
public boolean haveReadResult() {
|
||||
return readResult != null;
|
||||
}
|
||||
|
||||
public void parseReadResult() throws TangemException, NoSuchProviderException, NoSuchAlgorithmException {
|
||||
TLV tlvStatus = readResult.getTLV(TLV.Tag.TAG_Status);
|
||||
mCard.setStatus(Tangem_Card.Status.fromCode(Util.byteArrayToInt(tlvStatus.Value)));
|
||||
TLV tlvCID = readResult.getTLV(TLV.Tag.TAG_CardID);
|
||||
mCard.setCID(tlvCID.Value);
|
||||
mCard.setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString()), true);
|
||||
mCard.setHealth(readResult.getTLV(TLV.Tag.TAG_Health).getAsInt());
|
||||
|
||||
if (mCard.getStatus() != Tangem_Card.Status.NotPersonalized) {
|
||||
try {
|
||||
TLV tlvCardPubkicKey = readResult.getTLV(TLV.Tag.TAG_CardPublicKey);
|
||||
if (tlvCardPubkicKey == null)
|
||||
throw new TangemException("Invalid answer format");
|
||||
mCard.setCardPublicKey(tlvCardPubkicKey.Value);
|
||||
|
||||
TLVList tlvCardData = TLVList.fromBytes(readResult.getTLV(TLV.Tag.TAG_CardData).Value);
|
||||
|
||||
TLV tokenSymbol = tlvCardData.getTLV(TLV.Tag.TAG_Token_Symbol);
|
||||
TLV contractAddress = tlvCardData.getTLV(TLV.Tag.TAG_Token_Contract_Address);
|
||||
TLV tokens_decimal = tlvCardData.getTLV(TLV.Tag.TAG_Token_Decimal);
|
||||
|
||||
if (tokenSymbol != null)
|
||||
mCard.setTokenSymbol(tokenSymbol.getAsString());
|
||||
|
||||
if (contractAddress != null)
|
||||
mCard.setContractAddress(contractAddress.getAsString());
|
||||
|
||||
if (tokens_decimal != null)
|
||||
mCard.setTokensDecimal(tokens_decimal.getAsInt());
|
||||
|
||||
// this method has reflection TokenSymbol
|
||||
// you mast call setBlockchainIDFromCard after calling setTokensDecimal
|
||||
mCard.setBlockchainIDFromCard(tlvCardData.getTLV(TLV.Tag.TAG_Blockchain_ID).getAsString());
|
||||
byte[] tlvPersonalizationDT = tlvCardData.getTLV(TLV.Tag.TAG_ManufactureDateTime).Value;
|
||||
int year = (tlvPersonalizationDT[0] & 0xFF) << 8 | (tlvPersonalizationDT[1] & 0xFF);
|
||||
int month = tlvPersonalizationDT[2] - 1;
|
||||
int day = tlvPersonalizationDT[3];
|
||||
Calendar cd = Calendar.getInstance();
|
||||
cd.set(year, month, day, 0, 0, 0);
|
||||
mCard.setPersonalizationDateTime(cd.getTime());
|
||||
try {
|
||||
if (readResult.getTLV(TLV.Tag.TAG_Firmware) != null) {
|
||||
mCard.setFirmwareVersion(readResult.getTLV(TLV.Tag.TAG_Firmware).getAsString());
|
||||
} else {
|
||||
mCard.setFirmwareVersion(tlvCardData.getTLV(TLV.Tag.TAG_Firmware).getAsString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(logTag, "Cannot get firmware version");
|
||||
mCard.setFirmwareVersion("0.00");
|
||||
}
|
||||
|
||||
try {
|
||||
if (mCard.getFirmwareVersion().compareTo("1.05") < 0) {
|
||||
mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value));
|
||||
} else {
|
||||
mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Data_PublicKey).Value));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(logTag, "Cannot get issuer, try a version for older cards");
|
||||
try {
|
||||
mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value));
|
||||
} catch (Exception ee) {
|
||||
ee.printStackTrace();
|
||||
Log.e(logTag, "Cannot get issuer");
|
||||
mCard.setIssuer(Issuer.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
mCard.setSettingsMask(readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(logTag, "Can't get settings mask");
|
||||
}
|
||||
|
||||
if (readResult.getTLV(TLV.Tag.TAG_PauseBeforePIN2) != null) {
|
||||
mCard.setPauseBeforePIN2(10 * readResult.getTagAsInt(TLV.Tag.TAG_PauseBeforePIN2));
|
||||
}
|
||||
|
||||
try {
|
||||
mCard.setSigningMethod(readResult.getTagAsInt(TLV.Tag.TAG_SigningMethod));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(logTag, "Can't get signing method");
|
||||
mCard.setSigningMethod(0);
|
||||
}
|
||||
|
||||
try {
|
||||
mCard.setMaxSignatures(readResult.getTagAsInt(TLV.Tag.TAG_MaxSignatures));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(logTag, "Can't get max signatures");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new TangemException("Can't parse card data");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mCard.getStatus() == Tangem_Card.Status.Loaded) {
|
||||
|
||||
TLV tlvPublicKey = readResult.getTLV(TLV.Tag.TAG_Wallet_PublicKey);
|
||||
|
||||
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
ECPoint p1 = spec.getCurve().decodePoint(tlvPublicKey.Value);
|
||||
|
||||
byte pkUncompressed[] = p1.getEncoded(false);
|
||||
|
||||
byte pkCompresses[] = p1.getEncoded(true);
|
||||
mCard.setWalletPublicKey(pkUncompressed);
|
||||
mCard.setWalletPublicKeyRar(pkCompresses);
|
||||
|
||||
CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain());
|
||||
String wallet = engineCoin.calculateAddress(mCard, pkUncompressed);
|
||||
mCard.setWallet(wallet);
|
||||
//mCard.setWallet(Blockchain.calculateWalletAddress(mCard, pkUncompressed));
|
||||
|
||||
mCard.setRemainingSignatures(readResult.getTagAsInt(TLV.Tag.TAG_RemainingSignatures));
|
||||
|
||||
} else {
|
||||
mCard.setWallet("N/A");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public TLVList run_VerifyCard() throws Exception {
|
||||
if (mCard.getCardPublicKey() == null || readResult == null) {
|
||||
run_Read();
|
||||
}
|
||||
if (mCard.getStatus() == Tangem_Card.Status.NotPersonalized) {
|
||||
getCard().setManufacturer(Manufacturer.Unknown, false);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mCard.getCardPublicKey() == null) {
|
||||
throw new TangemException("Not all data read, can't verify card!");
|
||||
}
|
||||
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCard);
|
||||
byte[] bChallenge = Util.generateRandomBytes(16);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Challenge, bChallenge);
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
TLVList verifyResult = rspApdu.getTLVs();
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
verifyResult.add(new TLV(TLV.Tag.TAG_Challenge, bChallenge));
|
||||
|
||||
TLV tlvSalt = verifyResult.getTLV(TLV.Tag.TAG_Salt);
|
||||
TLV tlvCardSignature = verifyResult.getTLV(TLV.Tag.TAG_CardSignature);
|
||||
// TLV tlvManufacturerSignature = verifyResult.getTLV(TLV.Tag.TAG_Manufacturer_Signature);
|
||||
|
||||
if (tlvSalt == null || tlvCardSignature == null) {
|
||||
throw new TangemException("Not all data read, can't verify card!");
|
||||
}
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream bs = new ByteArrayOutputStream();
|
||||
bs.write(bChallenge);
|
||||
bs.write(tlvSalt.Value);
|
||||
byte[] dataArray = bs.toByteArray();
|
||||
if (CardCrypto.VerifySignature(mCard.getCardPublicKey(), dataArray, tlvCardSignature.Value)) {
|
||||
getCard().setCardPublicKeyValid(true);
|
||||
Log.i(logTag, "Card signature verification OK");
|
||||
} else {
|
||||
Log.e(logTag, "Card signature verification FAILED");
|
||||
getCard().setCardPublicKeyValid(false);
|
||||
}
|
||||
|
||||
//getCard().setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString(), bChallenge, tlvSalt.Value, tlvManufacturerSignature.Value), true);
|
||||
getCard().setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString()), true);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
getCard().setManufacturer(Manufacturer.Unknown, false);
|
||||
}
|
||||
return verifyResult;
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
getCard().setManufacturer(Manufacturer.Unknown, false);
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void run_CreateWallet(String PIN2) throws Exception {
|
||||
if (readResult == null) run_Read();
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.CreateWallet);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
}
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
public TLVList run_CheckWallet() throws Exception {
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.CheckWallet);
|
||||
byte[] bChallenge = Util.generateRandomBytes(16);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Challenge, bChallenge);
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
TLVList Result = rspApdu.getTLVs();
|
||||
Result.add(new TLV(TLV.Tag.TAG_Challenge, bChallenge));
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
return Result;
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
public void run_CheckWalletWithSignatureVerify() throws Exception {
|
||||
if (mCard.getCardPublicKey() == null || readResult == null) {
|
||||
run_Read();
|
||||
}
|
||||
if (mCard.getStatus() == Tangem_Card.Status.NotPersonalized) {
|
||||
getCard().setManufacturer(Manufacturer.Unknown, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (readResult.getTagAsInt(TLV.Tag.TAG_Status) != Tangem_Card.Status.Loaded.getCode()) {
|
||||
throw new TangemException("Card must be loaded");
|
||||
}
|
||||
TLVList checkResult = run_CheckWallet();
|
||||
if (checkResult == null) return;
|
||||
|
||||
TLV tlvPublicKey = readResult.getTLV(TLV.Tag.TAG_Wallet_PublicKey);
|
||||
TLV tlvChallenge = checkResult.getTLV(TLV.Tag.TAG_Challenge);
|
||||
TLV tlvSalt = checkResult.getTLV(TLV.Tag.TAG_Salt);
|
||||
TLV tlvSignature = checkResult.getTLV(TLV.Tag.TAG_Signature);
|
||||
|
||||
if (tlvPublicKey == null || tlvChallenge == null || tlvSalt == null || tlvSignature == null) {
|
||||
throw new TangemException("Not all data read, can't check signature!");
|
||||
}
|
||||
|
||||
ByteArrayOutputStream bs = new ByteArrayOutputStream();
|
||||
bs.write(tlvChallenge.Value);
|
||||
bs.write(tlvSalt.Value);
|
||||
byte[] dataArray = bs.toByteArray();
|
||||
|
||||
if (CardCrypto.VerifySignature(tlvPublicKey.Value, dataArray, tlvSignature.Value)) {
|
||||
Log.i(logTag, "Signature verification OK");
|
||||
mCard.setWalletPublicKeyValid(true);
|
||||
} else {
|
||||
mCard.setWalletPublicKeyValid(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void run_PurgeWallet(String PIN2) throws Exception {
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.PurgeWallet);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
}
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
public void run_SwapPIN(String PIN2, String newPin, String newPin2, boolean breakOnNeedPause) throws Exception {
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.SwapPIN);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
rqApdu.addTLV(TLV.Tag.TAG_NewPIN, Util.calculateSHA256(newPin));
|
||||
rqApdu.addTLV(TLV.Tag.TAG_NewPIN2, Util.calculateSHA256(newPin2));
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, breakOnNeedPause);
|
||||
|
||||
if (rspApdu.isStatus(SW.PIN1_CHANGED) || rspApdu.isStatus(SW.PIN2_CHANGED) || rspApdu.isStatus(SW.PINS_CHANGED) || rspApdu.isStatus(SW.PINS_NOT_CHANGED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
if (newPin2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
} else {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2()));
|
||||
} else if (breakOnNeedPause && rspApdu.isStatus(SW.NEED_PAUSE)) {
|
||||
throw new TangemException_NeedPause(String.format("FAILED: [%04X] - Need pause\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
public void run_CheckPIN2isDefault() throws Exception {
|
||||
if (mCard.isFirmwareNewer("1.19") || (mCard.isFirmwareNewer("1.12") && (mCard.getPauseBeforePIN2() == 0 || mCard.useSmartSecurityDelay()))) {
|
||||
// can obtain SwapPIN(to default) answer without security delay - try check if PIN2 is default with card request
|
||||
try {
|
||||
run_SwapPIN(DefaultPIN2, mPIN, DefaultPIN2, true);
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
} catch (TangemException_NeedPause e) {
|
||||
mCard.setUseDefaultPIN2(null);
|
||||
} catch (TangemException_InvalidPIN e) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
} else {
|
||||
mCard.setUseDefaultPIN2(null);
|
||||
}
|
||||
}
|
||||
|
||||
public TLVList run_SignHashes(String PIN2, byte[][] hashes, boolean UseIssuerValidation, byte[] issuerData, Issuer issuer) throws Exception {
|
||||
ByteArrayOutputStream bs = new ByteArrayOutputStream();
|
||||
if (hashes.length > 10) throw new Exception("To much hashes in one transaction!");
|
||||
for (int i = 0; i < hashes.length; i++) {
|
||||
if (i != 0 && hashes[0].length != hashes[i].length)
|
||||
throw new Exception("Hashes length must be identical!");
|
||||
bs.write(hashes[i]);
|
||||
}
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.Sign);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
rqApdu.addTLV_U8(TLV.Tag.TAG_TrOut_HashSize, hashes[0].length);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Hash, bs.toByteArray());
|
||||
if (UseIssuerValidation) {
|
||||
byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), bs.toByteArray());
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerSignature);
|
||||
}
|
||||
if (issuerData != null) {
|
||||
if (issuer == null || issuer == Issuer.Unknown)
|
||||
throw new Exception("Need known Issuer to write issuer Data");
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData);
|
||||
byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), issuerData);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerSignature);
|
||||
}
|
||||
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
TLVList Result = rspApdu.getTLVs();
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
}
|
||||
return Result;
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
public TLVList run_SignRaw(String PIN2, byte[] bTxOutData) throws Exception {
|
||||
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.Sign);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Raw, bTxOutData);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, "sha-256x2".getBytes("US-ASCII"));
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
TLVList Result = rspApdu.getTLVs();
|
||||
Result.add(new TLV(TLV.Tag.TAG_TrOut_Raw, bTxOutData));
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
}
|
||||
return Result;
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
|
||||
if (readResult == null) run_Read();
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII"));
|
||||
rqApdu.addTLV_U32(TLV.Tag.TAG_CodePageAddress, codePageAddress);
|
||||
rqApdu.addTLV_U16(TLV.Tag.TAG_CodePageCount, codePageCount);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Challenge, challenge);
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
return rspApdu.getTLVs().getTLV(TLV.Tag.TAG_CodeHash).Value;
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
private void run_ValidateCard(String PIN2) throws Exception {
|
||||
if (readResult == null) run_Read();
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.ValidateCard);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
}
|
||||
} else if (rspApdu.isStatus(SW_PIN_ERROR)) {
|
||||
if (PIN2.equals(DefaultPIN2)) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2()));
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
private void run_WriteIssuerData(byte[] issuerData, byte[] issuerSignature) throws Exception {
|
||||
run_Read();
|
||||
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.WriteIssuerData);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerSignature);
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
private TLVList run_ReadIssuerData() throws Exception {
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.GetIssuerData);
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
TLV issuerData = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data);
|
||||
TLV issuerDataSignature = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Signature);
|
||||
|
||||
if (issuerData == null || issuerDataSignature == null)
|
||||
throw new TangemException("Invalid answer format (GetIssuerData)");
|
||||
|
||||
ByteArrayOutputStream bsDataToVerify = new ByteArrayOutputStream();
|
||||
bsDataToVerify.write(mCard.getCID());
|
||||
bsDataToVerify.write(issuerData.Value);
|
||||
try {
|
||||
if (CardCrypto.VerifySignature(mCard.getIssuer().getPublicDataKey(), bsDataToVerify.toByteArray(), issuerDataSignature.Value)) {
|
||||
mCard.setIssuerData(issuerData.Value, issuerDataSignature.Value);
|
||||
return TLVList.fromBytes(issuerData.Value);
|
||||
} else {
|
||||
throw new TangemException("Invalid issuer data read (signature verification failed)");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new TangemException("Invalid issuer data read");
|
||||
}
|
||||
} else {
|
||||
throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2()));
|
||||
}
|
||||
}
|
||||
|
||||
public void run_GetSupportedEncryption() throws Exception {
|
||||
mCard.encryptionMode = Tangem_Card.EncryptionMode.None;
|
||||
do {
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.Read);
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.NEED_ENCRYPTION)) {
|
||||
if (mCard.encryptionMode == Tangem_Card.EncryptionMode.None) {
|
||||
mCard.encryptionMode = Tangem_Card.EncryptionMode.Fast;
|
||||
} else if (mCard.encryptionMode == Tangem_Card.EncryptionMode.Fast) {
|
||||
mCard.encryptionMode = Tangem_Card.EncryptionMode.Strong;
|
||||
} else {
|
||||
throw new Exception("Can't get supported encryption methods");
|
||||
}
|
||||
} else if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
readResult = rspApdu.getTLVs();
|
||||
mCard.setPIN(mPIN);
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue