Updated on 2026-08-14

This commit is contained in:
Tangem 2018-07-06 09:24:14 +03:00
parent 03a9f13214
commit 28f58e8059
13 changed files with 584 additions and 103 deletions

Binary file not shown.

View file

@ -61,6 +61,7 @@ dependencies {
implementation 'org.bitcoinj:bitcoinj-core:0.14.4'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'info.hoang8f:android-segmented:1.0.6'
implementation 'com.google.code.gson:gson:2.8.5'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

View file

@ -0,0 +1,192 @@
package com.tangem.data.network.request;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.util.Util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
public class VerificationServerProtocol {
public static class Request
{
public Request(CustomCommand command, Class answerClass)
{
this.command=command;
this.answerClass=answerClass;
}
public String error;
public CustomCommand command;
public CustomAnswer answer;
public Type answerClass;
public void doPost(String hostURL) throws IOException {
URL url = new URL(hostURL+"/"+command.getURL());
URLConnection con = url.openConnection();
HttpURLConnection http = (HttpURLConnection) con;
try {
http.setConnectTimeout(30000);
http.setReadTimeout(30000);
http.setRequestMethod("POST"); // PUT is another valid option
http.setDoOutput(true);
String sRequestBody = getGson().toJson(this);
byte[] out = sRequestBody.getBytes(StandardCharsets.UTF_8);
int length = out.length;
http.setFixedLengthStreamingMode(length);
http.setRequestProperty("Content-Type", "application/json");
http.connect();
try (OutputStream os = http.getOutputStream()) {
os.write(out);
}
try (InputStream is = http.getInputStream()) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
answer = getGson().fromJson(br, answerClass);
}
}
}
finally {
http.disconnect();
}
}
}
static abstract class CustomCommand {
public abstract String getURL();
public byte[] toBytes()
{
return getGson().toJson(this).getBytes(StandardCharsets.UTF_8);
}
@Override
public String toString() {
return getGson().toJson(this);
}
}
static class CustomAnswer {
public String error;
@Override
public String toString() {
return getGson().toJson(this);
}
}
public static class Verify {
static class RequestItem {
public String CID;
public String publicKey;
}
static class Command extends CustomCommand {
public RequestItem[] requests;
@Override
public String getURL() {
return "verify";
}
}
static class ResultItem {
public ResultItem(RequestItem request) {
CID = request.CID;
}
public String error;
public String CID;
public Boolean passed;
}
static class Answer extends CustomAnswer {
public ResultItem[] results;
}
public Request prepare(TangemCard card)
{
Command c=new Command();
c.requests=new RequestItem[1];
c.requests[0].CID= Util.bytesToHex(card.getCID());
c.requests[0].publicKey=Util.bytesToHex(card.getCardPublicKey());
return new Request(c,Answer.class);
}
}
public static class Validate {
static class RequestItem {
public String CID;
public int counter;
public String signature;
}
static class Command extends CustomCommand {
public RequestItem[] requests;
@Override
public String getURL() {
return "validate";
}
}
static class ResultItem {
public ResultItem(RequestItem request) {
CID = request.CID;
}
public String error;
public String CID;
public Integer previousCounter;
public Boolean passed;
}
static class Answer extends CustomAnswer {
public ResultItem[] results;
}
public Request prepare(TangemCard card, int ValidationCounter, byte[] ValidationSignature)
{
Command c=new Command();
c.requests=new RequestItem[1];
c.requests[0].CID= Util.bytesToHex(card.getCID());
c.requests[0].counter=ValidationCounter;
c.requests[0].signature=Util.bytesToHex(ValidationSignature);
return new Request(new Command(),Answer.class);
}
}
public static Date strToDate(String date) throws ParseException {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
return new Date(formatter.parse(date).getTime());
}
public static String dateToStr(Date date) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
return formatter.format(date);
}
public static Gson getGson() {
return new GsonBuilder().create();
}
}

View file

@ -0,0 +1,42 @@
package com.tangem.data.network.task;
import android.os.AsyncTask;
import com.tangem.data.network.request.VerificationServerProtocol;
import java.util.ArrayList;
import java.util.List;
public class VerificationServerTask extends AsyncTask<VerificationServerProtocol.Request, Void, List<VerificationServerProtocol.Request>> {
public static final String hostURL ="https://tangem-webapp.appspot.com";
public VerificationServerTask() {
}
protected List<VerificationServerProtocol.Request> doInBackground(VerificationServerProtocol.Request... requests) {
List<VerificationServerProtocol.Request> result = new ArrayList<>();
for (int i = 0; i < requests.length; i++) {
result.add(requests[i]);
}
for (VerificationServerProtocol.Request request : result) {
try {
request.doPost(hostURL);
} catch (Exception e) {
request.error = e.getMessage();
if( request.error==null || request.error.isEmpty() )
{
request.error = e.getClass().getName();
}
}
}
return result;
}
public String getValidationNodeDescription() {
return hostURL;
}
}

View file

@ -1,9 +1,8 @@
package com.tangem.domain
enum class BitcoinNodeTestNet(val host: String, val port: Int) {
arihanc_com("testnetnode.arihanc.com", 51001),
hsmiths_com("testnet.hsmiths.com", 53011),
qtornado_com("testnet.qtornado.com", 51001),
hsmiths_com("testnet.hsmiths.com", 53011),
bauerj_eu("testnet1.bauerj.eu", 50001),
arihanc_com("testnetnode.arihanc.com", 51001),
}

View file

@ -179,12 +179,12 @@ public class CardCrypto {
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;
}
@ -206,7 +206,7 @@ public class CardCrypto {
* @return the PBDKF2 hash of the password
*/
public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations)
throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
throws InvalidKeyException {
return PBKDF2.deriveKey(password, salt, iterations);
}
@ -219,22 +219,20 @@ public class CardCrypto {
return mEncryptedData;
}
public static byte[] Decrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
try {
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 ));
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
return decryptedData;
}
catch (Exception e)
{
} 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 ));
Log.e("decrypt",Util.bytesToHex(decryptedData));
throw e;
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
return decryptedData;
}
}
}

View file

@ -539,7 +539,7 @@ public class CardProtocol {
} catch (Exception ee) {
ee.printStackTrace();
Log.e(logTag, "Cannot get issuer");
mCard.setIssuer(Issuer.Unknown);
mCard.setIssuer(Issuer.Unknown());
}
}
@ -830,7 +830,7 @@ public class CardProtocol {
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerSignature);
}
if (issuerData != null) {
if (issuer == null || issuer == Issuer.Unknown)
if (issuer == null || issuer == Issuer.Unknown())
throw new Exception("Need known Issuer to write issuer Data");
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData);
byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), issuerData);

View file

@ -59,7 +59,7 @@ public class ResponseApdu {
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));
byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2),true);
ByteArrayInputStream inputStream = new ByteArrayInputStream(decryptedData);
byte[] baLength = new byte[2];

View file

@ -1,120 +1,289 @@
package com.tangem.domain.wallet;
import com.tangem.domain.cardReader.CardCrypto;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.tangem.domain.cardReader.CardCrypto;
import com.tangem.util.Util;
import com.tangem.wallet.R;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by dvol on 14.11.2017.
*/
public enum Issuer {
Unknown("Unknown", "Unknown", null, null, null, null),
SMART_CASH_AG("SMART CASH AG", "SMART CASH AG",
IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey),
IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)),
TANGEM_SDK("TANGEM SDK", "TANGEM SDK",
IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey),
IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)),
TANGEM("TANGEM", "TANGEM", null, IssuerKeyStorage.tangemPublicDataKey, null, IssuerKeyStorage.tangemPublicTransactionKey)
;
//public enum Issuer {
// Unknown("Unknown", "Unknown", null, null, null, null),
// SMART_CASH_AG("SMART CASH AG", "SMART CASH AG",
// IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey),
// IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)),
// TANGEM_SDK("TANGEM SDK", "TANGEM SDK",
// IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey),
// IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)),
// TANGEM("TANGEM", "TANGEM", null, IssuerKeyStorage.tangemPublicDataKey, null, IssuerKeyStorage.tangemPublicTransactionKey)
// ;
//
//
// static class IssuerKeyStorage {
// private static final byte[] sdkPrivateDataKey = new byte[]{
// (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18,
// (byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57,
// (byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08,
// (byte) 0x39, (byte) 0x27, (byte) 0x37, (byte) 0x2D, (byte) 0x40, (byte) 0xDA, (byte) 0x9E, (byte) 0x92};
//
// private static final byte[] sdkPrivateTransactionKey = new byte[]{
// (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18,
// (byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57,
// (byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08,
// (byte) 0x19, (byte) 0x18, (byte) 0x17, (byte) 0x16, (byte) 0x15, (byte) 0x14, (byte) 0x13, (byte) 0x12};
//
// private static byte[] tangemPublicDataKey = {
// (byte) 0x04 ,
// (byte) 0x81 ,(byte) 0x96 ,(byte) 0xAA ,(byte) 0x4B ,(byte) 0x41 ,(byte) 0x0A ,(byte) 0xC4 ,(byte) 0x4A,
// (byte) 0x3B ,(byte) 0x9C ,(byte) 0xCE ,(byte) 0x18 ,(byte) 0xE7 ,(byte) 0xBE ,(byte) 0x22 ,(byte) 0x6A,
// (byte) 0xEA ,(byte) 0x07 ,(byte) 0x0A ,(byte) 0xCC ,(byte) 0x83 ,(byte) 0xA9 ,(byte) 0xCF ,(byte) 0x67,
// (byte) 0x54 ,(byte) 0x0F ,(byte) 0xAC ,(byte) 0x49 ,(byte) 0xAF ,(byte) 0x25 ,(byte) 0x12 ,(byte) 0x9F,
// (byte) 0x6A ,(byte) 0x53 ,(byte) 0x8A ,(byte) 0x28 ,(byte) 0xAD ,(byte) 0x63 ,(byte) 0x41 ,(byte) 0x35,
// (byte) 0x8E ,(byte) 0x3C ,(byte) 0x4F ,(byte) 0x99 ,(byte) 0x63 ,(byte) 0x06 ,(byte) 0x4F ,(byte) 0x7E,
// (byte) 0x36 ,(byte) 0x53 ,(byte) 0x72 ,(byte) 0xA6 ,(byte) 0x51 ,(byte) 0xD3 ,(byte) 0x74 ,(byte) 0xE5,
// (byte) 0xC2 ,(byte) 0x3C ,(byte) 0xDD ,(byte) 0x37 ,(byte) 0xFD ,(byte) 0x09 ,(byte) 0x9B ,(byte) 0xF2};
//
// private static byte[] tangemPublicTransactionKey = {
// (byte) 0x04 ,
// (byte) 0x34 ,(byte) 0x3D ,(byte) 0x40 ,(byte) 0x49 ,(byte) 0x6C ,(byte) 0xBE ,(byte) 0x1F ,(byte) 0xE8,
// (byte) 0xA8 ,(byte) 0xC0 ,(byte) 0x26 ,(byte) 0x57 ,(byte) 0x5C ,(byte) 0x43 ,(byte) 0x5A ,(byte) 0x29,
// (byte) 0x14 ,(byte) 0x1E ,(byte) 0xA3 ,(byte) 0xBC ,(byte) 0x33 ,(byte) 0x5D ,(byte) 0xA5 ,(byte) 0x54,
// (byte) 0x9A ,(byte) 0xB6 ,(byte) 0xC6 ,(byte) 0x46 ,(byte) 0x85 ,(byte) 0xA6 ,(byte) 0x46 ,(byte) 0x84,
// (byte) 0x80 ,(byte) 0x36 ,(byte) 0xD4 ,(byte) 0x81 ,(byte) 0xCF ,(byte) 0x9A ,(byte) 0x98 ,(byte) 0x93,
// (byte) 0x90 ,(byte) 0xA8 ,(byte) 0xB0 ,(byte) 0x34 ,(byte) 0xB2 ,(byte) 0x29 ,(byte) 0xD9 ,(byte) 0x9B,
// (byte) 0xD4 ,(byte) 0x9E ,(byte) 0x6F ,(byte) 0x07 ,(byte) 0xD2 ,(byte) 0xFF ,(byte) 0x02 ,(byte) 0x74,
// (byte) 0x6E ,(byte) 0xA2 ,(byte) 0x65 ,(byte) 0xEF ,(byte) 0x99 ,(byte) 0x38 ,(byte) 0x0A ,(byte) 0x80};
//
// public static byte[] GeneratePublicKey(byte[] privateKey) {
// try {
// return CardCrypto.GeneratePublicKey(privateKey);
// }
// catch (Exception e)
// {
// e.printStackTrace();
// return null;
// }
// }
// }
//
// private String ID;
// private String officialName;
// private byte[] privateDataKeyArray;
// private byte[] publicDataKeyArray;
// private byte[] privateTransactionKeyArray;
// private byte[] publicTransactionKeyArray;
//
// Issuer(String id, String officialName, byte[] privateDataKey, byte[] publicDataKey, byte[] privateTransactionKey, byte[] publicTransactionKey) {
// this.ID = id;
// this.officialName = officialName;
// this.privateDataKeyArray = privateDataKey;
// this.privateTransactionKeyArray = privateTransactionKey;
// this.publicDataKeyArray = publicDataKey;
// this.publicTransactionKeyArray = publicTransactionKey;
// }
//
// public byte[] getPublicDataKey() {
// return publicDataKeyArray;
// }
//
// public byte[] getPublicTransactionKey() {
// return publicTransactionKeyArray;
// }
//
// public byte[] getPrivateDataKey() {
// return privateDataKeyArray;
// }
//
// public byte[] getPrivateTransactionKey() {
// return privateTransactionKeyArray;
// }
//
// public byte[] getID() {
// return ID.getBytes();
// }
//
// public String getOfficialName() {
// return officialName;
// }
//
// public static Issuer FindIssuer(String ID, byte[] publicDataKey) {
// Issuer[] issuers = Issuer.values();
// for (int i = 1; i < issuers.length; i++) {
// if (issuers[i].ID.equals(ID) && Arrays.equals(issuers[i].getPublicDataKey(), publicDataKey)) {
// return issuers[i];
// }
// }
// return Issuer.Unknown;
// }
//
//}
public class Issuer {
static class IssuerKeyStorage {
private static final byte[] sdkPrivateDataKey = new byte[]{
(byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18,
(byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57,
(byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08,
(byte) 0x39, (byte) 0x27, (byte) 0x37, (byte) 0x2D, (byte) 0x40, (byte) 0xDA, (byte) 0x9E, (byte) 0x92};
static class KeyPair
{
String privateKey;
String publicKey;
private static final byte[] sdkPrivateTransactionKey = new byte[]{
(byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18,
(byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57,
(byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08,
(byte) 0x19, (byte) 0x18, (byte) 0x17, (byte) 0x16, (byte) 0x15, (byte) 0x14, (byte) 0x13, (byte) 0x12};
private static byte[] tangemPublicDataKey = {
(byte) 0x04 ,
(byte) 0x81 ,(byte) 0x96 ,(byte) 0xAA ,(byte) 0x4B ,(byte) 0x41 ,(byte) 0x0A ,(byte) 0xC4 ,(byte) 0x4A,
(byte) 0x3B ,(byte) 0x9C ,(byte) 0xCE ,(byte) 0x18 ,(byte) 0xE7 ,(byte) 0xBE ,(byte) 0x22 ,(byte) 0x6A,
(byte) 0xEA ,(byte) 0x07 ,(byte) 0x0A ,(byte) 0xCC ,(byte) 0x83 ,(byte) 0xA9 ,(byte) 0xCF ,(byte) 0x67,
(byte) 0x54 ,(byte) 0x0F ,(byte) 0xAC ,(byte) 0x49 ,(byte) 0xAF ,(byte) 0x25 ,(byte) 0x12 ,(byte) 0x9F,
(byte) 0x6A ,(byte) 0x53 ,(byte) 0x8A ,(byte) 0x28 ,(byte) 0xAD ,(byte) 0x63 ,(byte) 0x41 ,(byte) 0x35,
(byte) 0x8E ,(byte) 0x3C ,(byte) 0x4F ,(byte) 0x99 ,(byte) 0x63 ,(byte) 0x06 ,(byte) 0x4F ,(byte) 0x7E,
(byte) 0x36 ,(byte) 0x53 ,(byte) 0x72 ,(byte) 0xA6 ,(byte) 0x51 ,(byte) 0xD3 ,(byte) 0x74 ,(byte) 0xE5,
(byte) 0xC2 ,(byte) 0x3C ,(byte) 0xDD ,(byte) 0x37 ,(byte) 0xFD ,(byte) 0x09 ,(byte) 0x9B ,(byte) 0xF2};
private static byte[] tangemPublicTransactionKey = {
(byte) 0x04 ,
(byte) 0x34 ,(byte) 0x3D ,(byte) 0x40 ,(byte) 0x49 ,(byte) 0x6C ,(byte) 0xBE ,(byte) 0x1F ,(byte) 0xE8,
(byte) 0xA8 ,(byte) 0xC0 ,(byte) 0x26 ,(byte) 0x57 ,(byte) 0x5C ,(byte) 0x43 ,(byte) 0x5A ,(byte) 0x29,
(byte) 0x14 ,(byte) 0x1E ,(byte) 0xA3 ,(byte) 0xBC ,(byte) 0x33 ,(byte) 0x5D ,(byte) 0xA5 ,(byte) 0x54,
(byte) 0x9A ,(byte) 0xB6 ,(byte) 0xC6 ,(byte) 0x46 ,(byte) 0x85 ,(byte) 0xA6 ,(byte) 0x46 ,(byte) 0x84,
(byte) 0x80 ,(byte) 0x36 ,(byte) 0xD4 ,(byte) 0x81 ,(byte) 0xCF ,(byte) 0x9A ,(byte) 0x98 ,(byte) 0x93,
(byte) 0x90 ,(byte) 0xA8 ,(byte) 0xB0 ,(byte) 0x34 ,(byte) 0xB2 ,(byte) 0x29 ,(byte) 0xD9 ,(byte) 0x9B,
(byte) 0xD4 ,(byte) 0x9E ,(byte) 0x6F ,(byte) 0x07 ,(byte) 0xD2 ,(byte) 0xFF ,(byte) 0x02 ,(byte) 0x74,
(byte) 0x6E ,(byte) 0xA2 ,(byte) 0x65 ,(byte) 0xEF ,(byte) 0x99 ,(byte) 0x38 ,(byte) 0x0A ,(byte) 0x80};
public static byte[] GeneratePublicKey(byte[] privateKey) {
try {
return CardCrypto.GeneratePublicKey(privateKey);
byte[] getPrivateKey() throws Exception {
if (privateKey != null) {
return CardCrypto.GeneratePublicKey(Util.hexToBytes(privateKey));
} else {
throw new Exception("No private key!");
}
catch (Exception e)
}
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);
}
}
}
String id;
String officialName;
KeyPair dataKey;
KeyPair transactionKey;
public String getID() {
return id;
}
private static List<Issuer> instances=new ArrayList<>();
public static boolean needInit() {
return instances.size()==0;
}
public static void Init(Context mContext){
try {
JsonArray jaIssuers;
JsonParser jsonParser=new JsonParser();
jaIssuers=jsonParser.parse(ReadJSONResource(mContext, R.raw.issuers)).getAsJsonArray();
instances.clear();
Issuer unknown=new Issuer();
unknown.id="UNKNOWN";
unknown.officialName="UNKNOWN";
instances.add(unknown);
for(JsonElement jeIssuer: jaIssuers)
{
e.printStackTrace();
return null;
Issuer instance=new Gson().fromJson(jeIssuer,Issuer.class);
instances.add(instance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String ID;
private String officialName;
private byte[] privateDataKeyArray;
private byte[] publicDataKeyArray;
private byte[] privateTransactionKeyArray;
private byte[] publicTransactionKeyArray;
static String ReadJSONResource(Context mContext, int id) {
Resources resources = mContext.getResources();
InputStream resourceReader = resources.openRawResource(id);
Writer writer = new StringWriter();
try {
try(BufferedReader reader = new BufferedReader(new InputStreamReader(resourceReader, "UTF-8"))) {
String line = reader.readLine();
while (line != null) {
writer.write(line);
line = reader.readLine();
}
}
} catch (Exception e) {
Log.e("ReadJSONResource", "Unhandled exception while using JSONResourceReader", e);
} finally {
try {
resourceReader.close();
} catch (Exception e) {
Log.e("ReadJSONResource", "Unhandled exception while using JSONResourceReader", e);
}
}
Issuer(String id, String officialName, byte[] privateDataKey, byte[] publicDataKey, byte[] privateTransactionKey, byte[] publicTransactionKey) {
this.ID = id;
this.officialName = officialName;
this.privateDataKeyArray = privateDataKey;
this.privateTransactionKeyArray = privateTransactionKey;
this.publicDataKeyArray = publicDataKey;
this.publicTransactionKeyArray = publicTransactionKey;
return writer.toString();
}
public byte[] getPublicDataKey() {
return publicDataKeyArray;
public byte[] getPublicDataKey() throws Exception {
if( dataKey==null )
throw new Exception("Data key not specified!");
return dataKey.getPublicKey();
}
public byte[] getPublicTransactionKey() {
return publicTransactionKeyArray;
public byte[] getPublicTransactionKey() throws Exception {
if( dataKey==null )
throw new Exception("Transaction key not specified!");
return transactionKey.getPublicKey();
}
public byte[] getPrivateDataKey() {
return privateDataKeyArray;
public byte[] getPrivateDataKey() throws Exception {
if( dataKey==null )
throw new Exception("Data key not specified!");
return dataKey.getPrivateKey();
}
public byte[] getPrivateTransactionKey() {
return privateTransactionKeyArray;
}
public byte[] getID() {
return ID.getBytes();
public byte[] getPrivateTransactionKey() throws Exception {
if( transactionKey==null )
throw new Exception("Transaction key not specified!");
return transactionKey.getPrivateKey();
}
public String getOfficialName() {
return officialName;
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) {
Issuer[] issuers = Issuer.values();
for (int i = 1; i < issuers.length; i++) {
if (issuers[i].ID.equals(ID) && Arrays.equals(issuers[i].getPublicDataKey(), publicDataKey)) {
return issuers[i];
for (int i = 0; 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 Issuer.Unknown;
return Unknown();
}
public static Issuer Unknown() {
return instances.get(0);
}
}

View file

@ -578,7 +578,7 @@ public class TangemCard {
return health == 0;
}
private Issuer issuer = Issuer.Unknown;
private Issuer issuer = Issuer.Unknown();
public void setIssuer(Issuer issuer) {
this.issuer = issuer;
@ -1202,7 +1202,7 @@ public class TangemCard {
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.name());
if (issuer != null) B.putString("Issuer", issuer.getID());
if (firmwareVersion != null) B.putString("FirmwareVersion", firmwareVersion);
B.putString("BalanceDecimal", balanceDecimal);
B.putString("BalanceDecimalAlter", balanceDecimalAlter);
@ -1291,7 +1291,7 @@ public class TangemCard {
else
encryptionMode = null;
if (B.containsKey("Issuer")) issuer = Issuer.valueOf(B.getString("Issuer"));
if (B.containsKey("Issuer")) issuer = Issuer.FindIssuer(B.getString("Issuer"));
if (B.containsKey("FirmwareVersion")) firmwareVersion = B.getString("FirmwareVersion");
cardPublicKeyValid = B.getBoolean("CardPublicKeyValid");

View file

@ -24,6 +24,7 @@ import android.widget.TextView;
import com.scottyab.rootbeer.RootBeer;
import com.skyfishjy.library.RippleBackground;
import com.tangem.domain.wallet.DeviceNFCAntennaLocation;
import com.tangem.domain.wallet.Issuer;
import com.tangem.domain.wallet.LastSignStorage;
import com.tangem.domain.wallet.Logger;
import com.tangem.domain.wallet.PINStorage;
@ -216,6 +217,10 @@ public class MainActivity extends AppCompatActivity implements PopupMenu.OnMenuI
if (LastSignStorage.needInit()) {
LastSignStorage.Init(context);
}
if(Issuer.needInit())
{
Issuer.Init(context);
}
}
public void showCleanButton() {

View file

@ -0,0 +1,36 @@
[
{
"fw":"1.24r",
"challenge":"00000000000000000000000000000001",
"sha-256":[
{"block":0,"count":1,"digest":"9B091DF443FE951D27AA8843517096DC421433F5603EBC566B11891F867E3D18"},
{"block":1,"count":1,"digest":"B4F723EE79075C8C00FA17D037BF10528BB0C7CF421539EE15F4A397204C87AF"},
{"block":2,"count":1,"digest":"1258585B438B6BC8F41FC22AFF383C86690E984E84E96AA752C9A54954DF731A"},
{"block":3,"count":1,"digest":"14082C75D6AA533A06E9B764642F97B5783FED91BD1173EFADC3CF57D8769AAE"},
{"block":4,"count":1,"digest":"A8F6CB02D56933361127072BB0D89999020CE196AE9718E9CF019DCCC9437E85"},
{"block":5,"count":1,"digest":"491604BA2A2F6EE03A2B4812958F0356D745678FB0ABABF0109A9E8F59099D9D"},
{"block":6,"count":1,"digest":"B2F057FF8B83343BC40058CA459F3C63B9670ABCD059730BE70223AEBDC88D77"},
{"block":7,"count":1,"digest":"73F4391010345E27745A1AFEC93C02B2CA460D9EAB437605F82A18281DF17DCE"},
{"block":8,"count":1,"digest":"74588EBB6704DD3DB9ECDFE6B85EC2872BD6A1F7C73E080E57697D3FF8DDD470"},
{"block":9,"count":1,"digest":"3B382C35ADBBF206B0A6B216EE95AF2321EE34148E93BBFECCF275AB13ED08A2"},
{"block":11500,"count":1,"digest":"21DDEC9788E3EA04663297AD0208F2714F139434E3540EBB7C96150EE1D320C9"},
{"block":11501,"count":1,"digest":"C545C5D092D1EBC4BA266C139CE0F2A399E4D23E74EB4EBD0E53019AB58CB486"},
{"block":0,"count":0,"digest":"C689C50352E9F6E41BF2E6EC852D8466814A51018F40BE32BAADEE9C2A9E6B6D"}
],
"crc-16":[
{"block":0,"count":1,"digest":"1BFC"},
{"block":1,"count":1,"digest":"D58D"},
{"block":2,"count":1,"digest":"7E3E"},
{"block":3,"count":1,"digest":"14EA"},
{"block":4,"count":1,"digest":"014F"},
{"block":5,"count":1,"digest":"5FF0"},
{"block":6,"count":1,"digest":"7D3A"},
{"block":7,"count":1,"digest":"702C"},
{"block":8,"count":1,"digest":"CC22"},
{"block":9,"count":1,"digest":"392C"},
{"block":11500,"count":1,"digest":"5FCB"},
{"block":11501,"count":1,"digest":"FF77"},
{"block":0,"count":0,"digest":"D07B"}
]
}
]

View file

@ -0,0 +1,39 @@
[
{
"id": "TANGEM SDK",
"dataKey": {
"privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92"
},
"transactionKey": {
"privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312"
}
},
{
"id": "TANGEM SDK OLD",
"dataKey": {
"privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92"
},
"transactionKey": {
"privateKey": "11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92"
}
},
{
"id": "TANGEM",
"dataKey": {
"publicKey": "048196AA4B410AC44A3B9CCE18E7BE226AEA070ACC83A9CF67540FAC49AF25129F6A538A28AD6341358E3C4F9963064F7E365372A651D374E5C23CDD37FD099BF2"
},
"transactionKey": {
"publicKey": "04343D40496CBE1FE8A8C026575C435A29141EA3BC335DA5549AB6C64685A646848036D481CF9A989390A8B034B229D99BD49E6F07D2FF02746EA265EF99380A80"
}
},
{
"id": "SUPERBLOOM",
"dataKey": {
"publicKey": "04EAB7F2444CFFA2E9CB4AA01CF1CA3694888811BDF0F8E0CDBCCFB5F497F14A5B86655FEF0C7E8F86841B0C14660E09F28DDFB9690F7302DA56F661A6A064564E"
},
"transactionKey": {
"publicKey": "04D292BFC802FFA70ABB1C094A19F6C6766DD353114C25415871BE4586129648CFD77B22FBC1AF3C612497732A04CF845B41D760AFDD820051AB250798503DB340"
}
}
]