Updated on 2026-08-14
This commit is contained in:
parent
ebc3abcfe1
commit
48db52b2a9
99 changed files with 860 additions and 895 deletions
|
|
@ -68,8 +68,7 @@ public class LogFileProvider extends ContentProvider {
|
|||
// Create & return a ParcelFileDescriptor pointing to the file
|
||||
// Note: I don't care what mode they ask for - they're only getting
|
||||
// read only
|
||||
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(
|
||||
fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
return pfd;
|
||||
|
||||
// Otherwise unrecognised Uri
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.data;
|
|||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
|
|
|
|||
|
|
@ -1,295 +0,0 @@
|
|||
package com.tangem.data.db
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import java.io.File
|
||||
import kotlin.collections.HashMap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.tangem.data.network.model.CardVerifyAndGetInfo
|
||||
import com.tangem.domain.cardReader.CardCrypto
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.util.Util
|
||||
import com.tangem.wallet.R
|
||||
import java.io.InputStream
|
||||
import java.lang.Exception
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.*
|
||||
|
||||
data class LocalStorage(
|
||||
val context: Context
|
||||
) {
|
||||
private lateinit var artworks: HashMap<String, ArtworkInfo>
|
||||
private lateinit var batches: HashMap<String, BatchInfo>
|
||||
private val artworksFile: File = File(context.filesDir, "artworks.json")
|
||||
private val batchesFile: File = File(context.filesDir, "batches.json")
|
||||
|
||||
private var cacheDir: File? = null
|
||||
|
||||
init {
|
||||
cacheDir = File(context.filesDir, "artworks")
|
||||
if (!cacheDir!!.exists())
|
||||
cacheDir!!.mkdirs()
|
||||
|
||||
if (artworksFile.exists()) {
|
||||
try {
|
||||
artworksFile.bufferedReader().use { artworks = Gson().fromJson(it, object : TypeToken<HashMap<String, ArtworkInfo>>() {}.type) }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
artworks = HashMap()
|
||||
}
|
||||
} else {
|
||||
artworks = HashMap()
|
||||
}
|
||||
|
||||
if (artworks.count() == 0) {
|
||||
putResourceArtworkToCatalog(R.drawable.card_default, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru006, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru007, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru011, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru012, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru013, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru014, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru015, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru016, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru020, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru021, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru022, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru023, true)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru024, true)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru028, true)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru029, true)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ru030, true)
|
||||
}
|
||||
if (batchesFile.exists()) {
|
||||
try {
|
||||
batchesFile.bufferedReader().use { batches = Gson().fromJson(it, object : TypeToken<HashMap<String, BatchInfo>>() {}.type) }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
batches = HashMap()
|
||||
}
|
||||
} else {
|
||||
batches = HashMap()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getArtworkFile(artworkId: String): File {
|
||||
return File(cacheDir, "$artworkId.png")
|
||||
}
|
||||
|
||||
private fun loadArtworkBitmapFromFile(artworkId: String): Bitmap? {
|
||||
return try {
|
||||
BitmapFactory.decodeFile(getArtworkFile(artworkId).absolutePath)
|
||||
} catch (E: Exception) {
|
||||
E.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveArtworkBitmapToFile(artworkId: String, data: ByteArray) {
|
||||
val file = getArtworkFile(artworkId)
|
||||
file.writeBytes(data)
|
||||
}
|
||||
|
||||
fun checkNeedUpdateArtwork(artwork: CardVerifyAndGetInfo.Response.Item.ArtworkInfo?): Boolean {
|
||||
if (artwork == null || artwork.id.isBlank()) return false
|
||||
val localArtwork = artworks[artwork.id] ?: return true
|
||||
|
||||
if (localArtwork.hash == artwork.hash) return false
|
||||
|
||||
|
||||
return localArtwork.updateDate == null || localArtwork.updateDate.before(artwork.getUpdateDate())
|
||||
}
|
||||
|
||||
fun checkBatchInfoChanged(card: TangemCard, result: CardVerifyAndGetInfo.Response.Item): Boolean {
|
||||
var sData: String? = result.substitution?.data
|
||||
var sSignature: String? = result.substitution?.signature
|
||||
if (card.batch != result.batch) {
|
||||
Log.e("LocalStorage", "Invalid batch received!")
|
||||
return false
|
||||
}
|
||||
if (!BatchInfo.CardDataSubstitution.verifySignature(card, sData, sSignature)) {
|
||||
sData = null
|
||||
sSignature = null
|
||||
}
|
||||
val batchInfo = batches[result.batch]
|
||||
if (batchInfo == null || batchInfo.artworkId?.toLowerCase() != result.artwork?.id?.toLowerCase() || batchInfo.dataSubstitution != sData) {
|
||||
putBatchToCatalog(result.batch, result.artwork?.id?.toLowerCase(), sData, sSignature)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun updateArtwork(artworkId: String, inputStream: InputStream, updateDate: Date) {
|
||||
val data = inputStream.readBytes()
|
||||
saveArtworkBitmapToFile(artworkId.toLowerCase(), data)
|
||||
putArtworkToCatalog(artworkId, false, data, updateDate, true)
|
||||
}
|
||||
|
||||
// private fun putBatchToCatalog(batch: String, resourceId: Int, forceSave: Boolean = true) {
|
||||
// putBatchToCatalog(batch, context.resources.getResourceEntryName(resourceId), null, null, forceSave)
|
||||
// }
|
||||
|
||||
private fun putBatchToCatalog(batch: String, artworkId: String?, substitution: String?, substitutionSignature: String?, forceSave: Boolean = true) {
|
||||
batches[batch] = BatchInfo(artworkId, substitution, substitutionSignature)
|
||||
if (forceSave) {
|
||||
val sBatches = Gson().toJson(batches)
|
||||
batchesFile.bufferedWriter().use { it.write(sBatches) }
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ResourceType")
|
||||
private fun putResourceArtworkToCatalog(resourceId: Int, forceSave: Boolean) {
|
||||
context.resources.openRawResource(R.drawable.card_default).use { putArtworkToCatalog(context.resources.getResourceEntryName(resourceId), true, it.readBytes(), null, forceSave) }
|
||||
}
|
||||
|
||||
private fun putArtworkToCatalog(artworkId: String, isResource: Boolean, data: ByteArray, updateDate: Date?, forceSave: Boolean = true) {
|
||||
val artworkInfo = ArtworkInfo(
|
||||
isResource, Util.bytesToHex(Util.calculateSHA256(data)), updateDate
|
||||
)
|
||||
artworks[artworkId.toLowerCase()] = artworkInfo
|
||||
if (forceSave) {
|
||||
val sArtworks = Gson().toJson(artworks)
|
||||
artworksFile.bufferedWriter().use { it.write(sArtworks) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getArtworkBitmap(artworkId: String): Bitmap? {
|
||||
if (artworkId.isBlank()) return null
|
||||
val info = artworks[artworkId.toLowerCase()] ?: return null
|
||||
return if (info.isResource) {
|
||||
val resID = context.resources.getIdentifier(artworkId, "drawable", context.packageName)
|
||||
if (resID == 0) return null
|
||||
BitmapFactory.decodeResource(context.resources, resID)
|
||||
} else {
|
||||
loadArtworkBitmapFromFile(artworkId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultArtworkBitmap(): Bitmap {
|
||||
val defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default)
|
||||
val info = artworks[defaultArtworkId]
|
||||
var bitmap: Bitmap? = null
|
||||
if (info != null && !info.isResource) {
|
||||
bitmap = loadArtworkBitmapFromFile(defaultArtworkId)
|
||||
}
|
||||
if (bitmap == null) return BitmapFactory.decodeResource(context.resources, R.drawable.card_default)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
fun getCardArtworkBitmap(card: TangemCard): Bitmap {
|
||||
// special cases (first series of cards, hardcode CID->artwork), on new series batch<->artwork
|
||||
val hexCID = Util.bytesToHex(card.cid)
|
||||
val artworkResourceId: Int? = when {
|
||||
hexCID in "AA01000000000000".."AA01000000004999" -> R.drawable.card_ru006
|
||||
hexCID in "AA01000000005000".."AA01000000009999" -> R.drawable.card_ru007
|
||||
|
||||
hexCID in "AE01000000000000".."AE01000000004999" -> R.drawable.card_ru006
|
||||
hexCID in "AE01000000005000".."AE01000000009999" -> R.drawable.card_ru007
|
||||
|
||||
hexCID in "CB01000000000000".."CB01000000009999" -> R.drawable.card_ru006
|
||||
hexCID in "CB01000000010000".."CB01000000019999" -> R.drawable.card_ru007
|
||||
|
||||
hexCID in "CB01000000020000".."CB01000000039999" -> R.drawable.card_ru006
|
||||
hexCID in "CB01000000040000".."CB01000000059999" -> R.drawable.card_ru007
|
||||
|
||||
hexCID in "CB02000000000000".."CB02000000024999" -> R.drawable.card_ru006
|
||||
hexCID in "CB02000000025000".."CB02000000049999" -> R.drawable.card_ru007
|
||||
|
||||
hexCID in "CB05000010000000".."CB05000010009999" -> R.drawable.card_ru006
|
||||
|
||||
card.batch == "0004" -> R.drawable.card_ru006
|
||||
card.batch == "0006" -> R.drawable.card_ru006
|
||||
card.batch == "0010" -> R.drawable.card_ru006
|
||||
card.batch == "0005" -> R.drawable.card_ru007
|
||||
card.batch == "0007" -> R.drawable.card_ru007
|
||||
card.batch == "0011" -> R.drawable.card_ru007
|
||||
card.batch == "0012" -> R.drawable.card_ru011
|
||||
card.batch == "0013" -> R.drawable.card_ru012
|
||||
card.batch == "0014" -> R.drawable.card_ru006
|
||||
card.batch == "0015" -> R.drawable.card_ru020
|
||||
card.batch == "0016" -> R.drawable.card_ru021
|
||||
card.batch == "0017" -> R.drawable.card_ru013
|
||||
card.batch == "0019" -> R.drawable.card_ru016
|
||||
card.batch == "001A" -> R.drawable.card_ru014
|
||||
card.batch == "001B" -> R.drawable.card_ru015
|
||||
card.batch == "001C" -> R.drawable.card_ru023
|
||||
card.batch == "001D" -> R.drawable.card_ru022
|
||||
card.batch == "001E" -> R.drawable.card_ru024
|
||||
card.batch == "001F" -> R.drawable.card_ru028
|
||||
card.batch == "0018" -> R.drawable.card_ru029
|
||||
card.batch == "0020" -> R.drawable.card_ru030
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (artworkResourceId != null) {
|
||||
val artworkId = context.resources.getResourceEntryName(artworkResourceId)
|
||||
return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap()
|
||||
}
|
||||
val batchInfo = batches[card.batch] ?: return getDefaultArtworkBitmap()
|
||||
val artworkId = batchInfo.artworkId ?: return getDefaultArtworkBitmap()
|
||||
return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap()
|
||||
}
|
||||
|
||||
fun applySubstitution(card: TangemCard) {
|
||||
val batchInfo = batches[card.batch] ?: return
|
||||
val substitution = batchInfo.getDataSubstitution(card) ?: return
|
||||
substitution.applyToCard(card)
|
||||
}
|
||||
|
||||
private data class ArtworkInfo(
|
||||
val isResource: Boolean,
|
||||
val hash: String,
|
||||
val updateDate: Date?
|
||||
)
|
||||
|
||||
private data class BatchInfo(
|
||||
val artworkId: String?,
|
||||
val dataSubstitution: String?,
|
||||
val dataSubstitutionSignature: String?
|
||||
) {
|
||||
|
||||
fun getDataSubstitution(card: TangemCard): CardDataSubstitution? {
|
||||
return if (CardDataSubstitution.verifySignature(card, dataSubstitution, dataSubstitutionSignature)) {
|
||||
Gson().fromJson(dataSubstitution, CardDataSubstitution::class.java)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
class CardDataSubstitution(
|
||||
@SerializedName("token_symbol")
|
||||
private val tokenSymbol: String?,
|
||||
@SerializedName("token_decimal")
|
||||
private val tokenDecimal: Int?,
|
||||
@SerializedName("token_contract_address")
|
||||
private val contractAddress: String?
|
||||
) {
|
||||
fun applyToCard(card: TangemCard) {
|
||||
if (tokenSymbol != null && (card.tokenSymbol.isNullOrBlank() || card.tokenSymbol.toLowerCase() == "not defined")) card.tokenSymbol = tokenSymbol
|
||||
if (tokenDecimal != null && card.tokensDecimal == 0) card.tokensDecimal = tokenDecimal
|
||||
if (contractAddress != null && (card.contractAddress.isNullOrBlank() || card.contractAddress.toLowerCase() == "not defined")) card.contractAddress = contractAddress
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun verifySignature(card: TangemCard, substitutionData: String?, substitutionSignature: String?): Boolean {
|
||||
if (substitutionData == null && substitutionSignature == null) return true
|
||||
if (substitutionData == null || substitutionSignature == null) return false
|
||||
val dataToSign = card.batch.toByteArray(StandardCharsets.UTF_8) + substitutionData.toByteArray(StandardCharsets.UTF_8)
|
||||
return try {
|
||||
CardCrypto.VerifySignature(card.issuerPublicDataKey, dataToSign, Util.hexToBytes(substitutionSignature))
|
||||
} catch (E: Exception) {
|
||||
E.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
package com.tangem.data.db;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
/**
|
||||
* Created by dvol on 12.09.2017.
|
||||
* Global PIN Storage
|
||||
*/
|
||||
|
||||
public class PINStorage {
|
||||
private static String mSavedPIN, mUserPIN, mLastUsedPIN, mEncryptedPIN, mPIN2;
|
||||
private static SharedPreferences sharedPreferences = null;
|
||||
|
||||
public static void init(Context context) {
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
mSavedPIN = sharedPreferences.getString("SavedPIN", null);
|
||||
mUserPIN = null;
|
||||
mLastUsedPIN = null;
|
||||
mEncryptedPIN = null;
|
||||
mPIN2 = null;
|
||||
}
|
||||
|
||||
|
||||
public static List<String> getPINs() {
|
||||
ArrayList<String> result = new ArrayList<>();
|
||||
if (mLastUsedPIN != null) result.add(mLastUsedPIN);
|
||||
if (mEncryptedPIN != null && !result.contains(mEncryptedPIN)) result.add(mEncryptedPIN);
|
||||
if (mUserPIN != null && !result.contains(mUserPIN)) result.add(mUserPIN);
|
||||
if (mSavedPIN != null && !result.contains(mSavedPIN)) result.add(mSavedPIN);
|
||||
if (!result.contains(CardProtocol.DefaultPIN)) result.add(CardProtocol.DefaultPIN);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void setLastUsedPIN(String PIN) {
|
||||
mLastUsedPIN = PIN;
|
||||
}
|
||||
|
||||
public static void setUserPIN(String PIN) {
|
||||
mUserPIN = PIN;
|
||||
}
|
||||
|
||||
public static void setPIN2(String PIN) {
|
||||
mPIN2 = PIN;
|
||||
}
|
||||
|
||||
public static void savePIN(String PIN) {
|
||||
mSavedPIN = PIN;
|
||||
if (mSavedPIN != null && !mSavedPIN.isEmpty()) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString("SavedPIN", mSavedPIN);
|
||||
editor.apply();
|
||||
} else {
|
||||
deletePIN();
|
||||
}
|
||||
}
|
||||
|
||||
public static void deletePIN() {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
if (mSavedPIN != null && mSavedPIN.equals(mLastUsedPIN)) {
|
||||
mLastUsedPIN = null;
|
||||
}
|
||||
mSavedPIN = null;
|
||||
editor.remove("SavedPIN");
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void saveEncryptedPIN(Cipher cipher, String PIN) {
|
||||
try {
|
||||
byte[] iv = cipher.getIV();
|
||||
byte[] bytes = cipher.doFinal(PIN.getBytes());
|
||||
String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP);
|
||||
String sIV = Base64.encodeToString(iv, Base64.NO_WRAP);
|
||||
|
||||
// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV));
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString("EncryptedPIN", encryptedPIN);
|
||||
editor.putString("EncryptedIV", sIV);
|
||||
editor.apply();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] loadEncryptedIV() {
|
||||
String sIV = sharedPreferences.getString("EncryptedIV", "");
|
||||
// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV));
|
||||
|
||||
return Base64.decode(sIV, Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
public static String loadEncryptedPIN(Cipher cipher) {
|
||||
String encryptedPIN = sharedPreferences.getString("EncryptedPIN", null);
|
||||
|
||||
try {
|
||||
byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP);
|
||||
mEncryptedPIN = new String(cipher.doFinal(bytes));
|
||||
// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
mEncryptedPIN = null;
|
||||
}
|
||||
return mEncryptedPIN;
|
||||
}
|
||||
|
||||
public static boolean haveEncryptedPIN() {
|
||||
return sharedPreferences.getString("EncryptedPIN", null) != null;
|
||||
}
|
||||
|
||||
public static void deleteEncryptedPIN() {
|
||||
if (mEncryptedPIN != null && mEncryptedPIN.equals(mLastUsedPIN)) {
|
||||
mLastUsedPIN = null;
|
||||
}
|
||||
mEncryptedPIN = null;
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove("EncryptedPIN");
|
||||
editor.remove("EncryptedIV");
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void saveEncryptedPIN2(Cipher cipher, String PIN) {
|
||||
try {
|
||||
byte[] iv = cipher.getIV();
|
||||
byte[] bytes = cipher.doFinal(PIN.getBytes());
|
||||
String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP);
|
||||
String sIV = Base64.encodeToString(iv, Base64.NO_WRAP);
|
||||
|
||||
// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV));
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString("EncryptedPIN2", encryptedPIN);
|
||||
editor.putString("EncryptedIV2", sIV);
|
||||
editor.apply();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] loadEncryptedIV2() {
|
||||
String sIV = sharedPreferences.getString("EncryptedIV2", "");
|
||||
// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV));
|
||||
return Base64.decode(sIV, Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
public static String loadEncryptedPIN2(Cipher cipher) {
|
||||
String encryptedPIN = sharedPreferences.getString("EncryptedPIN2", null);
|
||||
try {
|
||||
byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP);
|
||||
mPIN2 = new String(cipher.doFinal(bytes));
|
||||
// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
mPIN2 = null;
|
||||
}
|
||||
return mPIN2;
|
||||
}
|
||||
|
||||
public static boolean haveEncryptedPIN2() {
|
||||
return sharedPreferences.getString("EncryptedPIN2", null) != null;
|
||||
}
|
||||
|
||||
public static void deleteEncryptedPIN2() {
|
||||
mPIN2 = null;
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove("EncryptedPIN2");
|
||||
editor.remove("EncryptedIV2");
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static String getPIN2() {
|
||||
return mPIN2;
|
||||
}
|
||||
|
||||
public static boolean isDefaultPIN(String pin) {
|
||||
return (CardProtocol.DefaultPIN.equals(pin));
|
||||
}
|
||||
|
||||
public static boolean isDefaultPIN2(String pin2) {
|
||||
return (CardProtocol.DefaultPIN2.equals(pin2));
|
||||
}
|
||||
|
||||
public static String getDefaultPIN() {
|
||||
return CardProtocol.DefaultPIN;
|
||||
}
|
||||
|
||||
public static String getDefaultPIN2() {
|
||||
return CardProtocol.DefaultPIN2;
|
||||
}
|
||||
|
||||
public static boolean needInit() {
|
||||
return sharedPreferences == null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import android.security.keystore.KeyGenParameterSpec;
|
|||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
import android.security.keystore.KeyProperties;
|
||||
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.tangemcard.data.PINStorage;
|
||||
import com.tangem.presentation.activity.PinRequestActivity;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
|||
|
|
@ -4,30 +4,22 @@ import android.annotation.SuppressLint;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
|
||||
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
//import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Call;
|
||||
|
|
@ -36,9 +28,7 @@ import retrofit2.converter.gson.GsonConverterFactory;
|
|||
import retrofit2.http.Body;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.Headers;
|
||||
import retrofit2.http.Multipart;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Part;
|
||||
|
||||
/**
|
||||
* HTTP
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
|
@ -12,7 +11,7 @@ import com.google.gson.annotations.SerializedName;
|
|||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
|
||||
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import android.annotation.SuppressLint;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import org.spongycastle.util.encoders.Base64;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import android.util.Log;
|
|||
import com.tangem.App;
|
||||
import com.tangem.data.network.model.CardVerifyAndGetInfo;
|
||||
import com.tangem.data.network.model.RateInfoResponse;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.data.network;
|
|||
import android.util.Log;
|
||||
|
||||
import com.tangem.App;
|
||||
import com.tangem.domain.wallet.Blockchain;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.tangemcard.data.Blockchain;
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
import com.tangem.domain.wallet.bch.BitcoinCashNode;
|
||||
import com.tangem.domain.wallet.btc.BitcoinNode;
|
||||
import com.tangem.domain.wallet.btc.BitcoinNodeTestNet;
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
package com.tangem.data.nfc;
|
||||
|
||||
import android.content.Context;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.NfcManager;
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
|
||||
public class CreateNewWalletTask extends Thread {
|
||||
public static final String TAG = CreateNewWalletTask.class.getSimpleName();
|
||||
|
||||
private Context mContext;
|
||||
private TangemCard mCard;
|
||||
private NfcManager mNfcManager;
|
||||
private IsoDep mIsoDep;
|
||||
private CardProtocol.Notifications mNotifications;
|
||||
private boolean isCancelled = false;
|
||||
|
||||
public CreateNewWalletTask(Context context, TangemCard card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
mCard = card;
|
||||
mContext = context;
|
||||
mNfcManager = nfcManager;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsoDep == null) {
|
||||
return;
|
||||
}
|
||||
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
|
||||
mNotifications.onReadStart(protocol);
|
||||
try {
|
||||
// for Samsung's bugs -
|
||||
// Workaround for the Samsung Galaxy S5 (since the
|
||||
// first connection always hangs on transceive).
|
||||
int timeout = mIsoDep.getTimeout();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.close();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.setTimeout(timeout);
|
||||
try {
|
||||
mNotifications.onReadProgress(protocol, 5);
|
||||
|
||||
Log.i(TAG, "[-- Start create new wallet --]");
|
||||
|
||||
if (isCancelled) return;
|
||||
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());
|
||||
// }
|
||||
// try {
|
||||
protocol.run_CreateWallet(PINStorage.getPIN2());
|
||||
// } finally {
|
||||
// mNotifications.onReadWait(0);
|
||||
// }
|
||||
mNotifications.onReadProgress(protocol, 60);
|
||||
if (isCancelled) return;
|
||||
|
||||
protocol.run_Read();
|
||||
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
|
||||
} finally {
|
||||
Log.i(TAG, "[-- Finish create new wallet --]");
|
||||
mNotifications.onReadFinish(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(Boolean AllowInterrupt) {
|
||||
try {
|
||||
if (isAlive()) {
|
||||
isCancelled = true;
|
||||
join(500);
|
||||
}
|
||||
if (isAlive() && AllowInterrupt) {
|
||||
interrupt();
|
||||
mNotifications.onReadCancel();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.data.nfc
|
||||
|
||||
import android.os.Build
|
||||
import com.tangem.tangemcard.nfc.NFCLocation
|
||||
|
||||
class DeviceNFCAntennaLocation {
|
||||
companion object {
|
||||
const val CARD_ON_BACK = 0
|
||||
const val CARD_ON_FRONT = 1
|
||||
const val CARD_ORIENTATION_HORIZONTAL = 0
|
||||
const val CARD_ORIENTATION_VERTICAL = 1
|
||||
}
|
||||
|
||||
var orientation: Int = 0
|
||||
var fullName: String = ""
|
||||
var x: Float = 0.toFloat()
|
||||
var y: Float = 0.toFloat()
|
||||
var z: Int = 0
|
||||
|
||||
fun getAntennaLocation() {
|
||||
val codename = Build.DEVICE
|
||||
|
||||
// default values
|
||||
this.orientation = 0
|
||||
this.fullName = ""
|
||||
this.x = 0.5f
|
||||
this.y = 0.35f
|
||||
this.z = 0
|
||||
|
||||
for (nfcLocation in NFCLocation.values()) {
|
||||
if (codename.startsWith(nfcLocation.codename)) {
|
||||
this.fullName = nfcLocation.fullName
|
||||
this.orientation = nfcLocation.orientation
|
||||
this.x = nfcLocation.x / 100f
|
||||
this.y = nfcLocation.y / 100f
|
||||
this.z = nfcLocation.z
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
package com.tangem.data.nfc;
|
||||
|
||||
import android.content.Context;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.NfcManager;
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
|
||||
public class PurgeTask extends Thread {
|
||||
public static final String TAG = PurgeTask.class.getSimpleName();
|
||||
|
||||
private String txOutAddress;
|
||||
private Context mContext;
|
||||
private TangemCard mCard;
|
||||
private NfcManager mNfcManager;
|
||||
private IsoDep mIsoDep;
|
||||
private CardProtocol.Notifications mNotifications;
|
||||
private boolean isCancelled = false;
|
||||
|
||||
public PurgeTask(Context context, TangemCard card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
mCard = card;
|
||||
mContext = context;
|
||||
mNfcManager = nfcManager;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsoDep == null) {
|
||||
return;
|
||||
}
|
||||
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
|
||||
|
||||
mNotifications.onReadStart(protocol);
|
||||
try {
|
||||
|
||||
// for Samsung's bugs - Workaround for the Samsung Galaxy S5 (since the first connection always hangs on transceive).
|
||||
int timeout = mIsoDep.getTimeout();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.close();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.setTimeout(timeout);
|
||||
try {
|
||||
mNotifications.onReadProgress(protocol, 5);
|
||||
|
||||
Log.i(TAG, "[-- Start purge --]");
|
||||
|
||||
if (isCancelled) return;
|
||||
|
||||
if (mCard.getPauseBeforePIN2() > 0) {
|
||||
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
|
||||
}
|
||||
|
||||
// try {
|
||||
protocol.run_PurgeWallet(PINStorage.getPIN2());
|
||||
// } finally {
|
||||
// mNotifications.onReadWait(0);
|
||||
// }
|
||||
|
||||
mNotifications.onReadProgress(protocol, 50);
|
||||
|
||||
protocol.run_Read();
|
||||
|
||||
mNotifications.onReadProgress(protocol, 100);
|
||||
|
||||
if (isCancelled)
|
||||
return;
|
||||
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
mNotifications.onReadWait(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
|
||||
} finally {
|
||||
Log.i(TAG, "[-- Finish purge --]");
|
||||
mNotifications.onReadFinish(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(Boolean AllowInterrupt) {
|
||||
try {
|
||||
if (isAlive()) {
|
||||
isCancelled = true;
|
||||
join(500);
|
||||
}
|
||||
if (isAlive() && AllowInterrupt) {
|
||||
interrupt();
|
||||
mNotifications.onReadCancel();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
package com.tangem.data.nfc;
|
||||
|
||||
import android.content.Context;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.NfcManager;
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ReadCardInfoTask extends Thread {
|
||||
public static final String TAG = ReadCardInfoTask.class.getSimpleName();
|
||||
|
||||
private IsoDep mIsoDep;
|
||||
private CardProtocol.Notifications mNotifications;
|
||||
private boolean isCancelled = false;
|
||||
private Context mContext;
|
||||
private NfcManager mNfcManager;
|
||||
|
||||
// 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 ReadCardInfoTask(Context context, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
mContext = context;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
mNfcManager = nfcManager;
|
||||
// ReadCardInfoTask.lastRead_UID = lastRead_UID;
|
||||
}
|
||||
|
||||
@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).
|
||||
int timeout = mIsoDep.getTimeout();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.close();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.setTimeout(timeout);
|
||||
try {
|
||||
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mNotifications);
|
||||
mNotifications.onReadStart(protocol);
|
||||
try {
|
||||
mNotifications.onReadProgress(protocol, 5);
|
||||
|
||||
byte[] UID = mIsoDep.getTag().getId();
|
||||
String sUID = Util.byteArrayToHexString(UID);
|
||||
if (!lastRead_UID.equals(sUID)) {
|
||||
resetLastReadInfo();
|
||||
}
|
||||
|
||||
Log.i(TAG, "[-- Start read card info --]");
|
||||
|
||||
if (isCancelled) return;
|
||||
|
||||
protocol.setPIN(PINStorage.getDefaultPIN());
|
||||
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
|
||||
protocol.parseReadResult();
|
||||
protocol.run_ReadWriteIssuerData();
|
||||
mNotifications.onReadProgress(protocol, 60);
|
||||
PINStorage.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(PINStorage.getDefaultPIN());
|
||||
}
|
||||
}
|
||||
|
||||
boolean pinFound = false;
|
||||
for (String PIN : PINStorage.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();
|
||||
mNotifications.onReadProgress(protocol, 60);
|
||||
PINStorage.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!");
|
||||
}
|
||||
}
|
||||
|
||||
protocol.run_CheckPIN2isDefault();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
|
||||
} finally {
|
||||
Log.i(TAG, "[-- Finish read card info --]");
|
||||
mNotifications.onReadFinish(protocol);
|
||||
}
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
mNfcManager.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,159 +0,0 @@
|
|||
package com.tangem.data.nfc;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.NfcManager;
|
||||
import com.tangem.domain.wallet.Blockchain;
|
||||
import com.tangem.domain.wallet.CoinEngine;
|
||||
import com.tangem.domain.wallet.CoinEngineFactory;
|
||||
import com.tangem.domain.wallet.TangemContext;
|
||||
import com.tangem.presentation.activity.SendTransactionActivity;
|
||||
import com.tangem.presentation.activity.SignPaymentActivity;
|
||||
import com.tangem.domain.wallet.BTCUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SignPaymentTask extends Thread {
|
||||
public static final String TAG = SignPaymentTask.class.getSimpleName();
|
||||
|
||||
private CoinEngine.Amount txAmount;
|
||||
private CoinEngine.Amount txFee;
|
||||
private Boolean txIncFee = true;
|
||||
|
||||
public void SetTransactionValue(CoinEngine.Amount amount, CoinEngine.Amount fee, Boolean incfee) {
|
||||
txAmount = amount;
|
||||
txFee = fee;
|
||||
txIncFee = incfee;
|
||||
}
|
||||
|
||||
private String txOutAddress;
|
||||
private Activity mContext;
|
||||
private TangemContext mCtx;
|
||||
private NfcManager mNfcManager;
|
||||
private IsoDep mIsoDep;
|
||||
private CardProtocol.Notifications mNotifications;
|
||||
private boolean isCancelled = false;
|
||||
|
||||
public SignPaymentTask(Activity context, TangemContext ctx, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications, CoinEngine.Amount amount, CoinEngine.Amount fee, Boolean IncFee, String outAddress) {
|
||||
mCtx=ctx;
|
||||
mContext = context;
|
||||
mNfcManager = nfcManager;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
txOutAddress = outAddress;
|
||||
SetTransactionValue(amount, fee, IncFee);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsoDep == null) {
|
||||
return;
|
||||
}
|
||||
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCtx.getCard(), mNotifications);
|
||||
|
||||
mNotifications.onReadStart(protocol);
|
||||
try {
|
||||
|
||||
// for Samsung's bugs -
|
||||
// Workaround for the Samsung Galaxy S5 (since the
|
||||
// first connection always hangs on transceive).
|
||||
int timeout = mIsoDep.getTimeout();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.close();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.setTimeout(timeout);
|
||||
try {
|
||||
mNotifications.onReadProgress(protocol, 5);
|
||||
|
||||
Log.i(TAG, "[-- Start sign payment --]");
|
||||
|
||||
if (isCancelled) return;
|
||||
protocol.run_Read(false);
|
||||
protocol.run_VerifyCard();
|
||||
|
||||
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
|
||||
|
||||
mNotifications.onReadProgress(protocol, 30);
|
||||
if (isCancelled) return;
|
||||
//
|
||||
// if (mCard.getBlockchain() == Blockchain.Ethereum) {
|
||||
// SignETH_TX(protocol);
|
||||
// } else {
|
||||
// SignBTC_TX(protocol);
|
||||
// }
|
||||
|
||||
CoinEngine engine = CoinEngineFactory.INSTANCE.create(mCtx);
|
||||
if (engine != null) {
|
||||
if (mCtx.getCard().getPauseBeforePIN2() > 0) {
|
||||
mNotifications.onReadWait(mCtx.getCard().getPauseBeforePIN2());
|
||||
}
|
||||
|
||||
byte[] tx = null;
|
||||
try {
|
||||
tx = engine.sign(txFee, txAmount, txIncFee, txOutAddress, protocol);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
} finally {
|
||||
mNotifications.onReadWait(0);
|
||||
}
|
||||
|
||||
if (tx != null) {
|
||||
// TODO - move to engine!!!
|
||||
String txStr = BTCUtils.toHex(tx);
|
||||
if (mCtx.getBlockchain() == Blockchain.Ethereum || mCtx.getBlockchain() == Blockchain.EthereumTestNet || mCtx.getBlockchain() == Blockchain.Token) {
|
||||
txStr = String.format("0x%s", txStr);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(mContext, SendTransactionActivity.class);
|
||||
mCtx.saveToIntent(intent);
|
||||
intent.putExtra(SendTransactionActivity.EXTRA_TX, txStr);
|
||||
mContext.startActivityForResult(intent, SignPaymentActivity.REQUEST_CODE_SEND_PAYMENT);
|
||||
}
|
||||
}
|
||||
mNotifications.onReadProgress(protocol, 100);
|
||||
|
||||
|
||||
if (isCancelled) return;
|
||||
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
mNotifications.onReadWait(0);
|
||||
}
|
||||
} catch (CardProtocol.TangemException_InvalidPIN e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
} catch (CardProtocol.TangemException_WrongAmount e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
|
||||
} finally {
|
||||
Log.i(TAG, "[-- Finish sign payment --]");
|
||||
mNotifications.onReadFinish(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(Boolean AllowInterrupt) {
|
||||
try {
|
||||
if (isAlive()) {
|
||||
isCancelled = true;
|
||||
join(500);
|
||||
}
|
||||
if (isAlive() && AllowInterrupt) {
|
||||
interrupt();
|
||||
mNotifications.onReadCancel();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package com.tangem.data.nfc;
|
||||
|
||||
import android.content.Context;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.NfcManager;
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
|
||||
public class SwapPINTask extends Thread {
|
||||
public static final String TAG = SwapPINTask.class.getSimpleName();
|
||||
|
||||
private Context mContext;
|
||||
private TangemCard mCard;
|
||||
private NfcManager mNfcManager;
|
||||
private String newPIN, newPIN2;
|
||||
private IsoDep mIsoDep;
|
||||
private CardProtocol.Notifications mNotifications;
|
||||
private boolean isCancelled = false;
|
||||
|
||||
public SwapPINTask(Context context, TangemCard card, NfcManager nfcManager, String newPIN, String newPIN2, IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
this.newPIN = newPIN;
|
||||
this.newPIN2 = newPIN2;
|
||||
mCard = card;
|
||||
mContext = context;
|
||||
mNfcManager = nfcManager;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsoDep == null) {
|
||||
return;
|
||||
}
|
||||
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
|
||||
|
||||
mNotifications.onReadStart(protocol);
|
||||
try {
|
||||
|
||||
// for Samsung's bugs -
|
||||
// Workaround for the Samsung Galaxy S5 (since the
|
||||
// first connection always hangs on transceive).
|
||||
int timeout = mIsoDep.getTimeout();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.close();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.setTimeout(timeout);
|
||||
try {
|
||||
|
||||
mNotifications.onReadProgress(protocol, 5);
|
||||
|
||||
Log.i(TAG, "[-- Start swap pin --]");
|
||||
|
||||
if (isCancelled) return;
|
||||
|
||||
if (mCard.getPauseBeforePIN2() > 0) {
|
||||
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
|
||||
}
|
||||
|
||||
// try {
|
||||
protocol.run_SetPIN(PINStorage.getPIN2(), newPIN, newPIN2, false);
|
||||
protocol.setPIN(newPIN);
|
||||
mCard.setPIN(newPIN);
|
||||
// } finally {
|
||||
// mNotifications.onReadWait(0);
|
||||
// }
|
||||
|
||||
mNotifications.onReadProgress(protocol, 50);
|
||||
|
||||
protocol.run_Read();
|
||||
|
||||
mNotifications.onReadProgress(protocol, 100);
|
||||
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
mNotifications.onReadWait(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
protocol.setError(e);
|
||||
|
||||
} finally {
|
||||
Log.i(TAG, "[-- Finish purge --]");
|
||||
mNotifications.onReadFinish(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(Boolean AllowInterrupt) {
|
||||
try {
|
||||
if (isAlive()) {
|
||||
isCancelled = true;
|
||||
join(500);
|
||||
}
|
||||
if (isAlive() && AllowInterrupt) {
|
||||
interrupt();
|
||||
mNotifications.onReadCancel();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
package com.tangem.data.nfc;
|
||||
|
||||
import android.content.Context;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.Firmwares;
|
||||
import com.tangem.domain.cardReader.NfcManager;
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by dvol on 04.02.2018.
|
||||
*/
|
||||
|
||||
public class VerifyCardTask extends Thread {
|
||||
public static final String TAG = VerifyCardTask.class.getSimpleName();
|
||||
|
||||
private IsoDep mIsoDep;
|
||||
private CardProtocol.Notifications mNotifications;
|
||||
private boolean isCancelled = false;
|
||||
private Context mContext;
|
||||
private TangemCard mCard;
|
||||
private NfcManager mNfcManager;
|
||||
|
||||
public VerifyCardTask(Context context, TangemCard card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
mCard = card;
|
||||
mContext = context;
|
||||
mIsoDep = isoDep;
|
||||
mNotifications = notifications;
|
||||
mNfcManager = nfcManager;
|
||||
}
|
||||
|
||||
@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).
|
||||
int timeout = mIsoDep.getTimeout();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.close();
|
||||
mIsoDep.connect();
|
||||
mIsoDep.setTimeout(timeout);
|
||||
try {
|
||||
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
|
||||
mNotifications.onReadStart(protocol);
|
||||
try {
|
||||
mNotifications.onReadProgress(protocol, 5);
|
||||
|
||||
Log.i(TAG, "[-- Start verify card --]");
|
||||
|
||||
if (isCancelled) return;
|
||||
|
||||
String PIN = mCard.getPIN();
|
||||
protocol.setPIN(PIN);
|
||||
protocol.run_Read(false);
|
||||
PINStorage.setLastUsedPIN(PIN);
|
||||
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;
|
||||
Firmwares.VerifyCodeRecord record=Firmwares.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);
|
||||
|
||||
} finally {
|
||||
Log.i(TAG, "[-- Finish verify card --]");
|
||||
mNotifications.onReadFinish(protocol);
|
||||
}
|
||||
} finally {
|
||||
mNfcManager.ignoreTag(mIsoDep.getTag());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(Boolean AllowInterrupt) {
|
||||
try {
|
||||
if (isAlive()) {
|
||||
isCancelled = true;
|
||||
join(500);
|
||||
}
|
||||
if (isAlive() && AllowInterrupt) {
|
||||
interrupt();
|
||||
mNotifications.onReadCancel();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import android.app.Activity
|
|||
import android.nfc.Tag
|
||||
import android.os.Bundle
|
||||
import com.tangem.domain.wallet.CoinData
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.presentation.activity.EmptyWalletActivity
|
||||
import com.tangem.presentation.activity.LoadedWalletActivity
|
||||
import com.tangem.presentation.activity.MainActivity
|
||||
|
|
|
|||
|
|
@ -1,177 +0,0 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.util.Util;
|
||||
|
||||
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.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
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);
|
||||
}
|
||||
|
||||
public static PublicKey LoadPublicKey(byte[] publicKeyArray) throws Exception {
|
||||
if( publicKeyArray==null ) throw new Exception("Public key not specified!");
|
||||
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);
|
||||
}
|
||||
|
||||
public static boolean VerifySignature(byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
public static byte[] Signature(byte[] privateKeyArray, byte[] data) throws Exception {
|
||||
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;
|
||||
}
|
||||
|
||||
public static byte[] GeneratePublicKey(byte[] privateKeyArray) throws NoSuchProviderException, NoSuchAlgorithmException {
|
||||
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
|
||||
|
||||
byte[] publicKeyArray = spec.getG().multiply(new BigInteger(1,privateKeyArray)).getEncoded(false);
|
||||
|
||||
return publicKeyArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
|
||||
{
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
|
||||
byte[] mEncryptedData = cipher.doFinal(data);
|
||||
return mEncryptedData;
|
||||
}
|
||||
|
||||
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.domain.cardReader;
|
||||
|
||||
import com.tangem.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,64 +0,0 @@
|
|||
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 Firmwares {
|
||||
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 {
|
||||
|
||||
try {
|
||||
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)) & 0xFFFFFF) % 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;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
/**
|
||||
* 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,167 +0,0 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import com.tangem.presentation.dialog.NfcEnableDialog;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class NfcManager {
|
||||
public static final String TAG = NfcManager.class.getSimpleName();
|
||||
|
||||
// reader mode flags: listen for type A (not B), skipping ndef check
|
||||
private static final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS;
|
||||
|
||||
private NfcAdapter nfcAdapter;
|
||||
private NfcEnableDialog mEnableNfcDialog;
|
||||
private FragmentActivity activity;
|
||||
private NfcAdapter.ReaderCallback mReaderCallback;
|
||||
|
||||
private boolean broadcomWorkaround = false;
|
||||
private static final int DELAY_PRESENCE = 1500;
|
||||
|
||||
|
||||
public NfcManager(FragmentActivity activity, NfcAdapter.ReaderCallback readerCallback) {
|
||||
this.activity = activity;
|
||||
mReaderCallback = readerCallback;
|
||||
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
// register broadcast receiver
|
||||
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
|
||||
activity.registerReceiver(mBroadcastReceiver, filter);
|
||||
|
||||
if (nfcAdapter == null || !nfcAdapter.isEnabled())
|
||||
showNFCEnableDialog();
|
||||
else
|
||||
enableReaderMode();
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
activity.unregisterReceiver(mBroadcastReceiver);
|
||||
disableReaderMode();
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
if (mEnableNfcDialog != null) {
|
||||
mEnableNfcDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public void ignoreTag(Tag tag) throws IOException {
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
// nfcAdapter.ignore(tag, 500, null, null);
|
||||
// }else{
|
||||
IsoDep isoDep = IsoDep.get(tag);
|
||||
if (isoDep != null) {
|
||||
isoDep.close();
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
int errorCount = 0;
|
||||
|
||||
public void notifyReadResult(boolean success) {
|
||||
// if (success) {
|
||||
// errorCount = 0;
|
||||
// } else {
|
||||
// errorCount++;
|
||||
// }
|
||||
// if (errorCount >= 3) {
|
||||
// disableReaderMode();
|
||||
// nfcAdapter = null;
|
||||
//// Toast.makeText(activity,"NFC restarted!",Toast.LENGTH_SHORT).show();
|
||||
// activity.runOnUiThread(() -> {
|
||||
// nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
|
||||
// enableReaderMode();
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
private void showNFCEnableDialog() {
|
||||
mEnableNfcDialog = new NfcEnableDialog();
|
||||
mEnableNfcDialog.show(activity.getSupportFragmentManager(), NfcEnableDialog.Companion.getTAG());
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (action == null)
|
||||
return;
|
||||
if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) {
|
||||
int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_ON);
|
||||
if (state == NfcAdapter.STATE_ON || state == NfcAdapter.STATE_TURNING_ON) {
|
||||
// Log.d(TAG, "state: " + state + " , dialog: " + mEnableNfcDialog);
|
||||
if (mEnableNfcDialog != null) {
|
||||
mEnableNfcDialog.dismiss();
|
||||
}
|
||||
if (state == NfcAdapter.STATE_ON) {
|
||||
enableReaderMode();
|
||||
}
|
||||
} else {
|
||||
if (mEnableNfcDialog == null || !mEnableNfcDialog.isVisible()) {
|
||||
showNFCEnableDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private void enableReaderMode() {
|
||||
Bundle options = new Bundle();
|
||||
if (broadcomWorkaround) {
|
||||
/* This is a work around for some Broadcom chipsets that does
|
||||
* the presence check by sending commands that interrupt the
|
||||
* processing of the ongoing command.
|
||||
*/
|
||||
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, DELAY_PRESENCE);
|
||||
}
|
||||
nfcAdapter.enableReaderMode(activity, mReaderCallback, READER_FLAGS, options);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private void disableReaderMode() {
|
||||
if (nfcAdapter != null) {
|
||||
nfcAdapter.disableReaderMode(activity);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int REQUEST_NFC_PERMISSIONS = 1;
|
||||
private static String[] PERMISSIONS_NFC = {
|
||||
Manifest.permission.NFC
|
||||
};
|
||||
|
||||
// checks if the app has NFC permission if the app does not has permission then the user will be prompted to grant permissions
|
||||
public static void verifyPermissions(Activity activity) {
|
||||
// Check if we have write permission
|
||||
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.NFC);
|
||||
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
// We don't have permission so prompt the user
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
PERMISSIONS_NFC,
|
||||
REQUEST_NFC_PERMISSIONS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
|
||||
package com.tangem.domain.cardReader;
|
||||
|
||||
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,137 +0,0 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
import com.tangem.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.domain.cardReader;
|
||||
|
||||
/**
|
||||
* 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,27 +0,0 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
|
||||
}
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
import com.tangem.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);
|
||||
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 (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());
|
||||
sb.append("]");
|
||||
return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), sb.toString());
|
||||
}
|
||||
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.domain.cardReader;
|
||||
|
||||
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,74 +0,0 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
/**
|
||||
* Created by dvol on 23.06.2017.
|
||||
*/
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.tangem.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(@NonNull 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,21 +11,14 @@ import com.tangem.domain.wallet.btc.BitcoinOutputStream;
|
|||
import com.tangem.domain.wallet.btc.BtcData;
|
||||
import com.tangem.util.CryptoUtil;
|
||||
import com.tangem.util.FormatUtil;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "TryWithIdenticalCatches", "unused"})
|
||||
public final class BTCUtils {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.wallet;
|
||||
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
public class BalanceValidator {
|
||||
|
|
|
|||
|
|
@ -1,103 +0,0 @@
|
|||
package com.tangem.domain.wallet;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
/**
|
||||
* Created by dvol on 06.08.2017.
|
||||
*/
|
||||
public enum Blockchain {
|
||||
Unknown("", "", 1.0, R.drawable.ic_logo_unknown, ""),
|
||||
Bitcoin("BTC", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"),
|
||||
BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"),
|
||||
Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
|
||||
EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
|
||||
Token("Token", "ERC20", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
|
||||
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash");
|
||||
|
||||
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
|
||||
mID = ID;
|
||||
mCurrency = currency;
|
||||
// mMultiplier = multiplier;
|
||||
mImageResource = imageResource;
|
||||
mOfficialName = officialName;
|
||||
}
|
||||
|
||||
private String mID, mOfficialName;
|
||||
//private double mMultiplier;
|
||||
private String mCurrency;
|
||||
private int mImageResource;
|
||||
|
||||
public String getID() {
|
||||
return mID;
|
||||
}
|
||||
|
||||
public String getOfficialName() {
|
||||
return mOfficialName;
|
||||
}
|
||||
|
||||
// public double getMultiplier() {
|
||||
// return mMultiplier;
|
||||
// }
|
||||
|
||||
public String getCurrency() {
|
||||
return mCurrency;
|
||||
}
|
||||
|
||||
public static Blockchain fromId(String id) {
|
||||
for (Blockchain blockchain : values()) {
|
||||
if (blockchain.getID().equals(id)) return blockchain;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Blockchain fromCurrency(String currency) {
|
||||
for (Blockchain blockchain : values()) {
|
||||
if (blockchain.getCurrency() == currency) return blockchain;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] getCurrencies() {
|
||||
String[] result = new String[values().length - 1];
|
||||
for (int i = 0; i < result.length - 1; i++) {
|
||||
result[i] = values()[i + 1].getCurrency();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getImageResource() {
|
||||
return mImageResource;
|
||||
}
|
||||
|
||||
public int getImageResource(android.content.Context context, String name) {
|
||||
if (Strings.isNullOrEmpty(name))
|
||||
return getImageResource();
|
||||
|
||||
name = name.toLowerCase();
|
||||
|
||||
int resourceId = context.getResources().getIdentifier(name + "_token", "drawable", context.getPackageName());
|
||||
|
||||
if (resourceId <= 0)
|
||||
return R.drawable.ic_logo_ethereum;
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public static int getLogoImageResource(String blockchainID, String symbolName) {
|
||||
switch (blockchainID) {
|
||||
case "BTC":
|
||||
return R.drawable.ic_logo_bitcoin;
|
||||
|
||||
case "Token":
|
||||
if (symbolName.equals("SEED"))
|
||||
return R.drawable.ic_logo_seed;
|
||||
else
|
||||
return R.drawable.ic_logo_ethereum;
|
||||
|
||||
case "ETH":
|
||||
return R.drawable.ic_logo_ethereum;
|
||||
}
|
||||
return R.drawable.tangem2;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ package com.tangem.domain.wallet;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import com.tangem.tangemcard.data.Blockchain;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public abstract class CoinData {
|
||||
|
|
@ -12,6 +12,24 @@ public abstract class CoinData {
|
|||
public CoinData() {
|
||||
}
|
||||
|
||||
private String wallet;
|
||||
|
||||
public void setWallet(String wallet) {
|
||||
this.wallet = wallet;
|
||||
}
|
||||
|
||||
public String getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
|
||||
public String getShortWalletString() {
|
||||
if (wallet.length() < 22) {
|
||||
return wallet;
|
||||
} else {
|
||||
return wallet.substring(0, 10) + "......" + wallet.substring(wallet.length() - 10, wallet.length());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBalanceReceived() {
|
||||
return balanceReceived;
|
||||
}
|
||||
|
|
@ -23,6 +41,8 @@ public abstract class CoinData {
|
|||
}
|
||||
|
||||
public void loadFromBundle(Bundle B) {
|
||||
wallet = B.getString("Wallet");
|
||||
|
||||
if (B.containsKey("balanceReceived")) setBalanceReceived(B.getBoolean("balanceReceived"));
|
||||
|
||||
validationNodeDescription = B.getString("validationNodeDescription");
|
||||
|
|
@ -40,6 +60,8 @@ public abstract class CoinData {
|
|||
|
||||
public void saveToBundle(Bundle B) {
|
||||
try {
|
||||
B.putString("Wallet", wallet);
|
||||
|
||||
if (balanceEqual != null) B.putBoolean("isBalanceEqual", balanceEqual);
|
||||
|
||||
if (failedBalanceRequestCounter != null)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.domain.wallet;
|
|||
import android.net.Uri;
|
||||
import android.text.InputFilter;
|
||||
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.tangemcard.reader.CardProtocol;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.wallet.btc.BtcEngine
|
|||
import com.tangem.domain.wallet.eth.EthEngine
|
||||
import com.tangem.domain.wallet.token.TokenEngine
|
||||
import com.tangem.domain.wallet.bch.BtcCashEngine
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
|
||||
/**
|
||||
* Factory for create specific engine
|
||||
|
|
|
|||
|
|
@ -1,144 +0,0 @@
|
|||
package com.tangem.domain.wallet;
|
||||
|
||||
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 class Issuer {
|
||||
|
||||
static class KeyPair {
|
||||
String privateKey;
|
||||
String publicKey;
|
||||
|
||||
byte[] getPrivateKey() throws Exception {
|
||||
if (privateKey != null) {
|
||||
return CardCrypto.GeneratePublicKey(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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
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.domain.wallet;
|
||||
|
||||
/**
|
||||
* 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,811 +0,0 @@
|
|||
package com.tangem.domain.wallet;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.cardReader.SettingsMask;
|
||||
import com.tangem.util.Util;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Created by dvol on 16.07.2017.
|
||||
*/
|
||||
|
||||
public class TangemCard {
|
||||
|
||||
public static final String EXTRA_CARD = "Card";
|
||||
public static final String EXTRA_UID = "UID";
|
||||
|
||||
private byte[] CID;
|
||||
private Status status;
|
||||
private String wallet;
|
||||
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 Blockchain getBlockchain() {
|
||||
return Blockchain.fromId(blockchainID);
|
||||
}
|
||||
|
||||
public void setBlockchain(Blockchain blockchain) {
|
||||
blockchainID=blockchain.getID();
|
||||
}
|
||||
|
||||
public void setBlockchainID(String blockchainID) {
|
||||
this.blockchainID = blockchainID;
|
||||
}
|
||||
|
||||
public void addTokenToBlockchainName() {
|
||||
String token = getTokenSymbol();
|
||||
if (Strings.isNullOrEmpty(token))
|
||||
return;
|
||||
String oldName = getBlockchain().getOfficialName();
|
||||
//String newName = String.format("%s - %s ERC20 token", token, oldName);
|
||||
String newName = token + " <br><small><small> " + oldName + " ERC20 token</small></small>";
|
||||
blockchainName=newName;
|
||||
|
||||
}
|
||||
|
||||
private String blockchainName = "";
|
||||
|
||||
public String getBlockchainName() {
|
||||
if (Strings.isNullOrEmpty(blockchainName))
|
||||
return getBlockchain().getOfficialName();
|
||||
return blockchainName;
|
||||
}
|
||||
|
||||
public void setBlockchainIDFromCard(String blockchainID) {
|
||||
|
||||
if (Blockchain.fromId(blockchainID) != Blockchain.Ethereum && Blockchain.fromId(blockchainID) != Blockchain.EthereumTestNet)
|
||||
this.blockchainID = blockchainID;
|
||||
|
||||
if (isToken()) {
|
||||
this.blockchainID = Blockchain.Token.getID();
|
||||
addTokenToBlockchainName();
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
public String getPersonalizationDateTimeDescription() {
|
||||
return Util.formatDate(dtPersonalization);
|
||||
}
|
||||
|
||||
private int health = 0;
|
||||
|
||||
public void setHealth(int health) {
|
||||
if (health > this.health) this.health = health;
|
||||
}
|
||||
|
||||
public boolean isHealthOK() {
|
||||
return health == 0;
|
||||
}
|
||||
|
||||
private Issuer issuer = Issuer.Unknown();
|
||||
private byte[] issuerPublicDataKey = null;
|
||||
|
||||
// public void setIssuer(Issuer issuer) {
|
||||
// this.issuer = issuer;
|
||||
// }
|
||||
|
||||
public void setIssuer(String issuerID, byte[] issuerPublicDataKey) {
|
||||
this.issuerPublicDataKey = issuerPublicDataKey;
|
||||
this.issuer = Issuer.FindIssuer(issuerID, issuerPublicDataKey);
|
||||
}
|
||||
|
||||
public Issuer getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public byte[] getIssuerPublicDataKey() {
|
||||
return issuerPublicDataKey;
|
||||
}
|
||||
|
||||
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 !Strings.isNullOrEmpty(tokenSymbol);
|
||||
}
|
||||
|
||||
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 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 useDefaultPIN1() {
|
||||
return PINStorage.isDefaultPIN(getPIN());
|
||||
}
|
||||
|
||||
public Boolean useSmartSecurityDelay() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.SmartSecurityDelay) != 0;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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 void setWallet(String wallet) {
|
||||
this.wallet = wallet;
|
||||
}
|
||||
|
||||
public String getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
|
||||
public String getShortWalletString() {
|
||||
if (wallet.length() < 22) {
|
||||
return wallet;
|
||||
} else {
|
||||
return wallet.substring(0, 10) + "......" + wallet.substring(wallet.length() - 10, wallet.length());
|
||||
}
|
||||
}
|
||||
|
||||
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 void setDenomination(byte[] denomination) {
|
||||
this.Denomination = denomination;
|
||||
try {
|
||||
CoinEngine engine= CoinEngineFactory.INSTANCE.create(getBlockchain());
|
||||
CoinEngine.InternalAmount internalAmount=engine.convertToInternalAmount(denomination);
|
||||
CoinEngine.Amount amount=engine.convertToAmount(internalAmount);
|
||||
this.DenominationText = amount.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.DenominationText = "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
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.putString("Wallet", wallet);
|
||||
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 (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"));
|
||||
wallet = B.getString("Wallet");
|
||||
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")) setDenomination(B.getByteArray("Denomination"));
|
||||
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;
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ import android.content.Intent;
|
|||
import android.os.Bundle;
|
||||
|
||||
import com.tangem.Constant;
|
||||
import com.tangem.tangemcard.data.Blockchain;
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
|
||||
|
||||
public class TangemContext {
|
||||
|
|
@ -121,4 +123,17 @@ public class TangemContext {
|
|||
if (context != null) return getContext().getResources().getString(stringId);
|
||||
return "context.resources.string[" + stringId + "]";
|
||||
}
|
||||
|
||||
public void setDenomination(byte[] denomination) {
|
||||
try {
|
||||
CoinEngine engine= CoinEngineFactory.INSTANCE.create(getBlockchain());
|
||||
CoinEngine.InternalAmount internalAmount=engine.convertToInternalAmount(denomination);
|
||||
CoinEngine.Amount amount=engine.convertToAmount(internalAmount);
|
||||
card.setDenomination(denomination,amount.toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
card.setDenomination(denomination,"N/A");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@ package com.tangem.domain.wallet.bch;
|
|||
import android.net.Uri;
|
||||
import android.text.InputFilter;
|
||||
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.TLV;
|
||||
import com.tangem.tangemcard.data.PINStorage;
|
||||
import com.tangem.tangemcard.reader.CardProtocol;
|
||||
import com.tangem.tangemcard.reader.TLV;
|
||||
import com.tangem.domain.wallet.BalanceValidator;
|
||||
import com.tangem.domain.wallet.Blockchain;
|
||||
import com.tangem.tangemcard.data.Blockchain;
|
||||
import com.tangem.domain.wallet.CoinEngineFactory;
|
||||
import com.tangem.domain.wallet.btc.BtcData;
|
||||
import com.tangem.domain.wallet.CoinData;
|
||||
import com.tangem.domain.wallet.CoinEngine;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
import com.tangem.domain.wallet.TangemContext;
|
||||
import com.tangem.domain.wallet.Transaction;
|
||||
import com.tangem.domain.wallet.UnspentOutputInfo;
|
||||
|
|
@ -20,7 +20,7 @@ import com.tangem.domain.wallet.BTCUtils;
|
|||
import com.tangem.util.CryptoUtil;
|
||||
import com.tangem.util.DecimalDigitsInputFilter;
|
||||
import com.tangem.util.DerEncodingUtil;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ package com.tangem.domain.wallet.btc;
|
|||
import android.net.Uri;
|
||||
import android.text.InputFilter;
|
||||
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.TLV;
|
||||
import com.tangem.tangemcard.data.PINStorage;
|
||||
import com.tangem.tangemcard.reader.CardProtocol;
|
||||
import com.tangem.tangemcard.reader.TLV;
|
||||
import com.tangem.domain.wallet.BalanceValidator;
|
||||
import com.tangem.domain.wallet.Base58;
|
||||
import com.tangem.domain.wallet.Blockchain;
|
||||
import com.tangem.tangemcard.data.Blockchain;
|
||||
import com.tangem.domain.wallet.CoinData;
|
||||
import com.tangem.domain.wallet.CoinEngine;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
import com.tangem.domain.wallet.TangemContext;
|
||||
import com.tangem.domain.wallet.Transaction;
|
||||
import com.tangem.domain.wallet.UnspentOutputInfo;
|
||||
|
|
@ -19,7 +19,7 @@ import com.tangem.domain.wallet.BTCUtils;
|
|||
import com.tangem.util.CryptoUtil;
|
||||
import com.tangem.util.DecimalDigitsInputFilter;
|
||||
import com.tangem.util.DerEncodingUtil;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ import android.net.Uri;
|
|||
import android.text.InputFilter;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.TLV;
|
||||
import com.tangem.tangemcard.data.PINStorage;
|
||||
import com.tangem.tangemcard.reader.CardProtocol;
|
||||
import com.tangem.tangemcard.reader.TLV;
|
||||
import com.tangem.domain.wallet.BalanceValidator;
|
||||
import com.tangem.domain.wallet.Blockchain;
|
||||
import com.tangem.tangemcard.data.Blockchain;
|
||||
import com.tangem.domain.wallet.CoinData;
|
||||
import com.tangem.domain.wallet.CoinEngine;
|
||||
import com.tangem.domain.wallet.ECDSASignatureETH;
|
||||
import com.tangem.domain.wallet.EthTransaction;
|
||||
import com.tangem.domain.wallet.Issuer;
|
||||
import com.tangem.tangemcard.data.Issuer;
|
||||
import com.tangem.domain.wallet.Keccak256;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
import com.tangem.domain.wallet.TangemContext;
|
||||
import com.tangem.domain.wallet.BTCUtils;
|
||||
import com.tangem.util.CryptoUtil;
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ import android.text.InputFilter;
|
|||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.tangem.data.db.PINStorage;
|
||||
import com.tangem.domain.cardReader.CardProtocol;
|
||||
import com.tangem.domain.cardReader.TLV;
|
||||
import com.tangem.tangemcard.data.PINStorage;
|
||||
import com.tangem.tangemcard.reader.CardProtocol;
|
||||
import com.tangem.tangemcard.reader.TLV;
|
||||
import com.tangem.domain.wallet.BalanceValidator;
|
||||
import com.tangem.domain.wallet.CoinData;
|
||||
import com.tangem.domain.wallet.CoinEngine;
|
||||
import com.tangem.domain.wallet.ECDSASignatureETH;
|
||||
import com.tangem.domain.wallet.EthTransaction;
|
||||
import com.tangem.domain.wallet.Issuer;
|
||||
import com.tangem.tangemcard.data.Issuer;
|
||||
import com.tangem.domain.wallet.Keccak256;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.tangemcard.data.TangemCard;
|
||||
import com.tangem.domain.wallet.TangemContext;
|
||||
import com.tangem.domain.wallet.BTCUtils;
|
||||
import com.tangem.util.CryptoUtil;
|
||||
|
|
|
|||
|
|
@ -18,13 +18,15 @@ import com.tangem.data.network.ServerApiCommon
|
|||
import com.tangem.data.network.ServerApiElectrum
|
||||
import com.tangem.data.network.ServerApiInfura
|
||||
import com.tangem.data.network.model.InfuraResponse
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.domain.wallet.*
|
||||
import com.tangem.domain.wallet.btc.BtcData
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.tangemcard.util.Util
|
||||
import com.tangem.util.*
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_confirm_payment.*
|
||||
import org.json.JSONException
|
||||
import java.io.IOException
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
|
|
|||
|
|
@ -12,14 +12,13 @@ import android.support.v7.app.AppCompatActivity
|
|||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.nfc.CreateNewWalletTask
|
||||
import com.tangem.domain.cardReader.CardProtocol
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.tangemcard.tasks.CreateNewWalletTask
|
||||
import com.tangem.tangemcard.reader.CardProtocol
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.util.Util
|
||||
import com.tangem.tangemcard.util.Util
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_create_new_wallet.*
|
||||
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ import android.support.v7.app.AppCompatActivity
|
|||
import android.text.Html
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.nfc.VerifyCardTask
|
||||
import com.tangem.domain.cardReader.CardProtocol
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.tangemcard.tasks.VerifyCardTask
|
||||
import com.tangem.tangemcard.reader.CardProtocol
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.util.Util
|
||||
import com.tangem.tangemcard.util.Util
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_empty_wallet.*
|
||||
|
||||
|
|
|
|||
|
|
@ -26,18 +26,19 @@ import android.widget.Toast
|
|||
import com.scottyab.rootbeer.RootBeer
|
||||
import com.tangem.App
|
||||
import com.tangem.data.Logger
|
||||
import com.tangem.data.db.PINStorage
|
||||
import com.tangem.tangemcard.data.PINStorage
|
||||
import com.tangem.data.network.ServerApiCommon
|
||||
import com.tangem.data.nfc.DeviceNFCAntennaLocation
|
||||
import com.tangem.data.nfc.ReadCardInfoTask
|
||||
import com.tangem.tangemcard.data.DeviceNFCAntennaLocation
|
||||
import com.tangem.tangemcard.tasks.ReadCardInfoTask
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.domain.cardReader.CardProtocol
|
||||
import com.tangem.domain.cardReader.Firmwares
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.*
|
||||
import com.tangem.tangemcard.reader.CardProtocol
|
||||
import com.tangem.tangemcard.data.Firmwares
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.presentation.dialog.RootFoundDialog
|
||||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.tangemcard.data.Issuer
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.util.CommonUtil
|
||||
import com.tangem.util.PhoneUtility
|
||||
import com.tangem.wallet.BuildConfig
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import android.util.Log
|
|||
import android.view.View
|
||||
import android.widget.Button
|
||||
import com.tangem.data.fingerprint.StartFingerprintReaderTask
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.data.fingerprint.FingerprintHelper
|
||||
import com.tangem.data.db.PINStorage
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.tangemcard.data.PINStorage
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_pin_request.*
|
||||
import kotlinx.android.synthetic.main.layout_pin_buttons.*
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import android.widget.Button
|
|||
import android.widget.Toast
|
||||
import com.tangem.data.fingerprint.ConfirmWithFingerprintTask
|
||||
import com.tangem.data.fingerprint.FingerprintHelper
|
||||
import com.tangem.data.db.PINStorage
|
||||
import com.tangem.tangemcard.data.PINStorage
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_pin_save.*
|
||||
import kotlinx.android.synthetic.main.layout_pin_buttons.*
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ import android.support.v7.app.AppCompatActivity
|
|||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.nfc.SwapPINTask
|
||||
import com.tangem.domain.cardReader.CardProtocol
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.tangemcard.tasks.SwapPINTask
|
||||
import com.tangem.tangemcard.reader.CardProtocol
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.util.Util
|
||||
import com.tangem.tangemcard.util.Util
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_pin_swap.*
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import android.os.Bundle
|
|||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import com.tangem.data.network.CryptonitOtherApi
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.Blockchain
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.wallet.R
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import android.view.inputmethod.EditorInfo
|
|||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.network.Cryptonit
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.Blockchain
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.util.DecimalDigitsInputFilter
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import android.view.inputmethod.EditorInfo
|
|||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.network.Kraken
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.Blockchain
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.wallet.R
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import android.text.Html
|
|||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.Blockchain
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.domain.wallet.CoinEngineFactory
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.wallet.R
|
||||
|
|
|
|||
|
|
@ -12,14 +12,13 @@ import android.os.Bundle
|
|||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.nfc.PurgeTask
|
||||
import com.tangem.domain.cardReader.CardProtocol
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.tangemcard.tasks.PurgeTask
|
||||
import com.tangem.tangemcard.reader.CardProtocol
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.util.Util
|
||||
import com.tangem.tangemcard.util.Util
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_purge.*
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ import com.tangem.data.network.ElectrumRequest
|
|||
import com.tangem.data.network.ServerApiElectrum
|
||||
import com.tangem.data.network.ServerApiInfura
|
||||
import com.tangem.data.network.model.InfuraResponse
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.domain.wallet.*
|
||||
import com.tangem.domain.wallet.eth.EthData
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.R
|
||||
import java.io.IOException
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ import android.view.KeyEvent
|
|||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.nfc.SignPaymentTask
|
||||
import com.tangem.domain.cardReader.CardProtocol
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.tangemcard.tasks.SignPaymentTask
|
||||
import com.tangem.tangemcard.reader.CardProtocol
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.domain.wallet.CoinEngine
|
||||
import com.tangem.domain.wallet.TangemContext
|
||||
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
||||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.util.Util
|
||||
import com.tangem.tangemcard.util.Util
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.activity_sign_payment.*
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.App
|
|||
import com.tangem.Constant
|
||||
import com.tangem.di.Navigator
|
||||
import com.tangem.domain.wallet.CoinData
|
||||
import com.tangem.domain.wallet.TangemCard
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.presentation.fragment.VerifyCard
|
||||
import com.tangem.wallet.R
|
||||
import javax.inject.Inject
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.db.LocalStorage
|
||||
import com.tangem.data.db.PINStorage
|
||||
import com.tangem.tangemcard.data.LocalStorage
|
||||
import com.tangem.tangemcard.data.PINStorage
|
||||
import com.tangem.data.network.ElectrumRequest
|
||||
import com.tangem.data.network.ServerApiCommon
|
||||
import com.tangem.data.network.ServerApiElectrum
|
||||
import com.tangem.data.network.ServerApiInfura
|
||||
import com.tangem.data.network.model.CardVerifyAndGetInfo
|
||||
import com.tangem.data.network.model.InfuraResponse
|
||||
import com.tangem.data.nfc.VerifyCardTask
|
||||
import com.tangem.domain.cardReader.CardProtocol
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.tangemcard.tasks.VerifyCardTask
|
||||
import com.tangem.tangemcard.reader.CardProtocol
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.domain.wallet.*
|
||||
import com.tangem.domain.wallet.bch.BtcCashEngine
|
||||
import com.tangem.domain.wallet.btc.BtcData
|
||||
|
|
@ -41,7 +41,9 @@ import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
|
|||
import com.tangem.presentation.dialog.PINSwapWarningDialog
|
||||
import com.tangem.presentation.dialog.ShowQRCodeDialog
|
||||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
|
||||
import com.tangem.util.Util
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.tangemcard.util.Util
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fr_loaded_wallet.*
|
||||
|
|
|
|||
|
|
@ -13,14 +13,16 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.Toast
|
||||
import com.tangem.data.db.PINStorage
|
||||
import com.tangem.domain.cardReader.NfcManager
|
||||
import com.tangem.tangemcard.data.PINStorage
|
||||
import com.tangem.tangemcard.reader.NfcManager
|
||||
import com.tangem.domain.wallet.*
|
||||
import com.tangem.presentation.activity.CreateNewWalletActivity
|
||||
import com.tangem.presentation.activity.PurgeActivity
|
||||
import com.tangem.presentation.activity.PinRequestActivity
|
||||
import com.tangem.presentation.activity.PinSwapActivity
|
||||
import com.tangem.presentation.dialog.PINSwapWarningDialog
|
||||
import com.tangem.tangemcard.data.Blockchain
|
||||
import com.tangem.tangemcard.data.TangemCard
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fr_verify_card.*
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.util;
|
|||
import android.util.Log;
|
||||
|
||||
import com.tangem.domain.wallet.ECDSASignatureETH;
|
||||
import com.tangem.tangemcard.util.Util;
|
||||
|
||||
import org.spongycastle.asn1.ASN1EncodableVector;
|
||||
import org.spongycastle.asn1.ASN1Integer;
|
||||
|
|
|
|||
|
|
@ -1,958 +0,0 @@
|
|||
package com.tangem.util;
|
||||
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static String formatDateTime(Date date)
|
||||
{
|
||||
return formatDate(date)+" "+formatTime(date);
|
||||
}
|
||||
|
||||
public static String formatTime(Date date)
|
||||
{
|
||||
return new SimpleDateFormat("HH:mm:ss").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