Updated on 2026-08-14
This commit is contained in:
parent
4db63c1685
commit
4288dd4dc3
15 changed files with 177 additions and 26 deletions
|
|
@ -26,6 +26,9 @@ public class TangemCard {
|
|||
private byte[] pbCardKey = null;
|
||||
private byte[] pbWalletKeyRar = null;
|
||||
private Date dtPersonalization = null;
|
||||
private byte[] terminalPrivateKey;
|
||||
private byte[] terminalPublicKey;
|
||||
private boolean terminalIsLinked = false;
|
||||
|
||||
public String getBlockchainID() {
|
||||
return blockchainID;
|
||||
|
|
@ -156,6 +159,30 @@ public class TangemCard {
|
|||
this.dtPersonalization = dtPersonalization;
|
||||
}
|
||||
|
||||
public byte[] getTerminalPrivateKey() {
|
||||
return terminalPrivateKey;
|
||||
}
|
||||
|
||||
public void setTerminalPrivateKey(byte[] terminalPrivateKey) {
|
||||
this.terminalPrivateKey = terminalPrivateKey;
|
||||
}
|
||||
|
||||
public byte[] getTerminalPublicKey() {
|
||||
return terminalPublicKey;
|
||||
}
|
||||
|
||||
public void setTerminalPublicKey(byte[] terminalPublicKey) {
|
||||
this.terminalPublicKey = terminalPublicKey;
|
||||
}
|
||||
|
||||
public boolean getTerminalIsLinked() {
|
||||
return terminalIsLinked;
|
||||
}
|
||||
|
||||
public void setTerminalIsLinked(boolean terminalIsLinked) {
|
||||
this.terminalIsLinked = terminalIsLinked;
|
||||
}
|
||||
|
||||
private int health = 0;
|
||||
|
||||
public int getHealth() {
|
||||
|
|
@ -350,6 +377,11 @@ public class TangemCard {
|
|||
return (settingsMask & SettingsMask.UseBlock) != 0;
|
||||
}
|
||||
|
||||
public Boolean supportLinkingTerminal() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.SkipSecurityDelayIfValidatedByLinkedTerminal) != 0;
|
||||
}
|
||||
|
||||
public int getMaxSignatures() {
|
||||
return maxSignatures;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,7 @@ public interface PINsProvider {
|
|||
*/
|
||||
void setLastUsedPIN(String pin);
|
||||
|
||||
byte[] getTerminalPublicKey();
|
||||
byte[] getTerminalPrivateKey();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import java.security.PublicKey;
|
|||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
|
|
@ -150,7 +152,8 @@ public class CardCrypto {
|
|||
int rLength = enc[3];
|
||||
|
||||
if (enc[4 + rLength] != 0x02) throw new Exception("bad encoding 3");
|
||||
if ((enc[5 + rLength] & 0x80) != 0) throw new Exception("unsupported length encoding 3");
|
||||
if ((enc[5 + rLength] & 0x80) != 0)
|
||||
throw new Exception("unsupported length encoding 3");
|
||||
int sLength = enc[5 + rLength];
|
||||
|
||||
|
||||
|
|
@ -286,4 +289,15 @@ public class CardCrypto {
|
|||
return decryptedData;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, byte[]> generateTerminalKeys() throws Exception {
|
||||
byte[] privateKey = Util.generateRandomBytes(32);
|
||||
byte[] publicKey = GeneratePublicKey(privateKey);
|
||||
|
||||
Map<String, byte[]> keys = new HashMap<>();
|
||||
keys.put("terminalPrivateKey", privateKey);
|
||||
keys.put("terminalPublicKey", publicKey);
|
||||
return keys;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,11 @@ public class CardProtocol {
|
|||
return (DefaultPIN2.equals(pin2));
|
||||
}
|
||||
|
||||
private byte[] terminalPublicKey;
|
||||
|
||||
public void setTerminalPublicKey(byte[] terminalPubKey) {
|
||||
this.terminalPublicKey = terminalPubKey;
|
||||
}
|
||||
|
||||
private TangemCard mCard;
|
||||
private Exception mError;
|
||||
|
|
@ -488,12 +493,22 @@ public class CardProtocol {
|
|||
CommandApdu Apdu = new CommandApdu(ins);
|
||||
byte[] baPIN = Util.calculateSHA256(mPIN);
|
||||
Apdu.addTLV(TLV.Tag.TAG_PIN, baPIN);
|
||||
if (ins != INS.Read) {
|
||||
if (ins == INS.Read) {
|
||||
addTerminalPublicKeyToApdu(Apdu);
|
||||
} else {
|
||||
Apdu.addTLV(TLV.Tag.TAG_CardID, mCard.getCID());
|
||||
}
|
||||
return Apdu;
|
||||
}
|
||||
|
||||
private void addTerminalPublicKeyToApdu(CommandApdu apdu) {
|
||||
if (terminalPublicKey != null) {
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_PublicKey, terminalPublicKey);
|
||||
} else if (mCard.getTerminalPublicKey() != null) {
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_PublicKey, mCard.getTerminalPublicKey());
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Run READ command and parse answer
|
||||
// * {@see run_Read(boolean parseResult) }
|
||||
|
|
@ -633,7 +648,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
public void run_CreateWallet(String PIN2) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
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(" ")));
|
||||
|
|
@ -835,7 +851,8 @@ public class CardProtocol {
|
|||
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());
|
||||
byte[] hashesConcatenated = bs.toByteArray();
|
||||
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Hash, hashesConcatenated);
|
||||
if (issuerData != null) {
|
||||
if (!mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData))
|
||||
throw new TangemException("Card don't support simultaneous sign with write issuer data!");
|
||||
|
|
@ -853,6 +870,8 @@ public class CardProtocol {
|
|||
throw new TangemException("Card require issuer validation before sign the transaction!");
|
||||
}
|
||||
|
||||
prepareDataForLinkingTerminal(rqApdu, hashesConcatenated);
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
|
@ -915,6 +934,8 @@ public class CardProtocol {
|
|||
throw new TangemException("Card require issuer validation before sign the transaction!");
|
||||
}
|
||||
|
||||
prepareDataForLinkingTerminal(rqApdu, bTxOutData);
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
|
|
@ -935,6 +956,12 @@ public class CardProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
private void prepareDataForLinkingTerminal(CommandApdu apdu, byte[] data) throws Exception {
|
||||
byte[] transactionSignature = CardCrypto.Signature(mCard.getTerminalPrivateKey(), data);
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_TransactionSignature, transactionSignature);
|
||||
addTerminalPublicKeyToApdu(apdu);
|
||||
}
|
||||
|
||||
/**
|
||||
* VERIFY_CODE command
|
||||
* See [1] 8.8
|
||||
|
|
@ -952,7 +979,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
public byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII"));
|
||||
rqApdu.addTLV_U32(TLV.Tag.TAG_CodePageAddress, codePageAddress);
|
||||
|
|
@ -988,7 +1016,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
private void run_ValidateCard(String PIN2) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.ValidateCard);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
|
||||
|
|
@ -1022,7 +1051,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
public void run_WriteIssuerData(byte[] issuerData, byte[] issuerSignature) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.WriteIssuerData);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ public class SettingsMask {
|
|||
|
||||
public static final int DisablePrecomputedNDEF = 0x00010000;
|
||||
|
||||
public static final int SkipSecurityDelayIfValidatedByLinkedTerminal = 0x00080000;
|
||||
|
||||
public static String getDescription(int iValue) {
|
||||
StringBuilder sb=new StringBuilder();
|
||||
sb.append("[");
|
||||
|
|
@ -58,6 +60,8 @@ public class SettingsMask {
|
|||
if ((iValue & SettingsMask.ForbidPurgeWallet) != 0) sb.append("ForbidPurgeWallet, ");
|
||||
if ((iValue & SettingsMask.AllowSelectBlockchain) != 0) sb.append("AllowSelectBlockchain, ");
|
||||
if ((iValue & SettingsMask.DisablePrecomputedNDEF) != 0) sb.append("DisablePrecomputedNDEF, ");
|
||||
if ((iValue & SettingsMask.SkipSecurityDelayIfValidatedByLinkedTerminal) != 0)
|
||||
sb.append("SkipSecurityDelayIfValidatedByLinkedTerminal, ");
|
||||
|
||||
if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());
|
||||
sb.append("]");
|
||||
|
|
|
|||
|
|
@ -85,8 +85,11 @@ public class TLV {
|
|||
TAG_Denomination(0xC0),
|
||||
TAG_ValidatedBalance(0xC1),
|
||||
TAG_LastSign_Date(0xC2),
|
||||
TAG_DenominationText(0xC3);
|
||||
TAG_DenominationText(0xC3),
|
||||
|
||||
TAG_Terminal_IsLinked(0x58),
|
||||
TAG_Terminal_PublicKey(0x5C),
|
||||
TAG_Terminal_TransactionSignature(0x57);
|
||||
|
||||
Tag(int Code) {
|
||||
this.Code = Code;
|
||||
|
|
|
|||
|
|
@ -235,6 +235,9 @@ public class CustomReadCardTask extends Thread {
|
|||
Log.e(TAG, "Can't get max signatures");
|
||||
}
|
||||
|
||||
TLV terminalIsLinked = protocol.getReadResult().getTLV(TLV.Tag.TAG_Terminal_IsLinked);
|
||||
mCard.setTerminalIsLinked(terminalIsLinked != null);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new CardProtocol.TangemException("Can't parse card data");
|
||||
|
|
@ -299,6 +302,9 @@ public class CustomReadCardTask extends Thread {
|
|||
if (isCancelled) return;
|
||||
|
||||
protocol.setPIN(CardProtocol.DefaultPIN);
|
||||
if (pinsProvider != null) {
|
||||
protocol.setTerminalPublicKey(pinsProvider.getTerminalPublicKey());
|
||||
}
|
||||
protocol.clearReadResult();
|
||||
|
||||
if (lastRead_Encryption == null) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue