Updated on 2026-08-14

This commit is contained in:
Tangem 2018-07-16 00:37:04 +03:00
commit c74374611a
12 changed files with 424 additions and 120 deletions

Binary file not shown.

View file

@ -0,0 +1,70 @@
[
{
"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"}
]
},
{
"fw":"1.24d SDK",
"challenge":"00000000000000000000000000000001",
"sha-256":[
{"block":0,"count":1,"digest":"6B360B47FFB16641EF9CC778746FE6B1010AB60C69281CE431A46ECC29A72304"},
{"block":1,"count":1,"digest":"648F50D44E9A31CB4C2682EAD7B8E2E570E5F252035849CD9B08BD4F9106B432"},
{"block":2,"count":1,"digest":"BAA31588F599E2DBC8AC06CFBF4E710B9B0E3FEA0201AE67E5A9E605DB50D1ED"},
{"block":3,"count":1,"digest":"48CFA13758F0B8018A97831C11BB1668A39CC76B141C90038551D950F83A43BA"},
{"block":4,"count":1,"digest":"8932B8E9D93A28EEEAC8FD3477F38A8A384AC895B06E09B8396A7806C3DCFC09"},
{"block":5,"count":1,"digest":"A3E7F2911AB380B366E47EB9E7DCC0C3D8667EEE3CF355FCCD5B113918873EFC"},
{"block":6,"count":1,"digest":"32CFCDC12C3AAA6C8FE4935C76B795A81206141AE7C7AB941A4B5F74EB02D844"},
{"block":7,"count":1,"digest":"1BA7B6ED5F28A0744E177843F2A960CB757B27FF6052C17F9E57B266AEC4F564"},
{"block":8,"count":1,"digest":"7C1A2C88529DA038C3A124CC049B9A5CD73089C2CD9C66B15DF91020E981B94B"},
{"block":9,"count":1,"digest":"5F14E10CBE14230EEF9B8896C84998197E56738952A1ECF22202801B9AE64340"},
{"block":10,"count":1,"digest":"8FF4F496E7C68A654E574F13517EFA4F2058FDFC9E949421DA5ACC855C876F63"},
{"block":11491,"count":1,"digest":"E45CFEA35B1C4C217A55B78B0D78C841978F1008C3C1917D228EECB0324A0942"},
{"block":0,"count":0,"digest":"F4199A74F2D4BAF2E282A670989A50B9186665D8EC453D1CED02F4E4019E1C0F"}
],
"crc-16":[
{"block":0,"count":1,"digest":"22C3"},
{"block":1,"count":1,"digest":"ECB2"},
{"block":2,"count":1,"digest":"4701"},
{"block":3,"count":1,"digest":"2DD5"},
{"block":4,"count":1,"digest":"3870"},
{"block":5,"count":1,"digest":"66CF"},
{"block":6,"count":1,"digest":"4405"},
{"block":7,"count":1,"digest":"4913"},
{"block":8,"count":1,"digest":"F51D"},
{"block":9,"count":1,"digest":"0013"},
{"block":10,"count":1,"digest":"5E68"},
{"block":11491,"count":1,"digest":"4078"},
{"block":0,"count":0,"digest":"75C3"}
]
}
]

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"
}
}
]

View file

@ -4,10 +4,17 @@ import android.content.Context;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.FW;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.domain.wallet.PINStorage;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.util.Util;
import java.util.Arrays;
/**
* Created by dvol on 04.02.2018.
@ -59,23 +66,26 @@ public class VerifyCardTask extends Thread {
protocol.setPIN(PIN);
protocol.run_Read(false);
PINStorage.setLastUsedPIN(PIN);
mNotifications.OnReadProgress(protocol, 30);
mNotifications.OnReadProgress(protocol, 20);
if (isCancelled) return;
protocol.run_VerifyCard();
mNotifications.OnReadProgress(protocol, 60);
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, 90);
mNotifications.OnReadProgress(protocol, 80);
}
// if (isCancelled) return;
// if (protocol.getCard().getStatus() == TangemCard.Status.Loaded) {
// protocol.run_CheckWithSignatureVerify();
// }
if (isCancelled) return;
FW.VerifyCodeRecord record=FW.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);
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);

View file

@ -1,9 +1,8 @@
package com.tangem.domain
enum class BitcoinNodeTestNet(val host: String, val port: Int) {
n1("testnetnode.arihanc.com", 51001),
n2("testnet.hsmiths.com", 53011),
n3("testnet.qtornado.com", 51001),
n4("testnet1.bauerj.eu", 50001),
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

@ -553,7 +553,7 @@ public class CardProtocol {
} catch (Exception ee) {
ee.printStackTrace();
Log.e(logTag, "Cannot get issuer");
mCard.setIssuer(Issuer.Unknown);
mCard.setIssuer(Issuer.Unknown());
}
}
@ -844,7 +844,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);
@ -901,7 +901,7 @@ public class CardProtocol {
}
}
private byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
public byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
if (readResult == null) run_Read();
CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode);
rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII"));

View file

@ -0,0 +1,62 @@
package com.tangem.domain.cardReader;
import android.content.Context;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.tangem.util.Util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class FW {
private static JsonArray jaFirmwares=null;
public static boolean needInit() {
return jaFirmwares == null;
}
public static void Init(Context context) {
try(InputStream is=context.getAssets().open("fw_hashes.json")) {
try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
JsonParser parser = new JsonParser();
jaFirmwares = parser.parse(reader).getAsJsonArray();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static class VerifyCodeRecord
{
public String hashAlg;
public int blockIndex;
public int blockCount;
public byte[] challenge;
public byte[] digest;
}
public static VerifyCodeRecord selectRandomVerifyCodeBlock(String firmwareVersion) throws IOException {
for(int i=0; i<jaFirmwares.size(); i++) {
JsonObject jsVersion = jaFirmwares.get(i).getAsJsonObject();
if( jsVersion.get("fw").getAsString().equals(firmwareVersion) )
{
VerifyCodeRecord result=new VerifyCodeRecord();
result.hashAlg = "sha-256";
JsonArray jsHashes = jsVersion.get(result.hashAlg).getAsJsonArray();
result.challenge = Util.hexToBytes(jsVersion.get("challenge").getAsString());
int caseIndex= Util.byteArrayToInt(Util.generateRandomBytes(4))%jsHashes.size();
JsonObject jsRecord = jsHashes.get(caseIndex).getAsJsonObject();
result.blockIndex = jsRecord.get("block").getAsInt();
result.blockCount = jsRecord.get("count").getAsInt();
result.digest = Util.hexToBytes(jsRecord.get("digest").getAsString());
return result;
}
}
return null;
}
}

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,192 @@
package com.tangem.domain.wallet;
import com.tangem.domain.cardReader.CardCrypto;
import android.content.Context;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.tangem.domain.cardReader.CardCrypto;
import com.tangem.util.Util;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
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 class Issuer {
static class KeyPair {
String privateKey;
String publicKey;
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);
byte[] getPrivateKey() throws Exception {
if (privateKey != null) {
return CardCrypto.GeneratePublicKey(Util.hexToBytes(privateKey));
} else {
throw new Exception("No private key!");
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
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);
}
}
}
private String id;
private String officialName;
private KeyPair dataKey;
private 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 context) {
try {
// JsonArray jaIssuers=loadIssuersFile();
//
Issuer unknown = new Issuer();
unknown.id = "UNKNOWN";
unknown.officialName = "UNKNOWN";
try(InputStream is=context.getAssets().open("issuers.json")) {
try ( InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
Type listType = new TypeToken<List<Issuer>>() {
}.getType();
instances = new Gson().fromJson(reader, listType);
}
}
instances.add(0, unknown);
// for (JsonElement jeIssuer : jaIssuers) {
// 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;
// private static JsonArray loadIssuersFile() throws IOException {
// String result;
// JsonParser jsonParser = new JsonParser();
// try (BufferedReader reader = Files.newReader(new File("Issuers.json"), StandardCharsets.UTF_8)) {
// String str;
// StringBuilder buf = new StringBuilder();
// while ((str = reader.readLine()) != null) {
// buf.append(str);
// buf.append("\n");
// }
// result = buf.toString();
// }
// return jsonParser.parse(result).getAsJsonArray();
// }
//
// 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);
// }
// }
//
// return writer.toString();
// }
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() throws Exception {
if (dataKey == null)
throw new Exception("Data key not specified!");
return dataKey.getPublicKey();
}
public byte[] getPublicDataKey() {
return publicDataKeyArray;
public byte[] getPublicTransactionKey() throws Exception {
if (dataKey == null)
throw new Exception("Transaction key not specified!");
return transactionKey.getPublicKey();
}
public byte[] getPublicTransactionKey() {
return publicTransactionKeyArray;
public byte[] getPrivateDataKey() throws Exception {
if (dataKey == null)
throw new Exception("Data key not specified!");
return dataKey.getPrivateKey();
}
public byte[] getPrivateDataKey() {
return privateDataKeyArray;
}
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

@ -270,6 +270,33 @@ public class TangemCard {
}
}
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;
}
@ -579,7 +606,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;
@ -1203,7 +1230,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);
@ -1255,6 +1282,19 @@ public class TangemCard {
B.putFloat("rate", rate);
B.putFloat("rateAlter", rateAlter);
B.putString("confirmTx", GetConfirmTXCount().toString(16));
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);
}
public void LoadFromBundle(Bundle B) {
@ -1292,7 +1332,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");
@ -1364,6 +1404,15 @@ public class TangemCard {
rateAlter = B.getFloat("rateAlter");
if (B.containsKey("confirmTx"))
countConfirmTX = new BigInteger(B.getString("confirmTx"), 16);
if( B.containsKey("codeConfirmed") )
codeConfirmed=B.getBoolean("codeConfirmed");
if( B.containsKey("onlineVerified") )
onlineVerified=B.getBoolean("onlineVerified");
if( B.containsKey("onlineValidated") )
onlineValidated=B.getBoolean("onlineValidated");
}
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

View file

@ -3,6 +3,7 @@ package com.tangem.presentation.activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
@ -24,7 +25,9 @@ import android.widget.TextView;
import com.scottyab.rootbeer.RootBeer;
import com.skyfishjy.library.RippleBackground;
import com.tangem.domain.cardReader.FW;
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;
@ -244,6 +247,8 @@ public class MainActivity extends AppCompatActivity implements PopupMenu.OnMenuI
if (LastSignStorage.needInit()) {
LastSignStorage.Init(context);
}
if( Issuer.needInit() ) Issuer.Init(context);
if( FW.needInit() ) FW.Init(context);
}
public void showCleanButton() {