Updated on 2026-08-14
This commit is contained in:
parent
c713f23b66
commit
383a95bfc8
27 changed files with 0 additions and 5089 deletions
|
|
@ -1,122 +0,0 @@
|
|||
package com.tangem.cardcommon.data;
|
||||
|
||||
import com.tangem.cardcommon.reader.CardCrypto;
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by dvol on 14.11.2017.
|
||||
*/
|
||||
|
||||
public class Issuer {
|
||||
|
||||
static class KeyPair {
|
||||
String privateKey;
|
||||
String publicKey;
|
||||
|
||||
byte[] getPrivateKey() throws Exception {
|
||||
if (privateKey != null) {
|
||||
return Util.hexToBytes(privateKey);
|
||||
} else {
|
||||
throw new Exception("No private key!");
|
||||
}
|
||||
}
|
||||
|
||||
byte[] getPublicKey() throws Exception {
|
||||
if (publicKey == null) {
|
||||
if (privateKey != null) {
|
||||
return CardCrypto.GeneratePublicKey(Util.hexToBytes(privateKey));
|
||||
} else {
|
||||
throw new Exception("Invalid key format: no public and no private");
|
||||
}
|
||||
} else {
|
||||
return Util.hexToBytes(publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String id;
|
||||
public String officialName;
|
||||
private KeyPair dataKey;
|
||||
private KeyPair transactionKey;
|
||||
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private static List<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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.cardcommon.data;
|
||||
|
||||
/**
|
||||
* Created by dvol on 09.08.2017.
|
||||
*/
|
||||
|
||||
public enum Manufacturer {
|
||||
|
||||
Unknown("", "Unknown"),
|
||||
SMARTCASH_AG("SMART CASH AG", "SMART CASH AG"),
|
||||
DEVELOPERS_SMARTCASH_AG("DEVELOP CASH AG", "SMART CASH AG (DEVELOPERS)"),
|
||||
SMARTCASH("SMART CASH", "SMART CASH");
|
||||
|
||||
private String ID;
|
||||
private String officialName;
|
||||
|
||||
Manufacturer(String id, String officialName) {
|
||||
this.ID = id;
|
||||
this.officialName = officialName;
|
||||
}
|
||||
|
||||
public String getOfficialName() {
|
||||
return officialName;
|
||||
}
|
||||
|
||||
public static Manufacturer FindManufacturer(String ID) {
|
||||
Manufacturer[] manufacturers = Manufacturer.values();
|
||||
for (int i = 1; i < manufacturers.length; i++) {
|
||||
if (manufacturers[i].ID.equals(ID)) {
|
||||
return manufacturers[i];
|
||||
}
|
||||
}
|
||||
return Manufacturer.Unknown;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,743 +0,0 @@
|
|||
package com.tangem.cardcommon.data;
|
||||
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.reader.SettingsMask;
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Created by dvol on 16.07.2017.
|
||||
*/
|
||||
|
||||
public class TangemCard {
|
||||
|
||||
private byte[] CID;
|
||||
private Status status;
|
||||
private String UID;
|
||||
private String blockchainID;
|
||||
private Manufacturer manufacturer = Manufacturer.Unknown;
|
||||
private boolean manufacturerConfirmed = false;
|
||||
private int maxSignatures;
|
||||
private int remainingSignatures;
|
||||
private String PIN;
|
||||
private byte[] pbWalletKey = null;
|
||||
private byte[] pbCardKey = null;
|
||||
private byte[] pbWalletKeyRar = null;
|
||||
private Date dtPersonalization = null;
|
||||
|
||||
public String getBlockchainID() {
|
||||
return blockchainID;
|
||||
}
|
||||
|
||||
public void setBlockchainID(String blockchainID) {
|
||||
this.blockchainID = blockchainID;
|
||||
}
|
||||
|
||||
public void setWalletPublicKey(byte[] publicKey) {
|
||||
pbWalletKey = publicKey;
|
||||
}
|
||||
|
||||
public void setWalletPublicKeyRar(byte[] publicKey) {
|
||||
pbWalletKeyRar = publicKey;
|
||||
}
|
||||
|
||||
public byte[] getWalletPublicKey() {
|
||||
return pbWalletKey;
|
||||
}
|
||||
|
||||
public byte[] getWalletPublicKeyRar() {
|
||||
return pbWalletKeyRar;
|
||||
}
|
||||
|
||||
private boolean walletPublicKeyValid = false;
|
||||
|
||||
public void setWalletPublicKeyValid(boolean walletPublicKeyValid) {
|
||||
this.walletPublicKeyValid = walletPublicKeyValid;
|
||||
}
|
||||
|
||||
public boolean isWalletPublicKeyValid() {
|
||||
return walletPublicKeyValid;
|
||||
}
|
||||
|
||||
public void setCardPublicKey(byte[] publicKey) {
|
||||
pbCardKey = publicKey;
|
||||
}
|
||||
|
||||
public byte[] getCardPublicKey() {
|
||||
return pbCardKey;
|
||||
}
|
||||
|
||||
private boolean cardPublicKeyValid = false;
|
||||
|
||||
public void setCardPublicKeyValid(boolean cardPublicKeyValid) {
|
||||
this.cardPublicKeyValid = cardPublicKeyValid;
|
||||
}
|
||||
|
||||
public boolean isCardPublicKeyValid() {
|
||||
return cardPublicKeyValid;
|
||||
}
|
||||
|
||||
|
||||
public Manufacturer getManufacturer() {
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
public boolean isManufacturerConfirmed() {
|
||||
return manufacturerConfirmed;
|
||||
}
|
||||
|
||||
public void setManufacturer(Manufacturer manufacturer, boolean verified) {
|
||||
if (this.manufacturer == manufacturer) {
|
||||
this.manufacturerConfirmed |= verified;
|
||||
} else {
|
||||
this.manufacturer = manufacturer;
|
||||
this.manufacturerConfirmed = verified;
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean codeConfirmed;
|
||||
|
||||
public void setCodeConfirmed(Boolean codeConfirmed) {
|
||||
this.codeConfirmed = codeConfirmed;
|
||||
}
|
||||
|
||||
public Boolean isCodeConfirmed() {
|
||||
return codeConfirmed;
|
||||
}
|
||||
|
||||
private Boolean onlineVerified;
|
||||
|
||||
public void setOnlineVerified(Boolean verified) {
|
||||
this.onlineVerified = verified;
|
||||
}
|
||||
|
||||
public Boolean isOnlineVerified() {
|
||||
return onlineVerified;
|
||||
}
|
||||
|
||||
private Boolean onlineValidated;
|
||||
|
||||
public void setOnlineValidated(Boolean validated) {
|
||||
this.onlineValidated = validated;
|
||||
}
|
||||
|
||||
public Boolean isOnlineValidated() {
|
||||
return onlineValidated;
|
||||
}
|
||||
|
||||
public int getRemainingSignatures() {
|
||||
return remainingSignatures;
|
||||
}
|
||||
|
||||
public void setRemainingSignatures(int remainingSignatures) {
|
||||
this.remainingSignatures = remainingSignatures;
|
||||
}
|
||||
|
||||
public String getPIN() {
|
||||
return PIN;
|
||||
}
|
||||
|
||||
public void setPIN(String PIN) {
|
||||
this.PIN = PIN;
|
||||
}
|
||||
|
||||
// public void switchToInitialBlockchain() {
|
||||
// if (tokenSymbol.length() > 1)
|
||||
// blockchainID = Blockchain.Token.getID(); // Reset blockchain to Token from ETH for token cards with zero token balance on it
|
||||
// }
|
||||
|
||||
public Date getPersonalizationDateTime() {
|
||||
return dtPersonalization;
|
||||
}
|
||||
|
||||
public void setPersonalizationDateTime(Date dtPersonalization) {
|
||||
this.dtPersonalization = dtPersonalization;
|
||||
}
|
||||
|
||||
private int health = 0;
|
||||
|
||||
public int getHealth() {
|
||||
return health;
|
||||
}
|
||||
|
||||
public void setHealth(int health) {
|
||||
if (health > this.health) this.health = health;
|
||||
}
|
||||
|
||||
public boolean isHealthOK() {
|
||||
return health == 0;
|
||||
}
|
||||
|
||||
private byte[] issuerPublicDataKey = null;
|
||||
private String issuerID = null;
|
||||
|
||||
|
||||
public byte[] getIssuerPublicDataKey() {
|
||||
return issuerPublicDataKey;
|
||||
}
|
||||
|
||||
private Issuer issuer = Issuer.Unknown();
|
||||
|
||||
public void setIssuer(String issuerID, byte[] issuerPublicDataKey) {
|
||||
this.issuerPublicDataKey = issuerPublicDataKey;
|
||||
this.issuerID = issuerID;
|
||||
this.issuer = Issuer.FindIssuer(issuerID, issuerPublicDataKey);
|
||||
}
|
||||
|
||||
public Issuer getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public String getIssuerDescription() {
|
||||
return issuer.getOfficialName();
|
||||
}
|
||||
|
||||
|
||||
String contractAddress = "";
|
||||
|
||||
public void setContractAddress(String address) {
|
||||
contractAddress = address;
|
||||
}
|
||||
|
||||
public String getContractAddress() {
|
||||
return contractAddress;
|
||||
}
|
||||
|
||||
public String tokenSymbol = "";
|
||||
|
||||
public void setTokenSymbol(String symbol) {
|
||||
tokenSymbol = symbol;
|
||||
}
|
||||
|
||||
public String getTokenSymbol() {
|
||||
return tokenSymbol;
|
||||
}
|
||||
|
||||
public boolean isToken() {
|
||||
return !(tokenSymbol == null || tokenSymbol.isEmpty());
|
||||
}
|
||||
|
||||
int tokensDecimal = 18;
|
||||
|
||||
public void setTokensDecimal(int tokensDecimal) {
|
||||
this.tokensDecimal = tokensDecimal;
|
||||
}
|
||||
|
||||
public int getTokensDecimal() {
|
||||
return tokensDecimal;
|
||||
}
|
||||
|
||||
|
||||
private byte[] issuerData;
|
||||
private byte[] issuerDataSignature;
|
||||
|
||||
public byte[] getIssuerData() {
|
||||
return issuerData;
|
||||
}
|
||||
|
||||
public byte[] getIssuerDataSignature() {
|
||||
return issuerDataSignature;
|
||||
}
|
||||
|
||||
public void setIssuerData(byte[] value, byte[] signature) {
|
||||
issuerData = value;
|
||||
issuerDataSignature = signature;
|
||||
}
|
||||
|
||||
private boolean needWriteIssuerData = false;
|
||||
|
||||
public boolean getNeedWriteIssuerData() {
|
||||
return needWriteIssuerData;
|
||||
}
|
||||
|
||||
public void setNeedWriteIssuerData(boolean value) {
|
||||
needWriteIssuerData = value;
|
||||
}
|
||||
|
||||
public String getIssuerDataDescription() {
|
||||
return "";
|
||||
}
|
||||
|
||||
private int pauseBeforePIN2 = 0;
|
||||
|
||||
public void setPauseBeforePIN2(int value) {
|
||||
this.pauseBeforePIN2 = value;
|
||||
}
|
||||
|
||||
public int getPauseBeforePIN2() {
|
||||
return pauseBeforePIN2;
|
||||
}
|
||||
|
||||
private Integer settingsMask = null;
|
||||
|
||||
public Integer getSettingsMask() {
|
||||
return settingsMask;
|
||||
}
|
||||
|
||||
public void setSettingsMask(int settingsMask) {
|
||||
this.settingsMask = settingsMask;
|
||||
}
|
||||
|
||||
public Boolean isReusable() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.IsReusable) != 0;
|
||||
}
|
||||
|
||||
public Boolean allowSwapPIN() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.AllowSwapPIN) != 0;
|
||||
}
|
||||
|
||||
public Boolean allowSwapPIN2() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.AllowSwapPIN2) != 0;
|
||||
}
|
||||
|
||||
public Boolean needCVC() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.UseCVC) != 0;
|
||||
}
|
||||
|
||||
public Boolean useSmartSecurityDelay() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.SmartSecurityDelay) != 0;
|
||||
}
|
||||
|
||||
public Boolean useDefaultPIN1() {
|
||||
return CardProtocol.isDefaultPIN(getPIN());
|
||||
}
|
||||
|
||||
public enum PIN2_Mode {Unchecked, DefaultPIN2, CustomPIN2}
|
||||
|
||||
public PIN2_Mode PIN2 = PIN2_Mode.Unchecked;
|
||||
|
||||
public Boolean useDefaultPIN2() {
|
||||
if (PIN2 == PIN2_Mode.DefaultPIN2 || (PIN2 == PIN2_Mode.Unchecked && (needCVC() || (getPauseBeforePIN2() > 0)))) {
|
||||
// define that we use default PIN2 if we try it or not try and security delay or CVC is used
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setUseDefaultPIN2(Boolean value) {
|
||||
if (value != null) {
|
||||
PIN2 = value ? PIN2_Mode.DefaultPIN2 : PIN2_Mode.CustomPIN2;
|
||||
} else {
|
||||
PIN2 = PIN2_Mode.Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean supportNDEF() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.UseNDEF) != 0;
|
||||
}
|
||||
|
||||
public Boolean supportOnlyOneCommandAtTime() {
|
||||
if (settingsMask == null) return null;
|
||||
return supportNDEF() && ((settingsMask & SettingsMask.UseOneCommandAtTime) != 0);
|
||||
}
|
||||
|
||||
public Boolean supportDynamicNDEF() {
|
||||
if (settingsMask == null) return null;
|
||||
return supportNDEF() && ((settingsMask & SettingsMask.UseDynamicNDEF) != 0);
|
||||
}
|
||||
|
||||
public Boolean supportBlock() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.UseBlock) != 0;
|
||||
}
|
||||
|
||||
public int getMaxSignatures() {
|
||||
return maxSignatures;
|
||||
}
|
||||
|
||||
public void setMaxSignatures(int value) {
|
||||
maxSignatures = value;
|
||||
}
|
||||
|
||||
private String firmwareVersion;
|
||||
|
||||
public void setFirmwareVersion(String firmwareVersion) {
|
||||
this.firmwareVersion = firmwareVersion;
|
||||
}
|
||||
|
||||
public String getFirmwareVersion() {
|
||||
return firmwareVersion;
|
||||
}
|
||||
|
||||
public Boolean useDevelopersFirmware() {
|
||||
return getFirmwareVersion().endsWith("d") || getFirmwareVersion().endsWith("SDK");
|
||||
}
|
||||
|
||||
private static String getFirmwareVersionNumber(String version) throws Exception {
|
||||
if (version == null || version.length() < 4) {
|
||||
throw new Exception("Firmware version has unsupported format!");
|
||||
}
|
||||
if (version.endsWith("d SDK")) {
|
||||
return version.substring(0, version.length() - 5);
|
||||
} else if (version.endsWith("r")) {
|
||||
return version.substring(0, version.length() - 1);
|
||||
} else {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] getFirmwareVersionNumbers(String version) throws Exception {
|
||||
String fwNumber = getFirmwareVersionNumber(version);
|
||||
String[] strNumbers = fwNumber.split("\\.");
|
||||
|
||||
if (strNumbers.length != 2) throw new Exception("Firmware version has unsupported format!");
|
||||
try {
|
||||
int major = Integer.parseInt(strNumbers[0]), minor = Integer.parseInt(strNumbers[1]);
|
||||
return new int[]{major, minor};
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
throw new Exception("Firmware version has unsupported format!");
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isFirmwareOlder(String version) throws Exception {
|
||||
int[] numbers1 = getFirmwareVersionNumbers(firmwareVersion), numbers2 = getFirmwareVersionNumbers(version);
|
||||
return numbers1[0] < numbers2[0] || (numbers1[0] == numbers2[0] && numbers1[1] < numbers2[1]);
|
||||
}
|
||||
|
||||
public Boolean isFirmwareNewer(String version) throws Exception {
|
||||
int[] numbers1 = getFirmwareVersionNumbers(firmwareVersion), numbers2 = getFirmwareVersionNumbers(version);
|
||||
return numbers1[0] > numbers2[0] || (numbers1[0] == numbers2[0] && numbers1[1] > numbers2[1]);
|
||||
}
|
||||
|
||||
private String batch;
|
||||
|
||||
public void setBatch(String batch) {
|
||||
this.batch = batch;
|
||||
}
|
||||
|
||||
public String getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
public enum SigningMethod {
|
||||
Sign_Hash(0, "sign hash"),
|
||||
Sign_Raw(1, "sign raw tx"),
|
||||
Sign_Hash_Validated_By_Issuer(2, "sign hash validated by issuer"),
|
||||
Sign_Raw_Validated_By_Issuer(3, "sign raw tx validated by issuer"),
|
||||
Sign_Hash_Validated_By_Issuer_And_WriteIssuerData(4, "sign hash validated by issuer and write issuer data"),
|
||||
Sign_Raw_Validated_By_Issuer_And_WriteIssuerData(5, "sign raw tx validated by issuer and write issuer data");
|
||||
|
||||
public int ID;
|
||||
String mDescription;
|
||||
|
||||
SigningMethod(int ID, String description) {
|
||||
this.ID = ID;
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
static SigningMethod FindByID(int ID) {
|
||||
SigningMethod[] methods = values();
|
||||
for (SigningMethod m : methods) {
|
||||
if (m.ID == ID) return m;
|
||||
}
|
||||
return SigningMethod.Sign_Hash;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
}
|
||||
|
||||
private SigningMethod signingMethod;
|
||||
|
||||
public void setSigningMethod(int signingMethodID) {
|
||||
this.signingMethod = SigningMethod.FindByID(signingMethodID);
|
||||
}
|
||||
|
||||
public SigningMethod getSigningMethod() {
|
||||
return signingMethod;
|
||||
}
|
||||
|
||||
public TangemCard(String UID) {
|
||||
this.UID = UID;
|
||||
}
|
||||
|
||||
|
||||
public enum Status {
|
||||
NotPersonalized(0), Empty(1), Loaded(2), Purged(3);
|
||||
|
||||
Status(int Code) {
|
||||
mCode = Code;
|
||||
}
|
||||
|
||||
private int mCode;
|
||||
|
||||
public int getCode() {
|
||||
return mCode;
|
||||
}
|
||||
|
||||
public static Status fromCode(int code) {
|
||||
for (Status s : values()) {
|
||||
if (s.getCode() == code) return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public byte[] getCID() {
|
||||
return CID;
|
||||
}
|
||||
|
||||
public void setCID(byte[] value) {
|
||||
this.CID = value;
|
||||
}
|
||||
|
||||
public String getCIDDescription() {
|
||||
String strCID = Util.bytesToHex(CID);
|
||||
try {
|
||||
return strCID.substring(0, 4) + " " + strCID.substring(4, 8) + " " + strCID.substring(8, 12) + " " + strCID.substring(12, 16);
|
||||
} catch (Exception e) {
|
||||
return strCID;
|
||||
}
|
||||
}
|
||||
|
||||
public String getUID() {
|
||||
return UID;
|
||||
}
|
||||
|
||||
public void setUID(String UID) {
|
||||
this.UID = UID;
|
||||
}
|
||||
|
||||
|
||||
private byte[] offlineBalance;
|
||||
|
||||
public void setOfflineBalance(byte[] offlineBalance) {
|
||||
this.offlineBalance = offlineBalance;
|
||||
}
|
||||
|
||||
public byte[] getOfflineBalance() {
|
||||
return offlineBalance;
|
||||
}
|
||||
|
||||
public void clearOfflineBalance() {
|
||||
offlineBalance = null;
|
||||
}
|
||||
|
||||
private byte[] Denomination;
|
||||
private String DenominationText;
|
||||
|
||||
public void setDenomination(byte[] denomination, String denominationText) {
|
||||
this.Denomination = denomination;
|
||||
this.DenominationText = denominationText;
|
||||
}
|
||||
|
||||
public byte[] getDenomination() {
|
||||
return Denomination;
|
||||
}
|
||||
|
||||
public int SignedHashes = -1; // Will remain -1 if tag was not found on card (= not safe to accept)
|
||||
|
||||
public void setSignedHashes(int SignedHashes) {
|
||||
this.SignedHashes = SignedHashes;
|
||||
}
|
||||
|
||||
public int getSignedHashes() {
|
||||
return SignedHashes;
|
||||
}
|
||||
|
||||
|
||||
public String getDenominationText() {
|
||||
return DenominationText;
|
||||
}
|
||||
|
||||
public void setDenominationText(String denominationText) {
|
||||
DenominationText = denominationText;
|
||||
}
|
||||
|
||||
public void clearDenomination() {
|
||||
Denomination = null;
|
||||
DenominationText = null;
|
||||
}
|
||||
|
||||
// public Bundle getAsBundle() {
|
||||
// Bundle B = new Bundle();
|
||||
// saveToBundle(B);
|
||||
// return B;
|
||||
// }
|
||||
//
|
||||
// public void saveToBundle(Bundle B) {
|
||||
// try {
|
||||
// B.putString("UID", UID);
|
||||
// B.putByteArray("CID", CID);
|
||||
// B.putString("PIN", PIN);
|
||||
// B.putString("PIN2", PIN2.name());
|
||||
// B.putString("Status", status.name());
|
||||
// B.putString("Blockchain", blockchainID);
|
||||
// B.putString("BlockchainName", blockchainName);
|
||||
// B.putInt("TokensDecimal", tokensDecimal);
|
||||
// B.putString("TokenSymbol", tokenSymbol);
|
||||
// B.putString("ContractAddress", contractAddress);
|
||||
// if (dtPersonalization != null) B.putLong("dtPersonalization", dtPersonalization.getTime());
|
||||
// B.putInt("RemainingSignatures", remainingSignatures);
|
||||
// B.putInt("MaxSignatures", maxSignatures);
|
||||
// B.putInt("Health", health);
|
||||
// if (settingsMask != null) B.putInt("settingsMask", settingsMask);
|
||||
// B.putInt("pauseBeforePIN2", pauseBeforePIN2);
|
||||
// if (signingMethod != null) B.putString("signingMethod", signingMethod.name());
|
||||
// if (manufacturer != null) B.putString("Manufacturer", manufacturer.name());
|
||||
// if (encryptionMode != null) B.putString("EncryptionMode", encryptionMode.name());
|
||||
// if (issuer != null) B.putString("Issuer", issuer.getID());
|
||||
// if (issuerPublicDataKey != null) B.putByteArray("IssuerPublicDataKey", issuerPublicDataKey);
|
||||
// if (firmwareVersion != null) B.putString("FirmwareVersion", firmwareVersion);
|
||||
// if (batch != null) B.putString("Batch", batch);
|
||||
// B.putBoolean("ManufacturerConfirmed", manufacturerConfirmed);
|
||||
// B.putBoolean("CardPublicKeyValid", isCardPublicKeyValid());
|
||||
// B.putByteArray("CardPublicKey", getCardPublicKey());
|
||||
//
|
||||
// B.putInt("SignedHashes", getSignedHashes());
|
||||
// B.putBoolean("WalletPublicKeyValid", isWalletPublicKeyValid());
|
||||
// if (pbWalletKey != null)
|
||||
// B.putByteArray("PublicKey", pbWalletKey);
|
||||
// if (pbWalletKeyRar != null)
|
||||
// B.putByteArray("PublicKeyRar", pbWalletKeyRar);
|
||||
//
|
||||
// if (getOfflineBalance() != null) B.putByteArray("OfflineBalance", getOfflineBalance());
|
||||
//
|
||||
// if (getDenomination() != null) B.putByteArray("Denomination", getDenomination());
|
||||
// if (getDenominationText() != null) B.putString("DenominationText", getDenominationText());
|
||||
//
|
||||
// if (getIssuerData() != null && getIssuerDataSignature() != null) {
|
||||
// B.putByteArray("IssuerData", getIssuerData());
|
||||
// B.putByteArray("IssuerDataSignature", getIssuerDataSignature());
|
||||
// B.putBoolean("NeedWriteIssuerData", getNeedWriteIssuerData());
|
||||
// }
|
||||
//
|
||||
// if (codeConfirmed != null)
|
||||
// B.putBoolean("codeConfirmed", codeConfirmed);
|
||||
//
|
||||
// if (codeConfirmed != null)
|
||||
// B.putBoolean("codeConfirmed", codeConfirmed);
|
||||
//
|
||||
// if (onlineVerified != null)
|
||||
// B.putBoolean("onlineVerified", onlineVerified);
|
||||
//
|
||||
// if (onlineValidated != null)
|
||||
// B.putBoolean("onlineValidated", onlineValidated);
|
||||
//
|
||||
// if (codeConfirmed != null)
|
||||
// B.putBoolean("codeConfirmed", codeConfirmed);
|
||||
//
|
||||
// if (codeConfirmed != null)
|
||||
// B.putBoolean("codeConfirmed", codeConfirmed);
|
||||
//
|
||||
// if (onlineVerified != null)
|
||||
// B.putBoolean("onlineVerified", onlineVerified);
|
||||
//
|
||||
// if (onlineValidated != null)
|
||||
// B.putBoolean("onlineValidated", onlineValidated);
|
||||
// } catch (Exception e) {
|
||||
// Log.e("Can't save to bundle ", e.getMessage());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public void loadFromBundle(Bundle B) {
|
||||
// UID = B.getString("UID");
|
||||
// CID = B.getByteArray("CID");
|
||||
// PIN = B.getString("PIN");
|
||||
// PIN2 = PIN2_Mode.valueOf(B.getString("PIN2"));
|
||||
// status = Status.valueOf(B.getString("Status"));
|
||||
// blockchainID = B.getString("Blockchain");
|
||||
// tokensDecimal = B.getInt("TokensDecimal", 18);
|
||||
// tokenSymbol = B.getString("TokenSymbol", "");
|
||||
// contractAddress = B.getString("ContractAddress", "");
|
||||
// if (B.containsKey("BlockchainName"))
|
||||
// blockchainName = B.getString("BlockchainName", "");
|
||||
// if (B.containsKey("dtPersonalization")) {
|
||||
// dtPersonalization = new Date(B.getLong("dtPersonalization"));
|
||||
// }
|
||||
// remainingSignatures = B.getInt("RemainingSignatures");
|
||||
// maxSignatures = B.getInt("MaxSignatures");
|
||||
// health = B.getInt("health");
|
||||
// if (B.containsKey("settingsMask")) settingsMask = B.getInt("settingsMask");
|
||||
// pauseBeforePIN2 = B.getInt("pauseBeforePIN2");
|
||||
// if (B.containsKey("signingMethod"))
|
||||
// signingMethod = SigningMethod.valueOf(B.getString("signingMethod"));
|
||||
// if (B.containsKey("Manufacturer"))
|
||||
// manufacturer = Manufacturer.valueOf(B.getString("Manufacturer"));
|
||||
// manufacturerConfirmed = B.getBoolean("ManufacturerConfirmed");
|
||||
// if (B.containsKey("EncryptionMode"))
|
||||
// encryptionMode = EncryptionMode.valueOf(B.getString("EncryptionMode"));
|
||||
// else
|
||||
// encryptionMode = null;
|
||||
//
|
||||
// if (B.containsKey("SignedHashes")) setSignedHashes(B.getInt("SignedHashes"));
|
||||
//
|
||||
// if (B.containsKey("Issuer")) issuer = Issuer.FindIssuer(B.getString("Issuer"));
|
||||
// if (B.containsKey("IssuerPublicDataKey"))
|
||||
// issuerPublicDataKey = B.getByteArray("IssuerPublicDataKey");
|
||||
//
|
||||
// if (B.containsKey("FirmwareVersion")) firmwareVersion = B.getString("FirmwareVersion");
|
||||
// if (B.containsKey("Batch")) batch = B.getString("Batch");
|
||||
//
|
||||
// cardPublicKeyValid = B.getBoolean("CardPublicKeyValid");
|
||||
// if (B.containsKey("CardPublicKey")) setCardPublicKey(B.getByteArray("CardPublicKey"));
|
||||
//
|
||||
// if (B.containsKey("OfflineBalance")) setOfflineBalance(B.getByteArray("OfflineBalance"));
|
||||
// else clearOfflineBalance();
|
||||
//
|
||||
// if (B.containsKey("Denomination") && B.containsKey("DenominationText")) {
|
||||
// setDenomination(B.getByteArray("Denomination"), B.getString("DenominationText"));
|
||||
// } else if (B.containsKey("Denomination")) {
|
||||
// setDenomination(B.getByteArray("Denomination"), "N/A");
|
||||
// } else clearDenomination();
|
||||
//
|
||||
// if (B.containsKey("IssuerData") && B.containsKey("IssuerDataSignature"))
|
||||
// setIssuerData(B.getByteArray("IssuerData"), B.getByteArray("IssuerDataSignature"));
|
||||
// else setIssuerData(null, null);
|
||||
//
|
||||
// if (B.containsKey("NeedWriteIssuerData"))
|
||||
// setNeedWriteIssuerData(B.getBoolean("NeedWriteIssuerData"));
|
||||
//
|
||||
// walletPublicKeyValid = B.getBoolean("WalletPublicKeyValid");
|
||||
// if (B.containsKey("PublicKey")) {
|
||||
// pbWalletKey = B.getByteArray("PublicKey");
|
||||
// }
|
||||
// if (B.containsKey("PublicKeyRar")) {
|
||||
// pbWalletKeyRar = B.getByteArray("PublicKeyRar");
|
||||
// }
|
||||
//
|
||||
// if (B.containsKey("codeConfirmed"))
|
||||
// codeConfirmed = B.getBoolean("codeConfirmed");
|
||||
//
|
||||
// if (B.containsKey("onlineVerified"))
|
||||
// onlineVerified = B.getBoolean("onlineVerified");
|
||||
//
|
||||
// if (B.containsKey("onlineValidated"))
|
||||
// onlineValidated = B.getBoolean("onlineValidated");
|
||||
// }
|
||||
|
||||
public enum EncryptionMode {
|
||||
None((byte) 0x0), Fast((byte) 0x1), Strong((byte) 0x2);
|
||||
|
||||
private byte P;
|
||||
|
||||
EncryptionMode(byte P) {
|
||||
this.P = P;
|
||||
}
|
||||
|
||||
public int getP() {
|
||||
return P;
|
||||
}
|
||||
}
|
||||
|
||||
public EncryptionMode encryptionMode = EncryptionMode.None;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.cardcommon.data.external;
|
||||
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
|
||||
/**
|
||||
* This interface provide method to make substitution of read card data (token symbol, contract address)
|
||||
* if they was unknown when the card was produced
|
||||
*/
|
||||
public interface CardDataSubstitutionProvider {
|
||||
void applySubstitution(TangemCard card);
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.cardcommon.data.external;
|
||||
|
||||
/**
|
||||
* This interfaces provide function to randomly select parameters to run one VerifyCode command, check answer and
|
||||
* state that card is genuine or not
|
||||
*/
|
||||
public interface FirmwaresDigestsProvider {
|
||||
VerifyCodeRecord selectRandomVerifyCodeBlock(String firmwareVersion);
|
||||
|
||||
class VerifyCodeRecord {
|
||||
public String hashAlg;
|
||||
public int blockIndex;
|
||||
public int blockCount;
|
||||
public byte[] challenge;
|
||||
public byte[] digest;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package com.tangem.cardcommon.data.external;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface of PINsProvider - object that know some list of PINs (used when start first time read), PIN2 (used for protected operation) and store last used PIN
|
||||
* to use it in following operations
|
||||
*/
|
||||
public interface PINsProvider {
|
||||
|
||||
/**
|
||||
* @return list of known PINs
|
||||
* This PINs used when start reading of card
|
||||
* When start reading a PINs from this list used sequential in search PIN algorithm until right PIN found
|
||||
*/
|
||||
List<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);
|
||||
|
||||
}
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
import com.tangem.cardcommon.util.Log;
|
||||
import com.tangem.cardcommon.util.PBKDF2;
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
import org.spongycastle.asn1.ASN1EncodableVector;
|
||||
import org.spongycastle.asn1.ASN1Integer;
|
||||
import org.spongycastle.asn1.DERSequence;
|
||||
import org.spongycastle.jce.ECNamedCurveTable;
|
||||
import org.spongycastle.jce.spec.ECNamedCurveParameterSpec;
|
||||
import org.spongycastle.jce.spec.ECPrivateKeySpec;
|
||||
import org.spongycastle.jce.spec.ECPublicKeySpec;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* Created by dvol on 14.11.2017.
|
||||
*/
|
||||
|
||||
public class CardCrypto {
|
||||
static {
|
||||
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
|
||||
Security.addProvider(new EdDSASecurityProvider());
|
||||
}
|
||||
|
||||
public enum Curve {secp256k1, ed25519}
|
||||
|
||||
public static PublicKey LoadPublicKey(Curve curve, byte[] publicKeyArray) throws Exception {
|
||||
if (publicKeyArray == null) throw new Exception("Public key not specified!");
|
||||
switch (curve) {
|
||||
case secp256k1: {
|
||||
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
KeyFactory factory = KeyFactory.getInstance("EC", "SC");
|
||||
|
||||
ECPoint p1 = spec.getCurve().decodePoint(publicKeyArray);
|
||||
ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec);
|
||||
|
||||
return factory.generatePublic(keySpec);
|
||||
}
|
||||
case ed25519: {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
|
||||
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(publicKeyArray, spec);
|
||||
return new EdDSAPublicKey(pubKey);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Exception(curve.toString() + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public static PublicKey LoadPublicKey(byte[] publicKeyArray) throws Exception {
|
||||
return LoadPublicKey(Curve.secp256k1, publicKeyArray);
|
||||
}
|
||||
|
||||
public static boolean VerifySignature(Curve curve, byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception {
|
||||
switch (curve) {
|
||||
case secp256k1: {
|
||||
Signature signatureInstance = Signature.getInstance("SHA256withECDSA");
|
||||
PublicKey publicKey = LoadPublicKey(publicKeyArray);
|
||||
signatureInstance.initVerify(publicKey);
|
||||
signatureInstance.update(data);
|
||||
|
||||
ASN1EncodableVector v = new ASN1EncodableVector();
|
||||
int size = signature.length / 2;
|
||||
v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, 0, size))));
|
||||
v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, size, size * 2))));
|
||||
byte[] sigDer = new DERSequence(v).getEncoded();
|
||||
|
||||
return signatureInstance.verify(sigDer);
|
||||
}
|
||||
case ed25519: {
|
||||
data = Util.calculateSHA512(data);
|
||||
PublicKey publicKey = LoadPublicKey(curve, publicKeyArray);
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
|
||||
Signature signatureInstance = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
signatureInstance.initVerify(publicKey);
|
||||
|
||||
signatureInstance.update(data);
|
||||
|
||||
return signatureInstance.verify(signature);
|
||||
}
|
||||
default:
|
||||
throw new Exception(curve.toString() + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean VerifySignature(byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception {
|
||||
return VerifySignature(Curve.secp256k1, publicKeyArray, data, signature);
|
||||
}
|
||||
|
||||
public static boolean VerifySignature(String curveID, byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception {
|
||||
Curve curve;
|
||||
try {
|
||||
curve = Curve.valueOf(curveID);
|
||||
} catch (Exception e) {
|
||||
throw new Exception("Card EC curve (" + curveID + ") isn't supported!");
|
||||
}
|
||||
return VerifySignature(curve, publicKeyArray, data, signature);
|
||||
}
|
||||
|
||||
|
||||
public static byte[] Signature(Curve curve, byte[] privateKeyArray, byte[] data) throws Exception {
|
||||
switch (curve) {
|
||||
case secp256k1: {
|
||||
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
KeyFactory factory = KeyFactory.getInstance("EC", "SC");
|
||||
|
||||
ECPrivateKeySpec keySpecP = new ECPrivateKeySpec(new BigInteger(1, privateKeyArray), spec);
|
||||
|
||||
Signature signature = Signature.getInstance("SHA256withECDSA");
|
||||
|
||||
PrivateKey privateKey = factory.generatePrivate(keySpecP);
|
||||
signature.initSign(privateKey);
|
||||
signature.update(data);
|
||||
byte[] enc = signature.sign();
|
||||
|
||||
if (enc[0] != 0x30) throw new Exception("bad encoding 1");
|
||||
if ((enc[1] & 0x80) != 0) throw new Exception("unsupported length encoding 1");
|
||||
if (enc[2] != 0x02) throw new Exception("bad encoding 2");
|
||||
if ((enc[3] & 0x80) != 0) throw new Exception("unsupported length encoding 2");
|
||||
int rLength = enc[3];
|
||||
|
||||
if (enc[4 + rLength] != 0x02) throw new Exception("bad encoding 3");
|
||||
if ((enc[5 + rLength] & 0x80) != 0) throw new Exception("unsupported length encoding 3");
|
||||
int sLength = enc[5 + rLength];
|
||||
|
||||
|
||||
int sPos = 6 + rLength;
|
||||
byte[] res = new byte[64];
|
||||
if (rLength <= 32) {
|
||||
System.arraycopy(enc, 4, res, 32 - rLength, rLength);
|
||||
rLength = 32;
|
||||
} else if (rLength == 33 && enc[4] == 0) {
|
||||
rLength--;
|
||||
System.arraycopy(enc, 5, res, 0, rLength);
|
||||
} else {
|
||||
Log.e("cardCrypto", "r-length:" + String.valueOf(rLength));
|
||||
Log.e("cardCrypto", "s-length:" + String.valueOf(sLength));
|
||||
Log.e("cardCrypto", "enc:" + Util.bytesToHex(enc));
|
||||
throw new Exception("unsupported r-length - r-length:" + String.valueOf(rLength) + ",s-length:" + String.valueOf(sLength) + ",enc:" + Util.bytesToHex(enc));
|
||||
}
|
||||
if (sLength <= 32) {
|
||||
System.arraycopy(enc, sPos, res, rLength + 32 - sLength, sLength);
|
||||
sLength = 32;
|
||||
} else if (sLength == 33 && enc[sPos] == 0) {
|
||||
System.arraycopy(enc, sPos + 1, res, rLength, sLength - 1);
|
||||
} else {
|
||||
Log.e("cardCrypto", "s-length:" + String.valueOf(sLength));
|
||||
Log.e("cardCrypto", "r-length:" + String.valueOf(rLength));
|
||||
Log.e("cardCrypto", "enc:" + Util.bytesToHex(enc));
|
||||
throw new Exception("unsupported s-length - r-length:" + String.valueOf(rLength) + ",s-length:" + String.valueOf(sLength) + ",enc:" + Util.bytesToHex(enc));
|
||||
}
|
||||
|
||||
if (!VerifySignature(GeneratePublicKey(privateKeyArray), data, res)) {
|
||||
throw new Exception("Signature self verify failed - r-length:" + String.valueOf(rLength) + ",s-length:" + String.valueOf(sLength) + ",enc:" + Util.bytesToHex(enc) + ",res:" + Util.bytesToHex(res));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
case ed25519: {
|
||||
data = Util.calculateSHA512(data);
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
|
||||
//Signature sgr = Signature.getInstance("EdDSA", "I2P");
|
||||
Signature signatureInstance = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
|
||||
EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(privateKeyArray, spec);
|
||||
PrivateKey privateKey = new EdDSAPrivateKey(privateKeySpec);
|
||||
|
||||
signatureInstance.initSign(privateKey);
|
||||
signatureInstance.update(data);
|
||||
|
||||
return signatureInstance.sign();
|
||||
}
|
||||
default:
|
||||
throw new Exception(curve.toString() + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Signature(byte[] privateKeyArray, byte[] data) throws Exception {
|
||||
return Signature(Curve.secp256k1, privateKeyArray, data);
|
||||
}
|
||||
|
||||
public static byte[] GeneratePublicKey(Curve curve, byte[] privateKeyArray) throws Exception {
|
||||
switch (curve) {
|
||||
case secp256k1: {
|
||||
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
|
||||
|
||||
byte[] publicKeyArray = spec.getG().multiply(new BigInteger(1, privateKeyArray)).getEncoded(false);
|
||||
|
||||
return publicKeyArray;
|
||||
}
|
||||
case ed25519: {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
|
||||
|
||||
EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(privateKeyArray, spec);
|
||||
EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(privateKeySpec.getA(), spec);
|
||||
EdDSAPublicKey publicKey = new EdDSAPublicKey(publicKeySpec);
|
||||
return publicKey.getAbyte();
|
||||
}
|
||||
default:
|
||||
throw new Exception(curve.toString() + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] GeneratePublicKey(byte[] privateKeyArray) throws Exception {
|
||||
return GeneratePublicKey(Curve.secp256k1, privateKeyArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the PBKDF2 hash of a password.
|
||||
*
|
||||
* @param password the password to hash.
|
||||
* @param salt the salt
|
||||
* @param iterations the iteration count (slowness factor)
|
||||
* @return the PBDKF2 hash of the password
|
||||
*/
|
||||
public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations)
|
||||
throws InvalidKeyException {
|
||||
return PBKDF2.deriveKey(password, salt, iterations);
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(byte[] key, byte[] data, boolean UsePKCS7) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
|
||||
if (UsePKCS7) {
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING", "SC");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
|
||||
byte[] mEncryptedData = cipher.doFinal(data);
|
||||
return mEncryptedData;
|
||||
} else {
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING", "SC");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
|
||||
byte[] mEncryptedData = cipher.doFinal(data);
|
||||
return mEncryptedData;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
|
||||
return Encrypt(key, data, true);
|
||||
}
|
||||
|
||||
public static byte[] Decrypt(byte[] key, byte[] data, boolean UsePKCS7)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
|
||||
if (UsePKCS7) {
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
|
||||
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
|
||||
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
|
||||
return decryptedData;
|
||||
} else {
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
|
||||
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
|
||||
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
|
||||
return decryptedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,276 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
public class CommandApdu {
|
||||
|
||||
public static final byte ISO_CLA = (byte) 0x00;
|
||||
|
||||
protected String mCmdName;
|
||||
protected int mCla = 0x00;
|
||||
protected int mIns = 0x00;
|
||||
protected int mP1 = 0x00;
|
||||
protected int mP2 = 0x00;
|
||||
protected int mLc = 0x00;
|
||||
|
||||
protected byte[] mData = new byte[0];
|
||||
|
||||
protected int mLe = 0x00;
|
||||
protected boolean mLeUsed = false;
|
||||
protected TLVList tlvList = new TLVList();
|
||||
|
||||
public CommandApdu() {
|
||||
}
|
||||
|
||||
public CommandApdu(int cla, int ins, int p1, int p2) {
|
||||
setCommandName(ins);
|
||||
mCla = cla;
|
||||
mIns = ins;
|
||||
mP1 = p1;
|
||||
mP2 = p2;
|
||||
}
|
||||
|
||||
public CommandApdu(int cla, int ins, int p1, int p2, byte[] data) {
|
||||
setCommandName(ins);
|
||||
mCla = cla;
|
||||
mIns = ins;
|
||||
mLc = data.length;
|
||||
mP1 = p1;
|
||||
mP2 = p2;
|
||||
mData = data;
|
||||
}
|
||||
|
||||
public CommandApdu(INS ins) {
|
||||
setCommandName(ins.name());
|
||||
mCla = ISO_CLA;
|
||||
mIns = ins.Code;
|
||||
mP1 = 0;
|
||||
mP2 = 0;
|
||||
}
|
||||
|
||||
public CommandApdu(int cla, int ins, int p1, int p2, byte[] data, int le) {
|
||||
setCommandName(ins);
|
||||
mCla = cla;
|
||||
mIns = ins;
|
||||
mLc = data.length;
|
||||
mP1 = p1;
|
||||
mP2 = p2;
|
||||
mData = data;
|
||||
mLe = le;
|
||||
mLeUsed = true;
|
||||
}
|
||||
|
||||
public CommandApdu(int cla, int ins, int p1, int p2, int le) {
|
||||
setCommandName(ins);
|
||||
mCla = cla;
|
||||
mIns = ins;
|
||||
mP1 = p1;
|
||||
mP2 = p2;
|
||||
mLe = le;
|
||||
mLeUsed = true;
|
||||
}
|
||||
|
||||
public void setCommandName(String cmdName) {
|
||||
mCmdName = cmdName;
|
||||
}
|
||||
|
||||
private void setCommandName(int ins) {
|
||||
INS ins1 = INS.ByCode(ins);
|
||||
if (ins1 != null) {
|
||||
mCmdName = ins1.toString();
|
||||
} else {
|
||||
mCmdName = String.format("INS[%2X]", ins);
|
||||
}
|
||||
}
|
||||
|
||||
public String getCommandName() {
|
||||
return mCmdName;
|
||||
}
|
||||
|
||||
public void setP1(int p1) {
|
||||
mP1 = p1;
|
||||
}
|
||||
|
||||
public void setP2(int p2) {
|
||||
mP2 = p2;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
mLc = data.length;
|
||||
mData = data;
|
||||
}
|
||||
|
||||
public void addTLV(TLV.Tag tag, byte[] value) {
|
||||
tlvList.add(new TLV(tag, value));
|
||||
}
|
||||
|
||||
public void addTLV_U8(TLV.Tag tag, int U8) {
|
||||
addTLV(tag, new byte[]{(byte) U8});
|
||||
}
|
||||
|
||||
public void addTLV_U16(TLV.Tag tag, int U16) {
|
||||
addTLV(tag, Util.intToByteArray2(U16));
|
||||
}
|
||||
|
||||
public void addTLV_U32(TLV.Tag tag, int U32) {
|
||||
addTLV(tag, Util.intToByteArray4(U32));
|
||||
}
|
||||
|
||||
public void setLe(int le) {
|
||||
mLe = le;
|
||||
mLeUsed = true;
|
||||
}
|
||||
|
||||
public int getP1() {
|
||||
return mP1;
|
||||
}
|
||||
|
||||
public int getP2() {
|
||||
return mP2;
|
||||
}
|
||||
|
||||
public int getLc() {
|
||||
return mLc;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
public TLVList getTLVs() {
|
||||
return tlvList;
|
||||
}
|
||||
|
||||
public int getLe() {
|
||||
return mLe;
|
||||
}
|
||||
|
||||
public static String toString(byte[] cmdApdu, int Lc) {
|
||||
String cmd = Util.bytesToHex(cmdApdu);
|
||||
if (Lc == 0) return cmd;
|
||||
return cmd.substring(0, 8) + " " + cmd.substring(8, 10) + " " +
|
||||
cmd.substring(10, 10 + Lc * 2) + " " + cmd.substring(10 + Lc * 2, cmd.length());
|
||||
}
|
||||
|
||||
|
||||
public void Crypt(byte[] key) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException, InvalidAlgorithmParameterException, NoSuchProviderException {
|
||||
if (tlvList.size() != 0) {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
for (TLV tlv : tlvList) {
|
||||
try {
|
||||
tlv.WriteToStream(stream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mData = stream.toByteArray();
|
||||
byte[] crc = Util.calculateCRC16(mData);
|
||||
stream = new ByteArrayOutputStream();
|
||||
stream.write(Util.intToByteArray2(mData.length));
|
||||
stream.write(crc);
|
||||
stream.write(mData);
|
||||
mData = stream.toByteArray();
|
||||
|
||||
byte[] mEncryptedData = CardCrypto.Encrypt(key, mData);
|
||||
|
||||
mData = mEncryptedData;
|
||||
mLc = mData.length;
|
||||
|
||||
tlvList.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
int length = 4; // CLA, INS, P1, P2
|
||||
|
||||
if (tlvList.size() != 0) {
|
||||
mData = tlvList.toBytes();
|
||||
mLc = mData.length;
|
||||
}
|
||||
|
||||
if (mData.length != 0) {
|
||||
length += 1; // LC
|
||||
if (mLc >= 256)
|
||||
length += 2;
|
||||
length += mData.length; // DATA
|
||||
}
|
||||
if (mLeUsed) {
|
||||
length += 1; // LE
|
||||
if (mLc >= 256)
|
||||
length += 2;
|
||||
}
|
||||
|
||||
byte[] apdu = new byte[length];
|
||||
|
||||
int index = 0;
|
||||
apdu[index] = (byte) mCla;
|
||||
index++;
|
||||
apdu[index] = (byte) mIns;
|
||||
index++;
|
||||
apdu[index] = (byte) mP1;
|
||||
index++;
|
||||
apdu[index] = (byte) mP2;
|
||||
index++;
|
||||
if (mLc != 0) {
|
||||
if (mLc < 256) {
|
||||
apdu[index] = (byte) mLc;
|
||||
index++;
|
||||
} else {
|
||||
apdu[index] = 0;
|
||||
index++;
|
||||
apdu[index] = (byte) (mLc >> 8);
|
||||
index++;
|
||||
apdu[index] = (byte) (mLc & 0xFF);
|
||||
index++;
|
||||
}
|
||||
|
||||
System.arraycopy(mData, 0, apdu, index, mData.length);
|
||||
index += mData.length;
|
||||
}
|
||||
if (mLeUsed) {
|
||||
if (mLc < 256) {
|
||||
apdu[index] += (byte) mLe; // LE
|
||||
} else {
|
||||
apdu[index] = 0;
|
||||
index++;
|
||||
apdu[index] = (byte) (mLe >> 8);
|
||||
index++;
|
||||
apdu[index] = (byte) (mLe & 0xFF);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu;
|
||||
}
|
||||
|
||||
public CommandApdu clone() {
|
||||
CommandApdu apdu = new CommandApdu();
|
||||
apdu.setCommandName(mCmdName);
|
||||
apdu.mCla = mCla;
|
||||
apdu.mIns = mIns;
|
||||
apdu.mP1 = mP1;
|
||||
apdu.mP2 = mP2;
|
||||
apdu.mLc = mLc;
|
||||
apdu.mData = new byte[mData.length];
|
||||
System.arraycopy(mData, 0, apdu.mData, 0, mData.length);
|
||||
apdu.mLe = mLe;
|
||||
apdu.mLeUsed = mLeUsed;
|
||||
apdu.tlvList = new TLVList(tlvList);
|
||||
return apdu;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
/**
|
||||
* Created by dvol on 07.03.2018.
|
||||
*/
|
||||
public enum INS {
|
||||
Unknown(0x00),
|
||||
Read(0xF2),
|
||||
VerifyCard(0xF3),
|
||||
ValidateCard(0xF4),
|
||||
VerifyCode(0xF5),
|
||||
WriteIssuerData(0xF6),
|
||||
GetIssuerData(0xF7),
|
||||
CreateWallet(0xF8),
|
||||
CheckWallet(0xF9),
|
||||
SwapPIN(0xFA),
|
||||
Sign(0xFB),
|
||||
PurgeWallet(0xFC),
|
||||
Activate(0xFE),
|
||||
OpenSession(0xFF);
|
||||
|
||||
INS(int Code) {
|
||||
this.Code = Code;
|
||||
}
|
||||
|
||||
public int Code;
|
||||
|
||||
public static INS ByCode(int Code) {
|
||||
INS[] allINS = INS.values();
|
||||
for (INS i : allINS) {
|
||||
if (i.Code == Code) return i;
|
||||
}
|
||||
return Unknown;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface NfcReader {
|
||||
byte[] getId();
|
||||
|
||||
void setTimeout(int timeout)throws IOException;
|
||||
|
||||
int getTimeout();
|
||||
|
||||
byte[] transceive(byte[] data) throws IOException;
|
||||
|
||||
void ignoreTag() throws IOException;
|
||||
|
||||
void notifyReadResult(boolean success);
|
||||
|
||||
void connect();
|
||||
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ResponseApdu {
|
||||
|
||||
private int mSw1 = 0x00;
|
||||
private int mSw2 = 0x00;
|
||||
|
||||
private byte[] mData = new byte[0];
|
||||
private byte[] mBytes = new byte[0];
|
||||
|
||||
private TLVList tlvList = new TLVList();
|
||||
|
||||
private String parseError = null;
|
||||
|
||||
private ResponseApdu() {
|
||||
}
|
||||
|
||||
ResponseApdu(byte[] respApdu) {
|
||||
if (respApdu.length < 2) {
|
||||
return;
|
||||
}
|
||||
if (respApdu.length > 2) {
|
||||
mData = new byte[respApdu.length - 2];
|
||||
System.arraycopy(respApdu, 0, mData, 0, respApdu.length - 2);
|
||||
|
||||
try {
|
||||
tlvList = TLVList.fromBytes(mData);
|
||||
} catch (TLVException e) {
|
||||
parseError = e.getMessage();
|
||||
}
|
||||
|
||||
}else{
|
||||
tlvList=new TLVList();
|
||||
parseError=null;
|
||||
}
|
||||
mSw1 = 0x00FF & respApdu[respApdu.length - 2];
|
||||
mSw2 = 0x00FF & respApdu[respApdu.length - 1];
|
||||
mBytes = respApdu;
|
||||
}
|
||||
|
||||
public static boolean isStatusWord(byte[] respApdu, int SW)
|
||||
{
|
||||
int mSw1 = 0x00FF & respApdu[respApdu.length - 2];
|
||||
int mSw2 = 0x00FF & respApdu[respApdu.length - 1];
|
||||
return ((mSw1 << 8) | mSw2)==SW;
|
||||
}
|
||||
|
||||
public static ResponseApdu Decrypt(byte[] data, byte[] key) throws Exception{
|
||||
|
||||
if( data.length==2 )
|
||||
{
|
||||
ResponseApdu responseApdu = new ResponseApdu();
|
||||
responseApdu.mSw1 = ((int) data[0] & 0xFF);
|
||||
responseApdu.mSw2 = ((int) data[1] & 0xFF);
|
||||
return responseApdu;
|
||||
}else if( data.length>=18 ){
|
||||
byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2),true);
|
||||
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(decryptedData);
|
||||
byte[] baLength = new byte[2];
|
||||
inputStream.read(baLength);
|
||||
int length = ((int) baLength[0] & 0xFF) * 256 + ((int) baLength[1] & 0xFF);
|
||||
if (length > decryptedData.length - 4)
|
||||
throw new Exception("Can't decrypt - data size invalid");
|
||||
byte[] baCRC = new byte[2];
|
||||
inputStream.read(baCRC);
|
||||
byte[] answerData = new byte[length];
|
||||
inputStream.read(answerData);
|
||||
byte[] crc = Util.calculateCRC16(answerData);
|
||||
if (!Arrays.equals(baCRC, crc)) throw new Exception("Can't decrypt - crc invalid");
|
||||
|
||||
ResponseApdu responseApdu = new ResponseApdu();
|
||||
responseApdu.mSw1 = ((int) data[data.length - 2] & 0xFF);
|
||||
responseApdu.mSw2 = ((int) data[data.length - 1] & 0xFF);
|
||||
responseApdu.mBytes = data;
|
||||
responseApdu.mData = answerData;
|
||||
|
||||
try {
|
||||
responseApdu.tlvList = TLVList.fromBytes(answerData);
|
||||
} catch (TLVException e) {
|
||||
responseApdu.parseError = e.getMessage();
|
||||
}
|
||||
|
||||
return responseApdu;
|
||||
}else{
|
||||
throw new Exception("Can't decrypt - data size to small");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getSW1() {
|
||||
return mSw1;
|
||||
}
|
||||
|
||||
public int getSW2() {
|
||||
return mSw2;
|
||||
}
|
||||
|
||||
public int getSW1SW2() {
|
||||
return (mSw1 << 8) | mSw2;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
public TLVList getTLVs() {
|
||||
return tlvList;
|
||||
}
|
||||
|
||||
public boolean isParsedWithError() {
|
||||
return parseError != null;
|
||||
}
|
||||
public String getParseErroMessage() {
|
||||
return parseError;
|
||||
}
|
||||
public byte[] toBytes() {
|
||||
return mBytes;
|
||||
}
|
||||
|
||||
public boolean isStatus(int sw1sw2) {
|
||||
if (getSW1SW2() == sw1sw2) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getSW1SW2Description() {
|
||||
return SW.getDescription(getSW1SW2());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
/**
|
||||
* Created by dvol on 07.03.2018.
|
||||
*/
|
||||
|
||||
public class SW {
|
||||
public static final int PROCESS_COMPLETED = 0x9000;
|
||||
public static final int INVALID_PARAMS = 0x6A86;
|
||||
public static final int ERROR_PROCESSING_COMMAND = 0x6286;
|
||||
public static final int INVALID_STATE = 0x6985;
|
||||
public static final int PINS_NOT_CHANGED = PROCESS_COMPLETED;
|
||||
public static final int PIN1_CHANGED = PROCESS_COMPLETED + 0x0001;
|
||||
public static final int PIN2_CHANGED = PROCESS_COMPLETED + 0x0002;
|
||||
public static final int PINS_CHANGED = PROCESS_COMPLETED + 0x0003;
|
||||
public static final int INS_NOT_SUPPORTED = 0x6D00;
|
||||
public static final int NEED_ENCRYPTION = 0x6982;
|
||||
public static final int NEED_PAUSE = 0x9789;
|
||||
|
||||
public static String getDescription(int sw) {
|
||||
switch (sw) {
|
||||
case ERROR_PROCESSING_COMMAND:
|
||||
return "SW_ERROR_PROCESSING_COMMAND";
|
||||
case INVALID_PARAMS:
|
||||
return "SW_INVALID_PARAMS";
|
||||
case INVALID_STATE:
|
||||
return "SW_INVALID_STATE";
|
||||
case INS_NOT_SUPPORTED:
|
||||
return "SW_INS_NOT_SUPPORTED";
|
||||
case NEED_ENCRYPTION:
|
||||
return "SW_NEED_ENCRYPTION";
|
||||
case PIN1_CHANGED:
|
||||
return "SW_PIN1_CHANGED";
|
||||
case PIN2_CHANGED:
|
||||
return "SW_PIN2_CHANGED";
|
||||
case PINS_CHANGED:
|
||||
return "SW_PINS_CHANGED";
|
||||
case PROCESS_COMPLETED:
|
||||
return "SW_PROCESS_COMPLETED";
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
/**
|
||||
* Created by dvol on 07.03.2018.
|
||||
*/
|
||||
|
||||
public class SettingsMask {
|
||||
public static final int IsReusable = 0x0001;
|
||||
public static final int UseActivation = 0x0002;
|
||||
public static final int ForbidPurgeWallet = 0x0004;
|
||||
public static final int UseBlock = 0x0008;
|
||||
|
||||
public static final int AllowSwapPIN = 0x0010;
|
||||
public static final int AllowSwapPIN2 = 0x0020;
|
||||
public static final int UseCVC = 0x0040;
|
||||
public static final int ForbidDefaultPIN = 0x0080;
|
||||
|
||||
public static final int UseOneCommandAtTime = 0x0100;
|
||||
public static final int UseNDEF = 0x0200;
|
||||
public static final int UseDynamicNDEF = 0x0400;
|
||||
public static final int SmartSecurityDelay = 0x0800;
|
||||
|
||||
public static final int Protocol_AllowUnencrypted = 0x1000;
|
||||
public static final int Protocol_AllowStaticEncryption = 0x2000;
|
||||
|
||||
public static final int ProtectIssuerDataAgainstReplay = 0x4000;
|
||||
|
||||
public static final int AllowSelectBlockchain = 0x8000;
|
||||
|
||||
public static final int DisablePrecomputedNDEF = 0x00010000;
|
||||
|
||||
public static String getDescription(int iValue) {
|
||||
StringBuilder sb=new StringBuilder();
|
||||
sb.append("[");
|
||||
if ((iValue & SettingsMask.AllowSwapPIN) != 0) sb.append("AllowSwapPIN, ");
|
||||
if ((iValue & SettingsMask.AllowSwapPIN2) != 0)
|
||||
sb.append("AllowSwapPIN2, ");
|
||||
if ((iValue & SettingsMask.ForbidDefaultPIN) != 0)
|
||||
sb.append("ForbidDefaultPIN, ");
|
||||
if ((iValue & SettingsMask.IsReusable) != 0) sb.append("IsReusable, ");
|
||||
if ((iValue & SettingsMask.Protocol_AllowStaticEncryption) != 0)
|
||||
sb.append("Protocol_AllowStaticEncryption, ");
|
||||
if ((iValue & SettingsMask.Protocol_AllowUnencrypted) != 0)
|
||||
sb.append("Protocol_AllowUnencrypted, ");
|
||||
if ((iValue & SettingsMask.SmartSecurityDelay) != 0)
|
||||
sb.append("SmartSecurityDelay, ");
|
||||
if ((iValue & SettingsMask.UseActivation) != 0)
|
||||
sb.append("UseActivation, ");
|
||||
if ((iValue & SettingsMask.UseBlock) != 0) sb.append("UseBlock, ");
|
||||
if ((iValue & SettingsMask.UseCVC) != 0) sb.append("UseCVC, ");
|
||||
if ((iValue & SettingsMask.UseDynamicNDEF) != 0)
|
||||
sb.append("UseDynamicNDEF, ");
|
||||
if ((iValue & SettingsMask.UseNDEF) != 0) sb.append("UseNDEF, ");
|
||||
if ((iValue & SettingsMask.UseOneCommandAtTime) != 0)
|
||||
sb.append("UseOneCommandAtTime, ");
|
||||
if ((iValue & SettingsMask.ProtectIssuerDataAgainstReplay) != 0)
|
||||
sb.append("ProtectIssuerDataAgainstReplay, ");
|
||||
if ((iValue & SettingsMask.ForbidPurgeWallet) != 0) sb.append("ForbidPurgeWallet, ");
|
||||
if ((iValue & SettingsMask.AllowSelectBlockchain) != 0) sb.append("AllowSelectBlockchain, ");
|
||||
if ((iValue & SettingsMask.DisablePrecomputedNDEF) != 0) sb.append("DisablePrecomputedNDEF, ");
|
||||
|
||||
if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by dvol on 23.06.2017.
|
||||
*/
|
||||
|
||||
public class TLV {
|
||||
public enum Tag {
|
||||
TAG_Unknown(0x00),
|
||||
TAG_CardID(0x01),
|
||||
TAG_Status(0x02),
|
||||
TAG_CardPublicKey(0x03),
|
||||
TAG_CardSignature(0x04),
|
||||
TAG_CurveID(0x05),
|
||||
TAG_HashAlgID(0x06),
|
||||
TAG_SigningMethod(0x07),
|
||||
TAG_MaxSignatures(0x08),
|
||||
TAG_PauseBeforePIN2(0x09),
|
||||
TAG_SettingsMask(0x0A),
|
||||
TAG_CardData(0x0C),
|
||||
TAG_NDEFData(0x0D),
|
||||
TAG_Health(0x0F),
|
||||
|
||||
TAG_PIN(0x10),
|
||||
TAG_PIN2(0x11),
|
||||
TAG_NewPIN(0x12),
|
||||
TAG_NewPIN2(0x13),
|
||||
TAG_NewPIN_Hash(0x14),
|
||||
TAG_NewPIN2_Hash(0x15),
|
||||
TAG_Challenge(0x16),
|
||||
TAG_Salt(0x17),
|
||||
TAG_ValidationCounter(0x18),
|
||||
TAG_CVC(0x19),
|
||||
|
||||
TAG_Session_Key_A(0x1A),
|
||||
TAG_Session_Key_B(0x1B),
|
||||
TAG_Pause(0x1C),
|
||||
|
||||
TAG_Manufacture_ID(0x20),
|
||||
TAG_Manufacturer_Signature(0x21),
|
||||
|
||||
TAG_Issuer_Data_PublicKey(0x30),
|
||||
TAG_Issuer_Transaction_PublicKey(0x31),
|
||||
TAG_Issuer_Data(0x32),
|
||||
TAG_Issuer_Data_Signature(0x33),
|
||||
TAG_Issuer_Transaction_Signature(0x34),
|
||||
TAG_Issuer_Data_Counter(0x35),
|
||||
|
||||
TAG_IsActivated(0x3A),
|
||||
TAG_ActivationSeed(0x3B),
|
||||
TAG_ResetPIN(0x36),
|
||||
|
||||
TAG_CodePageAddress(0x40),
|
||||
TAG_CodePageCount(0x41),
|
||||
TAG_CodeHash(0x42),
|
||||
|
||||
TAG_TrOut_Hash(0x50),
|
||||
TAG_TrOut_HashSize(0x51),
|
||||
TAG_TrOut_Raw(0x52),
|
||||
|
||||
TAG_Wallet_PublicKey(0x60),
|
||||
TAG_Signature(0x61),
|
||||
TAG_RemainingSignatures(0x62),
|
||||
TAG_SignedHashes(0x63),
|
||||
|
||||
TAG_Firmware(0x80),
|
||||
TAG_Batch(0x81),
|
||||
TAG_ManufactureDateTime(0x82),
|
||||
TAG_Issuer_ID(0x83),
|
||||
TAG_Blockchain_ID(0x84),
|
||||
TAG_Manufacturer_PublicKey(0x85),
|
||||
TAG_CardID_Manufacturer_Signature(0x86),
|
||||
|
||||
TAG_Token_Symbol(0xA0),
|
||||
TAG_Token_Contract_Address(0xA1),
|
||||
TAG_Token_Decimal(0xA2),
|
||||
TAG_Denomination(0xC0),
|
||||
TAG_ValidatedBalance(0xC1),
|
||||
TAG_LastSign_Date(0xC2),
|
||||
TAG_DenominationText(0xC3);
|
||||
|
||||
|
||||
Tag(int Code) {
|
||||
this.Code = Code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return Code;
|
||||
}
|
||||
|
||||
private int Code;
|
||||
|
||||
public static Tag ByCode(int Code) {
|
||||
Tag[] allTags = Tag.values();
|
||||
for (Tag t : allTags) if (t.getCode() == Code) return t;
|
||||
return TAG_Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
private Tag tag;
|
||||
|
||||
public Tag getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public byte[] Value;
|
||||
|
||||
public TLV(Tag tag, byte[] value) {
|
||||
this.tag = tag;
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public void WriteToStream(ByteArrayOutputStream stream) throws IOException {
|
||||
stream.write(tag.getCode());
|
||||
if (Value != null) {
|
||||
if (Value.length > 0xFE) {
|
||||
stream.write(0xFF);
|
||||
stream.write((Value.length >> 8) & 0xFF);
|
||||
stream.write(Value.length & 0xFF);
|
||||
} else {
|
||||
stream.write(Value.length & 0xFF);
|
||||
}
|
||||
stream.write(Value);
|
||||
} else {
|
||||
stream.write(0x00);
|
||||
}
|
||||
}
|
||||
|
||||
public static TLV ReadFromStream(ByteArrayInputStream stream) throws IOException {
|
||||
int code = stream.read();
|
||||
if (code == -1) return null;
|
||||
int len = stream.read();
|
||||
if (len == -1)
|
||||
throw new IOException("Can't read TLV");
|
||||
if (len == 0xFF) {
|
||||
int lenH = stream.read();
|
||||
if (lenH == -1)
|
||||
throw new IOException("Can't read TLV");
|
||||
len = stream.read();
|
||||
if (len == -1)
|
||||
throw new IOException("Can't read TLV");
|
||||
len |= (lenH << 8);
|
||||
}
|
||||
byte[] value = new byte[len];
|
||||
if (len > 0) {
|
||||
if (len != stream.read(value)) {
|
||||
throw new IOException("Can't read TLV");
|
||||
}
|
||||
}
|
||||
Tag tag = Tag.ByCode(code);
|
||||
TLV result = new TLV(tag, value);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getAsInt() {
|
||||
return Util.byteArrayToInt(Value);
|
||||
}
|
||||
|
||||
public String getAsHexString() {
|
||||
return Util.bytesToHex(Value);
|
||||
}
|
||||
|
||||
public String getAsString() {
|
||||
if( Value.length==0 ) return "";
|
||||
|
||||
if (Value[Value.length - 1] == 0) {
|
||||
String s1 = new String(Arrays.copyOfRange(Value, 0, Value.length - 1), Charset.forName("utf-8"));
|
||||
return s1.trim();
|
||||
} else {
|
||||
String s1 = new String(Value, Charset.forName("utf-8"));
|
||||
return s1.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (tag) {
|
||||
case TAG_CardData:
|
||||
case TAG_Issuer_Data: {
|
||||
try {
|
||||
TLVList tlvSub = TLVList.fromBytes(Value);
|
||||
return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), tlvSub.toString());
|
||||
} catch (TLVException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (Value != null) {
|
||||
return String.format("%s[%d]: %s (non TLV)", tag.name(), Value.length, Util.bytesToHex(Value));
|
||||
} else {
|
||||
return String.format("%s[]: [[NULL]]", tag.name());
|
||||
}
|
||||
}
|
||||
case TAG_CurveID:
|
||||
case TAG_HashAlgID:
|
||||
case TAG_Blockchain_ID:
|
||||
case TAG_Manufacture_ID:
|
||||
case TAG_Firmware:
|
||||
case TAG_Issuer_ID:
|
||||
case TAG_Token_Symbol:
|
||||
if (Value != null) {
|
||||
return String.format("%s[%d]: %s(%s)", tag.name(), Value.length, Util.bytesToHex(Value), getAsString());
|
||||
} else {
|
||||
return String.format("%s[]: [[NULL]]", tag.name());
|
||||
}
|
||||
case TAG_SettingsMask: {
|
||||
StringBuilder sb=new StringBuilder();
|
||||
if( Value!=null ) {
|
||||
try {
|
||||
int iValue = Util.byteArrayToInt(Value);
|
||||
return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), SettingsMask.getDescription(iValue));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value));
|
||||
}
|
||||
}else{
|
||||
return String.format("%s[]: [[NULL]]", tag.name());
|
||||
}
|
||||
}
|
||||
default:
|
||||
if (Value != null) {
|
||||
return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value));
|
||||
} else {
|
||||
return String.format("%s[]: [[NULL]]", tag.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
public class TLVException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public TLVException(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TLVException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TLVException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
package com.tangem.cardcommon.reader;
|
||||
|
||||
/**
|
||||
* Created by dvol on 23.06.2017.
|
||||
*/
|
||||
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class TLVList extends ArrayList<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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package com.tangem.cardcommon.tasks;
|
||||
|
||||
import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider;
|
||||
import com.tangem.cardcommon.data.external.PINsProvider;
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
import com.tangem.cardcommon.reader.NfcReader;
|
||||
import com.tangem.cardcommon.util.Log;
|
||||
|
||||
public class CreateNewWalletTask extends CustomReadCardTask {
|
||||
public static final String TAG = CreateNewWalletTask.class.getSimpleName();
|
||||
|
||||
public CreateNewWalletTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications) {
|
||||
super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run_Task() throws Exception {
|
||||
mNotifications.onReadProgress(protocol, 20);
|
||||
protocol.run_VerifyCard();
|
||||
|
||||
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
|
||||
|
||||
mNotifications.onReadProgress(protocol, 30);
|
||||
if (isCancelled) return;
|
||||
protocol.run_CreateWallet(pinsProvider.getPIN2());
|
||||
mNotifications.onReadProgress(protocol, 60);
|
||||
if (isCancelled) return;
|
||||
|
||||
protocol.run_Read();
|
||||
parseReadResult();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,449 +0,0 @@
|
|||
package com.tangem.cardcommon.tasks;
|
||||
|
||||
import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider;
|
||||
import com.tangem.cardcommon.data.Manufacturer;
|
||||
import com.tangem.cardcommon.data.external.PINsProvider;
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
import com.tangem.cardcommon.reader.CardCrypto;
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.reader.NfcReader;
|
||||
import com.tangem.cardcommon.reader.TLV;
|
||||
import com.tangem.cardcommon.reader.TLVException;
|
||||
import com.tangem.cardcommon.reader.TLVList;
|
||||
import com.tangem.cardcommon.util.Log;
|
||||
import com.tangem.cardcommon.util.Util;
|
||||
|
||||
import org.spongycastle.jce.ECNamedCurveTable;
|
||||
import org.spongycastle.jce.spec.ECNamedCurveParameterSpec;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Base class for card communication task
|
||||
*/
|
||||
public class CustomReadCardTask extends Thread {
|
||||
public static final String TAG = CustomReadCardTask.class.getSimpleName();
|
||||
|
||||
protected NfcReader mIsoDep;
|
||||
protected CardProtocol.Notifications mNotifications;
|
||||
protected boolean isCancelled = false;
|
||||
protected TangemCard mCard;
|
||||
CardDataSubstitutionProvider localStorage;
|
||||
PINsProvider pinsProvider;
|
||||
CardProtocol protocol;
|
||||
|
||||
// this fields are static to optimize process when need enter pin and scan card again
|
||||
private static ArrayList<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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.cardcommon.tasks;
|
||||
|
||||
import com.tangem.cardcommon.data.external.PINsProvider;
|
||||
import com.tangem.cardcommon.reader.NfcReader;
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider;
|
||||
|
||||
public class PurgeTask extends CustomReadCardTask {
|
||||
public static final String TAG = PurgeTask.class.getSimpleName();
|
||||
|
||||
public PurgeTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications) {
|
||||
super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run_Task() throws Exception {
|
||||
if (mCard.getPauseBeforePIN2() > 0) {
|
||||
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
|
||||
}
|
||||
protocol.run_PurgeWallet(pinsProvider.getPIN2());
|
||||
mNotifications.onReadProgress(protocol, 50);
|
||||
protocol.run_Read();
|
||||
mNotifications.onReadProgress(protocol, 100);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package com.tangem.cardcommon.tasks;
|
||||
|
||||
import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider;
|
||||
import com.tangem.cardcommon.data.external.PINsProvider;
|
||||
import com.tangem.cardcommon.reader.NfcReader;
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
|
||||
public class ReadCardInfoTask extends CustomReadCardTask {
|
||||
public static final String TAG = ReadCardInfoTask.class.getSimpleName();
|
||||
|
||||
public ReadCardInfoTask(NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications) {
|
||||
super(null, reader, cardDataSubstitutionProvider, pinsProvider, notifications);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if default PIN2 is set on the card and enable UseDefaultPIN2 flag {@link TangemCard}
|
||||
* Works in firmware 1.12 and later, for older version UseDefaultPIN2 is always null
|
||||
* This function NEVER triggers security delay
|
||||
*
|
||||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
private void run_CheckPIN2isDefault() throws Exception {
|
||||
// can obtain SetPIN(to default) answer without security delay - try check if PIN2 is default with card request
|
||||
if (mCard.isFirmwareNewer("1.19") || (mCard.isFirmwareNewer("1.12") && (mCard.getPauseBeforePIN2() == 0 || mCard.useSmartSecurityDelay()))) {
|
||||
try {
|
||||
protocol.run_SetPIN(CardProtocol.DefaultPIN2, mCard.getPIN(), CardProtocol.DefaultPIN2, true);
|
||||
mCard.setUseDefaultPIN2(true);
|
||||
} catch (CardProtocol.TangemException_NeedPause e) {
|
||||
mCard.setUseDefaultPIN2(null);
|
||||
} catch (CardProtocol.TangemException_InvalidPIN e) {
|
||||
mCard.setUseDefaultPIN2(false);
|
||||
}
|
||||
} else {
|
||||
mCard.setUseDefaultPIN2(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run_Task() throws Exception {
|
||||
mNotifications.onReadProgress(protocol, 20);
|
||||
run_ReadOrWriteIssuerData();
|
||||
mNotifications.onReadProgress(protocol, 50);
|
||||
run_CheckPIN2isDefault();
|
||||
mNotifications.onReadProgress(protocol, 90);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
package com.tangem.cardcommon.tasks;
|
||||
|
||||
import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider;
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
import com.tangem.cardcommon.data.external.PINsProvider;
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.reader.NfcReader;
|
||||
import com.tangem.cardcommon.reader.TLV;
|
||||
import com.tangem.cardcommon.reader.TLVList;
|
||||
import com.tangem.cardcommon.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class SignTask extends CustomReadCardTask {
|
||||
public static final String TAG = SignTask.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Transaction Engine request/notifications during sign process
|
||||
*/
|
||||
public interface TransactionToSign {
|
||||
boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod);
|
||||
|
||||
byte[][] getHashesToSign() throws Exception;
|
||||
|
||||
byte[] getRawDataToSign() throws Exception;
|
||||
|
||||
String getHashAlgToSign() throws Exception;
|
||||
|
||||
byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception;
|
||||
|
||||
byte[] onSignCompleted(byte[] signature) throws Exception;
|
||||
}
|
||||
|
||||
private TransactionToSign transactionToSign;
|
||||
|
||||
public SignTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications, TransactionToSign transactionToSign) {
|
||||
super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications);
|
||||
this.transactionToSign = transactionToSign;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run_Task() throws Exception {
|
||||
protocol.run_VerifyCard();
|
||||
|
||||
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
|
||||
|
||||
mNotifications.onReadProgress(protocol, 30);
|
||||
if (isCancelled) return;
|
||||
|
||||
if (mCard.getPauseBeforePIN2() > 0) {
|
||||
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
|
||||
}
|
||||
|
||||
if (!transactionToSign.isSigningMethodSupported(mCard.getSigningMethod())) {
|
||||
throw new CardProtocol.TangemException("Signing method isn't supported!");
|
||||
}
|
||||
|
||||
TLVList signResult;
|
||||
switch (mCard.getSigningMethod()) {
|
||||
case Sign_Hash:
|
||||
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), transactionToSign.getHashesToSign(), null, null, null);
|
||||
break;
|
||||
case Sign_Hash_Validated_By_Issuer:
|
||||
case Sign_Hash_Validated_By_Issuer_And_WriteIssuerData:
|
||||
ByteArrayOutputStream bs = new ByteArrayOutputStream();
|
||||
byte[][] hashes = transactionToSign.getHashesToSign();
|
||||
if (hashes.length > 10) throw new CardProtocol.TangemException("To much hashes in one transaction!");
|
||||
for (int i = 0; i < hashes.length; i++) {
|
||||
if (i != 0 && hashes[0].length != hashes[i].length)
|
||||
throw new CardProtocol.TangemException("Hashes length must be identical!");
|
||||
bs.write(hashes[i]);
|
||||
}
|
||||
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), hashes, transactionToSign.getIssuerTransactionSignature(bs.toByteArray()), null, null);
|
||||
break;
|
||||
case Sign_Raw:
|
||||
signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), transactionToSign.getHashAlgToSign(), transactionToSign.getRawDataToSign(), null, null, null);
|
||||
break;
|
||||
case Sign_Raw_Validated_By_Issuer:
|
||||
case Sign_Raw_Validated_By_Issuer_And_WriteIssuerData:
|
||||
byte[] txOut = transactionToSign.getRawDataToSign();
|
||||
signResult = protocol.run_SignRaw(pinsProvider.getPIN2(), transactionToSign.getHashAlgToSign(), txOut, transactionToSign.getIssuerTransactionSignature(txOut), null, null);
|
||||
break;
|
||||
default:
|
||||
throw new CardProtocol.TangemException("Signing method isn't supported!");
|
||||
}
|
||||
|
||||
transactionToSign.onSignCompleted(signResult.getTLV(TLV.Tag.TAG_Signature).Value);
|
||||
mNotifications.onReadProgress(protocol, 100);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.cardcommon.tasks;
|
||||
|
||||
import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider;
|
||||
import com.tangem.cardcommon.data.external.PINsProvider;
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
import com.tangem.cardcommon.reader.NfcReader;
|
||||
|
||||
public class SwapPINTask extends CustomReadCardTask {
|
||||
public static final String TAG = SwapPINTask.class.getSimpleName();
|
||||
|
||||
private String newPIN, newPIN2;
|
||||
|
||||
public SwapPINTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, CardProtocol.Notifications notifications, String newPIN, String newPIN2) {
|
||||
super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications);
|
||||
this.newPIN = newPIN;
|
||||
this.newPIN2 = newPIN2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run_Task() throws Exception {
|
||||
|
||||
if (mCard.getPauseBeforePIN2() > 0) {
|
||||
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
|
||||
}
|
||||
|
||||
protocol.run_SetPIN(pinsProvider.getPIN2(), newPIN, newPIN2, false);
|
||||
protocol.setPIN(newPIN);
|
||||
mCard.setPIN(newPIN);
|
||||
|
||||
mNotifications.onReadProgress(protocol, 50);
|
||||
|
||||
protocol.run_Read();
|
||||
|
||||
mNotifications.onReadProgress(protocol, 100);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package com.tangem.cardcommon.tasks;
|
||||
|
||||
import com.tangem.cardcommon.data.TangemCard;
|
||||
import com.tangem.cardcommon.data.external.CardDataSubstitutionProvider;
|
||||
import com.tangem.cardcommon.data.external.FirmwaresDigestsProvider;
|
||||
import com.tangem.cardcommon.data.external.PINsProvider;
|
||||
import com.tangem.cardcommon.reader.CardProtocol;
|
||||
import com.tangem.cardcommon.reader.NfcReader;
|
||||
import com.tangem.cardcommon.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by dvol on 04.02.2018.
|
||||
*/
|
||||
|
||||
public class VerifyCardTask extends CustomReadCardTask {
|
||||
public static final String TAG = VerifyCardTask.class.getSimpleName();
|
||||
|
||||
private FirmwaresDigestsProvider firmwaresDigestsProvider;
|
||||
public VerifyCardTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, FirmwaresDigestsProvider firmwaresDigestsProvider, CardProtocol.Notifications notifications) {
|
||||
super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications);
|
||||
this.firmwaresDigestsProvider=firmwaresDigestsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run_Task() throws Exception {
|
||||
mNotifications.onReadProgress(protocol, 20);
|
||||
if (isCancelled) return;
|
||||
protocol.run_VerifyCard();
|
||||
mNotifications.onReadProgress(protocol, 50);
|
||||
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
|
||||
if (isCancelled) return;
|
||||
if (protocol.getCard().getStatus() == TangemCard.Status.Loaded) {
|
||||
protocol.run_CheckWalletWithSignatureVerify();
|
||||
mNotifications.onReadProgress(protocol, 80);
|
||||
}
|
||||
if (isCancelled) return;
|
||||
FirmwaresDigestsProvider.VerifyCodeRecord record = firmwaresDigestsProvider.selectRandomVerifyCodeBlock(mCard.getFirmwareVersion());
|
||||
if (isCancelled) return;
|
||||
if (record != null) {
|
||||
byte[] returnedDigest = protocol.run_VerifyCode(record.hashAlg, record.blockIndex, record.blockCount, record.challenge);
|
||||
mCard.setCodeConfirmed(Arrays.equals(returnedDigest, record.digest));
|
||||
} else {
|
||||
mCard.setCodeConfirmed(null);
|
||||
}
|
||||
mNotifications.onReadProgress(protocol, 90);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.tangem.cardcommon.util;
|
||||
|
||||
public class Log {
|
||||
public static void i(String logTag, String message) {
|
||||
|
||||
}
|
||||
public static void e(String logTag, String message) {
|
||||
|
||||
}
|
||||
public static void v(String logTag, String message) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
package com.tangem.cardcommon.util;
|
||||
|
||||
import org.spongycastle.crypto.CipherParameters;
|
||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||
import org.spongycastle.crypto.macs.HMac;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class PBKDF2 {
|
||||
private static final HMac F =new HMac(new SHA256Digest());
|
||||
|
||||
/**
|
||||
* Derive a key.
|
||||
*
|
||||
* @param password The password to derive the key from.
|
||||
* @param iterations The iteration count.
|
||||
* @return Returns a key derived with the specified parameters.
|
||||
* @throws InvalidKeyException If the specified length for the derived key
|
||||
* is to long.
|
||||
*/
|
||||
public static byte[] deriveKey(final byte[] password, final byte[] salt, final int iterations) throws InvalidKeyException {
|
||||
return deriveKey(password, salt, iterations, F.getMacSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a key with a specified length.
|
||||
*
|
||||
* @param password The password to derive the key from.
|
||||
* @param iterations The iteration count.
|
||||
* @param len The length of the derived key.
|
||||
* @return Returns a key derived with the specified parameters.
|
||||
* @throws InvalidKeyException If the specified length for the derived key
|
||||
* is to long.
|
||||
*/
|
||||
public static byte[] deriveKey(final byte[] password, final byte[] salt, final int iterations, final int len) throws InvalidKeyException {
|
||||
// Check key length
|
||||
if (len > ((Math.pow(2, 32) - 1) * F.getMacSize()))
|
||||
throw new InvalidKeyException("Derived key to long");
|
||||
|
||||
byte[] derivedKey = new byte[len];
|
||||
|
||||
final int J = 0;
|
||||
final int K = F.getMacSize();
|
||||
final int U = F.getMacSize() << 1;
|
||||
final int B = K + U;
|
||||
final byte[] workingArray = new byte[K + U + 4];
|
||||
|
||||
// Initialize F
|
||||
CipherParameters macParams = new KeyParameter(password);
|
||||
F.init(macParams);
|
||||
|
||||
// Perform iterations
|
||||
for (int kpos = 0, blk = 1; kpos < len; kpos += K, blk++) {
|
||||
storeInt32BE(blk, workingArray, B);
|
||||
|
||||
F.update(salt, 0, salt.length);
|
||||
|
||||
F.reset();
|
||||
F.update(salt, 0, salt.length);
|
||||
F.update(workingArray, B, 4);
|
||||
F.doFinal(workingArray, U);
|
||||
System.arraycopy(workingArray, U, workingArray, J, K);
|
||||
|
||||
for (int i = 1, j = J, k = K; i < iterations; i++) {
|
||||
F.init(macParams);
|
||||
F.update(workingArray, j, K);
|
||||
F.doFinal(workingArray, k);
|
||||
|
||||
for (int u = U, v = k; u < B; u++, v++)
|
||||
workingArray[u] ^= workingArray[v];
|
||||
|
||||
int swp = k;
|
||||
k = j;
|
||||
j = swp;
|
||||
}
|
||||
|
||||
int tocpy = Math.min(len - kpos, K);
|
||||
System.arraycopy(workingArray, U, derivedKey, kpos, tocpy);
|
||||
}
|
||||
|
||||
Arrays.fill(workingArray, (byte) 0);
|
||||
|
||||
return derivedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a 32-bit integer value into a big-endian byte array
|
||||
*
|
||||
* @param value The integer value to convert
|
||||
* @param bytes The byte array to store the converted value
|
||||
* @param offSet The offset in the output byte array
|
||||
*/
|
||||
public static void storeInt32BE(int value, byte[] bytes, int offSet) {
|
||||
bytes[offSet + 3] = (byte) (value);
|
||||
bytes[offSet + 2] = (byte) (value >>> 8);
|
||||
bytes[offSet + 1] = (byte) (value >>> 16);
|
||||
bytes[offSet] = (byte) (value >>> 24);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,957 +0,0 @@
|
|||
package com.tangem.cardcommon.util;
|
||||
|
||||
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.BitSet;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class Util {
|
||||
|
||||
public static String getSpaces(int length) {
|
||||
StringBuilder buf = new StringBuilder(length);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
buf.append(" ");
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(String in, int indent, boolean wrapLines) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
char c = in.charAt(i);
|
||||
buf.append(c);
|
||||
|
||||
int nextPos = i+1;
|
||||
if (wrapLines && nextPos % 32 == 0 && nextPos != in.length()) {
|
||||
buf.append("\n").append(getSpaces(indent));
|
||||
} else if (nextPos % 2 == 0 && nextPos != in.length()) {
|
||||
//buf.append(" ");
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(String in, int indent){
|
||||
return prettyPrintHex(in, indent, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(byte[] data, int indent) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data), indent, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(byte[] data) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(byte[] data, int startPos, int length) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHexNoWrap(byte[] data) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, false);
|
||||
}
|
||||
|
||||
public static String prettyPrintHexNoWrap(byte[] data, int startPos, int length) {
|
||||
return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, false);
|
||||
}
|
||||
|
||||
public static String prettyPrintHexNoWrap(String in) {
|
||||
return Util.prettyPrintHex(in, 0, false);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(String in) {
|
||||
return prettyPrintHex(in, 0, true);
|
||||
}
|
||||
|
||||
public static String prettyPrintHex(BigInteger bi) {
|
||||
byte[] data = bi.toByteArray();
|
||||
if (data[0] == (byte) 0x00) {
|
||||
byte[] tmp = new byte[data.length - 1];
|
||||
System.arraycopy(data, 1, tmp, 0, data.length - 1);
|
||||
data = tmp;
|
||||
}
|
||||
return prettyPrintHex(data);
|
||||
}
|
||||
|
||||
public static byte[] performRSA(byte[] dataBytes, byte[] expBytes, byte[] modBytes) {
|
||||
|
||||
int inBytesLength = dataBytes.length;
|
||||
|
||||
if (expBytes[0] >= (byte) 0x80) {
|
||||
//Prepend 0x00 to modulus
|
||||
byte[] tmp = new byte[expBytes.length + 1];
|
||||
tmp[0] = (byte) 0x00;
|
||||
System.arraycopy(expBytes, 0, tmp, 1, expBytes.length);
|
||||
expBytes = tmp;
|
||||
}
|
||||
|
||||
if (modBytes[0] >= (byte) 0x80) {
|
||||
//Prepend 0x00 to modulus
|
||||
byte[] tmp = new byte[modBytes.length + 1];
|
||||
tmp[0] = (byte) 0x00;
|
||||
System.arraycopy(modBytes, 0, tmp, 1, modBytes.length);
|
||||
modBytes = tmp;
|
||||
}
|
||||
|
||||
if (dataBytes[0] >= (byte) 0x80) {
|
||||
//Prepend 0x00 to signed data to avoid that the most significant bit is interpreted as the "signed" bit
|
||||
byte[] tmp = new byte[dataBytes.length + 1];
|
||||
tmp[0] = (byte) 0x00;
|
||||
System.arraycopy(dataBytes, 0, tmp, 1, dataBytes.length);
|
||||
dataBytes = tmp;
|
||||
}
|
||||
|
||||
BigInteger exp = new BigInteger(expBytes);
|
||||
BigInteger mod = new BigInteger(modBytes);
|
||||
BigInteger data = new BigInteger(dataBytes);
|
||||
|
||||
byte[] result = data.modPow(exp, mod).toByteArray();
|
||||
|
||||
if (result.length == (inBytesLength+1) && result[0] == (byte)0x00) {
|
||||
//Remove 0x00 from beginning of array
|
||||
byte[] tmp = new byte[inBytesLength];
|
||||
System.arraycopy(result, 1, tmp, 0, inBytesLength);
|
||||
result = tmp;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] calculateSHA1(byte[] data) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
|
||||
return sha1.digest(data);
|
||||
}
|
||||
|
||||
public static byte[] calculateSHA224(byte[] data) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA-224");
|
||||
return sha.digest(data);
|
||||
}
|
||||
|
||||
public static byte[] calculateSHA256(byte[] data) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||
return sha256.digest(data);
|
||||
}
|
||||
|
||||
public static byte[] calculateSHA384(byte[] data) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA-384");
|
||||
return sha.digest(data);
|
||||
}
|
||||
|
||||
public static byte[] calculateSHA512(byte[] data) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA-512");
|
||||
return sha.digest(data);
|
||||
}
|
||||
|
||||
public static byte[] calculateSHA256(String Message) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||
byte data[]=Message.getBytes(Charset.forName("UTF-8"));
|
||||
return sha256.digest(data);
|
||||
}
|
||||
|
||||
public static byte[] calculateRIPEMD160(byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||
//MessageDigest hashAlg = MessageDigest.getInstance("RIPEMD-160", "SC");
|
||||
//return hashAlg.digest(data);
|
||||
|
||||
RIPEMD160Digest digest = new RIPEMD160Digest();
|
||||
digest.update(data, 0, data.length);
|
||||
byte[] out = new byte[20];
|
||||
digest.doFinal(out, 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static String byte2Hex(byte b) {
|
||||
String[] HEX_DIGITS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
|
||||
int nb = b & 0xFF;
|
||||
int i_1 = (nb >>> 4) & 0xF;
|
||||
int i_2 = nb & 0xF;
|
||||
return HEX_DIGITS[i_1] + HEX_DIGITS[i_2];
|
||||
}
|
||||
|
||||
public static String short2Hex(short s) {
|
||||
byte b1 = (byte) (s >>> 8);
|
||||
byte b2 = (byte) (s & 0xFF);
|
||||
return byte2Hex(b1) + byte2Hex(b2);
|
||||
}
|
||||
|
||||
public static int byteToInt(byte b) {
|
||||
return (int) b & 0xFF;
|
||||
}
|
||||
|
||||
public static int byteToInt(byte first, byte second) {
|
||||
int value = (first & 0xFF) << 8;
|
||||
value += second & 0xFF;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static short byte2Short(byte b1, byte b2) {
|
||||
return (short) ((b1 << 8) | (b2 & 0xFF));
|
||||
}
|
||||
|
||||
public static String getFormattedNanoTime(long nano) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append((int) (nano / 1000000));
|
||||
buf.append("ms ");
|
||||
buf.append(nano % 1000000);
|
||||
buf.append("ns");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
// public static String formatDate(Date date)
|
||||
// {
|
||||
// //return DateUtils.formatDateTime(null, date.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR);
|
||||
//// return DateFormat.getDateInstance(DateFormat.SHORT).format(date);
|
||||
// return new SimpleDateFormat("yyyy_MM_dd__HH_mm_ss", Locale.US).format(date);
|
||||
// }
|
||||
//
|
||||
// public static String formatDateTime(Date date)
|
||||
// {
|
||||
// return formatDate(date)+" "+formatTime(date);
|
||||
// }
|
||||
//
|
||||
// public static String formatTime(Date date)
|
||||
// {
|
||||
// return new SimpleDateFormat("HH:mm:ss", Locale.US).format(date);
|
||||
//// DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date)
|
||||
//// return DateUtils.formatDateTime(null, date.getTime(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR);//DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date);
|
||||
// }
|
||||
|
||||
public static byte[] getCurrentDateAsNumericEncodedByteArray(){
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyMMdd", Locale.US);
|
||||
return fromHexString(format.format(new Date()));
|
||||
}
|
||||
|
||||
//This prints all non-control characters common to all parts of ISO/IEC 8859
|
||||
//See EMV book 4 Annex B: Table 36: VolleyHelper Character Set
|
||||
public static String getSafePrintChars(byte[] byteArray) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
// throw new IllegalArgumentException("Argument 'byteArray' cannot be null");
|
||||
}
|
||||
return getSafePrintChars(byteArray, 0, byteArray.length);
|
||||
}
|
||||
|
||||
public static String getSafePrintChars(byte[] byteArray, int startPos, int length) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
// throw new IllegalArgumentException("Argument 'byteArray' cannot be null");
|
||||
}
|
||||
if(byteArray.length < startPos+length){
|
||||
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")");
|
||||
}
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = startPos; i < startPos+length; i++) {
|
||||
if (byteArray[i] >= (byte) 0x20 && byteArray[i] < (byte) 0x7F) {
|
||||
buf.append((char) byteArray[i]);
|
||||
} else {
|
||||
buf.append(".");
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static byte[] hexToBytes(String str) {
|
||||
byte[] bytes = new byte[str.length() / 2];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[i] = (byte) Integer.parseInt(str.substring(2 * i, 2 * i + 2),
|
||||
16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
final private static char[] hexArray = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
if( bytes==null ) return "[EMPTY]";
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = hexArray[v >>> 4];
|
||||
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array into a hex string.
|
||||
* @param byteArray the byte array source
|
||||
* @return a hex string representing the byte array
|
||||
*/
|
||||
public static String byteArrayToHexString(final byte[] byteArray) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
}
|
||||
return byteArrayToHexString(byteArray, 0, byteArray.length);
|
||||
}
|
||||
|
||||
public static String byteArrayToHexString(final byte[] byteArray, int startPos, int length) {
|
||||
if (byteArray == null) {
|
||||
return "";
|
||||
}
|
||||
if(byteArray.length < startPos+length){
|
||||
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")");
|
||||
}
|
||||
// int readBytes = byteArray.length;
|
||||
StringBuilder hexData = new StringBuilder();
|
||||
int onebyte;
|
||||
for (int i = 0; i < length; i++) {
|
||||
onebyte = ((0x000000ff & byteArray[startPos+i]) | 0xffffff00);
|
||||
hexData.append(Integer.toHexString(onebyte).substring(6));
|
||||
}
|
||||
return hexData.toString();
|
||||
}
|
||||
|
||||
public static String int2Hex(int i) {
|
||||
String hex = Integer.toHexString(i);
|
||||
if (hex.length() % 2 != 0) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
public static String int2HexZeroPad(int i) {
|
||||
String hex = int2Hex(i);
|
||||
if (hex.length() % 2 != 0) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
/**
|
||||
* The length of the returned array depends on the size of the int
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToByteArray(int value) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
byte one = (byte) (value >>> 24);
|
||||
byte two = (byte) (value >>> 16);
|
||||
byte three = (byte) (value >>> 8);
|
||||
byte four = (byte) (value);
|
||||
|
||||
boolean found = false;
|
||||
|
||||
if (one > 0x00) {
|
||||
baos.write(one);
|
||||
found = true;
|
||||
}
|
||||
if (found || two > 0x00) {
|
||||
baos.write(two);
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found || three > 0x00) {
|
||||
baos.write(three);
|
||||
}
|
||||
|
||||
baos.write(four);
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array with length = 2
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToByteArray2(int value) {
|
||||
return new byte[]{
|
||||
(byte) (value >>> 8),
|
||||
(byte) value};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array with length = 4
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToByteArray4(int value) {
|
||||
return new byte[]{
|
||||
(byte) (value >>> 24),
|
||||
(byte) (value >>> 16),
|
||||
(byte) (value >>> 8),
|
||||
(byte) value};
|
||||
}
|
||||
|
||||
public static byte[] longToByteArray8(long value) {
|
||||
return new byte[]{
|
||||
(byte) (value >>> 56),
|
||||
(byte) (value >>> 48),
|
||||
(byte) (value >>> 40),
|
||||
(byte) (value >>> 32),
|
||||
(byte) (value >>> 24),
|
||||
(byte) (value >>> 16),
|
||||
(byte) (value >>> 8),
|
||||
(byte) value};
|
||||
}
|
||||
|
||||
public static int byteArrayToInt(byte[] byteArray) throws IllegalArgumentException {
|
||||
if( byteArray.length==1 ) return byteArray[0]&0xFF;
|
||||
java.nio.ByteBuffer BB=java.nio.ByteBuffer.wrap(byteArray);
|
||||
switch (byteArray.length)
|
||||
{
|
||||
case 2: return BB.getShort();
|
||||
case 4: return BB.getInt();
|
||||
default: throw new IllegalArgumentException("Length must be 1,2 or 4. Length = " + byteArray.length);
|
||||
}
|
||||
}
|
||||
|
||||
public static long byteArrayToLong(byte[] byteArray) throws IllegalArgumentException {
|
||||
if( byteArray.length==1 ) return byteArray[0]&0xFF;
|
||||
java.nio.ByteBuffer BB=java.nio.ByteBuffer.wrap(byteArray);
|
||||
switch (byteArray.length)
|
||||
{
|
||||
case 2: return BB.getShort();
|
||||
case 4: return BB.getInt();
|
||||
case 8: return BB.getLong();
|
||||
default: throw new IllegalArgumentException("Length must be 1,2,4 or 8. Length = " + byteArray.length);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] longToByteArray(long value)
|
||||
{
|
||||
return new byte[]{
|
||||
(byte) (value >>> 56),
|
||||
(byte) (value >>> 48),
|
||||
(byte) (value >>> 40),
|
||||
(byte) (value >>> 32),
|
||||
(byte) (value >>> 24),
|
||||
(byte) (value >>> 16),
|
||||
(byte) (value >>> 8),
|
||||
(byte) value};
|
||||
}
|
||||
|
||||
public static int byteArrayToInt(byte[] byteArray, int startPos, int length) throws IllegalArgumentException {
|
||||
if (byteArray == null) {
|
||||
throw new IllegalArgumentException("Parameter 'byteArray' cannot be null");
|
||||
}
|
||||
if (length <= 0 || length > 4) {
|
||||
throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length);
|
||||
}
|
||||
if (length == 4 && Util.isBitSet(byteArray[startPos], 8)){
|
||||
throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos]));
|
||||
}
|
||||
int value = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
value += ((byteArray[startPos+i] & 0xFF) << 8 * (length - i - 1));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static long byteArrayToLong(byte[] byteArray, int startPos, int length) throws IllegalArgumentException {
|
||||
if (byteArray == null) {
|
||||
throw new IllegalArgumentException("Parameter 'byteArray' cannot be null");
|
||||
}
|
||||
if (length <= 0 || length > 8) {
|
||||
throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length);
|
||||
}
|
||||
if (length == 8 && Util.isBitSet(byteArray[startPos], 8)){
|
||||
throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos]));
|
||||
}
|
||||
long value = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
value += ((byteArray[startPos+i] & (long)0xFF) << 8 * (length - i - 1));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static byte[] fromHexString(String encoded) {
|
||||
encoded = removeSpaces(encoded);
|
||||
if (encoded.length() == 0){
|
||||
return new byte[0];
|
||||
}
|
||||
if ((encoded.length() % 2) != 0) {
|
||||
throw new IllegalArgumentException("Input string must contain an even number of characters: "+encoded);
|
||||
}
|
||||
final byte result[] = new byte[encoded.length() / 2];
|
||||
final char enc[] = encoded.toCharArray();
|
||||
for (int i = 0; i < enc.length; i += 2) {
|
||||
StringBuilder curr = new StringBuilder(2);
|
||||
curr.append(enc[i]).append(enc[i + 1]);
|
||||
result[i / 2] = (byte) Integer.parseInt(curr.toString(), 16);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String removeCRLFTab(String s) {
|
||||
StringTokenizer st = new StringTokenizer(s, "\r\n\t", false);
|
||||
StringBuilder buf = new StringBuilder();
|
||||
while (st.hasMoreElements()) {
|
||||
buf.append(st.nextElement());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String removeSpaces(String s) {
|
||||
return s.replaceAll(" ", "");
|
||||
}
|
||||
|
||||
public static String readInputStreamToString(InputStream is, String encoding) throws IOException {
|
||||
InputStreamReader input = new InputStreamReader(is, encoding);
|
||||
final int CHARS_PER_PAGE = 5000; //counting spaces
|
||||
final char[] buffer = new char[CHARS_PER_PAGE];
|
||||
StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
|
||||
for (int read = input.read(buffer, 0, buffer.length);
|
||||
read != -1;
|
||||
read = input.read(buffer, 0, buffer.length)) {
|
||||
output.append(buffer, 0, read);
|
||||
}
|
||||
|
||||
String text = output.toString();
|
||||
return text;
|
||||
}
|
||||
|
||||
public static void writeStringToFile(String string, String fileName, boolean append) throws IOException {
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(fileName, append));
|
||||
out.write(string);
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary Coded Decimal (BCD)
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public static byte[] intToBinaryEncodedDecimalByteArray(int val){
|
||||
String str = String.valueOf(val);
|
||||
if(str.length() % 2 != 0){
|
||||
str = "0"+str;
|
||||
}
|
||||
return Util.fromHexString(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts the literal hex representation of a byte to an int.
|
||||
* eg 0x70 = 70 (int)
|
||||
* @param b
|
||||
*/
|
||||
public static int binaryCodedDecimalToInt(byte b) {
|
||||
String hex = Util.byte2Hex(b);
|
||||
try {
|
||||
return Integer.parseInt(hex);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("The hex representation of argument b must be digits", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts the literal hex representation of a decimal
|
||||
* encoded in 1-5 bytes to an int.
|
||||
* The value should not be larger than Integer.MAX_VALUE
|
||||
*
|
||||
* eg 0x70 = 70 (decimal)
|
||||
* eg 0x21 47 48 36 47 = 2147483647 (decimal)
|
||||
* @param hex
|
||||
*/
|
||||
public static int binaryHexCodedDecimalToInt(String hex) {
|
||||
if (hex == null) {
|
||||
throw new IllegalArgumentException("Param hex cannot be null");
|
||||
}
|
||||
hex = Util.removeSpaces(hex);
|
||||
if (hex.length() > 10) {
|
||||
throw new IllegalArgumentException("There must be a maximum of 5 hex octets. hex=" + hex);
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(hex);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("Argument hex must be all digits. hex="+hex, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts a 1-5 byte BCD to an int.
|
||||
* eg 0x7099 = 7099 (int)
|
||||
* @param bcdArray
|
||||
*/
|
||||
public static int binaryHexCodedDecimalToInt(byte[] bcdArray) {
|
||||
if (bcdArray == null) {
|
||||
throw new IllegalArgumentException("Param bcdArray cannot be null");
|
||||
}
|
||||
return binaryHexCodedDecimalToInt(Util.byteArrayToHexString(bcdArray));
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a String with length = 8
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public static String byte2BinaryLiteral(byte val) {
|
||||
String s = Integer.toBinaryString(Util.byteToInt(val));
|
||||
if (s.length() < 8) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < 8 - s.length(); i++) {
|
||||
sb.append('0');
|
||||
}
|
||||
sb.append(s);
|
||||
s = sb.toString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bitset containing the values in bytes.
|
||||
* The byte-ordering of bytes must be big-endian which means the most significant bit is in element 0.
|
||||
*
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static BitSet byteArray2BitSet(byte[] bytes) {
|
||||
BitSet bits = new BitSet();
|
||||
for (int i = 0; i < bytes.length * 8; i++) {
|
||||
if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
|
||||
bits.set(i);
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
/* Returns a byte array of at least length 1.
|
||||
* The most significant bit in the result is guaranteed not to be a 1
|
||||
* (since BitSet does not support sign extension).
|
||||
* The byte-ordering of the result is big-endian which means the most significant bit is in element 0.
|
||||
* The bit at index 0 of the bit set is assumed to be the least significant bit.
|
||||
*/
|
||||
public static byte[] bitSet2ByteArray(BitSet bits) {
|
||||
byte[] bytes = new byte[bits.length() / 8 + 1];
|
||||
for (int i = 0; i < bits.length(); i++) {
|
||||
if (bits.get(i)) {
|
||||
bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @param bitPos The leftmost bit is 8 (the most significant bit)
|
||||
* @return
|
||||
*/
|
||||
public static boolean isBitSet(byte val, int bitPos) {
|
||||
if (bitPos < 1 || bitPos > 8) {
|
||||
throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos);
|
||||
}
|
||||
if ((val >>> (bitPos - 1) & 0x1) == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// /**
|
||||
// *
|
||||
// * @param val
|
||||
// * @return
|
||||
// */
|
||||
// public static int getBitsSetCount(byte val) {
|
||||
// int numBitsSet = 0;
|
||||
// for(int i=1; i<=8; i++){
|
||||
// if(Util.isBitSet(val, i)){
|
||||
// numBitsSet++;
|
||||
// }
|
||||
// }
|
||||
// return numBitsSet;
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param bitPos The leftmost bit is 8
|
||||
* @param on
|
||||
* @return
|
||||
*/
|
||||
public static byte setBit(byte data, int bitPos, boolean on) {
|
||||
if (bitPos < 1 || bitPos > 8) {
|
||||
throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos);
|
||||
}
|
||||
if (on) {
|
||||
// set bit
|
||||
return data |= 1 << (bitPos - 1);
|
||||
} else {
|
||||
// clear bit
|
||||
return data &= ~(1 << (bitPos - 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] generateRandomBytes(int numBytes) {
|
||||
// TODO: get bytes from a hardware RNG, or set seed
|
||||
byte[] rndBytes = new byte[numBytes];
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.nextBytes(rndBytes);
|
||||
return rndBytes;
|
||||
}
|
||||
|
||||
public static byte generateRandomByte() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
return (byte)(random.nextInt()&0xFF);
|
||||
}
|
||||
|
||||
|
||||
public static InputStream loadResource(Class<?> cls, String path){
|
||||
return cls.getResourceAsStream(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified array, prepending 0x00, or cutting off MSBytes if necessary
|
||||
* @param original
|
||||
* @param newLength
|
||||
* @return
|
||||
*/
|
||||
public static byte[] resizeArray(byte[] original, int newLength) {
|
||||
if(original == null){
|
||||
throw new IllegalArgumentException("byte array cannot be null");
|
||||
}
|
||||
if(newLength < 0){
|
||||
throw new IllegalArgumentException("Illegal new length: "+newLength+". Must be >= 0");
|
||||
}
|
||||
if(newLength == 0){
|
||||
return new byte[0];
|
||||
}
|
||||
byte[] tmp = new byte[newLength];
|
||||
|
||||
int srcPos = tmp.length > original.length ? 0 : original.length - tmp.length;
|
||||
int destPos = tmp.length > original.length ? tmp.length - original.length : 0;
|
||||
int length = tmp.length > original.length ? original.length : tmp.length;
|
||||
|
||||
System.arraycopy(original, srcPos, tmp, destPos, length);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public static byte[] copyByteArray(byte[] array2Copy){
|
||||
// byte[] copy = new byte[array2Copy.length];
|
||||
// System.arraycopy(array2Copy, 0, copy, 0, array2Copy.length);
|
||||
// return copy;
|
||||
if (array2Copy == null) {
|
||||
//return new byte[0] instead?
|
||||
throw new IllegalArgumentException("Argument 'array2Copy' cannot be null");
|
||||
}
|
||||
return copyByteArray(array2Copy, 0, array2Copy.length);
|
||||
}
|
||||
|
||||
public static byte[] copyByteArray(byte[] array2Copy, int startPos, int length){
|
||||
if (array2Copy == null) {
|
||||
//return new byte[0] instead?
|
||||
throw new IllegalArgumentException("Argument 'array2Copy' cannot be null");
|
||||
}
|
||||
if(array2Copy.length < startPos+length){
|
||||
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+array2Copy.length+")");
|
||||
}
|
||||
byte[] copy = new byte[array2Copy.length];
|
||||
System.arraycopy(array2Copy, startPos, copy, 0, length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static String getStackTrace(Throwable t){
|
||||
StringWriter sw = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
public static Class<?> getCallerClass(int i) {
|
||||
Class<?>[] classContext = new SecurityManager() {
|
||||
@Override public Class<?>[] getClassContext() {
|
||||
return super.getClassContext();
|
||||
}
|
||||
}.getClassContext();
|
||||
if (classContext != null) {
|
||||
for (int j = 0; j < classContext.length; j++) {
|
||||
if (classContext[j] == Util.class) {
|
||||
return classContext[i+j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SecurityManager.getClassContext() returns null on Android 4.0
|
||||
try {
|
||||
StackTraceElement[] classNames = Thread.currentThread().getStackTrace();
|
||||
for (int j = 0; j < classNames.length; j++) {
|
||||
if (Class.forName(classNames[j].getClassName()) == Util.class) {
|
||||
return Class.forName(classNames[i+j].getClassName());
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String decodeOID(byte[] enc){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
//First OID Component (standard)
|
||||
//0: ITU-T
|
||||
//1: ISO
|
||||
//2: joint-iso-itu-t
|
||||
|
||||
//Second OID Component (part in a multi part standard)
|
||||
//0: standard
|
||||
//1: registration-authority
|
||||
//2: member-body
|
||||
//3: identified-organization
|
||||
|
||||
|
||||
long firstSubidentifier = 0;
|
||||
|
||||
int i=0;
|
||||
while(Util.isBitSet(enc[i], 8)){
|
||||
firstSubidentifier = (firstSubidentifier << 7) | (enc[i] & 0x7f);
|
||||
i++;
|
||||
}
|
||||
firstSubidentifier = (firstSubidentifier << 7) | (enc[i] & 0x7f);
|
||||
i++;
|
||||
|
||||
if(firstSubidentifier >= 80){
|
||||
long firstOIDComp = 2;
|
||||
long secondOIDComp = firstSubidentifier - 80;
|
||||
sb.append(firstOIDComp).append(".").append(secondOIDComp);
|
||||
}else{
|
||||
long secondOIDComp = firstSubidentifier % 40;
|
||||
long firstOIDComp = (firstSubidentifier - secondOIDComp)/40;
|
||||
sb.append(firstOIDComp).append(".").append(secondOIDComp);
|
||||
}
|
||||
|
||||
for(; i<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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue