package com.tangem.tangemcommon.data; import com.tangem.tangemcommon.reader.CardProtocol; import com.tangem.tangemcommon.reader.SettingsMask; import com.tangem.tangemcommon.util.Util; import java.util.Date; /** * Created by dvol on 16.07.2017. */ public class TangemCard { private byte[] CID; private Status status; private String UID; private String blockchainID; private Manufacturer manufacturer = Manufacturer.Unknown; private boolean manufacturerConfirmed = false; private int maxSignatures; private int remainingSignatures; private String PIN; private byte[] pbWalletKey = null; private byte[] pbCardKey = null; private byte[] pbWalletKeyRar = null; private Date dtPersonalization = null; public String getBlockchainID() { return blockchainID; } public void setBlockchainID(String blockchainID) { this.blockchainID = blockchainID; } public void setWalletPublicKey(byte[] publicKey) { pbWalletKey = publicKey; } public void setWalletPublicKeyRar(byte[] publicKey) { pbWalletKeyRar = publicKey; } public byte[] getWalletPublicKey() { return pbWalletKey; } public byte[] getWalletPublicKeyRar() { return pbWalletKeyRar; } private boolean walletPublicKeyValid = false; public void setWalletPublicKeyValid(boolean walletPublicKeyValid) { this.walletPublicKeyValid = walletPublicKeyValid; } public boolean isWalletPublicKeyValid() { return walletPublicKeyValid; } public void setCardPublicKey(byte[] publicKey) { pbCardKey = publicKey; } public byte[] getCardPublicKey() { return pbCardKey; } private boolean cardPublicKeyValid = false; public void setCardPublicKeyValid(boolean cardPublicKeyValid) { this.cardPublicKeyValid = cardPublicKeyValid; } public boolean isCardPublicKeyValid() { return cardPublicKeyValid; } public Manufacturer getManufacturer() { return manufacturer; } public boolean isManufacturerConfirmed() { return manufacturerConfirmed; } public void setManufacturer(Manufacturer manufacturer, boolean verified) { if (this.manufacturer == manufacturer) { this.manufacturerConfirmed |= verified; } else { this.manufacturer = manufacturer; this.manufacturerConfirmed = verified; } } private Boolean codeConfirmed; public void setCodeConfirmed(Boolean codeConfirmed) { this.codeConfirmed = codeConfirmed; } public Boolean isCodeConfirmed() { return codeConfirmed; } private Boolean onlineVerified; public void setOnlineVerified(Boolean verified) { this.onlineVerified = verified; } public Boolean isOnlineVerified() { return onlineVerified; } private Boolean onlineValidated; public void setOnlineValidated(Boolean validated) { this.onlineValidated = validated; } public Boolean isOnlineValidated() { return onlineValidated; } public int getRemainingSignatures() { return remainingSignatures; } public void setRemainingSignatures(int remainingSignatures) { this.remainingSignatures = remainingSignatures; } public String getPIN() { return PIN; } public void setPIN(String PIN) { this.PIN = PIN; } // public void switchToInitialBlockchain() { // if (tokenSymbol.length() > 1) // blockchainID = Blockchain.Token.getID(); // Reset blockchain to Token from ETH for token cards with zero token balance on it // } public Date getPersonalizationDateTime() { return dtPersonalization; } public void setPersonalizationDateTime(Date dtPersonalization) { this.dtPersonalization = dtPersonalization; } private int health = 0; public int getHealth() { return health; } public void setHealth(int health) { if (health > this.health) this.health = health; } public boolean isHealthOK() { return health == 0; } private byte[] issuerPublicDataKey = null; private String issuerID = null; public byte[] getIssuerPublicDataKey() { return issuerPublicDataKey; } private Issuer issuer = Issuer.Unknown(); public void setIssuer(String issuerID, byte[] issuerPublicDataKey) { this.issuerPublicDataKey = issuerPublicDataKey; this.issuerID = issuerID; this.issuer = Issuer.FindIssuer(issuerID, issuerPublicDataKey); } public Issuer getIssuer() { return issuer; } public String getIssuerDescription() { return issuer.getOfficialName(); } String contractAddress = ""; public void setContractAddress(String address) { contractAddress = address; } public String getContractAddress() { return contractAddress; } public String tokenSymbol = ""; public void setTokenSymbol(String symbol) { tokenSymbol = symbol; } public String getTokenSymbol() { return tokenSymbol; } public boolean isToken() { return !(tokenSymbol == null || tokenSymbol.isEmpty()); } int tokensDecimal = 18; public void setTokensDecimal(int tokensDecimal) { this.tokensDecimal = tokensDecimal; } public int getTokensDecimal() { return tokensDecimal; } private byte[] issuerData; private byte[] issuerDataSignature; public byte[] getIssuerData() { return issuerData; } public byte[] getIssuerDataSignature() { return issuerDataSignature; } public void setIssuerData(byte[] value, byte[] signature) { issuerData = value; issuerDataSignature = signature; } private boolean needWriteIssuerData = false; public boolean getNeedWriteIssuerData() { return needWriteIssuerData; } public void setNeedWriteIssuerData(boolean value) { needWriteIssuerData = value; } public String getIssuerDataDescription() { return ""; } private int pauseBeforePIN2 = 0; public void setPauseBeforePIN2(int value) { this.pauseBeforePIN2 = value; } public int getPauseBeforePIN2() { return pauseBeforePIN2; } private Integer settingsMask = null; public Integer getSettingsMask() { return settingsMask; } public void setSettingsMask(int settingsMask) { this.settingsMask = settingsMask; } public Boolean isReusable() { if (settingsMask == null) return null; return (settingsMask & SettingsMask.IsReusable) != 0; } public Boolean allowSwapPIN() { if (settingsMask == null) return null; return (settingsMask & SettingsMask.AllowSwapPIN) != 0; } public Boolean allowSwapPIN2() { if (settingsMask == null) return null; return (settingsMask & SettingsMask.AllowSwapPIN2) != 0; } public Boolean needCVC() { if (settingsMask == null) return null; return (settingsMask & SettingsMask.UseCVC) != 0; } public Boolean useSmartSecurityDelay() { if (settingsMask == null) return null; return (settingsMask & SettingsMask.SmartSecurityDelay) != 0; } public Boolean useDefaultPIN1() { return CardProtocol.isDefaultPIN(getPIN()); } public enum PIN2_Mode {Unchecked, DefaultPIN2, CustomPIN2} public PIN2_Mode PIN2 = PIN2_Mode.Unchecked; public Boolean useDefaultPIN2() { if (PIN2 == PIN2_Mode.DefaultPIN2 || (PIN2 == PIN2_Mode.Unchecked && (needCVC() || (getPauseBeforePIN2() > 0)))) { // define that we use default PIN2 if we try it or not try and security delay or CVC is used return true; } else { return false; } } public void setUseDefaultPIN2(Boolean value) { if (value != null) { PIN2 = value ? PIN2_Mode.DefaultPIN2 : PIN2_Mode.CustomPIN2; } else { PIN2 = PIN2_Mode.Unchecked; } } public Boolean supportNDEF() { if (settingsMask == null) return null; return (settingsMask & SettingsMask.UseNDEF) != 0; } public Boolean supportOnlyOneCommandAtTime() { if (settingsMask == null) return null; return supportNDEF() && ((settingsMask & SettingsMask.UseOneCommandAtTime) != 0); } public Boolean supportDynamicNDEF() { if (settingsMask == null) return null; return supportNDEF() && ((settingsMask & SettingsMask.UseDynamicNDEF) != 0); } public Boolean supportBlock() { if (settingsMask == null) return null; return (settingsMask & SettingsMask.UseBlock) != 0; } public int getMaxSignatures() { return maxSignatures; } public void setMaxSignatures(int value) { maxSignatures = value; } private String firmwareVersion; public void setFirmwareVersion(String firmwareVersion) { this.firmwareVersion = firmwareVersion; } public String getFirmwareVersion() { return firmwareVersion; } public Boolean useDevelopersFirmware() { return getFirmwareVersion().endsWith("d") || getFirmwareVersion().endsWith("SDK"); } private static String getFirmwareVersionNumber(String version) throws Exception { if (version == null || version.length() < 4) { throw new Exception("Firmware version has unsupported format!"); } if (version.endsWith("d SDK")) { return version.substring(0, version.length() - 5); } else if (version.endsWith("r")) { return version.substring(0, version.length() - 1); } else { return version; } } private static int[] getFirmwareVersionNumbers(String version) throws Exception { String fwNumber = getFirmwareVersionNumber(version); String[] strNumbers = fwNumber.split("\\."); if (strNumbers.length != 2) throw new Exception("Firmware version has unsupported format!"); try { int major = Integer.parseInt(strNumbers[0]), minor = Integer.parseInt(strNumbers[1]); return new int[]{major, minor}; } catch (NumberFormatException e) { e.printStackTrace(); throw new Exception("Firmware version has unsupported format!"); } } public Boolean isFirmwareOlder(String version) throws Exception { int[] numbers1 = getFirmwareVersionNumbers(firmwareVersion), numbers2 = getFirmwareVersionNumbers(version); return numbers1[0] < numbers2[0] || (numbers1[0] == numbers2[0] && numbers1[1] < numbers2[1]); } public Boolean isFirmwareNewer(String version) throws Exception { int[] numbers1 = getFirmwareVersionNumbers(firmwareVersion), numbers2 = getFirmwareVersionNumbers(version); return numbers1[0] > numbers2[0] || (numbers1[0] == numbers2[0] && numbers1[1] > numbers2[1]); } private String batch; public void setBatch(String batch) { this.batch = batch; } public String getBatch() { return batch; } public enum SigningMethod { Sign_Hash(0, "sign hash"), Sign_Raw(1, "sign raw tx"), Sign_Hash_Validated_By_Issuer(2, "sign hash validated by issuer"), Sign_Raw_Validated_By_Issuer(3, "sign raw tx validated by issuer"), Sign_Hash_Validated_By_Issuer_And_WriteIssuerData(4, "sign hash validated by issuer and write issuer data"), Sign_Raw_Validated_By_Issuer_And_WriteIssuerData(5, "sign raw tx validated by issuer and write issuer data"); public int ID; String mDescription; SigningMethod(int ID, String description) { this.ID = ID; mDescription = description; } static SigningMethod FindByID(int ID) { SigningMethod[] methods = values(); for (SigningMethod m : methods) { if (m.ID == ID) return m; } return SigningMethod.Sign_Hash; } public String getDescription() { return mDescription; } } private SigningMethod signingMethod; public void setSigningMethod(int signingMethodID) { this.signingMethod = SigningMethod.FindByID(signingMethodID); } public SigningMethod getSigningMethod() { return signingMethod; } public TangemCard(String UID) { this.UID = UID; } public enum Status { NotPersonalized(0), Empty(1), Loaded(2), Purged(3); Status(int Code) { mCode = Code; } private int mCode; public int getCode() { return mCode; } public static Status fromCode(int code) { for (Status s : values()) { if (s.getCode() == code) return s; } return null; } } public void setStatus(Status status) { this.status = status; } public Status getStatus() { return status; } public byte[] getCID() { return CID; } public void setCID(byte[] value) { this.CID = value; } public String getCIDDescription() { String strCID = Util.bytesToHex(CID); try { return strCID.substring(0, 4) + " " + strCID.substring(4, 8) + " " + strCID.substring(8, 12) + " " + strCID.substring(12, 16); } catch (Exception e) { return strCID; } } public String getUID() { return UID; } public void setUID(String UID) { this.UID = UID; } private byte[] offlineBalance; public void setOfflineBalance(byte[] offlineBalance) { this.offlineBalance = offlineBalance; } public byte[] getOfflineBalance() { return offlineBalance; } public void clearOfflineBalance() { offlineBalance = null; } private byte[] Denomination; private String DenominationText; public void setDenomination(byte[] denomination, String denominationText) { this.Denomination = denomination; this.DenominationText = denominationText; } public byte[] getDenomination() { return Denomination; } public int SignedHashes = -1; // Will remain -1 if tag was not found on card (= not safe to accept) public void setSignedHashes(int SignedHashes) { this.SignedHashes = SignedHashes; } public int getSignedHashes() { return SignedHashes; } public String getDenominationText() { return DenominationText; } public void setDenominationText(String denominationText) { DenominationText = denominationText; } public void clearDenomination() { Denomination = null; DenominationText = null; } // public Bundle getAsBundle() { // Bundle B = new Bundle(); // saveToBundle(B); // return B; // } // // public void saveToBundle(Bundle B) { // try { // B.putString("UID", UID); // B.putByteArray("CID", CID); // B.putString("PIN", PIN); // B.putString("PIN2", PIN2.name()); // B.putString("Status", status.name()); // B.putString("Blockchain", blockchainID); // B.putString("BlockchainName", blockchainName); // B.putInt("TokensDecimal", tokensDecimal); // B.putString("TokenSymbol", tokenSymbol); // B.putString("ContractAddress", contractAddress); // if (dtPersonalization != null) B.putLong("dtPersonalization", dtPersonalization.getTime()); // B.putInt("RemainingSignatures", remainingSignatures); // B.putInt("MaxSignatures", maxSignatures); // B.putInt("Health", health); // if (settingsMask != null) B.putInt("settingsMask", settingsMask); // B.putInt("pauseBeforePIN2", pauseBeforePIN2); // if (signingMethod != null) B.putString("signingMethod", signingMethod.name()); // if (manufacturer != null) B.putString("Manufacturer", manufacturer.name()); // if (encryptionMode != null) B.putString("EncryptionMode", encryptionMode.name()); // if (issuer != null) B.putString("Issuer", issuer.getID()); // if (issuerPublicDataKey != null) B.putByteArray("IssuerPublicDataKey", issuerPublicDataKey); // if (firmwareVersion != null) B.putString("FirmwareVersion", firmwareVersion); // if (batch != null) B.putString("Batch", batch); // B.putBoolean("ManufacturerConfirmed", manufacturerConfirmed); // B.putBoolean("CardPublicKeyValid", isCardPublicKeyValid()); // B.putByteArray("CardPublicKey", getCardPublicKey()); // // B.putInt("SignedHashes", getSignedHashes()); // B.putBoolean("WalletPublicKeyValid", isWalletPublicKeyValid()); // if (pbWalletKey != null) // B.putByteArray("PublicKey", pbWalletKey); // if (pbWalletKeyRar != null) // B.putByteArray("PublicKeyRar", pbWalletKeyRar); // // if (getOfflineBalance() != null) B.putByteArray("OfflineBalance", getOfflineBalance()); // // if (getDenomination() != null) B.putByteArray("Denomination", getDenomination()); // if (getDenominationText() != null) B.putString("DenominationText", getDenominationText()); // // if (getIssuerData() != null && getIssuerDataSignature() != null) { // B.putByteArray("IssuerData", getIssuerData()); // B.putByteArray("IssuerDataSignature", getIssuerDataSignature()); // B.putBoolean("NeedWriteIssuerData", getNeedWriteIssuerData()); // } // // if (codeConfirmed != null) // B.putBoolean("codeConfirmed", codeConfirmed); // // if (codeConfirmed != null) // B.putBoolean("codeConfirmed", codeConfirmed); // // if (onlineVerified != null) // B.putBoolean("onlineVerified", onlineVerified); // // if (onlineValidated != null) // B.putBoolean("onlineValidated", onlineValidated); // // if (codeConfirmed != null) // B.putBoolean("codeConfirmed", codeConfirmed); // // if (codeConfirmed != null) // B.putBoolean("codeConfirmed", codeConfirmed); // // if (onlineVerified != null) // B.putBoolean("onlineVerified", onlineVerified); // // if (onlineValidated != null) // B.putBoolean("onlineValidated", onlineValidated); // } catch (Exception e) { // Log.e("Can't save to bundle ", e.getMessage()); // } // // } // // public void loadFromBundle(Bundle B) { // UID = B.getString("UID"); // CID = B.getByteArray("CID"); // PIN = B.getString("PIN"); // PIN2 = PIN2_Mode.valueOf(B.getString("PIN2")); // status = Status.valueOf(B.getString("Status")); // blockchainID = B.getString("Blockchain"); // tokensDecimal = B.getInt("TokensDecimal", 18); // tokenSymbol = B.getString("TokenSymbol", ""); // contractAddress = B.getString("ContractAddress", ""); // if (B.containsKey("BlockchainName")) // blockchainName = B.getString("BlockchainName", ""); // if (B.containsKey("dtPersonalization")) { // dtPersonalization = new Date(B.getLong("dtPersonalization")); // } // remainingSignatures = B.getInt("RemainingSignatures"); // maxSignatures = B.getInt("MaxSignatures"); // health = B.getInt("health"); // if (B.containsKey("settingsMask")) settingsMask = B.getInt("settingsMask"); // pauseBeforePIN2 = B.getInt("pauseBeforePIN2"); // if (B.containsKey("signingMethod")) // signingMethod = SigningMethod.valueOf(B.getString("signingMethod")); // if (B.containsKey("Manufacturer")) // manufacturer = Manufacturer.valueOf(B.getString("Manufacturer")); // manufacturerConfirmed = B.getBoolean("ManufacturerConfirmed"); // if (B.containsKey("EncryptionMode")) // encryptionMode = EncryptionMode.valueOf(B.getString("EncryptionMode")); // else // encryptionMode = null; // // if (B.containsKey("SignedHashes")) setSignedHashes(B.getInt("SignedHashes")); // // if (B.containsKey("Issuer")) issuer = Issuer.FindIssuer(B.getString("Issuer")); // if (B.containsKey("IssuerPublicDataKey")) // issuerPublicDataKey = B.getByteArray("IssuerPublicDataKey"); // // if (B.containsKey("FirmwareVersion")) firmwareVersion = B.getString("FirmwareVersion"); // if (B.containsKey("Batch")) batch = B.getString("Batch"); // // cardPublicKeyValid = B.getBoolean("CardPublicKeyValid"); // if (B.containsKey("CardPublicKey")) setCardPublicKey(B.getByteArray("CardPublicKey")); // // if (B.containsKey("OfflineBalance")) setOfflineBalance(B.getByteArray("OfflineBalance")); // else clearOfflineBalance(); // // if (B.containsKey("Denomination") && B.containsKey("DenominationText")) { // setDenomination(B.getByteArray("Denomination"), B.getString("DenominationText")); // } else if (B.containsKey("Denomination")) { // setDenomination(B.getByteArray("Denomination"), "N/A"); // } else clearDenomination(); // // if (B.containsKey("IssuerData") && B.containsKey("IssuerDataSignature")) // setIssuerData(B.getByteArray("IssuerData"), B.getByteArray("IssuerDataSignature")); // else setIssuerData(null, null); // // if (B.containsKey("NeedWriteIssuerData")) // setNeedWriteIssuerData(B.getBoolean("NeedWriteIssuerData")); // // walletPublicKeyValid = B.getBoolean("WalletPublicKeyValid"); // if (B.containsKey("PublicKey")) { // pbWalletKey = B.getByteArray("PublicKey"); // } // if (B.containsKey("PublicKeyRar")) { // pbWalletKeyRar = B.getByteArray("PublicKeyRar"); // } // // if (B.containsKey("codeConfirmed")) // codeConfirmed = B.getBoolean("codeConfirmed"); // // if (B.containsKey("onlineVerified")) // onlineVerified = B.getBoolean("onlineVerified"); // // if (B.containsKey("onlineValidated")) // onlineValidated = B.getBoolean("onlineValidated"); // } public enum EncryptionMode { None((byte) 0x0), Fast((byte) 0x1), Strong((byte) 0x2); private byte P; EncryptionMode(byte P) { this.P = P; } public int getP() { return P; } } public EncryptionMode encryptionMode = EncryptionMode.None; }