Updated on 2026-08-14
This commit is contained in:
parent
03a9f13214
commit
28f58e8059
13 changed files with 584 additions and 103 deletions
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
36
app/src/main/res/raw/fw_hashes.json
Normal file
36
app/src/main/res/raw/fw_hashes.json
Normal 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"}
|
||||
]
|
||||
}
|
||||
]
|
||||
39
app/src/main/res/raw/issuers.json
Normal file
39
app/src/main/res/raw/issuers.json
Normal 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"
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue