Updated on 2026-08-14
This commit is contained in:
parent
0fab3ec90e
commit
4e42025453
9 changed files with 123 additions and 8 deletions
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
|
|
@ -72,6 +72,7 @@ public class PurgeTask extends Thread {
|
|||
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
mNotifications.OnReadWait(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ public class SignPaymentTask extends Thread {
|
|||
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
mNotifications.OnReadWait(0);
|
||||
}
|
||||
} catch (CardProtocol.TangemException_InvalidPIN e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public class SwapPINTask extends Thread {
|
|||
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
mNotifications.OnReadWait(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue