diff --git a/card-common/src/main/java/com/tangem/cardcommon/data/Issuer.java b/card-common/src/main/java/com/tangem/cardcommon/data/Issuer.java deleted file mode 100644 index 39a52a7bbd..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/data/Issuer.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.tangem.cardcommon.data; - -import com.tangem.cardcommon.reader.CardCrypto; -import com.tangem.cardcommon.util.Util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Created by dvol on 14.11.2017. - */ - -public class Issuer { - - static class KeyPair { - String privateKey; - String publicKey; - - byte[] getPrivateKey() throws Exception { - if (privateKey != null) { - return Util.hexToBytes(privateKey); - } else { - throw new Exception("No private key!"); - } - } - - byte[] getPublicKey() throws Exception { - if (publicKey == null) { - if (privateKey != null) { - return CardCrypto.GeneratePublicKey(Util.hexToBytes(privateKey)); - } else { - throw new Exception("Invalid key format: no public and no private"); - } - } else { - return Util.hexToBytes(publicKey); - } - } - - } - - public String id; - public String officialName; - private KeyPair dataKey; - private KeyPair transactionKey; - - public String getID() { - return id; - } - - private static List instances = new ArrayList<>(); - - static { - Issuer unknown = new Issuer(); - unknown.id = "UNKNOWN"; - unknown.officialName = "UNKNOWN"; - instances.add(unknown); - } - - public static void fillIssuers(List issuers) - { - instances.addAll(issuers); - } - - public byte[] getPublicDataKey() throws Exception { - if (dataKey == null) - throw new Exception("Data key not specified!"); - return dataKey.getPublicKey(); - } - - public byte[] getPublicTransactionKey() throws Exception { - if (dataKey == null) - throw new Exception("Transaction key not specified!"); - return transactionKey.getPublicKey(); - } - - public byte[] getPrivateDataKey() throws Exception { - if (dataKey == null) - throw new Exception("Data key not specified!"); - return dataKey.getPrivateKey(); - } - - public byte[] getPrivateTransactionKey() throws Exception { - if (transactionKey == null) - throw new Exception("Transaction key not specified!"); - return transactionKey.getPrivateKey(); - } - - public String getOfficialName() { - return officialName != null ? officialName : id; - } - - public static Issuer FindIssuer(String ID) { - for (int i = 0; i < instances.size(); i++) { - try { - if (instances.get(i).id.equals(ID)) { - return instances.get(i); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - return Unknown(); - } - - public static Issuer FindIssuer(String ID, byte[] publicDataKey) { - for (int i = 1; i < instances.size(); i++) { - try { - if (instances.get(i).id.equals(ID) && Arrays.equals(instances.get(i).getPublicDataKey(), publicDataKey)) { - return instances.get(i); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - return Unknown(); - } - - public static Issuer Unknown() { - return instances.get(0); - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/data/Manufacturer.java b/card-common/src/main/java/com/tangem/cardcommon/data/Manufacturer.java deleted file mode 100644 index 38f0a9aaab..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/data/Manufacturer.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.cardcommon.data; - -/** - * Created by dvol on 09.08.2017. - */ - -public enum Manufacturer { - - Unknown("", "Unknown"), - SMARTCASH_AG("SMART CASH AG", "SMART CASH AG"), - DEVELOPERS_SMARTCASH_AG("DEVELOP CASH AG", "SMART CASH AG (DEVELOPERS)"), - SMARTCASH("SMART CASH", "SMART CASH"); - - private String ID; - private String officialName; - - Manufacturer(String id, String officialName) { - this.ID = id; - this.officialName = officialName; - } - - public String getOfficialName() { - return officialName; - } - - public static Manufacturer FindManufacturer(String ID) { - Manufacturer[] manufacturers = Manufacturer.values(); - for (int i = 1; i < manufacturers.length; i++) { - if (manufacturers[i].ID.equals(ID)) { - return manufacturers[i]; - } - } - return Manufacturer.Unknown; - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/data/TangemCard.java b/card-common/src/main/java/com/tangem/cardcommon/data/TangemCard.java deleted file mode 100644 index f11c2ff413..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/data/TangemCard.java +++ /dev/null @@ -1,743 +0,0 @@ -package com.tangem.cardcommon.data; - -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.reader.SettingsMask; -import com.tangem.cardcommon.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; -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/data/external/CardDataSubstitutionProvider.java b/card-common/src/main/java/com/tangem/cardcommon/data/external/CardDataSubstitutionProvider.java deleted file mode 100644 index 55745f7a27..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/data/external/CardDataSubstitutionProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.cardcommon.data.external; - -import com.tangem.cardcommon.data.TangemCard; - -/** - * This interface provide method to make substitution of read card data (token symbol, contract address) - * if they was unknown when the card was produced - */ -public interface CardDataSubstitutionProvider { - void applySubstitution(TangemCard card); -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/data/external/FirmwaresDigestsProvider.java b/card-common/src/main/java/com/tangem/cardcommon/data/external/FirmwaresDigestsProvider.java deleted file mode 100644 index 702f38d10d..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/data/external/FirmwaresDigestsProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.cardcommon.data.external; - -/** - * This interfaces provide function to randomly select parameters to run one VerifyCode command, check answer and - * state that card is genuine or not - */ -public interface FirmwaresDigestsProvider { - VerifyCodeRecord selectRandomVerifyCodeBlock(String firmwareVersion); - - class VerifyCodeRecord { - public String hashAlg; - public int blockIndex; - public int blockCount; - public byte[] challenge; - public byte[] digest; - } - -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/data/external/PINsProvider.java b/card-common/src/main/java/com/tangem/cardcommon/data/external/PINsProvider.java deleted file mode 100644 index a343d79686..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/data/external/PINsProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.cardcommon.data.external; - -import java.util.List; - -/** - * Interface of PINsProvider - object that know some list of PINs (used when start first time read), PIN2 (used for protected operation) and store last used PIN - * to use it in following operations - */ -public interface PINsProvider { - - /** - * @return list of known PINs - * This PINs used when start reading of card - * When start reading a PINs from this list used sequential in search PIN algorithm until right PIN found - */ - List getPINs(); - - /** - * @return PIN2 for protected operations - */ - String getPIN2(); - - - /** - * Call after successful first time reading of card to store founded PIN (normally this PIN must be returned in next time {@see getPINs} at first position) - * @param pin - */ - void setLastUsedPIN(String pin); - -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/CardCrypto.java b/card-common/src/main/java/com/tangem/cardcommon/reader/CardCrypto.java deleted file mode 100644 index bab5e328c3..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/CardCrypto.java +++ /dev/null @@ -1,289 +0,0 @@ -package com.tangem.cardcommon.reader; - -import com.tangem.cardcommon.util.Log; -import com.tangem.cardcommon.util.PBKDF2; -import com.tangem.cardcommon.util.Util; - -import net.i2p.crypto.eddsa.EdDSAEngine; -import net.i2p.crypto.eddsa.EdDSAPrivateKey; -import net.i2p.crypto.eddsa.EdDSAPublicKey; -import net.i2p.crypto.eddsa.EdDSASecurityProvider; -import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; -import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec; -import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; -import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; - -import org.spongycastle.asn1.ASN1EncodableVector; -import org.spongycastle.asn1.ASN1Integer; -import org.spongycastle.asn1.DERSequence; -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; -import org.spongycastle.jce.spec.ECPrivateKeySpec; -import org.spongycastle.jce.spec.ECPublicKeySpec; -import org.spongycastle.math.ec.ECPoint; - -import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.Security; -import java.security.Signature; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Created by dvol on 14.11.2017. - */ - -public class CardCrypto { - static { - Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); - Security.addProvider(new EdDSASecurityProvider()); - } - - public enum Curve {secp256k1, ed25519} - - public static PublicKey LoadPublicKey(Curve curve, byte[] publicKeyArray) throws Exception { - if (publicKeyArray == null) throw new Exception("Public key not specified!"); - switch (curve) { - case secp256k1: { - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - KeyFactory factory = KeyFactory.getInstance("EC", "SC"); - - ECPoint p1 = spec.getCurve().decodePoint(publicKeyArray); - ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec); - - return factory.generatePublic(keySpec); - } - case ed25519: { - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); - EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(publicKeyArray, spec); - return new EdDSAPublicKey(pubKey); - } - - default: - throw new Exception(curve.toString() + " not supported"); - } - } - - public static PublicKey LoadPublicKey(byte[] publicKeyArray) throws Exception { - return LoadPublicKey(Curve.secp256k1, publicKeyArray); - } - - public static boolean VerifySignature(Curve curve, byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception { - switch (curve) { - case secp256k1: { - Signature signatureInstance = Signature.getInstance("SHA256withECDSA"); - PublicKey publicKey = LoadPublicKey(publicKeyArray); - signatureInstance.initVerify(publicKey); - signatureInstance.update(data); - - ASN1EncodableVector v = new ASN1EncodableVector(); - int size = signature.length / 2; - v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, 0, size)))); - v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, size, size * 2)))); - byte[] sigDer = new DERSequence(v).getEncoded(); - - return signatureInstance.verify(sigDer); - } - case ed25519: { - data = Util.calculateSHA512(data); - PublicKey publicKey = LoadPublicKey(curve, publicKeyArray); - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); - Signature signatureInstance = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm())); - signatureInstance.initVerify(publicKey); - - signatureInstance.update(data); - - return signatureInstance.verify(signature); - } - default: - throw new Exception(curve.toString() + " not supported"); - } - } - - public static boolean VerifySignature(byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception { - return VerifySignature(Curve.secp256k1, publicKeyArray, data, signature); - } - - public static boolean VerifySignature(String curveID, byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception { - Curve curve; - try { - curve = Curve.valueOf(curveID); - } catch (Exception e) { - throw new Exception("Card EC curve (" + curveID + ") isn't supported!"); - } - return VerifySignature(curve, publicKeyArray, data, signature); - } - - - public static byte[] Signature(Curve curve, byte[] privateKeyArray, byte[] data) throws Exception { - switch (curve) { - case secp256k1: { - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - KeyFactory factory = KeyFactory.getInstance("EC", "SC"); - - ECPrivateKeySpec keySpecP = new ECPrivateKeySpec(new BigInteger(1, privateKeyArray), spec); - - Signature signature = Signature.getInstance("SHA256withECDSA"); - - PrivateKey privateKey = factory.generatePrivate(keySpecP); - signature.initSign(privateKey); - signature.update(data); - byte[] enc = signature.sign(); - - if (enc[0] != 0x30) throw new Exception("bad encoding 1"); - if ((enc[1] & 0x80) != 0) throw new Exception("unsupported length encoding 1"); - if (enc[2] != 0x02) throw new Exception("bad encoding 2"); - if ((enc[3] & 0x80) != 0) throw new Exception("unsupported length encoding 2"); - 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"); - int sLength = enc[5 + rLength]; - - - int sPos = 6 + rLength; - byte[] res = new byte[64]; - if (rLength <= 32) { - System.arraycopy(enc, 4, res, 32 - rLength, rLength); - rLength = 32; - } else if (rLength == 33 && enc[4] == 0) { - rLength--; - System.arraycopy(enc, 5, res, 0, rLength); - } else { - Log.e("cardCrypto", "r-length:" + String.valueOf(rLength)); - Log.e("cardCrypto", "s-length:" + String.valueOf(sLength)); - Log.e("cardCrypto", "enc:" + Util.bytesToHex(enc)); - throw new Exception("unsupported r-length - r-length:" + String.valueOf(rLength) + ",s-length:" + String.valueOf(sLength) + ",enc:" + Util.bytesToHex(enc)); - } - if (sLength <= 32) { - System.arraycopy(enc, sPos, res, rLength + 32 - sLength, sLength); - sLength = 32; - } else if (sLength == 33 && enc[sPos] == 0) { - System.arraycopy(enc, sPos + 1, res, rLength, sLength - 1); - } else { - Log.e("cardCrypto", "s-length:" + String.valueOf(sLength)); - Log.e("cardCrypto", "r-length:" + String.valueOf(rLength)); - Log.e("cardCrypto", "enc:" + Util.bytesToHex(enc)); - throw new Exception("unsupported s-length - r-length:" + String.valueOf(rLength) + ",s-length:" + String.valueOf(sLength) + ",enc:" + Util.bytesToHex(enc)); - } - - if (!VerifySignature(GeneratePublicKey(privateKeyArray), data, res)) { - throw new Exception("Signature self verify failed - r-length:" + String.valueOf(rLength) + ",s-length:" + String.valueOf(sLength) + ",enc:" + Util.bytesToHex(enc) + ",res:" + Util.bytesToHex(res)); - } - - return res; - } - case ed25519: { - data = Util.calculateSHA512(data); - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); - //Signature sgr = Signature.getInstance("EdDSA", "I2P"); - Signature signatureInstance = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm())); - - EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(privateKeyArray, spec); - PrivateKey privateKey = new EdDSAPrivateKey(privateKeySpec); - - signatureInstance.initSign(privateKey); - signatureInstance.update(data); - - return signatureInstance.sign(); - } - default: - throw new Exception(curve.toString() + " not supported"); - } - } - - public static byte[] Signature(byte[] privateKeyArray, byte[] data) throws Exception { - return Signature(Curve.secp256k1, privateKeyArray, data); - } - - public static byte[] GeneratePublicKey(Curve curve, byte[] privateKeyArray) throws Exception { - switch (curve) { - case secp256k1: { - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - - - byte[] publicKeyArray = spec.getG().multiply(new BigInteger(1, privateKeyArray)).getEncoded(false); - - return publicKeyArray; - } - case ed25519: { - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); - - EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(privateKeyArray, spec); - EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(privateKeySpec.getA(), spec); - EdDSAPublicKey publicKey = new EdDSAPublicKey(publicKeySpec); - return publicKey.getAbyte(); - } - default: - throw new Exception(curve.toString() + " not supported"); - } - } - - - public static byte[] GeneratePublicKey(byte[] privateKeyArray) throws Exception { - return GeneratePublicKey(Curve.secp256k1, privateKeyArray); - } - - /** - * Computes the PBKDF2 hash of a password. - * - * @param password the password to hash. - * @param salt the salt - * @param iterations the iteration count (slowness factor) - * @return the PBDKF2 hash of the password - */ - public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations) - throws InvalidKeyException { - return PBKDF2.deriveKey(password, salt, iterations); - } - - public static byte[] Encrypt(byte[] key, byte[] data, boolean UsePKCS7) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { - if (UsePKCS7) { - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING"); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING", "SC"); - cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] mEncryptedData = cipher.doFinal(data); - return mEncryptedData; - } else { - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING"); - Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING", "SC"); - cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] mEncryptedData = cipher.doFinal(data); - return mEncryptedData; - } - } - - public static byte[] Encrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { - return Encrypt(key, data, true); - } - - public static byte[] Decrypt(byte[] key, byte[] data, boolean UsePKCS7) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException { - if (UsePKCS7) { - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING"); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING"); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length)); - return decryptedData; - } else { - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING"); - Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING"); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length)); - return decryptedData; - } - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/CardProtocol.java b/card-common/src/main/java/com/tangem/cardcommon/reader/CardProtocol.java deleted file mode 100644 index e0cf11fe7c..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/CardProtocol.java +++ /dev/null @@ -1,1125 +0,0 @@ -package com.tangem.cardcommon.reader; - -import com.tangem.cardcommon.data.TangemCard; -import com.tangem.cardcommon.data.Manufacturer; -import com.tangem.cardcommon.util.Log; -import com.tangem.cardcommon.util.Util; - -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.interfaces.ECPublicKey; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; - -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.SecureRandom; -import java.security.spec.ECGenParameterSpec; - -import javax.crypto.KeyAgreement; - -/** - * Implementation of the Tangem Card NFC Protocol - * See "Tangem card and NFC protocol Technical manual" [1] - * - * @author dvol - * 14.07.2017 - */ -public class CardProtocol { - private static final String logTag = "CardProtocol"; - - /** - * UI notifications - */ - public interface Notifications { - /** - * call on start reading card - * - * @param cardProtocol - instance of protocol class - */ - void onReadStart(CardProtocol cardProtocol); - - /** - * called during reading thread progress - to show progress bar, etc. - * - * @param cardProtocol - instance of protocol class - * @param progress - progress in percents - */ - void onReadProgress(CardProtocol cardProtocol, int progress); - - /** - * called after the reading is over - * - * @param cardProtocol - instance of protocol class - */ - void onReadFinish(CardProtocol cardProtocol); - - /** - * called if reading thread was canceled (e.g. due to paused activity) - */ - void onReadCancel(); - - /** - * called approx. every second while card security delay is pending - * called with msec=0 after the delay is over and command has been executed - * - * @param msec - estimated time in ms before the delay is finished - */ - void onReadWait(int msec); - - /** - * called before sending command to a card - * - * @param timeout - maximum waiting time before the answer is received or timeout occurs - */ - void onReadBeforeRequest(int timeout); - - /** - * called when the command answer is received or error/timeout occurred - */ - void onReadAfterRequest(); - } - - private Notifications mNotifications; - - - /** - * Return object containing data received from a card - * - * @return TangemCard - * @see TangemCard - */ - public TangemCard getCard() { - return mCard; - } - - private static final int SW_PIN_ERROR = SW.INVALID_PARAMS; - - private NfcReader mIsoDep; - - - public static final String DefaultPIN = "000000"; - public static final String DefaultPIN2 = "000"; - - private String mPIN; - - public void setPIN(String PIN) { - mPIN = PIN; - if (mCard != null) { - mCard.setPIN(PIN); - } - } - - public static boolean isDefaultPIN(String pin) { - return (DefaultPIN.equals(pin)); - } - - public static boolean isDefaultPIN2(String pin2) { - return (DefaultPIN2.equals(pin2)); - } - - - private TangemCard mCard; - private Exception mError; - - /** - * Set occurred error - * - * @param error Exception object - */ - public void setError(Exception error) { - mError = error; - } - - /** - * Get occurred error - * - * @return Exception occurred during reading a card - */ - public Exception getError() { - return mError; - } - - /** - * Constructor - * - * @param reader - NFC Reader interface - * @param card - TangemCard object, stored data from previous reading or null for reading a card for the first time - * @param notifications - UI notification callbacks - */ - public CardProtocol(NfcReader reader, TangemCard card, Notifications notifications) { - mIsoDep = reader; - mNotifications = notifications; - if (card != null) { - mPIN = card.getPIN(); - mCard = card; - } else { - mPIN = null; - mCard = new TangemCard(Util.byteArrayToHexString(mIsoDep.getId())); - } - } - - /** - * Base exception class for exceptions on card reading - */ - public static class TangemException extends Exception { - public TangemException(String message) { - super(message); - } - } - - /** - * Base exception class for exceptions on card reading - */ - public static class TangemException_TagLost extends Exception { - public TangemException_TagLost() { - super("Tag lost"); - } - - public TangemException_TagLost(String message) { - super(message); - } - } - - - /** - * Thrown on failed attempt of reading with possible wrong PIN - */ - public static class TangemException_InvalidPIN extends TangemException { - public TangemException_InvalidPIN(String message) { - super(message); - } - } - - /** - * Thrown when card enforces security delay - */ - public static class TangemException_NeedPause extends TangemException { - public TangemException_NeedPause(String message) { - super(message); - } - } - - - /** - * Thrown if APDU commands with extended length is not supported by an Android NFC device. This issue can occur on some legacy devices. - * This exception means that this particular action can't be executed on this device because it's impossible to transfer all needed data to/from a card. - */ - public static class TangemException_ExtendedLengthNotSupported extends TangemException { - public TangemException_ExtendedLengthNotSupported(String message) { - super(message); - } - } - - public static class TangemException_WrongAmount extends TangemException { - public TangemException_WrongAmount(String message) { - super(message); - } - } - - /** - * Return ISO 14443-3 tag UID - unique card identifier - * The tag identifier is a low level number used for anti-collision - * and identification. It has nothing to do with CID. - *

- * Used internally for protocol encryption - *

- * Can be used on subsequent reading to first fast check that - * you read the same card - * - * @return UID byte array - */ - public byte[] GetUID() { - if (mIsoDep == null) return null; - return mIsoDep.getId(); - } - - /** - * Return ISO 14443-3 tag reading timeout - */ - public int getTimeout() { - if (mIsoDep == null) return 60000; - return mIsoDep.getTimeout(); - } - - - /** - * protocolKey - * a base value used to construct the communication encryption key derived from PIN and UID - * See [1] 4.3, 4.4, 4.5 - * {@see run_OpenSession} - */ - private byte[] protocolKey; - - public void resetProtocolKey() { - protocolKey = null; - } - - /** - * Calculate protocolKey - * See [1] 4.3, 4.4, 4.5 - * - * @throws NoSuchAlgorithmException - * @throws InvalidKeyException - */ - public void CreateProtocolKey() throws NoSuchAlgorithmException, InvalidKeyException { - protocolKey = CardCrypto.pbkdf2(Util.calculateSHA256(mPIN), GetUID(), 50); - //Log.e("Reader", String.format("PIN: %s, Protocol key: %s", mPIN, Util.bytesToHex(protocolKey))); - if (sessionKey != null) { - sessionKey = null; - } - } - - /** - * current session communication encryption key - * See [1] 4.1, 4.3, 4.4, 4.5 - */ - private byte[] sessionKey = null; - - - /** - * Execute open session command and calculate session key {@see CardProtocol.sessionKey} - * During this command the card and the device exchange with random challenges (for Fast encryption mode) or - * public keys (for Strong encryption mode) and calculate sessionKey based protocolKey as: - * - sha256(challengeDevice|challengeCard|protocolKey) for Fast encryption - * - sha256(ECDH shared secret|protocolKey) for Strong encryption - * See [1] 4.1, 4.3, 4.4, 4.5 - * - * @param encryptionMode - mode of encryption {@link TangemCard.EncryptionMode} - * @throws Exception if something went wrong - */ - private void run_OpenSession(TangemCard.EncryptionMode encryptionMode) throws Exception { - sessionKey = null; - try { - CommandApdu cmdApdu = new CommandApdu(CommandApdu.ISO_CLA, INS.OpenSession.Code, 0, encryptionMode.getP()); - - switch (encryptionMode) { - case Fast: { - // See [1] 4.3 - 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 TangemException_TagLost(); - } - 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: { - // See [1] 4.4 - 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 TangemException_TagLost(); - } - //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 the specified APDU command to a card and receive an answer - * Should have a prior opened encryption session if encryption is used - * See [1] 4 - * - * @param cmdApdu - APDU command to send - * @param breakOnNeedPause - Specifies what to do when the card requests a security delay (interrupt transfer or wait till the end of the delay ) - * @return - response APDU - * @throws Exception - if something went wrong - */ - 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")) { - throw new TangemException_ExtendedLengthNotSupported(e.getMessage()); - } - throw e; - } - if (mCard.encryptionMode != TangemCard.EncryptionMode.None && !ResponseApdu.isStatusWord(rsp, SW.NEED_PAUSE)) { - 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 = 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; - } 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; - } - - /** - * Helper for preparing APDU command - init CommandApdu object and add common TLV tags ([CID,] PIN) - * - * @param ins - instruction code {@link INS} - * @return CommandAPDU - * @throws NoSuchAlgorithmException - if no sha256 library found - */ - 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; - } - -// /** -// * Run READ command and parse answer -// * {@see run_Read(boolean parseResult) } -// * -// * @throws Exception if something went wrong -// */ -// public void run_Read() throws Exception { -// run_Read(true); -// } - - /** - * Run READ command and parse answer (if specified) - *

- * This command returns all card and wallet data, including unique card number (CID) that has to be submitted when further calling all other commands. Therefore, - * READ_CARD should always be used in the beginning of communication session between NFC device and Tangem card - *

- * In order to obtain card’s data, the app should call READ_CARD command with correct PIN1 value as a parameter. The card will not respond if wrong PIN1 - * has been submitted - * This command requires only PIN1 parameter while other commands also need CID - * See [1] 8, 8.2 - * - * @throws Exception if something went wrong - */ - 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(); - } 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())); - } - } - - - /** - * A TLV list containing response of the last READ command - */ - private TLVList readResult = null; - - public void clearReadResult() { - sessionKey = null; - readResult = null; - } - - public boolean haveReadResult() { - return readResult != null; - } - - public TLVList getReadResult() { - return readResult; - } - - - /** - * VERIFY_CARD command and verify card's signature - * By using standard challenge-response scheme, the card proves - * possession of CARD_PRIVATE_KEY that corresponds to CARD_PUBLIC_KEY returned by READ_CARD command - * See [1] 3.2, 8.9 - * - * @return TLVList with response in case of success - * @throws Exception - if something went wrong - */ - public TLVList run_VerifyCard() throws Exception { - if (mCard.getCardPublicKey() == null || readResult == null) { - throw new TangemException("Before run_VerifyCard execute run_Read card first!"); - } - if (mCard.getStatus() == TangemCard.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); - - 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()), 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())); - } - - } - - /** - * CREATE_WALLET command - * This command will create a new wallet on the card having ‘Empty’ state. A key pair WALLET_PUBLIC_KEY / WALLET_PRIVATE_KEY is generated and securely stored in - * the card. - * See [1] 3.4, 8.3 - * - * @param PIN2 - PIN2 code to confirm operation - * @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!"); - 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())); - } - } - - /** - * CHECK_WALLET command without signature verification - * Card will sign a challenge to prove that it possesses WALLET_PRIVATE_KEY corresponding to WALLET_PUBLIC_KEY. Standard challenge/response scheme is used. - * See [1] 3.4, 8.4 - * - * @return TLVList from answer in case of success - * @throws Exception - if something went wrong - */ - private 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())); - } - } - - /** - * CHECK_WALLET command and verify wallet's signature - * Card will sign a challenge to prove that it possesses WALLET_PRIVATE_KEY corresponding to WALLET_PUBLIC_KEY. Standard challenge/response scheme is used. - * It will first run READ command if no reads where made before. Then execute CHECK_WALLET command and verify signature in the response. - * Set WalletPublicKeyValid in {@link TangemCard} - * See [1] 3.4, 8.4 - * - * @throws Exception - if something went wrong - */ - public void run_CheckWalletWithSignatureVerify() throws Exception { - if (mCard.getCardPublicKey() == null || readResult == null) { - throw new TangemException("Before run_VerifyCard execute run_Read card first!"); - } - if (mCard.getStatus() == TangemCard.Status.NotPersonalized) { - getCard().setManufacturer(Manufacturer.Unknown, false); - return; - } - - if (readResult.getTagAsInt(TLV.Tag.TAG_Status) != TangemCard.Status.Loaded.getCode()) { - throw new TangemException("Card must be loaded"); - } - TLVList checkResult = run_CheckWallet(); - if (checkResult == null) return; - - TLV tlvCurveID = readResult.getTLV(TLV.Tag.TAG_CurveID); - 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 (tlvCurveID == null || 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(tlvCurveID.getAsString(), tlvPublicKey.Value, dataArray, tlvSignature.Value)) { - Log.i(logTag, "Signature verification OK"); - mCard.setWalletPublicKeyValid(true); - } else { - mCard.setWalletPublicKeyValid(false); - } - } - - /** - * PURGE_WALLET command - * See [1] 3.4, 8.11 - * This command deletes all wallet data. If Is_Reusable flag is enabled during personalization, the card changes state to ‘Empty’ and a new wallet can be created by - * CREATE_WALLET command. If Is_Reusable flag is disabled, the card switches to ‘Purged’ state. ‘Purged’ state is final, it makes the card useless. - * - * @param PIN2 - PIN2 code to confirm operation - * @throws Exception - if something went wrong - */ - 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())); - } - } - - /** - * Execute SET_PIN command - * This command changes PIN1 and PIN2 passwords if it is allowed by Allow_SET_PIN1 and Allow_SET_PIN2 flags in Settings_Mask. Host application can submit - * unchanged passwords (New_PIN1 = PIN1 and New_PIN2 = PIN2) in order to check its correctness. Depending on the result, Status_Word in the command response will have - * these values: - * SW_PINS_NOT_CHANGED = 0x9000 - * SW_PIN1_CHANGED = 0x9001 - * SW_PIN2_CHANGED = 0x9002 - * SW_PINS_CHANGED = 0x9003 - * - * @param PIN2 - PIN2 code to confirm operation - * @param newPin - new value of PIN code - * @param newPin2 - new value of PIN2 code - * @param breakOnNeedPause - flag that specify what - * @throws Exception - if something went wrong - */ - public void run_SetPIN(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())); - } - } - - /** - * SIGN command to sign hashes - SigningMethod=0,2,4 (see {@link TangemCard.SigningMethod}) - * See [1] 8.6 - * - * @param PIN2 - PIN2 code to confirm operation - * @param hashes - array of digests to sign (max 10 digest at a time) - * @param issuerTransactionSignature - signature of hashes, if card need issuer validation before sign (for SigningMethod=2) - * @param issuerData - new issuerData to write on card (only for SigningMethod=4, null for other) - * @param issuerDataSignature - signature of issuerData, if issuerData specified(for SigningMethod=4) - * @return TLVList with card answer contained wallet signatures of digests from hashes array (in case of success) - * @throws Exception - if something went wrong - */ - public TLVList run_SignHashes(String PIN2, byte[][] hashes, byte[] issuerTransactionSignature, byte[] issuerData, byte[] issuerDataSignature) throws Exception { - if (mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer && mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash) { - throw new TangemException("Card don't support signing hashes!"); - } - - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - if (hashes.length > 10) throw new TangemException("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 TangemException("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 (issuerData != null) { - if (mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData) - throw new TangemException("Card don't support simultaneous sign with write issuer data!"); - - if (issuerDataSignature == null) - throw new TangemException("Card require issuer validation before write issuer data"); - bs.write(issuerData); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerDataSignature); - } - if (issuerTransactionSignature != null) { - //byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), bs.toByteArray()); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerTransactionSignature); - } else if (mCard.getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer) { - throw new TangemException("Card require issuer validation before sign the transaction!"); - } - - 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())); - } - } - - /** - * SIGN raw tx - SigningMethod=1 (see {@link TangemCard.SigningMethod}) - * See [1] 8.6 - * - * @param PIN2 - PIN2 code to confirm operation - * @param hashAlgID - name of hash alg, used for signature - * @param bTxOutData - part of raw transaction to sign - * @param issuerTransactionSignature - signature of hashes, if card need issuer validation before sign (for SigningMethod=2) - * @param issuerData - new issuerData to write on card (only for SigningMethod=4, null for other) - * @param issuerDataSignature - signature of issuerData, if issuerData specified(for SigningMethod=4) - * @return TLVList with card answer contained wallet signatures of bTxOutData(in case of success) - * @throws Exception - if something went wrong - */ - public TLVList run_SignRaw(String PIN2, String hashAlgID, byte[] bTxOutData, byte[] issuerTransactionSignature, byte[] issuerData, byte[] issuerDataSignature) 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, hashAlgID.getBytes("US-ASCII")); - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - if (bTxOutData.length > 1024) throw new TangemException("Raw transaction size is to big!"); - bs.write(bTxOutData); - - if (issuerData != null) { - if (mCard.getSigningMethod() != TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData) - throw new TangemException("Card don't support simultaneous sign with write issuer data!"); - - if (issuerDataSignature == null) - throw new TangemException("Card require issuer validation before write issuer data"); - bs.write(issuerData); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerDataSignature); - } - if (issuerTransactionSignature != null) { - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerTransactionSignature); - } else if (mCard.getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer) { - throw new TangemException("Card require issuer validation before sign the transaction!"); - } - - 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())); - } - } - - /** - * VERIFY_CODE command - * See [1] 8.8 - * This command challenges the card to prove integrity of COS binary code. For this purpose, the host application should have a special ‘hash library’ publicly provided - * by Tangem. It may contains ~150.000 precalculated hashes of COS binary code segments. - * VERIFY_CODE command internally reads a segment of COS binary code beginning at Code_Page_Address and having length of [64 x Code_Page_Count] bytes. - * Then it appends Challenge to the code segment, calculates resulting hash and returns it in the response. - * The application needs to ensure that returned hash coincides with the one stored in the hash library (see {@see Firmwares}). - * - * @param hashAlgID - ‘sha-256’, ‘sha-1’, ‘sha-224’, ‘sha-384’, ‘sha-512’, ‘crc-16’ - * @param codePageAddress - Value from 0 to ~3000, take from {@see Firmwares} - * @param codePageCount - Number of 32-byte pages to read: from 1 to 5, take from {@see Firmwares} - * @param challenge - Additional challenge value from 1 to 10, take from {@see Firmwares} - * @return digest bytes to compare with one stored in {@see Firmwares} - * @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!"); - 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())); - } - } - - /** - * VALIDATE_CARD command - * See [1] 3.2, 8.10 - * This is an optional command that the issuer can support if there is a real risk of mass counterfeiting by making multiple clones of a single card. This can be the case for - * transferrable Tangem cards that are almost never redeemed by users. - * The issuer has to have a back-end service storing and updating a counter value (Card_Validation_Counter) for each card (CID). This function should also be supported - * by the issuer’s application. - * The application may occasionally call VALIDATE_CARD command to ensure that there’s only one card having this CID is circulating out there. VALIDATE_CARD - * will increase COS internal Card_Validation_Counter by 1 and sign the new value with CARD_PRIVATE_KEY. Then the application should submit increased - * Card_Validation_Counter and its signature to issuer’s card validation back-end (server). The server should verify the signature and update Card_Validation_Counter value if - * previous value is less than the new one. If the server reveals that submitted Card_Validation_Counter value is less than previous value, then the card having this CID is - * deemed compromised and should not be accepted by the application. - * - * @param PIN2 - PIN2 code to confirm operation - * @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!"); - 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())); - } - } - - /** - * WRITE_ISSUER_DATA command - * This command re-writes Issuer_Data data block (max 512 bytes) and its issuer’s signature. - * Issuer_Data is never changed or parsed from within the Tangem COS. The issuer defines purpose of use, format and payload of Issuer_Data. - * For example, this field may contain information about wallet balance signed by the issuer or additional issuer’s attestation data - * - * @param issuerData - new issuerData - * @param issuerSignature - signature of issuerData with IssuerDataKey - * @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!"); - - 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())); - } - } - - /** - * GET_ISSUER_DATA command and verify verify issuer signature of returned data - * See [1] 3.3, 8.7 - * This command returns Issuer_Data data block and its issuer’s signature. - * - * @return TLVList with issuerData (if success read and verify) - * @throws Exception - if something went wrong - */ - public TLVList run_GetIssuerData() 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); - TLV issuerDataCounter = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Counter); - - boolean protectIssuerDataAgainstReplay = (readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask) & SettingsMask.ProtectIssuerDataAgainstReplay) != 0; - - if (issuerData == null || issuerDataSignature == null) - throw new TangemException("Invalid answer format (GetIssuerData)"); - - ByteArrayOutputStream bsDataToVerify = new ByteArrayOutputStream(); - bsDataToVerify.write(mCard.getCID()); - bsDataToVerify.write(issuerData.Value); - if (protectIssuerDataAgainstReplay) { - bsDataToVerify.write(issuerDataCounter.Value); - } - try { - if (CardCrypto.VerifySignature(mCard.getIssuerPublicDataKey(), 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())); - } - } - - /** - * Execute consecutive READ commands with increasing encryption level from EncryptionMode.None to EncryptionMode.Strong, see {@link TangemCard.EncryptionMode} - * If card requires stricter encryption level it returns SW.NEED_ENCRYPTION status word - * Once READ is successfully executed - save answer to {@see readResult}, save current PIN and encryption mode to {@link TangemCard} and return - * - * @throws Exception - if something went wrong - */ - public void run_GetSupportedEncryption() throws Exception { - mCard.encryptionMode = TangemCard.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 == TangemCard.EncryptionMode.None) { - mCard.encryptionMode = TangemCard.EncryptionMode.Fast; - } else if (mCard.encryptionMode == TangemCard.EncryptionMode.Fast) { - mCard.encryptionMode = TangemCard.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); - } - - -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/CommandApdu.java b/card-common/src/main/java/com/tangem/cardcommon/reader/CommandApdu.java deleted file mode 100644 index 95321a07da..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/CommandApdu.java +++ /dev/null @@ -1,276 +0,0 @@ -package com.tangem.cardcommon.reader; - -import com.tangem.cardcommon.util.Util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - -public class CommandApdu { - - public static final byte ISO_CLA = (byte) 0x00; - - protected String mCmdName; - protected int mCla = 0x00; - protected int mIns = 0x00; - protected int mP1 = 0x00; - protected int mP2 = 0x00; - protected int mLc = 0x00; - - protected byte[] mData = new byte[0]; - - protected int mLe = 0x00; - protected boolean mLeUsed = false; - protected TLVList tlvList = new TLVList(); - - public CommandApdu() { - } - - public CommandApdu(int cla, int ins, int p1, int p2) { - setCommandName(ins); - mCla = cla; - mIns = ins; - mP1 = p1; - mP2 = p2; - } - - public CommandApdu(int cla, int ins, int p1, int p2, byte[] data) { - setCommandName(ins); - mCla = cla; - mIns = ins; - mLc = data.length; - mP1 = p1; - mP2 = p2; - mData = data; - } - - public CommandApdu(INS ins) { - setCommandName(ins.name()); - mCla = ISO_CLA; - mIns = ins.Code; - mP1 = 0; - mP2 = 0; - } - - public CommandApdu(int cla, int ins, int p1, int p2, byte[] data, int le) { - setCommandName(ins); - mCla = cla; - mIns = ins; - mLc = data.length; - mP1 = p1; - mP2 = p2; - mData = data; - mLe = le; - mLeUsed = true; - } - - public CommandApdu(int cla, int ins, int p1, int p2, int le) { - setCommandName(ins); - mCla = cla; - mIns = ins; - mP1 = p1; - mP2 = p2; - mLe = le; - mLeUsed = true; - } - - public void setCommandName(String cmdName) { - mCmdName = cmdName; - } - - private void setCommandName(int ins) { - INS ins1 = INS.ByCode(ins); - if (ins1 != null) { - mCmdName = ins1.toString(); - } else { - mCmdName = String.format("INS[%2X]", ins); - } - } - - public String getCommandName() { - return mCmdName; - } - - public void setP1(int p1) { - mP1 = p1; - } - - public void setP2(int p2) { - mP2 = p2; - } - - public void setData(byte[] data) { - mLc = data.length; - mData = data; - } - - public void addTLV(TLV.Tag tag, byte[] value) { - tlvList.add(new TLV(tag, value)); - } - - public void addTLV_U8(TLV.Tag tag, int U8) { - addTLV(tag, new byte[]{(byte) U8}); - } - - public void addTLV_U16(TLV.Tag tag, int U16) { - addTLV(tag, Util.intToByteArray2(U16)); - } - - public void addTLV_U32(TLV.Tag tag, int U32) { - addTLV(tag, Util.intToByteArray4(U32)); - } - - public void setLe(int le) { - mLe = le; - mLeUsed = true; - } - - public int getP1() { - return mP1; - } - - public int getP2() { - return mP2; - } - - public int getLc() { - return mLc; - } - - public byte[] getData() { - return mData; - } - - public TLVList getTLVs() { - return tlvList; - } - - public int getLe() { - return mLe; - } - - public static String toString(byte[] cmdApdu, int Lc) { - String cmd = Util.bytesToHex(cmdApdu); - if (Lc == 0) return cmd; - return cmd.substring(0, 8) + " " + cmd.substring(8, 10) + " " + - cmd.substring(10, 10 + Lc * 2) + " " + cmd.substring(10 + Lc * 2, cmd.length()); - } - - - public void Crypt(byte[] key) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException, InvalidAlgorithmParameterException, NoSuchProviderException { - if (tlvList.size() != 0) { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - for (TLV tlv : tlvList) { - try { - tlv.WriteToStream(stream); - } catch (IOException e) { - e.printStackTrace(); - break; - } - } - - mData = stream.toByteArray(); - byte[] crc = Util.calculateCRC16(mData); - stream = new ByteArrayOutputStream(); - stream.write(Util.intToByteArray2(mData.length)); - stream.write(crc); - stream.write(mData); - mData = stream.toByteArray(); - - byte[] mEncryptedData = CardCrypto.Encrypt(key, mData); - - mData = mEncryptedData; - mLc = mData.length; - - tlvList.clear(); - - } - - } - - public byte[] toBytes() { - int length = 4; // CLA, INS, P1, P2 - - if (tlvList.size() != 0) { - mData = tlvList.toBytes(); - mLc = mData.length; - } - - if (mData.length != 0) { - length += 1; // LC - if (mLc >= 256) - length += 2; - length += mData.length; // DATA - } - if (mLeUsed) { - length += 1; // LE - if (mLc >= 256) - length += 2; - } - - byte[] apdu = new byte[length]; - - int index = 0; - apdu[index] = (byte) mCla; - index++; - apdu[index] = (byte) mIns; - index++; - apdu[index] = (byte) mP1; - index++; - apdu[index] = (byte) mP2; - index++; - if (mLc != 0) { - if (mLc < 256) { - apdu[index] = (byte) mLc; - index++; - } else { - apdu[index] = 0; - index++; - apdu[index] = (byte) (mLc >> 8); - index++; - apdu[index] = (byte) (mLc & 0xFF); - index++; - } - - System.arraycopy(mData, 0, apdu, index, mData.length); - index += mData.length; - } - if (mLeUsed) { - if (mLc < 256) { - apdu[index] += (byte) mLe; // LE - } else { - apdu[index] = 0; - index++; - apdu[index] = (byte) (mLe >> 8); - index++; - apdu[index] = (byte) (mLe & 0xFF); - index++; - } - } - - return apdu; - } - - public CommandApdu clone() { - CommandApdu apdu = new CommandApdu(); - apdu.setCommandName(mCmdName); - apdu.mCla = mCla; - apdu.mIns = mIns; - apdu.mP1 = mP1; - apdu.mP2 = mP2; - apdu.mLc = mLc; - apdu.mData = new byte[mData.length]; - System.arraycopy(mData, 0, apdu.mData, 0, mData.length); - apdu.mLe = mLe; - apdu.mLeUsed = mLeUsed; - apdu.tlvList = new TLVList(tlvList); - return apdu; - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/INS.java b/card-common/src/main/java/com/tangem/cardcommon/reader/INS.java deleted file mode 100644 index 152b4368ef..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/INS.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.cardcommon.reader; - -/** - * Created by dvol on 07.03.2018. - */ -public enum INS { - Unknown(0x00), - Read(0xF2), - VerifyCard(0xF3), - ValidateCard(0xF4), - VerifyCode(0xF5), - WriteIssuerData(0xF6), - GetIssuerData(0xF7), - CreateWallet(0xF8), - CheckWallet(0xF9), - SwapPIN(0xFA), - Sign(0xFB), - PurgeWallet(0xFC), - Activate(0xFE), - OpenSession(0xFF); - - INS(int Code) { - this.Code = Code; - } - - public int Code; - - public static INS ByCode(int Code) { - INS[] allINS = INS.values(); - for (INS i : allINS) { - if (i.Code == Code) return i; - } - return Unknown; - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/NfcReader.java b/card-common/src/main/java/com/tangem/cardcommon/reader/NfcReader.java deleted file mode 100644 index 7626757e64..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/NfcReader.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.cardcommon.reader; - -import java.io.IOException; - -public interface NfcReader { - byte[] getId(); - - void setTimeout(int timeout)throws IOException; - - int getTimeout(); - - byte[] transceive(byte[] data) throws IOException; - - void ignoreTag() throws IOException; - - void notifyReadResult(boolean success); - - void connect(); - -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/ResponseApdu.java b/card-common/src/main/java/com/tangem/cardcommon/reader/ResponseApdu.java deleted file mode 100644 index 92dcf58192..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/ResponseApdu.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.tangem.cardcommon.reader; - -import com.tangem.cardcommon.util.Util; - -import java.io.ByteArrayInputStream; -import java.util.Arrays; - -public class ResponseApdu { - - private int mSw1 = 0x00; - private int mSw2 = 0x00; - - private byte[] mData = new byte[0]; - private byte[] mBytes = new byte[0]; - - private TLVList tlvList = new TLVList(); - - private String parseError = null; - - private ResponseApdu() { - } - - ResponseApdu(byte[] respApdu) { - if (respApdu.length < 2) { - return; - } - if (respApdu.length > 2) { - mData = new byte[respApdu.length - 2]; - System.arraycopy(respApdu, 0, mData, 0, respApdu.length - 2); - - try { - tlvList = TLVList.fromBytes(mData); - } catch (TLVException e) { - 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 ) - { - ResponseApdu responseApdu = new ResponseApdu(); - responseApdu.mSw1 = ((int) data[0] & 0xFF); - responseApdu.mSw2 = ((int) data[1] & 0xFF); - return responseApdu; - }else if( data.length>=18 ){ - byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2),true); - - ByteArrayInputStream inputStream = new ByteArrayInputStream(decryptedData); - byte[] baLength = new byte[2]; - inputStream.read(baLength); - int length = ((int) baLength[0] & 0xFF) * 256 + ((int) baLength[1] & 0xFF); - if (length > decryptedData.length - 4) - throw new Exception("Can't decrypt - data size invalid"); - byte[] baCRC = new byte[2]; - inputStream.read(baCRC); - byte[] answerData = new byte[length]; - inputStream.read(answerData); - byte[] crc = Util.calculateCRC16(answerData); - if (!Arrays.equals(baCRC, crc)) throw new Exception("Can't decrypt - crc invalid"); - - ResponseApdu responseApdu = new ResponseApdu(); - responseApdu.mSw1 = ((int) data[data.length - 2] & 0xFF); - responseApdu.mSw2 = ((int) data[data.length - 1] & 0xFF); - responseApdu.mBytes = data; - responseApdu.mData = answerData; - - try { - responseApdu.tlvList = TLVList.fromBytes(answerData); - } catch (TLVException e) { - responseApdu.parseError = e.getMessage(); - } - - return responseApdu; - }else{ - throw new Exception("Can't decrypt - data size to small"); - } - } - - - public int getSW1() { - return mSw1; - } - - public int getSW2() { - return mSw2; - } - - public int getSW1SW2() { - return (mSw1 << 8) | mSw2; - } - - public byte[] getData() { - return mData; - } - - public TLVList getTLVs() { - return tlvList; - } - - public boolean isParsedWithError() { - return parseError != null; - } - public String getParseErroMessage() { - return parseError; - } - public byte[] toBytes() { - return mBytes; - } - - public boolean isStatus(int sw1sw2) { - if (getSW1SW2() == sw1sw2) { - return true; - } else { - return false; - } - } - - public String getSW1SW2Description() { - return SW.getDescription(getSW1SW2()); - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/SW.java b/card-common/src/main/java/com/tangem/cardcommon/reader/SW.java deleted file mode 100644 index e7676d0ac2..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/SW.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.tangem.cardcommon.reader; - -/** - * Created by dvol on 07.03.2018. - */ - -public class SW { - public static final int PROCESS_COMPLETED = 0x9000; - public static final int INVALID_PARAMS = 0x6A86; - public static final int ERROR_PROCESSING_COMMAND = 0x6286; - public static final int INVALID_STATE = 0x6985; - public static final int PINS_NOT_CHANGED = PROCESS_COMPLETED; - public static final int PIN1_CHANGED = PROCESS_COMPLETED + 0x0001; - public static final int PIN2_CHANGED = PROCESS_COMPLETED + 0x0002; - public static final int PINS_CHANGED = PROCESS_COMPLETED + 0x0003; - public static final int INS_NOT_SUPPORTED = 0x6D00; - public static final int NEED_ENCRYPTION = 0x6982; - public static final int NEED_PAUSE = 0x9789; - - public static String getDescription(int sw) { - switch (sw) { - case ERROR_PROCESSING_COMMAND: - return "SW_ERROR_PROCESSING_COMMAND"; - case INVALID_PARAMS: - return "SW_INVALID_PARAMS"; - case INVALID_STATE: - return "SW_INVALID_STATE"; - case INS_NOT_SUPPORTED: - return "SW_INS_NOT_SUPPORTED"; - case NEED_ENCRYPTION: - return "SW_NEED_ENCRYPTION"; - case PIN1_CHANGED: - return "SW_PIN1_CHANGED"; - case PIN2_CHANGED: - return "SW_PIN2_CHANGED"; - case PINS_CHANGED: - return "SW_PINS_CHANGED"; - case PROCESS_COMPLETED: - return "SW_PROCESS_COMPLETED"; - } - return "???"; - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/SettingsMask.java b/card-common/src/main/java/com/tangem/cardcommon/reader/SettingsMask.java deleted file mode 100644 index 283192df73..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/SettingsMask.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.tangem.cardcommon.reader; - -/** - * Created by dvol on 07.03.2018. - */ - -public class SettingsMask { - public static final int IsReusable = 0x0001; - public static final int UseActivation = 0x0002; - public static final int ForbidPurgeWallet = 0x0004; - public static final int UseBlock = 0x0008; - - public static final int AllowSwapPIN = 0x0010; - public static final int AllowSwapPIN2 = 0x0020; - public static final int UseCVC = 0x0040; - public static final int ForbidDefaultPIN = 0x0080; - - public static final int UseOneCommandAtTime = 0x0100; - public static final int UseNDEF = 0x0200; - public static final int UseDynamicNDEF = 0x0400; - public static final int SmartSecurityDelay = 0x0800; - - public static final int Protocol_AllowUnencrypted = 0x1000; - public static final int Protocol_AllowStaticEncryption = 0x2000; - - public static final int ProtectIssuerDataAgainstReplay = 0x4000; - - public static final int AllowSelectBlockchain = 0x8000; - - public static final int DisablePrecomputedNDEF = 0x00010000; - - public static String getDescription(int iValue) { - StringBuilder sb=new StringBuilder(); - 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 ((iValue & SettingsMask.ForbidPurgeWallet) != 0) sb.append("ForbidPurgeWallet, "); - if ((iValue & SettingsMask.AllowSelectBlockchain) != 0) sb.append("AllowSelectBlockchain, "); - if ((iValue & SettingsMask.DisablePrecomputedNDEF) != 0) sb.append("DisablePrecomputedNDEF, "); - - if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length()); - sb.append("]"); - return sb.toString(); - } - -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/TLV.java b/card-common/src/main/java/com/tangem/cardcommon/reader/TLV.java deleted file mode 100644 index 000f12eb38..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/TLV.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.tangem.cardcommon.reader; - -import com.tangem.cardcommon.util.Util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.Arrays; - -/** - * Created by dvol on 23.06.2017. - */ - -public class TLV { - public enum Tag { - TAG_Unknown(0x00), - TAG_CardID(0x01), - TAG_Status(0x02), - TAG_CardPublicKey(0x03), - TAG_CardSignature(0x04), - TAG_CurveID(0x05), - TAG_HashAlgID(0x06), - TAG_SigningMethod(0x07), - TAG_MaxSignatures(0x08), - TAG_PauseBeforePIN2(0x09), - TAG_SettingsMask(0x0A), - TAG_CardData(0x0C), - TAG_NDEFData(0x0D), - TAG_Health(0x0F), - - TAG_PIN(0x10), - TAG_PIN2(0x11), - TAG_NewPIN(0x12), - TAG_NewPIN2(0x13), - TAG_NewPIN_Hash(0x14), - TAG_NewPIN2_Hash(0x15), - TAG_Challenge(0x16), - TAG_Salt(0x17), - TAG_ValidationCounter(0x18), - TAG_CVC(0x19), - - TAG_Session_Key_A(0x1A), - TAG_Session_Key_B(0x1B), - TAG_Pause(0x1C), - - TAG_Manufacture_ID(0x20), - TAG_Manufacturer_Signature(0x21), - - TAG_Issuer_Data_PublicKey(0x30), - TAG_Issuer_Transaction_PublicKey(0x31), - 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), - TAG_ResetPIN(0x36), - - TAG_CodePageAddress(0x40), - TAG_CodePageCount(0x41), - TAG_CodeHash(0x42), - - TAG_TrOut_Hash(0x50), - TAG_TrOut_HashSize(0x51), - TAG_TrOut_Raw(0x52), - - TAG_Wallet_PublicKey(0x60), - TAG_Signature(0x61), - TAG_RemainingSignatures(0x62), - TAG_SignedHashes(0x63), - - TAG_Firmware(0x80), - TAG_Batch(0x81), - TAG_ManufactureDateTime(0x82), - TAG_Issuer_ID(0x83), - TAG_Blockchain_ID(0x84), - TAG_Manufacturer_PublicKey(0x85), - TAG_CardID_Manufacturer_Signature(0x86), - - TAG_Token_Symbol(0xA0), - TAG_Token_Contract_Address(0xA1), - TAG_Token_Decimal(0xA2), - TAG_Denomination(0xC0), - TAG_ValidatedBalance(0xC1), - TAG_LastSign_Date(0xC2), - TAG_DenominationText(0xC3); - - - Tag(int Code) { - this.Code = Code; - } - - public int getCode() { - return Code; - } - - private int Code; - - public static Tag ByCode(int Code) { - Tag[] allTags = Tag.values(); - for (Tag t : allTags) if (t.getCode() == Code) return t; - return TAG_Unknown; - } - } - - private Tag tag; - - public Tag getTag() { - return tag; - } - - public byte[] Value; - - public TLV(Tag tag, byte[] value) { - this.tag = tag; - this.Value = value; - } - - public void WriteToStream(ByteArrayOutputStream stream) throws IOException { - stream.write(tag.getCode()); - if (Value != null) { - if (Value.length > 0xFE) { - stream.write(0xFF); - stream.write((Value.length >> 8) & 0xFF); - stream.write(Value.length & 0xFF); - } else { - stream.write(Value.length & 0xFF); - } - stream.write(Value); - } else { - stream.write(0x00); - } - } - - public static TLV ReadFromStream(ByteArrayInputStream stream) throws IOException { - int code = stream.read(); - if (code == -1) return null; - int len = stream.read(); - if (len == -1) - throw new IOException("Can't read TLV"); - if (len == 0xFF) { - int lenH = stream.read(); - if (lenH == -1) - throw new IOException("Can't read TLV"); - len = stream.read(); - if (len == -1) - throw new IOException("Can't read TLV"); - len |= (lenH << 8); - } - byte[] value = new byte[len]; - if (len > 0) { - if (len != stream.read(value)) { - throw new IOException("Can't read TLV"); - } - } - Tag tag = Tag.ByCode(code); - TLV result = new TLV(tag, value); - return result; - } - - public int getAsInt() { - return Util.byteArrayToInt(Value); - } - - public String getAsHexString() { - return Util.bytesToHex(Value); - } - - public String getAsString() { - if( Value.length==0 ) return ""; - - if (Value[Value.length - 1] == 0) { - String s1 = new String(Arrays.copyOfRange(Value, 0, Value.length - 1), Charset.forName("utf-8")); - return s1.trim(); - } else { - String s1 = new String(Value, Charset.forName("utf-8")); - return s1.trim(); - - } - } - - @Override - public String toString() { - switch (tag) { - case TAG_CardData: - case TAG_Issuer_Data: { - try { - TLVList tlvSub = TLVList.fromBytes(Value); - return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), tlvSub.toString()); - } catch (TLVException e) { - e.printStackTrace(); - } - if (Value != null) { - return String.format("%s[%d]: %s (non TLV)", tag.name(), Value.length, Util.bytesToHex(Value)); - } else { - return String.format("%s[]: [[NULL]]", tag.name()); - } - } - case TAG_CurveID: - case TAG_HashAlgID: - case TAG_Blockchain_ID: - case TAG_Manufacture_ID: - case TAG_Firmware: - case TAG_Issuer_ID: - case TAG_Token_Symbol: - if (Value != null) { - return String.format("%s[%d]: %s(%s)", tag.name(), Value.length, Util.bytesToHex(Value), getAsString()); - } else { - return String.format("%s[]: [[NULL]]", tag.name()); - } - case TAG_SettingsMask: { - StringBuilder sb=new StringBuilder(); - if( Value!=null ) { - try { - int iValue = Util.byteArrayToInt(Value); - return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), SettingsMask.getDescription(iValue)); - } - 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)); - } else { - return String.format("%s[]: [[NULL]]", tag.name()); - } - } - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/TLVException.java b/card-common/src/main/java/com/tangem/cardcommon/reader/TLVException.java deleted file mode 100644 index 57b208b3b5..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/TLVException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.cardcommon.reader; - -public class TLVException extends Exception { - - private static final long serialVersionUID = 1L; - - public TLVException(String message){ - super(message); - } - - public TLVException(String message, Throwable cause) { - super(message, cause); - } - - public TLVException(Throwable cause) { - super(cause); - } -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/reader/TLVList.java b/card-common/src/main/java/com/tangem/cardcommon/reader/TLVList.java deleted file mode 100644 index 734ac7763e..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/reader/TLVList.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.tangem.cardcommon.reader; - -/** - * Created by dvol on 23.06.2017. - */ - -import com.tangem.cardcommon.util.Util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; - -public class TLVList extends ArrayList { - public String getParsedTLVs(String Prefix) { - String parsed = ""; - for (int i = 0; i < size(); i++) { - parsed += Prefix + this.get(i).toString() + (i < size() - 1 ? "\n" : ""); - } - return parsed;//.substring(0,parsed.length()-2); - } - - public TLVList() { - super(); - } - - public TLVList(Collection c) { - super(c); - } - - public TLV getTLV(TLV.Tag tag) { - for (TLV tlv : this) { - if (tlv.getTag() == tag) return tlv; - } - return null; - } - - public int getTagAsInt(TLV.Tag tag) { - TLV tlv = getTLV(tag); - return Util.byteArrayToInt(tlv.Value); - } - - public byte[] toBytes() { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - for (TLV tlv : this) { - try { - tlv.WriteToStream(stream); - } catch (IOException e) { - e.printStackTrace(); - break; - } - } - return stream.toByteArray(); - } - - public static TLVList fromBytes(byte[] mData) throws TLVException { - TLVList tlvList = new TLVList(); - ByteArrayInputStream stream = new ByteArrayInputStream(mData); - TLV tlv = null; - do { - try { - tlv = TLV.ReadFromStream(stream); - if (tlv != null) tlvList.add(tlv); - } catch (IOException e) { - throw new TLVException("TLVError: " + e.getMessage()); - } - } - while (tlv != null); - return tlvList; - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/tasks/CreateNewWalletTask.java b/card-common/src/main/java/com/tangem/cardcommon/tasks/CreateNewWalletTask.java deleted file mode 100644 index 59174a66be..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/tasks/CreateNewWalletTask.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.cardcommon.tasks; - -import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider; -import com.tangem.cardcommon.data.external.PINsProvider; -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.data.TangemCard; -import com.tangem.cardcommon.reader.NfcReader; -import com.tangem.cardcommon.util.Log; - -public class CreateNewWalletTask extends CustomReadCardTask { - public static final String TAG = CreateNewWalletTask.class.getSimpleName(); - - public CreateNewWalletTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications) { - super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications); - } - - @Override - public void run_Task() throws Exception { - mNotifications.onReadProgress(protocol, 20); - protocol.run_VerifyCard(); - - Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName()); - - mNotifications.onReadProgress(protocol, 30); - if (isCancelled) return; - protocol.run_CreateWallet(pinsProvider.getPIN2()); - mNotifications.onReadProgress(protocol, 60); - if (isCancelled) return; - - protocol.run_Read(); - parseReadResult(); - } - -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/tasks/CustomReadCardTask.java b/card-common/src/main/java/com/tangem/cardcommon/tasks/CustomReadCardTask.java deleted file mode 100644 index eb5f3f7747..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/tasks/CustomReadCardTask.java +++ /dev/null @@ -1,449 +0,0 @@ -package com.tangem.cardcommon.tasks; - -import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider; -import com.tangem.cardcommon.data.Manufacturer; -import com.tangem.cardcommon.data.external.PINsProvider; -import com.tangem.cardcommon.data.TangemCard; -import com.tangem.cardcommon.reader.CardCrypto; -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.reader.NfcReader; -import com.tangem.cardcommon.reader.TLV; -import com.tangem.cardcommon.reader.TLVException; -import com.tangem.cardcommon.reader.TLVList; -import com.tangem.cardcommon.util.Log; -import com.tangem.cardcommon.util.Util; - -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; -import org.spongycastle.math.ec.ECPoint; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; - -/** - * Base class for card communication task - */ -public class CustomReadCardTask extends Thread { - public static final String TAG = CustomReadCardTask.class.getSimpleName(); - - protected NfcReader mIsoDep; - protected CardProtocol.Notifications mNotifications; - protected boolean isCancelled = false; - protected TangemCard mCard; - CardDataSubstitutionProvider localStorage; - PINsProvider pinsProvider; - CardProtocol protocol; - - // this fields are static to optimize process when need enter pin and scan card again - private static ArrayList lastRead_UnsuccessfullPINs = new ArrayList<>(); - private static TangemCard.EncryptionMode lastRead_Encryption = null; - private static String lastRead_UID; - - public static void resetLastReadInfo() { - lastRead_UID = ""; - lastRead_Encryption = null; - lastRead_UnsuccessfullPINs.clear(); - } - - public CustomReadCardTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications) { - mIsoDep = reader; - mNotifications = notifications; - localStorage = cardDataSubstitutionProvider; - this.pinsProvider = pinsProvider; - mCard = card; - } - - /** - * Executing GET_ISSUER_DATA or WRITE_ISSUER_DATA depending on state in TangemCard object {@see TangemCard.getNeedWriteIssuerData()} - * See [1] 8.7 - * Usually this function must be called if run_Task() of descendants - * - * @throws Exception if something went wrong - */ - protected void run_ReadOrWriteIssuerData() throws Exception { - TLVList tlvIssuerData; - if (mCard.getNeedWriteIssuerData()) { - protocol.run_WriteIssuerData(mCard.getIssuerData(), mCard.getIssuerDataSignature()); - try { - tlvIssuerData = TLVList.fromBytes(mCard.getIssuerData()); - } catch (TLVException e) { - e.printStackTrace(); - tlvIssuerData = null; - } - } else { - try { - tlvIssuerData = protocol.run_GetIssuerData(); - } catch (Exception e) { - e.printStackTrace(); - tlvIssuerData = null; - } - } - - // try read offline balance data - if (mCard.getStatus() == TangemCard.Status.Loaded) { - if (tlvIssuerData != null && tlvIssuerData.getTLV(TLV.Tag.TAG_ValidatedBalance) != null && mCard.getMaxSignatures() == mCard.getRemainingSignatures()) { - mCard.setOfflineBalance(tlvIssuerData.getTLV(TLV.Tag.TAG_ValidatedBalance).Value); - } else { - mCard.clearOfflineBalance(); - } - } else { - mCard.clearOfflineBalance(); - } - - // try read denomination - if (tlvIssuerData != null && tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination) != null) { - if (tlvIssuerData.getTLV(TLV.Tag.TAG_DenominationText) != null) { - mCard.setDenomination(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value, tlvIssuerData.getTLV(TLV.Tag.TAG_DenominationText).getAsString()); - } else { - mCard.setDenomination(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value, null); - } - } else { - mCard.clearDenomination(); - } - } - - /** - * Parse response of the last READ command into TangemCard object - * See [1] 8.2, 5.3, 3.3 - * - * @throws CardProtocol.TangemException - if something went wrong - */ - public void parseReadResult() throws CardProtocol.TangemException { - if (mCard == null) mCard = protocol.getCard(); - // These tags always present in the parsed response: TAG_Status, TAG_CID, TAG_Manufacture_ID, TAG_Health, TAG_Firmware - TLV tlvStatus = protocol.getReadResult().getTLV(TLV.Tag.TAG_Status); - mCard.setStatus(TangemCard.Status.fromCode(Util.byteArrayToInt(tlvStatus.Value))); - TLV tlvCID = protocol.getReadResult().getTLV(TLV.Tag.TAG_CardID); - mCard.setCID(tlvCID.Value); - mCard.setManufacturer(Manufacturer.FindManufacturer(protocol.getReadResult().getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString()), true); - - // Support of legacy Firmware - if (protocol.getReadResult().getTLV(TLV.Tag.TAG_Firmware) != null) { - mCard.setFirmwareVersion(protocol.getReadResult().getTLV(TLV.Tag.TAG_Firmware).getAsString()); - } - mCard.setHealth(protocol.getReadResult().getTLV(TLV.Tag.TAG_Health).getAsInt()); - - // If the card was previously personalized then parse personalized card data - card public key, blockchain data and other settings - if (mCard.getStatus() != TangemCard.Status.NotPersonalized) { - try { - TLV tlvCardPubkicKey = protocol.getReadResult().getTLV(TLV.Tag.TAG_CardPublicKey); - if (tlvCardPubkicKey == null) - throw new CardProtocol.TangemException("Invalid answer format"); - - mCard.setCardPublicKey(tlvCardPubkicKey.Value); - - TLVList tlvCardData = TLVList.fromBytes(protocol.getReadResult().getTLV(TLV.Tag.TAG_CardData).Value); - - mCard.setBatch(tlvCardData.getTLV(TLV.Tag.TAG_Batch).getAsHexString()); - - 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()); - - 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 { - // Support of legacy Firmware - if (mCard.getFirmwareVersion() == null) { - mCard.setFirmwareVersion(tlvCardData.getTLV(TLV.Tag.TAG_Firmware).getAsString()); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "Cannot get firmware version"); - mCard.setFirmwareVersion("0.00"); - } - - try { - if (mCard.getFirmwareVersion().compareTo("1.05") < 0) { - // In FW ver 1.05, the issuer has one key pair used as both DataKey and TransactionKey, see [1] 3.3.1 and [1] 3.3.2 - //mCard.setIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), protocol.getReadResult().getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value); - mCard.setIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), protocol.getReadResult().getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value); - } else { - // In newer FW versions, the issuer has two different key pairs - DataKey and TransactionKey, see [1] 3.3.1 and [1] 3.3.2 - mCard.setIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), protocol.getReadResult().getTLV(TLV.Tag.TAG_Issuer_Data_PublicKey).Value); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "Cannot get issuer, try a version for older cards"); - try { - // for very very old cards Issuer key can be stored in different TLV tag - mCard.setIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), protocol.getReadResult().getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value); - } catch (Exception ee) { - ee.printStackTrace(); - Log.e(TAG, "Cannot get issuer"); - mCard.setIssuer("Unknown", null); - } - } - - // Overriding of missing card data, e.g. for cards with unknown ERC20 contract data - // This method must be called after the issuer data is defined because newly written data is verified by issuer data key - try { - // Hardcoded for some known batches, or, for other batches, received from Tangem server and stored in local storage - if (mCard.getBatch().equals("0017")) { - mCard.setContractAddress("0x9Eef75bA8e81340da9D8d1fd06B2f313DB88839c"); - } else if (mCard.getBatch().equals("0019")) { - mCard.setContractAddress("0x0c056b0cda0763cc14b8b2d6c02465c91e33ec72"); - } else { - //CardDataSubstitutionProvider localStorage = new CardDataSubstitutionProvider(mContext); - if (localStorage != null) localStorage.applySubstitution(mCard); - } - } catch (Exception e) { - Log.e(TAG, "Can't apply card data substitution"); - e.printStackTrace(); - } - - mCard.setBlockchainID(tlvCardData.getTLV(TLV.Tag.TAG_Blockchain_ID).getAsString()); - - try { - mCard.setSettingsMask(protocol.getReadResult().getTagAsInt(TLV.Tag.TAG_SettingsMask)); - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "Can't get settings mask"); - } - - if (protocol.getReadResult().getTLV(TLV.Tag.TAG_PauseBeforePIN2) != null) { - mCard.setPauseBeforePIN2(10 * protocol.getReadResult().getTagAsInt(TLV.Tag.TAG_PauseBeforePIN2)); - } - - try { - mCard.setSigningMethod(protocol.getReadResult().getTagAsInt(TLV.Tag.TAG_SigningMethod)); - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "Can't get signing method"); - mCard.setSigningMethod(0); - } - - try { - mCard.setMaxSignatures(protocol.getReadResult().getTagAsInt(TLV.Tag.TAG_MaxSignatures)); - } catch (Exception e) { - e.printStackTrace(); - Log.e(TAG, "Can't get max signatures"); - } - - } catch (Exception e) { - e.printStackTrace(); - throw new CardProtocol.TangemException("Can't parse card data"); - } - } - - - // Parse additional parameters for Loaded cards: wallet public key, remaining signatures, etc - if (mCard.getStatus() == TangemCard.Status.Loaded) { - - TLV tlvPublicKey = protocol.getReadResult().getTLV(TLV.Tag.TAG_Wallet_PublicKey); - String curveID = protocol.getReadResult().getTLV(TLV.Tag.TAG_CurveID).getAsString(); - - CardCrypto.Curve curve = CardCrypto.Curve.valueOf(curveID); - switch (curve) { - case secp256k1: - 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); - break; - case ed25519: - mCard.setWalletPublicKey(tlvPublicKey.Value); - mCard.setWalletPublicKeyRar(tlvPublicKey.Value); - break; - - } - - mCard.setRemainingSignatures(protocol.getReadResult().getTagAsInt(TLV.Tag.TAG_RemainingSignatures)); - - if (protocol.getReadResult() != null && protocol.getReadResult().getTLV(TLV.Tag.TAG_SignedHashes) != null) { - mCard.setSignedHashes(protocol.getReadResult().getTagAsInt(TLV.Tag.TAG_SignedHashes)); - } - - } else { -// mCard.setWallet("N/A"); - } - } - - - /** - * On first time card reading run READ command, parse answer and Executing GET_ISSUER_DATA or WRITE_ISSUER_DATA depending on state in TangemCard object {@see TangemCard.getNeedWriteIssuerData()} - * See [1] 8.7 - * - * @throws Exception if something went wrong - */ - public void run_FirstTimeRead() throws Exception { - - - byte[] UID = mIsoDep.getId(); - String sUID = Util.byteArrayToHexString(UID); - if (!lastRead_UID.equals(sUID)) { - resetLastReadInfo(); - } - - Log.i(TAG, "[-- Start read card info --]"); - - if (isCancelled) return; - - protocol.setPIN(CardProtocol.DefaultPIN); - protocol.clearReadResult(); - - if (lastRead_Encryption == null) { - Log.i(TAG, "Try get supported encryption mode"); - protocol.run_GetSupportedEncryption(); - } else { - Log.i(TAG, "Use already defined encryption mode: " + lastRead_Encryption.name()); - protocol.getCard().encryptionMode = lastRead_Encryption; - } - - if (protocol.haveReadResult()) { - //already have read result (obtained while get supported encryption), only read issuer data and define offline balance - parseReadResult(); - pinsProvider.setLastUsedPIN(protocol.getCard().getPIN()); - } else { - //don't have read result - may be don't get supported encryption on this try, need encryption or need another PIN - if (lastRead_Encryption == null) { - // we try get supported encryption on this time - lastRead_Encryption = protocol.getCard().encryptionMode; - if (protocol.getCard().encryptionMode == TangemCard.EncryptionMode.None) { - // default pin not accepted - lastRead_UnsuccessfullPINs.add(CardProtocol.DefaultPIN); - } - } - - boolean pinFound = false; - for (String PIN : pinsProvider.getPINs()) { - Log.e(TAG, "PIN: " + PIN); - - boolean skipPin = false; - for (int i = 0; i < lastRead_UnsuccessfullPINs.size(); i++) { - if (lastRead_UnsuccessfullPINs.get(i).equals(PIN)) { - skipPin = true; - break; - } - } - - if (skipPin) { - Log.e(TAG, "Skip PIN - already checked before"); - continue; - } - - try { - protocol.setPIN(PIN); - if (protocol.getCard().encryptionMode != TangemCard.EncryptionMode.None) { - protocol.CreateProtocolKey(); - } - protocol.run_Read(); - // After first successful read, data will be parsed into TangemCard object - parseReadResult(); - - pinsProvider.setLastUsedPIN(PIN); - pinFound = true; - protocol.getCard().setPIN(PIN); - break; - } catch (CardProtocol.TangemException_InvalidPIN e) { - Log.e(TAG, e.getMessage()); - lastRead_UnsuccessfullPINs.add(PIN); - } - } - if (!pinFound) { - throw new CardProtocol.TangemException_InvalidPIN("No valid PIN found!"); - } - } - } - - /** - * Run read and check that the card is the same - * - * @throws Exception if something went wrong - */ - public void run_SecondTimeRead() throws Exception { - String PIN = mCard.getPIN(); - protocol.setPIN(PIN); - protocol.run_Read(); - pinsProvider.setLastUsedPIN(PIN); - if (!Arrays.equals(mCard.getCID(), protocol.getReadResult().getTLV(TLV.Tag.TAG_CardID).Value)) { - throw new CardProtocol.TangemException("Card must be the same. Reading attempt on different card!"); - } - } - - /** - * This function must be override in descendants to arrive the task goal - sign, verify and etc - * @throws Exception if something went wrong - */ - public void run_Task() throws Exception { - - } - - @Override - public void run() { - if (mIsoDep == null) { - return; - } - try { - // for Samsung's bugs - - // Workaround for the Samsung Galaxy S5 (since the - // first connection always hangs on transceive). - - mIsoDep.connect(); - try { - protocol = new CardProtocol(mIsoDep, mCard, mNotifications); - mNotifications.onReadStart(protocol); - try { - Log.i(TAG, String.format("[-- Start task -- %s --]", getClass().getSimpleName())); - mNotifications.onReadProgress(protocol, 5); - if (mCard == null) { - // first time reading - run_FirstTimeRead(); - } else { - run_SecondTimeRead(); - } - run_Task(); - mNotifications.onReadProgress(protocol, 100); - } catch (Exception e) { - e.printStackTrace(); - protocol.setError(e); - - } finally { - Log.i(TAG, String.format("[-- Finish task -- %s --]", getClass().getSimpleName())); - mNotifications.onReadFinish(protocol); - } - } finally { - mIsoDep.ignoreTag(); - } - } catch (Exception e) { - e.printStackTrace(); - mIsoDep.notifyReadResult(false); - } - } - - public void cancel(Boolean AllowInterrupt) { - try { - if (isAlive()) { - isCancelled = true; - join(500); - } - if (isAlive() && AllowInterrupt) { - interrupt(); - mNotifications.onReadCancel(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/tasks/PurgeTask.java b/card-common/src/main/java/com/tangem/cardcommon/tasks/PurgeTask.java deleted file mode 100644 index cf05ca08f0..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/tasks/PurgeTask.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.tangem.cardcommon.tasks; - -import com.tangem.cardcommon.data.external.PINsProvider; -import com.tangem.cardcommon.reader.NfcReader; -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.data.TangemCard; -import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider; - -public class PurgeTask extends CustomReadCardTask { - public static final String TAG = PurgeTask.class.getSimpleName(); - - public PurgeTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications) { - super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications); - } - - @Override - public void run_Task() throws Exception { - if (mCard.getPauseBeforePIN2() > 0) { - mNotifications.onReadWait(mCard.getPauseBeforePIN2()); - } - protocol.run_PurgeWallet(pinsProvider.getPIN2()); - mNotifications.onReadProgress(protocol, 50); - protocol.run_Read(); - mNotifications.onReadProgress(protocol, 100); - - } -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/tasks/ReadCardInfoTask.java b/card-common/src/main/java/com/tangem/cardcommon/tasks/ReadCardInfoTask.java deleted file mode 100644 index b22553f672..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/tasks/ReadCardInfoTask.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.tangem.cardcommon.tasks; - -import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider; -import com.tangem.cardcommon.data.external.PINsProvider; -import com.tangem.cardcommon.reader.NfcReader; -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.data.TangemCard; - -public class ReadCardInfoTask extends CustomReadCardTask { - public static final String TAG = ReadCardInfoTask.class.getSimpleName(); - - public ReadCardInfoTask(NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications) { - super(null, reader, cardDataSubstitutionProvider, pinsProvider, notifications); - } - - /** - * Verify if default PIN2 is set on the card and enable UseDefaultPIN2 flag {@link TangemCard} - * Works in firmware 1.12 and later, for older version UseDefaultPIN2 is always null - * This function NEVER triggers security delay - * - * @throws Exception - if something went wrong - */ - private void run_CheckPIN2isDefault() throws Exception { - // can obtain SetPIN(to default) answer without security delay - try check if PIN2 is default with card request - if (mCard.isFirmwareNewer("1.19") || (mCard.isFirmwareNewer("1.12") && (mCard.getPauseBeforePIN2() == 0 || mCard.useSmartSecurityDelay()))) { - try { - protocol.run_SetPIN(CardProtocol.DefaultPIN2, mCard.getPIN(), CardProtocol.DefaultPIN2, true); - mCard.setUseDefaultPIN2(true); - } catch (CardProtocol.TangemException_NeedPause e) { - mCard.setUseDefaultPIN2(null); - } catch (CardProtocol.TangemException_InvalidPIN e) { - mCard.setUseDefaultPIN2(false); - } - } else { - mCard.setUseDefaultPIN2(null); - } - } - - @Override - public void run_Task() throws Exception { - mNotifications.onReadProgress(protocol, 20); - run_ReadOrWriteIssuerData(); - mNotifications.onReadProgress(protocol, 50); - run_CheckPIN2isDefault(); - mNotifications.onReadProgress(protocol, 90); - } - -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/tasks/SignTask.java b/card-common/src/main/java/com/tangem/cardcommon/tasks/SignTask.java deleted file mode 100644 index 921a311a09..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/tasks/SignTask.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.tangem.cardcommon.tasks; - -import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider; -import com.tangem.cardcommon.data.TangemCard; -import com.tangem.cardcommon.data.external.PINsProvider; -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.reader.NfcReader; -import com.tangem.cardcommon.reader.TLV; -import com.tangem.cardcommon.reader.TLVList; -import com.tangem.cardcommon.util.Log; - -import java.io.ByteArrayOutputStream; - -public class SignTask extends CustomReadCardTask { - public static final String TAG = SignTask.class.getSimpleName(); - - /** - * Transaction Engine request/notifications during sign process - */ - public interface TransactionToSign { - boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod); - - byte[][] getHashesToSign() throws Exception; - - byte[] getRawDataToSign() throws Exception; - - String getHashAlgToSign() throws Exception; - - byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception; - - byte[] onSignCompleted(byte[] signature) throws Exception; - } - - private TransactionToSign transactionToSign; - - public SignTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications, TransactionToSign transactionToSign) { - super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications); - this.transactionToSign = transactionToSign; - } - - @Override - public void run_Task() throws Exception { - protocol.run_VerifyCard(); - - Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName()); - - mNotifications.onReadProgress(protocol, 30); - if (isCancelled) return; - - if (mCard.getPauseBeforePIN2() > 0) { - mNotifications.onReadWait(mCard.getPauseBeforePIN2()); - } - - if (!transactionToSign.isSigningMethodSupported(mCard.getSigningMethod())) { - throw new CardProtocol.TangemException("Signing method isn't supported!"); - } - - TLVList signResult; - switch (mCard.getSigningMethod()) { - case Sign_Hash: - signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), transactionToSign.getHashesToSign(), null, null, null); - break; - case Sign_Hash_Validated_By_Issuer: - case Sign_Hash_Validated_By_Issuer_And_WriteIssuerData: - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - byte[][] hashes = transactionToSign.getHashesToSign(); - if (hashes.length > 10) throw new CardProtocol.TangemException("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 CardProtocol.TangemException("Hashes length must be identical!"); - bs.write(hashes[i]); - } - signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), hashes, transactionToSign.getIssuerTransactionSignature(bs.toByteArray()), null, null); - break; - case Sign_Raw: - signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), transactionToSign.getHashAlgToSign(), transactionToSign.getRawDataToSign(), null, null, null); - break; - case Sign_Raw_Validated_By_Issuer: - case Sign_Raw_Validated_By_Issuer_And_WriteIssuerData: - byte[] txOut = transactionToSign.getRawDataToSign(); - signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), transactionToSign.getHashAlgToSign(), txOut, transactionToSign.getIssuerTransactionSignature(txOut), null, null); - break; - default: - throw new CardProtocol.TangemException("Signing method isn't supported!"); - } - - transactionToSign.onSignCompleted(signResult.getTLV(TLV.Tag.TAG_Signature).Value); - mNotifications.onReadProgress(protocol, 100); - - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/tasks/SwapPINTask.java b/card-common/src/main/java/com/tangem/cardcommon/tasks/SwapPINTask.java deleted file mode 100644 index 2fb372a405..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/tasks/SwapPINTask.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.cardcommon.tasks; - -import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider; -import com.tangem.cardcommon.data.external.PINsProvider; -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.data.TangemCard; -import com.tangem.cardcommon.reader.NfcReader; - -public class SwapPINTask extends CustomReadCardTask { - public static final String TAG = SwapPINTask.class.getSimpleName(); - - private String newPIN, newPIN2; - - public SwapPINTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications, String newPIN, String newPIN2) { - super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications); - this.newPIN = newPIN; - this.newPIN2 = newPIN2; - } - - @Override - public void run_Task() throws Exception { - - if (mCard.getPauseBeforePIN2() > 0) { - mNotifications.onReadWait(mCard.getPauseBeforePIN2()); - } - - protocol.run_SetPIN(pinsProvider.getPIN2(), newPIN, newPIN2, false); - protocol.setPIN(newPIN); - mCard.setPIN(newPIN); - - mNotifications.onReadProgress(protocol, 50); - - protocol.run_Read(); - - mNotifications.onReadProgress(protocol, 100); - - } - -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/tasks/VerifyCardTask.java b/card-common/src/main/java/com/tangem/cardcommon/tasks/VerifyCardTask.java deleted file mode 100644 index 4c1f405feb..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/tasks/VerifyCardTask.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.cardcommon.tasks; - -import com.tangem.cardcommon.data.TangemCard; -import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider; -import com.tangem.cardcommon.data.external.FirmwaresDigestsProvider; -import com.tangem.cardcommon.data.external.PINsProvider; -import com.tangem.cardcommon.reader.CardProtocol; -import com.tangem.cardcommon.reader.NfcReader; -import com.tangem.cardcommon.util.Log; - -import java.util.Arrays; - -/** - * Created by dvol on 04.02.2018. - */ - -public class VerifyCardTask extends CustomReadCardTask { - public static final String TAG = VerifyCardTask.class.getSimpleName(); - - private FirmwaresDigestsProvider firmwaresDigestsProvider; - public VerifyCardTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, FirmwaresDigestsProvider firmwaresDigestsProvider, CardProtocol.Notifications notifications) { - super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications); - this.firmwaresDigestsProvider=firmwaresDigestsProvider; - } - - @Override - public void run_Task() throws Exception { - mNotifications.onReadProgress(protocol, 20); - if (isCancelled) return; - protocol.run_VerifyCard(); - mNotifications.onReadProgress(protocol, 50); - Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName()); - if (isCancelled) return; - if (protocol.getCard().getStatus() == TangemCard.Status.Loaded) { - protocol.run_CheckWalletWithSignatureVerify(); - mNotifications.onReadProgress(protocol, 80); - } - if (isCancelled) return; - FirmwaresDigestsProvider.VerifyCodeRecord record = firmwaresDigestsProvider.selectRandomVerifyCodeBlock(mCard.getFirmwareVersion()); - if (isCancelled) return; - if (record != null) { - byte[] returnedDigest = protocol.run_VerifyCode(record.hashAlg, record.blockIndex, record.blockCount, record.challenge); - mCard.setCodeConfirmed(Arrays.equals(returnedDigest, record.digest)); - } else { - mCard.setCodeConfirmed(null); - } - mNotifications.onReadProgress(protocol, 90); - - } - -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/util/Log.java b/card-common/src/main/java/com/tangem/cardcommon/util/Log.java deleted file mode 100644 index 738f176df5..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/util/Log.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.cardcommon.util; - -public class Log { - public static void i(String logTag, String message) { - - } - public static void e(String logTag, String message) { - - } - public static void v(String logTag, String message) { - - } -} diff --git a/card-common/src/main/java/com/tangem/cardcommon/util/PBKDF2.java b/card-common/src/main/java/com/tangem/cardcommon/util/PBKDF2.java deleted file mode 100644 index db054db1b2..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/util/PBKDF2.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.tangem.cardcommon.util; - -import org.spongycastle.crypto.CipherParameters; -import org.spongycastle.crypto.digests.SHA256Digest; -import org.spongycastle.crypto.macs.HMac; -import org.spongycastle.crypto.params.KeyParameter; - -import java.security.InvalidKeyException; -import java.util.Arrays; - -public final class PBKDF2 { - private static final HMac F =new HMac(new SHA256Digest()); - - /** - * Derive a key. - * - * @param password The password to derive the key from. - * @param iterations The iteration count. - * @return Returns a key derived with the specified parameters. - * @throws InvalidKeyException If the specified length for the derived key - * is to long. - */ - public static byte[] deriveKey(final byte[] password, final byte[] salt, final int iterations) throws InvalidKeyException { - return deriveKey(password, salt, iterations, F.getMacSize()); - } - - /** - * Derive a key with a specified length. - * - * @param password The password to derive the key from. - * @param iterations The iteration count. - * @param len The length of the derived key. - * @return Returns a key derived with the specified parameters. - * @throws InvalidKeyException If the specified length for the derived key - * is to long. - */ - public static byte[] deriveKey(final byte[] password, final byte[] salt, final int iterations, final int len) throws InvalidKeyException { - // Check key length - if (len > ((Math.pow(2, 32) - 1) * F.getMacSize())) - throw new InvalidKeyException("Derived key to long"); - - byte[] derivedKey = new byte[len]; - - final int J = 0; - final int K = F.getMacSize(); - final int U = F.getMacSize() << 1; - final int B = K + U; - final byte[] workingArray = new byte[K + U + 4]; - - // Initialize F - CipherParameters macParams = new KeyParameter(password); - F.init(macParams); - - // Perform iterations - for (int kpos = 0, blk = 1; kpos < len; kpos += K, blk++) { - storeInt32BE(blk, workingArray, B); - - F.update(salt, 0, salt.length); - - F.reset(); - F.update(salt, 0, salt.length); - F.update(workingArray, B, 4); - F.doFinal(workingArray, U); - System.arraycopy(workingArray, U, workingArray, J, K); - - for (int i = 1, j = J, k = K; i < iterations; i++) { - F.init(macParams); - F.update(workingArray, j, K); - F.doFinal(workingArray, k); - - for (int u = U, v = k; u < B; u++, v++) - workingArray[u] ^= workingArray[v]; - - int swp = k; - k = j; - j = swp; - } - - int tocpy = Math.min(len - kpos, K); - System.arraycopy(workingArray, U, derivedKey, kpos, tocpy); - } - - Arrays.fill(workingArray, (byte) 0); - - return derivedKey; - } - - /** - * Convert a 32-bit integer value into a big-endian byte array - * - * @param value The integer value to convert - * @param bytes The byte array to store the converted value - * @param offSet The offset in the output byte array - */ - public static void storeInt32BE(int value, byte[] bytes, int offSet) { - bytes[offSet + 3] = (byte) (value); - bytes[offSet + 2] = (byte) (value >>> 8); - bytes[offSet + 1] = (byte) (value >>> 16); - bytes[offSet] = (byte) (value >>> 24); - } - -} \ No newline at end of file diff --git a/card-common/src/main/java/com/tangem/cardcommon/util/Util.java b/card-common/src/main/java/com/tangem/cardcommon/util/Util.java deleted file mode 100644 index 341049edc3..0000000000 --- a/card-common/src/main/java/com/tangem/cardcommon/util/Util.java +++ /dev/null @@ -1,957 +0,0 @@ -package com.tangem.cardcommon.util; - -import org.spongycastle.crypto.digests.RIPEMD160Digest; - -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.text.SimpleDateFormat; -import java.util.BitSet; -import java.util.Date; -import java.util.Locale; -import java.util.StringTokenizer; - -public class Util { - - public static String getSpaces(int length) { - StringBuilder buf = new StringBuilder(length); - - for (int i = 0; i < length; i++) { - buf.append(" "); - } - - return buf.toString(); - } - - public static String prettyPrintHex(String in, int indent, boolean wrapLines) { - StringBuilder buf = new StringBuilder(); - - for (int i = 0; i < in.length(); i++) { - char c = in.charAt(i); - buf.append(c); - - int nextPos = i+1; - if (wrapLines && nextPos % 32 == 0 && nextPos != in.length()) { - buf.append("\n").append(getSpaces(indent)); - } else if (nextPos % 2 == 0 && nextPos != in.length()) { - //buf.append(" "); - } - } - return buf.toString(); - } - - public static String prettyPrintHex(String in, int indent){ - return prettyPrintHex(in, indent, true); - } - - public static String prettyPrintHex(byte[] data, int indent) { - return Util.prettyPrintHex(Util.byteArrayToHexString(data), indent, true); - } - - public static String prettyPrintHex(byte[] data) { - return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, true); - } - - public static String prettyPrintHex(byte[] data, int startPos, int length) { - return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, true); - } - - public static String prettyPrintHexNoWrap(byte[] data) { - return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, false); - } - - public static String prettyPrintHexNoWrap(byte[] data, int startPos, int length) { - return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, false); - } - - public static String prettyPrintHexNoWrap(String in) { - return Util.prettyPrintHex(in, 0, false); - } - - public static String prettyPrintHex(String in) { - return prettyPrintHex(in, 0, true); - } - - public static String prettyPrintHex(BigInteger bi) { - byte[] data = bi.toByteArray(); - if (data[0] == (byte) 0x00) { - byte[] tmp = new byte[data.length - 1]; - System.arraycopy(data, 1, tmp, 0, data.length - 1); - data = tmp; - } - return prettyPrintHex(data); - } - - public static byte[] performRSA(byte[] dataBytes, byte[] expBytes, byte[] modBytes) { - - int inBytesLength = dataBytes.length; - - if (expBytes[0] >= (byte) 0x80) { - //Prepend 0x00 to modulus - byte[] tmp = new byte[expBytes.length + 1]; - tmp[0] = (byte) 0x00; - System.arraycopy(expBytes, 0, tmp, 1, expBytes.length); - expBytes = tmp; - } - - if (modBytes[0] >= (byte) 0x80) { - //Prepend 0x00 to modulus - byte[] tmp = new byte[modBytes.length + 1]; - tmp[0] = (byte) 0x00; - System.arraycopy(modBytes, 0, tmp, 1, modBytes.length); - modBytes = tmp; - } - - if (dataBytes[0] >= (byte) 0x80) { - //Prepend 0x00 to signed data to avoid that the most significant bit is interpreted as the "signed" bit - byte[] tmp = new byte[dataBytes.length + 1]; - tmp[0] = (byte) 0x00; - System.arraycopy(dataBytes, 0, tmp, 1, dataBytes.length); - dataBytes = tmp; - } - - BigInteger exp = new BigInteger(expBytes); - BigInteger mod = new BigInteger(modBytes); - BigInteger data = new BigInteger(dataBytes); - - byte[] result = data.modPow(exp, mod).toByteArray(); - - if (result.length == (inBytesLength+1) && result[0] == (byte)0x00) { - //Remove 0x00 from beginning of array - byte[] tmp = new byte[inBytesLength]; - System.arraycopy(result, 1, tmp, 0, inBytesLength); - result = tmp; - } - - return result; - } - - public static byte[] calculateSHA1(byte[] data) throws NoSuchAlgorithmException { - MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); - return sha1.digest(data); - } - - public static byte[] calculateSHA224(byte[] data) throws NoSuchAlgorithmException { - MessageDigest sha = MessageDigest.getInstance("SHA-224"); - return sha.digest(data); - } - - public static byte[] calculateSHA256(byte[] data) throws NoSuchAlgorithmException { - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - return sha256.digest(data); - } - - public static byte[] calculateSHA384(byte[] data) throws NoSuchAlgorithmException { - MessageDigest sha = MessageDigest.getInstance("SHA-384"); - return sha.digest(data); - } - - public static byte[] calculateSHA512(byte[] data) throws NoSuchAlgorithmException { - MessageDigest sha = MessageDigest.getInstance("SHA-512"); - return sha.digest(data); - } - - public static byte[] calculateSHA256(String Message) throws NoSuchAlgorithmException { - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - byte data[]=Message.getBytes(Charset.forName("UTF-8")); - return sha256.digest(data); - } - - public static byte[] calculateRIPEMD160(byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException { - //MessageDigest hashAlg = MessageDigest.getInstance("RIPEMD-160", "SC"); - //return hashAlg.digest(data); - - RIPEMD160Digest digest = new RIPEMD160Digest(); - digest.update(data, 0, data.length); - byte[] out = new byte[20]; - digest.doFinal(out, 0); - return out; - } - - public static String byte2Hex(byte b) { - String[] HEX_DIGITS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; - int nb = b & 0xFF; - int i_1 = (nb >>> 4) & 0xF; - int i_2 = nb & 0xF; - return HEX_DIGITS[i_1] + HEX_DIGITS[i_2]; - } - - public static String short2Hex(short s) { - byte b1 = (byte) (s >>> 8); - byte b2 = (byte) (s & 0xFF); - return byte2Hex(b1) + byte2Hex(b2); - } - - public static int byteToInt(byte b) { - return (int) b & 0xFF; - } - - public static int byteToInt(byte first, byte second) { - int value = (first & 0xFF) << 8; - value += second & 0xFF; - return value; - } - - public static short byte2Short(byte b1, byte b2) { - return (short) ((b1 << 8) | (b2 & 0xFF)); - } - - public static String getFormattedNanoTime(long nano) { - StringBuilder buf = new StringBuilder(); - buf.append((int) (nano / 1000000)); - buf.append("ms "); - buf.append(nano % 1000000); - buf.append("ns"); - return buf.toString(); - } - - -// public static String formatDate(Date date) -// { -// //return DateUtils.formatDateTime(null, date.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR); -//// return DateFormat.getDateInstance(DateFormat.SHORT).format(date); -// return new SimpleDateFormat("yyyy_MM_dd__HH_mm_ss", Locale.US).format(date); -// } -// -// public static String formatDateTime(Date date) -// { -// return formatDate(date)+" "+formatTime(date); -// } -// -// public static String formatTime(Date date) -// { -// return new SimpleDateFormat("HH:mm:ss", Locale.US).format(date); -//// DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date) -//// return DateUtils.formatDateTime(null, date.getTime(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR);//DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date); -// } - - public static byte[] getCurrentDateAsNumericEncodedByteArray(){ - SimpleDateFormat format = new SimpleDateFormat("yyMMdd", Locale.US); - return fromHexString(format.format(new Date())); - } - - //This prints all non-control characters common to all parts of ISO/IEC 8859 - //See EMV book 4 Annex B: Table 36: VolleyHelper Character Set - public static String getSafePrintChars(byte[] byteArray) { - if (byteArray == null) { - return ""; -// throw new IllegalArgumentException("Argument 'byteArray' cannot be null"); - } - return getSafePrintChars(byteArray, 0, byteArray.length); - } - - public static String getSafePrintChars(byte[] byteArray, int startPos, int length) { - if (byteArray == null) { - return ""; -// throw new IllegalArgumentException("Argument 'byteArray' cannot be null"); - } - if(byteArray.length < startPos+length){ - throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")"); - } - StringBuilder buf = new StringBuilder(); - for (int i = startPos; i < startPos+length; i++) { - if (byteArray[i] >= (byte) 0x20 && byteArray[i] < (byte) 0x7F) { - buf.append((char) byteArray[i]); - } else { - buf.append("."); - } - } - return buf.toString(); - } - - public static byte[] hexToBytes(String str) { - byte[] bytes = new byte[str.length() / 2]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) Integer.parseInt(str.substring(2 * i, 2 * i + 2), - 16); - } - return bytes; - } - - final private static char[] hexArray = "0123456789ABCDEF".toCharArray(); - - public static String bytesToHex(byte[] bytes) { - if( bytes==null ) return "[EMPTY]"; - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } - - /** - * Converts a byte array into a hex string. - * @param byteArray the byte array source - * @return a hex string representing the byte array - */ - public static String byteArrayToHexString(final byte[] byteArray) { - if (byteArray == null) { - return ""; - } - return byteArrayToHexString(byteArray, 0, byteArray.length); - } - - public static String byteArrayToHexString(final byte[] byteArray, int startPos, int length) { - if (byteArray == null) { - return ""; - } - if(byteArray.length < startPos+length){ - throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")"); - } -// int readBytes = byteArray.length; - StringBuilder hexData = new StringBuilder(); - int onebyte; - for (int i = 0; i < length; i++) { - onebyte = ((0x000000ff & byteArray[startPos+i]) | 0xffffff00); - hexData.append(Integer.toHexString(onebyte).substring(6)); - } - return hexData.toString(); - } - - public static String int2Hex(int i) { - String hex = Integer.toHexString(i); - if (hex.length() % 2 != 0) { - hex = "0" + hex; - } - return hex; - } - - public static String int2HexZeroPad(int i) { - String hex = int2Hex(i); - if (hex.length() % 2 != 0) { - hex = "0" + hex; - } - return hex; - } - /** - * The length of the returned array depends on the size of the int - * @param value - * @return - */ - public static byte[] intToByteArray(int value) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - byte one = (byte) (value >>> 24); - byte two = (byte) (value >>> 16); - byte three = (byte) (value >>> 8); - byte four = (byte) (value); - - boolean found = false; - - if (one > 0x00) { - baos.write(one); - found = true; - } - if (found || two > 0x00) { - baos.write(two); - found = true; - } - - if (found || three > 0x00) { - baos.write(three); - } - - baos.write(four); - - return baos.toByteArray(); - } - - /** - * Returns a byte array with length = 2 - * @param value - * @return - */ - public static byte[] intToByteArray2(int value) { - return new byte[]{ - (byte) (value >>> 8), - (byte) value}; - } - - /** - * Returns a byte array with length = 4 - * @param value - * @return - */ - public static byte[] intToByteArray4(int value) { - return new byte[]{ - (byte) (value >>> 24), - (byte) (value >>> 16), - (byte) (value >>> 8), - (byte) value}; - } - - public static byte[] longToByteArray8(long value) { - return new byte[]{ - (byte) (value >>> 56), - (byte) (value >>> 48), - (byte) (value >>> 40), - (byte) (value >>> 32), - (byte) (value >>> 24), - (byte) (value >>> 16), - (byte) (value >>> 8), - (byte) value}; - } - - public static int byteArrayToInt(byte[] byteArray) throws IllegalArgumentException { - if( byteArray.length==1 ) return byteArray[0]&0xFF; - java.nio.ByteBuffer BB=java.nio.ByteBuffer.wrap(byteArray); - switch (byteArray.length) - { - case 2: return BB.getShort(); - case 4: return BB.getInt(); - default: throw new IllegalArgumentException("Length must be 1,2 or 4. Length = " + byteArray.length); - } - } - - public static long byteArrayToLong(byte[] byteArray) throws IllegalArgumentException { - if( byteArray.length==1 ) return byteArray[0]&0xFF; - java.nio.ByteBuffer BB=java.nio.ByteBuffer.wrap(byteArray); - switch (byteArray.length) - { - case 2: return BB.getShort(); - case 4: return BB.getInt(); - case 8: return BB.getLong(); - default: throw new IllegalArgumentException("Length must be 1,2,4 or 8. Length = " + byteArray.length); - } - } - - public static byte[] longToByteArray(long value) - { - return new byte[]{ - (byte) (value >>> 56), - (byte) (value >>> 48), - (byte) (value >>> 40), - (byte) (value >>> 32), - (byte) (value >>> 24), - (byte) (value >>> 16), - (byte) (value >>> 8), - (byte) value}; - } - - public static int byteArrayToInt(byte[] byteArray, int startPos, int length) throws IllegalArgumentException { - if (byteArray == null) { - throw new IllegalArgumentException("Parameter 'byteArray' cannot be null"); - } - if (length <= 0 || length > 4) { - throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length); - } - if (length == 4 && Util.isBitSet(byteArray[startPos], 8)){ - throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos])); - } - int value = 0; - for (int i = 0; i < length; i++) { - value += ((byteArray[startPos+i] & 0xFF) << 8 * (length - i - 1)); - } - return value; - } - - public static long byteArrayToLong(byte[] byteArray, int startPos, int length) throws IllegalArgumentException { - if (byteArray == null) { - throw new IllegalArgumentException("Parameter 'byteArray' cannot be null"); - } - if (length <= 0 || length > 8) { - throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length); - } - if (length == 8 && Util.isBitSet(byteArray[startPos], 8)){ - throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos])); - } - long value = 0; - for (int i = 0; i < length; i++) { - value += ((byteArray[startPos+i] & (long)0xFF) << 8 * (length - i - 1)); - } - return value; - } - - public static byte[] fromHexString(String encoded) { - encoded = removeSpaces(encoded); - if (encoded.length() == 0){ - return new byte[0]; - } - if ((encoded.length() % 2) != 0) { - throw new IllegalArgumentException("Input string must contain an even number of characters: "+encoded); - } - final byte result[] = new byte[encoded.length() / 2]; - final char enc[] = encoded.toCharArray(); - for (int i = 0; i < enc.length; i += 2) { - StringBuilder curr = new StringBuilder(2); - curr.append(enc[i]).append(enc[i + 1]); - result[i / 2] = (byte) Integer.parseInt(curr.toString(), 16); - } - return result; - } - - public static String removeCRLFTab(String s) { - StringTokenizer st = new StringTokenizer(s, "\r\n\t", false); - StringBuilder buf = new StringBuilder(); - while (st.hasMoreElements()) { - buf.append(st.nextElement()); - } - return buf.toString(); - } - - public static String removeSpaces(String s) { - return s.replaceAll(" ", ""); - } - - public static String readInputStreamToString(InputStream is, String encoding) throws IOException { - InputStreamReader input = new InputStreamReader(is, encoding); - final int CHARS_PER_PAGE = 5000; //counting spaces - final char[] buffer = new char[CHARS_PER_PAGE]; - StringBuilder output = new StringBuilder(CHARS_PER_PAGE); - for (int read = input.read(buffer, 0, buffer.length); - read != -1; - read = input.read(buffer, 0, buffer.length)) { - output.append(buffer, 0, read); - } - - String text = output.toString(); - return text; - } - - public static void writeStringToFile(String string, String fileName, boolean append) throws IOException { - BufferedWriter out = new BufferedWriter(new FileWriter(fileName, append)); - out.write(string); - out.close(); - } - - /** - * Binary Coded Decimal (BCD) - * @param val - * @return - */ - public static byte[] intToBinaryEncodedDecimalByteArray(int val){ - String str = String.valueOf(val); - if(str.length() % 2 != 0){ - str = "0"+str; - } - return Util.fromHexString(str); - } - - /** - * This method converts the literal hex representation of a byte to an int. - * eg 0x70 = 70 (int) - * @param b - */ - public static int binaryCodedDecimalToInt(byte b) { - String hex = Util.byte2Hex(b); - try { - return Integer.parseInt(hex); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("The hex representation of argument b must be digits", ex); - } - } - - /** - * This method converts the literal hex representation of a decimal - * encoded in 1-5 bytes to an int. - * The value should not be larger than Integer.MAX_VALUE - * - * eg 0x70 = 70 (decimal) - * eg 0x21 47 48 36 47 = 2147483647 (decimal) - * @param hex - */ - public static int binaryHexCodedDecimalToInt(String hex) { - if (hex == null) { - throw new IllegalArgumentException("Param hex cannot be null"); - } - hex = Util.removeSpaces(hex); - if (hex.length() > 10) { - throw new IllegalArgumentException("There must be a maximum of 5 hex octets. hex=" + hex); - } - try { - return Integer.parseInt(hex); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Argument hex must be all digits. hex="+hex, ex); - } - } - - /** - * This method converts a 1-5 byte BCD to an int. - * eg 0x7099 = 7099 (int) - * @param bcdArray - */ - public static int binaryHexCodedDecimalToInt(byte[] bcdArray) { - if (bcdArray == null) { - throw new IllegalArgumentException("Param bcdArray cannot be null"); - } - return binaryHexCodedDecimalToInt(Util.byteArrayToHexString(bcdArray)); - } - - /** - * This returns a String with length = 8 - * @param val - * @return - */ - public static String byte2BinaryLiteral(byte val) { - String s = Integer.toBinaryString(Util.byteToInt(val)); - if (s.length() < 8) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 8 - s.length(); i++) { - sb.append('0'); - } - sb.append(s); - s = sb.toString(); - } - return s; - } - - /** - * Returns a bitset containing the values in bytes. - * The byte-ordering of bytes must be big-endian which means the most significant bit is in element 0. - * - * @param bytes - * @return - */ - public static BitSet byteArray2BitSet(byte[] bytes) { - BitSet bits = new BitSet(); - for (int i = 0; i < bytes.length * 8; i++) { - if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) { - bits.set(i); - } - } - return bits; - } - - /* Returns a byte array of at least length 1. - * The most significant bit in the result is guaranteed not to be a 1 - * (since BitSet does not support sign extension). - * The byte-ordering of the result is big-endian which means the most significant bit is in element 0. - * The bit at index 0 of the bit set is assumed to be the least significant bit. - */ - public static byte[] bitSet2ByteArray(BitSet bits) { - byte[] bytes = new byte[bits.length() / 8 + 1]; - for (int i = 0; i < bits.length(); i++) { - if (bits.get(i)) { - bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8); - } - } - return bytes; - } - - /** - * - * @param val - * @param bitPos The leftmost bit is 8 (the most significant bit) - * @return - */ - public static boolean isBitSet(byte val, int bitPos) { - if (bitPos < 1 || bitPos > 8) { - throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos); - } - if ((val >>> (bitPos - 1) & 0x1) == 1) { - return true; - } - return false; - } - -// /** -// * -// * @param val -// * @return -// */ -// public static int getBitsSetCount(byte val) { -// int numBitsSet = 0; -// for(int i=1; i<=8; i++){ -// if(Util.isBitSet(val, i)){ -// numBitsSet++; -// } -// } -// return numBitsSet; -// } - - /** - * - * @param data - * @param bitPos The leftmost bit is 8 - * @param on - * @return - */ - public static byte setBit(byte data, int bitPos, boolean on) { - if (bitPos < 1 || bitPos > 8) { - throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos); - } - if (on) { - // set bit - return data |= 1 << (bitPos - 1); - } else { - // clear bit - return data &= ~(1 << (bitPos - 1)); - } - } - - public static byte[] generateRandomBytes(int numBytes) { - // TODO: get bytes from a hardware RNG, or set seed - byte[] rndBytes = new byte[numBytes]; - SecureRandom random = new SecureRandom(); - random.nextBytes(rndBytes); - return rndBytes; - } - - public static byte generateRandomByte() { - SecureRandom random = new SecureRandom(); - return (byte)(random.nextInt()&0xFF); - } - - - public static InputStream loadResource(Class cls, String path){ - return cls.getResourceAsStream(path); - } - - /** - * Copies the specified array, prepending 0x00, or cutting off MSBytes if necessary - * @param original - * @param newLength - * @return - */ - public static byte[] resizeArray(byte[] original, int newLength) { - if(original == null){ - throw new IllegalArgumentException("byte array cannot be null"); - } - if(newLength < 0){ - throw new IllegalArgumentException("Illegal new length: "+newLength+". Must be >= 0"); - } - if(newLength == 0){ - return new byte[0]; - } - byte[] tmp = new byte[newLength]; - - int srcPos = tmp.length > original.length ? 0 : original.length - tmp.length; - int destPos = tmp.length > original.length ? tmp.length - original.length : 0; - int length = tmp.length > original.length ? original.length : tmp.length; - - System.arraycopy(original, srcPos, tmp, destPos, length); - - return tmp; - } - - public static byte[] copyByteArray(byte[] array2Copy){ -// byte[] copy = new byte[array2Copy.length]; -// System.arraycopy(array2Copy, 0, copy, 0, array2Copy.length); -// return copy; - if (array2Copy == null) { - //return new byte[0] instead? - throw new IllegalArgumentException("Argument 'array2Copy' cannot be null"); - } - return copyByteArray(array2Copy, 0, array2Copy.length); - } - - public static byte[] copyByteArray(byte[] array2Copy, int startPos, int length){ - if (array2Copy == null) { - //return new byte[0] instead? - throw new IllegalArgumentException("Argument 'array2Copy' cannot be null"); - } - if(array2Copy.length < startPos+length){ - throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+array2Copy.length+")"); - } - byte[] copy = new byte[array2Copy.length]; - System.arraycopy(array2Copy, startPos, copy, 0, length); - return copy; - } - - public static String getStackTrace(Throwable t){ - StringWriter sw = new StringWriter(); - t.printStackTrace(new PrintWriter(sw)); - return sw.toString(); - } - - public static Class getCallerClass(int i) { - Class[] classContext = new SecurityManager() { - @Override public Class[] getClassContext() { - return super.getClassContext(); - } - }.getClassContext(); - if (classContext != null) { - for (int j = 0; j < classContext.length; j++) { - if (classContext[j] == Util.class) { - return classContext[i+j]; - } - } - } else { - // SecurityManager.getClassContext() returns null on Android 4.0 - try { - StackTraceElement[] classNames = Thread.currentThread().getStackTrace(); - for (int j = 0; j < classNames.length; j++) { - if (Class.forName(classNames[j].getClassName()) == Util.class) { - return Class.forName(classNames[i+j].getClassName()); - } - } - } catch (ClassNotFoundException e) { } - } - return null; - } - - public static String decodeOID(byte[] enc){ - StringBuilder sb = new StringBuilder(); - - //First OID Component (standard) - //0: ITU-T - //1: ISO - //2: joint-iso-itu-t - - //Second OID Component (part in a multi part standard) - //0: standard - //1: registration-authority - //2: member-body - //3: identified-organization - - - long firstSubidentifier = 0; - - int i=0; - while(Util.isBitSet(enc[i], 8)){ - firstSubidentifier = (firstSubidentifier << 7) | (enc[i] & 0x7f); - i++; - } - firstSubidentifier = (firstSubidentifier << 7) | (enc[i] & 0x7f); - i++; - - if(firstSubidentifier >= 80){ - long firstOIDComp = 2; - long secondOIDComp = firstSubidentifier - 80; - sb.append(firstOIDComp).append(".").append(secondOIDComp); - }else{ - long secondOIDComp = firstSubidentifier % 40; - long firstOIDComp = (firstSubidentifier - secondOIDComp)/40; - sb.append(firstOIDComp).append(".").append(secondOIDComp); - } - - for(; i> 8) ^ ((chBlock & 0xFF) << 8) & 0xFFFF) ^ (((chBlock & 0xFF) << 3) & 0xFFFF) ^ (((chBlock & 0xFF) >> 4) & 0xFFFF);// (wCRC>>8)^((int)chBlock<<8)^((int) chBlock<<3)^((int)chBlock>>4); - } while (i < bytes.length); - - return new byte[]{(byte) (wCRC & 0xFF), (byte) ((wCRC & 0xFFFF) >> 8)}; - } - - public static String formatDateTimeToFileName(Date date) { - return new SimpleDateFormat("yyyy_MM_dd__HH_mm_ss", Locale.US).format(date); - } -}