diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 03bb85faeb..1ddfc6d9b9 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/src/main/java/com/tangem/data/nfc/PurgeTask.java b/app/src/main/java/com/tangem/data/nfc/PurgeTask.java index e95b385e41..2c62dc1e59 100644 --- a/app/src/main/java/com/tangem/data/nfc/PurgeTask.java +++ b/app/src/main/java/com/tangem/data/nfc/PurgeTask.java @@ -72,6 +72,7 @@ public class PurgeTask extends Thread { } finally { mNfcManager.ignoreTag(mIsoDep.getTag()); + mNotifications.OnReadWait(0); } } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/com/tangem/data/nfc/SignPaymentTask.java b/app/src/main/java/com/tangem/data/nfc/SignPaymentTask.java index d72ddc9397..7d9a15992d 100644 --- a/app/src/main/java/com/tangem/data/nfc/SignPaymentTask.java +++ b/app/src/main/java/com/tangem/data/nfc/SignPaymentTask.java @@ -118,6 +118,7 @@ public class SignPaymentTask extends Thread { } finally { mNfcManager.ignoreTag(mIsoDep.getTag()); + mNotifications.OnReadWait(0); } } catch (CardProtocol.TangemException_InvalidPIN e) { e.printStackTrace(); diff --git a/app/src/main/java/com/tangem/data/nfc/SwapPINTask.java b/app/src/main/java/com/tangem/data/nfc/SwapPINTask.java index a0bd0c2f6f..b02d9760f1 100644 --- a/app/src/main/java/com/tangem/data/nfc/SwapPINTask.java +++ b/app/src/main/java/com/tangem/data/nfc/SwapPINTask.java @@ -76,6 +76,7 @@ public class SwapPINTask extends Thread { } finally { mNfcManager.ignoreTag(mIsoDep.getTag()); + mNotifications.OnReadWait(0); } } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java b/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java index e314ad0f9f..dae5d50b8e 100644 --- a/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java +++ b/app/src/main/java/com/tangem/domain/cardReader/CardProtocol.java @@ -280,6 +280,62 @@ public class CardProtocol { // send command APDU, get response APDU, and display HEX data to user +// private ResponseApdu SendAndReceive(CommandApdu cmdApdu, boolean breakOnNeedPause) throws Exception { +// if (mCard.encryptionMode != TangemCard.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 != TangemCard.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 ResponseApdu SendAndReceive(CommandApdu cmdApdu, boolean breakOnNeedPause) throws Exception { if (mCard.encryptionMode != TangemCard.EncryptionMode.None) { if (sessionKey == null) { @@ -308,7 +364,7 @@ public class CardProtocol { } throw e; } - if (mCard.encryptionMode != TangemCard.EncryptionMode.None) { + if (mCard.encryptionMode != TangemCard.EncryptionMode.None && !ResponseApdu.isStatusWord(rsp, SW.NEED_PAUSE)) { rspApdu = ResponseApdu.Decrypt(rsp, sessionKey); } else { rspApdu = new ResponseApdu(rsp); @@ -318,7 +374,12 @@ public class CardProtocol { 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; + int remainingPause = 60000; + TLV tlvPause = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Pause); + if (tlvPause != null) { + 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; diff --git a/app/src/main/java/com/tangem/domain/cardReader/INS.java b/app/src/main/java/com/tangem/domain/cardReader/INS.java index 0b653f775c..e180bb2bba 100644 --- a/app/src/main/java/com/tangem/domain/cardReader/INS.java +++ b/app/src/main/java/com/tangem/domain/cardReader/INS.java @@ -20,12 +20,7 @@ public enum INS { Sign(0xFB), PurgeWallet(0xFC), Activate(0xFE), - OpenSession(0xFF), - ReadBlockedData(0xE4), - CreateTestWallet(0xE0), - ExtractWalletKey(0xE1), - Test(0xE2), - Depersonalize(0xE3); + OpenSession(0xFF); INS(int Code) { this.Code = Code; diff --git a/app/src/main/java/com/tangem/domain/cardReader/ResponseApdu.java b/app/src/main/java/com/tangem/domain/cardReader/ResponseApdu.java index 2b74906c06..f6160245ef 100644 --- a/app/src/main/java/com/tangem/domain/cardReader/ResponseApdu.java +++ b/app/src/main/java/com/tangem/domain/cardReader/ResponseApdu.java @@ -34,12 +34,22 @@ public class ResponseApdu { parseError = e.getMessage(); } + }else{ + tlvList=new TLVList(); + parseError=null; } mSw1 = 0x00FF & respApdu[respApdu.length - 2]; mSw2 = 0x00FF & respApdu[respApdu.length - 1]; mBytes = respApdu; } + public static boolean isStatusWord(byte[] respApdu, int SW) + { + int mSw1 = 0x00FF & respApdu[respApdu.length - 2]; + int mSw2 = 0x00FF & respApdu[respApdu.length - 1]; + return ((mSw1 << 8) | mSw2)==SW; + } + public static ResponseApdu Decrypt(byte[] data, byte[] key) throws Exception{ if( data.length==2 ) diff --git a/app/src/main/java/com/tangem/domain/cardReader/SettingsMask.java b/app/src/main/java/com/tangem/domain/cardReader/SettingsMask.java index f69a4a1cb3..2472e06a33 100644 --- a/app/src/main/java/com/tangem/domain/cardReader/SettingsMask.java +++ b/app/src/main/java/com/tangem/domain/cardReader/SettingsMask.java @@ -22,4 +22,6 @@ public class SettingsMask { public static final int Protocol_AllowUnencrypted = 0x1000; public static final int Protocol_AllowStaticEncryption = 0x2000; + public static final int ProtectIssuerDataAgainstReplay = 0x4000; + } diff --git a/app/src/main/java/com/tangem/domain/cardReader/TLV.java b/app/src/main/java/com/tangem/domain/cardReader/TLV.java index 40c613b890..33326d36c8 100644 --- a/app/src/main/java/com/tangem/domain/cardReader/TLV.java +++ b/app/src/main/java/com/tangem/domain/cardReader/TLV.java @@ -53,6 +53,7 @@ public class TLV { TAG_Issuer_Data(0x32), TAG_Issuer_Data_Signature(0x33), TAG_Issuer_Transaction_Signature(0x34), + TAG_Issuer_Data_Counter(0x35), TAG_IsActivated(0x3A), TAG_ActivationSeed(0x3B), @@ -213,6 +214,49 @@ public class TLV { } else { return String.format("%s[]: [[NULL]]", tag.name()); } + case TAG_SettingsMask: { + StringBuilder sb=new StringBuilder(); + if( Value!=null ) { + try { + int iValue = Util.byteArrayToInt(Value); + sb.append("["); + if ((iValue & SettingsMask.AllowSwapPIN) != 0) sb.append("AllowSwapPIN, "); + if ((iValue & SettingsMask.AllowSwapPIN2) != 0) + sb.append("AllowSwapPIN2, "); + if ((iValue & SettingsMask.ForbidDefaultPIN) != 0) + sb.append("ForbidDefaultPIN, "); + if ((iValue & SettingsMask.IsReusable) != 0) sb.append("IsReusable, "); + if ((iValue & SettingsMask.Protocol_AllowStaticEncryption) != 0) + sb.append("Protocol_AllowStaticEncryption, "); + if ((iValue & SettingsMask.Protocol_AllowUnencrypted) != 0) + sb.append("Protocol_AllowUnencrypted, "); + if ((iValue & SettingsMask.SmartSecurityDelay) != 0) + sb.append("SmartSecurityDelay, "); + if ((iValue & SettingsMask.UseActivation) != 0) + sb.append("UseActivation, "); + if ((iValue & SettingsMask.UseBlock) != 0) sb.append("UseBlock, "); + if ((iValue & SettingsMask.UseCVC) != 0) sb.append("UseCVC, "); + if ((iValue & SettingsMask.UseDynamicNDEF) != 0) + sb.append("UseDynamicNDEF, "); + if ((iValue & SettingsMask.UseNDEF) != 0) sb.append("UseNDEF, "); + if ((iValue & SettingsMask.UseOneCommandAtTime) != 0) + sb.append("UseOneCommandAtTime, "); + if ((iValue & SettingsMask.ProtectIssuerDataAgainstReplay) != 0) + sb.append("ProtectIssuerDataAgainstReplay, "); + + if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length()); + sb.append("]"); + return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), sb.toString()); + } + catch (Exception e) + { + e.printStackTrace(); + return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value)); + } + }else{ + return String.format("%s[]: [[NULL]]", tag.name()); + } + } default: if (Value != null) { return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value));