Updated on 2026-08-14

This commit is contained in:
Tangem 2019-06-13 17:23:45 +03:00
commit bd8d84a85a
680 changed files with 51751 additions and 6799 deletions

View file

@ -1 +0,0 @@
/build

View file

@ -1,13 +0,0 @@
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.madgag.spongycastle:core:1.56.0.0'
implementation 'com.madgag.spongycastle:prov:1.56.0.0'
implementation 'net.i2p.crypto:eddsa:0.3.0'
}
sourceCompatibility = "7"
targetCompatibility = "7"

View file

@ -1,122 +0,0 @@
package com.tangem.tangemcard.data;
import com.tangem.tangemcard.reader.CardCrypto;
import com.tangem.tangemcard.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<Issuer> instances = new ArrayList<>();
static {
Issuer unknown = new Issuer();
unknown.id = "UNKNOWN";
unknown.officialName = "UNKNOWN";
instances.add(unknown);
}
public static void fillIssuers(List<Issuer> 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);
}
}

View file

@ -1,35 +0,0 @@
package com.tangem.tangemcard.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;
}
}

View file

@ -1,743 +0,0 @@
package com.tangem.tangemcard.data;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.SettingsMask;
import com.tangem.tangemcard.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");
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;
}

View file

@ -1,12 +0,0 @@
package com.tangem.tangemcard.data.external;
import com.tangem.tangemcard.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);
}

View file

@ -1,18 +0,0 @@
package com.tangem.tangemcard.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;
}
}

View file

@ -1,30 +0,0 @@
package com.tangem.tangemcard.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<String> 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);
}

View file

@ -1,289 +0,0 @@
package com.tangem.tangemcard.reader;
import com.tangem.tangemcard.util.Log;
import com.tangem.tangemcard.util.PBKDF2;
import com.tangem.tangemcard.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;
}
}
}

View file

@ -1,276 +0,0 @@
package com.tangem.tangemcard.reader;
import com.tangem.tangemcard.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;
}
}

View file

@ -1,35 +0,0 @@
package com.tangem.tangemcard.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;
}
}

View file

@ -1,20 +0,0 @@
package com.tangem.tangemcard.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();
}

View file

@ -1,137 +0,0 @@
package com.tangem.tangemcard.reader;
import com.tangem.tangemcard.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());
}
}

View file

@ -1,43 +0,0 @@
package com.tangem.tangemcard.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 "???";
}
}

View file

@ -1,67 +0,0 @@
package com.tangem.tangemcard.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();
}
}

View file

@ -1,237 +0,0 @@
package com.tangem.tangemcard.reader;
import com.tangem.tangemcard.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());
}
}
}
}

View file

@ -1,18 +0,0 @@
package com.tangem.tangemcard.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);
}
}

View file

@ -1,72 +0,0 @@
package com.tangem.tangemcard.reader;
/**
* Created by dvol on 23.06.2017.
*/
import com.tangem.tangemcard.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<TLV> {
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<? extends TLV> 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;
}
}

View file

@ -1,35 +0,0 @@
package com.tangem.tangemcard.tasks;
import com.tangem.tangemcard.data.external.CardDataSubstitutionProvider;
import com.tangem.tangemcard.data.external.PINsProvider;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.tangemcard.reader.NfcReader;
import com.tangem.tangemcard.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();
}
}

View file

@ -1,452 +0,0 @@
package com.tangem.tangemcard.tasks;
import com.tangem.tangemcard.data.external.CardDataSubstitutionProvider;
import com.tangem.tangemcard.data.Manufacturer;
import com.tangem.tangemcard.data.external.PINsProvider;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.tangemcard.reader.CardCrypto;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.NfcReader;
import com.tangem.tangemcard.reader.TLV;
import com.tangem.tangemcard.reader.TLVException;
import com.tangem.tangemcard.reader.TLVList;
import com.tangem.tangemcard.util.Log;
import com.tangem.tangemcard.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;
import static com.tangem.tangemcard.reader.CardCrypto.Curve.secp256k1;
/**
* 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<String> 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();
}
}
}

View file

@ -1,27 +0,0 @@
package com.tangem.tangemcard.tasks;
import com.tangem.tangemcard.data.external.PINsProvider;
import com.tangem.tangemcard.reader.NfcReader;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.tangemcard.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);
}
}

View file

@ -1,48 +0,0 @@
package com.tangem.tangemcard.tasks;
import com.tangem.tangemcard.data.external.CardDataSubstitutionProvider;
import com.tangem.tangemcard.data.external.PINsProvider;
import com.tangem.tangemcard.reader.NfcReader;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.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);
}
}

View file

@ -1,91 +0,0 @@
package com.tangem.tangemcard.tasks;
import com.tangem.tangemcard.data.external.CardDataSubstitutionProvider;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.tangemcard.data.external.PINsProvider;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.NfcReader;
import com.tangem.tangemcard.reader.TLV;
import com.tangem.tangemcard.reader.TLVList;
import com.tangem.tangemcard.util.Log;
import java.io.ByteArrayOutputStream;
public class SignTask extends CustomReadCardTask {
public static final String TAG = SignTask.class.getSimpleName();
/**
* Payment Engine request/notifications during sign process
*/
public interface PaymentToSign {
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 PaymentToSign paymentToSign;
public SignTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications, PaymentToSign paymentToSign) {
super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications);
this.paymentToSign = paymentToSign;
}
@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 (!paymentToSign.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(), paymentToSign.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 = paymentToSign.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, paymentToSign.getIssuerTransactionSignature(bs.toByteArray()), null, null);
break;
case Sign_Raw:
signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), paymentToSign.getHashAlgToSign(), paymentToSign.getRawDataToSign(), null, null, null);
break;
case Sign_Raw_Validated_By_Issuer:
case Sign_Raw_Validated_By_Issuer_And_WriteIssuerData:
byte[] txOut = paymentToSign.getRawDataToSign();
signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), paymentToSign.getHashAlgToSign(), txOut, paymentToSign.getIssuerTransactionSignature(txOut), null, null);
break;
default:
throw new CardProtocol.TangemException("Signing method isn't supported!");
}
paymentToSign.onSignCompleted(signResult.getTLV(TLV.Tag.TAG_Signature).Value);
mNotifications.onReadProgress(protocol, 100);
}
}

View file

@ -1,40 +0,0 @@
package com.tangem.tangemcard.tasks;
import com.tangem.tangemcard.data.external.CardDataSubstitutionProvider;
import com.tangem.tangemcard.data.external.PINsProvider;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.tangemcard.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);
}
}

View file

@ -1,51 +0,0 @@
package com.tangem.tangemcard.tasks;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.tangemcard.data.external.CardDataSubstitutionProvider;
import com.tangem.tangemcard.data.external.FirmwaresDigestsProvider;
import com.tangem.tangemcard.data.external.PINsProvider;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.NfcReader;
import com.tangem.tangemcard.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);
}
}

View file

@ -1,13 +0,0 @@
package com.tangem.tangemcard.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) {
}
}

View file

@ -1,103 +0,0 @@
package com.tangem.tangemcard.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);
}
}

View file

@ -1,957 +0,0 @@
package com.tangem.tangemcard.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<enc.length; i++){
sb.append(".");
long subIdentifier = 0;
while(Util.isBitSet(enc[i], 8)){
subIdentifier = (subIdentifier << 7) | (enc[i] & 0x7f);
i++;
}
subIdentifier = (subIdentifier << 7) | (enc[i] & 0x7f);
sb.append(subIdentifier);
}
String oid = sb.toString();
String desc = getOIDDescription(oid);
return oid + ((desc!=null && !desc.isEmpty())?" ("+desc +")":"");
}
//Simple OID registry
//See: http://www.oid-info.com/
public static String getOIDDescription(String oid){
// 1.2.840 - one of 2 US country OIDs
// 1.2.840.114283 - Global Platform
// 1.3.6.1 - the Internet OID
// 1.3.6.1.4.1 - IANA-assigned company OIDs, used for private MIBs and such things
// 1.3.6.1.4.1.42 - Sun Microsystems
// 1.3.6.1.4.1.42.2 - Sun Products
// 1.3.6.1.4.1.42.2.110 - java[XML]software
// 1.3.6.1.4.1.42.2.110.1.2 - (Unknown - Java Card?)
if(oid.startsWith("1.2.840.114283.1")){
return "Global Platform - Card Recognition Data";
}
if(oid.startsWith("1.2.840.114283.2")){
return "Global Platform v"+oid.substring(17);
}
if(oid.startsWith("1.2.840.114283.3")){
return "Global Platform - Card Identification Scheme";
}
if(oid.startsWith("1.2.840.114283.4")){
return "Global Platform SCP "+oid.substring(17, 18) + " implementation option 0x"+Util.int2Hex(Integer.parseInt(oid.substring(19)));
}
if(oid.startsWith("1.2.840.114283")){
return "Global Platform";
}
if(oid.startsWith("1.2.840")){
return "USA";
}
if(oid.startsWith("1.3.6.1.4.1.42.2.110.1.2")){
return "Sun Microsystems - Java Card ?";
}
if(oid.startsWith("1.3.6.1.4.1.42.2")){
return "Sun Microsystems - Products";
}
// if(oid.startsWith("1.3.656.840."))
//JCOP includes GP refinements according to Visa GP 2.1.1 specification.
//This tag is populated accordingly (Visa specific).
//The last number tells you what configuration it is (3: SSD + PKI, 2: PKI, 1: just symmetric crypto).
//Unfortunately this standard is not open.
return "";
}
public static void main(String[] args) {
// System.out.println(Util.isBitSet((byte) 0x5f, 2)); // 0101 1111
// System.out.println(Util.isBitSet((byte) 0x9f, 2)); // 1001 1111
//
// System.out.println(Util.byte2Short((byte) 0x6F, (byte) 0xEF));
// System.out.println(Util.short2Hex(Util.byte2Short((byte) 0x6F, (byte) 0xEF)));
//
// System.out.println(Util.byteArrayToInt(new byte[]{(byte) 0x6F, (byte) 0xEF}));
// System.out.println(Util.byteArrayToHexString(Util.intToByteArray(28655)));
//
// System.out.println(Util.byte2BinaryLiteral((byte) 0x00));
// System.out.println(Util.byte2BinaryLiteral((byte) 0x3F));
// System.out.println(Util.byte2BinaryLiteral((byte) 0x80));
// System.out.println(Util.byte2BinaryLiteral((byte) 0xAA));
// System.out.println(Util.byte2BinaryLiteral((byte) 0xFF));
//
// System.out.println(Util.byte2BinaryLiteral((byte) 0x8A));
// System.out.println(Util.byte2BinaryLiteral(Util.setBit((byte) 0x8A, 5, true)));
// System.out.println(Util.byte2BinaryLiteral(Util.setBit((byte) 0x8A, 8, false)));
//
// System.out.println(Util.byteArrayToLong(Util.fromHexString("7f ff ff ff ff ff ff ff"), 0, 8));
// System.out.println(Util.byteArrayToLong(Util.fromHexString("22 18 09 04 0b 00 e0 30 23 07 00 00 00 42 d2 85 4e 23 07 00 00 00 00 21 69 42"), 13, 4));
System.out.println("1.2.840.114283.1 : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 01")));
System.out.println("1.2.840.114283.2.2.1.1 : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 02 02 01 01")));
System.out.println("1.2.840.114283.4.XXXX : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 04 02 15"))); //JCOP 31
System.out.println("1.2.840.114283.4.XXXX : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 04 01 05"))); //JCOP 31
System.out.println("Sun Microsystems : " + decodeOID(Util.fromHexString("2b 06 01 04 01 2a 02 6e 01 02")));
System.out.println("Unknown : " + decodeOID(Util.fromHexString("2b 85 10 86 48 64 02 01 03")));
System.out.println("{2 100 3} : " + decodeOID(Util.fromHexString("813403")));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 0)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 1)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 2)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01, 0x02}, 1)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01, 0x02}, 4)));
}
public static byte[] calculateCRC16(byte[] bytes) {
byte chBlock;
// STEP 1 Initialize the CRC-16 value
int wCRC = 0x6363; // ITU-V.41
int i = 0;
// STEP 2 Update data and Calucuate their CRC
do {
chBlock = bytes[i++];
chBlock ^= (byte) (wCRC & 0x00FF);
chBlock = (byte) (chBlock ^ (chBlock << 4));
wCRC = ((wCRC >> 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);
}
}