Updated on 2026-08-14
This commit is contained in:
commit
ab13ff8636
16 changed files with 530 additions and 175 deletions
|
|
@ -179,12 +179,12 @@ public class CardCrypto {
|
|||
Log.e("cardCrypto","enc:" +Util.bytesToHex(enc));
|
||||
throw new Exception("unsupported s-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc));
|
||||
}
|
||||
|
||||
|
||||
if(!VerifySignature(GeneratePublicKey(privateKeyArray), data, res))
|
||||
{
|
||||
throw new Exception("Signature self verify failed - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)+",res:"+Util.bytesToHex(res));
|
||||
}
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ public class CardCrypto {
|
|||
* @return the PBDKF2 hash of the password
|
||||
*/
|
||||
public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
|
||||
throws InvalidKeyException {
|
||||
return PBKDF2.deriveKey(password, salt, iterations);
|
||||
}
|
||||
|
||||
|
|
@ -219,22 +219,20 @@ public class CardCrypto {
|
|||
return mEncryptedData;
|
||||
}
|
||||
|
||||
public static byte[] Decrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
|
||||
try {
|
||||
public static byte[] Decrypt(byte[] key, byte[] data, boolean UsePKCS7)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
|
||||
if (UsePKCS7) {
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
|
||||
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
|
||||
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length ));
|
||||
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
|
||||
return decryptedData;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
} else {
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING");
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
|
||||
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
|
||||
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length ));
|
||||
Log.e("decrypt",Util.bytesToHex(decryptedData));
|
||||
throw e;
|
||||
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
|
||||
return decryptedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -470,7 +470,11 @@ public class CardProtocol {
|
|||
|
||||
// try read denomination
|
||||
if (tlvIssuerData != null && tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination) != null) {
|
||||
mCard.setDenomination(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value);
|
||||
if(tlvIssuerData.getTLV(TLV.Tag.TAG_DenominationText) != null) {
|
||||
mCard.setDenomination(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value, tlvIssuerData.getTLV(TLV.Tag.TAG_DenominationText).getAsString());
|
||||
}else{
|
||||
mCard.setDenomination(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value);
|
||||
}
|
||||
} else {
|
||||
mCard.clearDenomination();
|
||||
}
|
||||
|
|
@ -504,6 +508,8 @@ public class CardProtocol {
|
|||
|
||||
TLVList tlvCardData = TLVList.fromBytes(readResult.getTLV(TLV.Tag.TAG_CardData).Value);
|
||||
|
||||
mCard.setBatch(tlvCardData.getTLV(TLV.Tag.TAG_Batch).getAsHexString());
|
||||
|
||||
TLV tokenSymbol = tlvCardData.getTLV(TLV.Tag.TAG_Token_Symbol);
|
||||
TLV contractAddress = tlvCardData.getTLV(TLV.Tag.TAG_Token_Contract_Address);
|
||||
TLV tokens_decimal = tlvCardData.getTLV(TLV.Tag.TAG_Token_Decimal);
|
||||
|
|
@ -553,7 +559,7 @@ public class CardProtocol {
|
|||
} catch (Exception ee) {
|
||||
ee.printStackTrace();
|
||||
Log.e(logTag, "Cannot get issuer");
|
||||
mCard.setIssuer(Issuer.Unknown);
|
||||
mCard.setIssuer(Issuer.Unknown());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -848,7 +854,7 @@ public class CardProtocol {
|
|||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerSignature);
|
||||
}
|
||||
if (issuerData != null) {
|
||||
if (issuer == null || issuer == Issuer.Unknown)
|
||||
if (issuer == null || issuer == Issuer.Unknown())
|
||||
throw new Exception("Need known Issuer to write issuer Data");
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData);
|
||||
byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), issuerData);
|
||||
|
|
@ -905,7 +911,7 @@ public class CardProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
private byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
|
||||
public byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
|
||||
if (readResult == null) run_Read();
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII"));
|
||||
|
|
@ -978,6 +984,9 @@ public class CardProtocol {
|
|||
Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" ")));
|
||||
TLV issuerData = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data);
|
||||
TLV issuerDataSignature = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Signature);
|
||||
TLV issuerDataCounter = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Counter);
|
||||
|
||||
boolean protectIssuerDataAgainstReplay=(readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask)&SettingsMask.ProtectIssuerDataAgainstReplay)!=0;
|
||||
|
||||
if (issuerData == null || issuerDataSignature == null)
|
||||
throw new TangemException("Invalid answer format (GetIssuerData)");
|
||||
|
|
@ -985,6 +994,9 @@ public class CardProtocol {
|
|||
ByteArrayOutputStream bsDataToVerify = new ByteArrayOutputStream();
|
||||
bsDataToVerify.write(mCard.getCID());
|
||||
bsDataToVerify.write(issuerData.Value);
|
||||
if( protectIssuerDataAgainstReplay ) {
|
||||
bsDataToVerify.write(issuerDataCounter.Value);
|
||||
}
|
||||
try {
|
||||
if (CardCrypto.VerifySignature(mCard.getIssuer().getPublicDataKey(), bsDataToVerify.toByteArray(), issuerDataSignature.Value)) {
|
||||
mCard.setIssuerData(issuerData.Value, issuerDataSignature.Value);
|
||||
|
|
|
|||
62
app/src/main/java/com/tangem/domain/cardReader/FW.java
Normal file
62
app/src/main/java/com/tangem/domain/cardReader/FW.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.domain.cardReader;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.tangem.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class FW {
|
||||
private static JsonArray jaFirmwares=null;
|
||||
|
||||
public static boolean needInit() {
|
||||
return jaFirmwares == null;
|
||||
}
|
||||
|
||||
public static void Init(Context context) {
|
||||
try(InputStream is=context.getAssets().open("fw_hashes.json")) {
|
||||
try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
||||
JsonParser parser = new JsonParser();
|
||||
jaFirmwares = parser.parse(reader).getAsJsonArray();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static class VerifyCodeRecord
|
||||
{
|
||||
public String hashAlg;
|
||||
public int blockIndex;
|
||||
public int blockCount;
|
||||
public byte[] challenge;
|
||||
public byte[] digest;
|
||||
}
|
||||
|
||||
public static VerifyCodeRecord selectRandomVerifyCodeBlock(String firmwareVersion) throws IOException {
|
||||
|
||||
for(int i=0; i<jaFirmwares.size(); i++) {
|
||||
JsonObject jsVersion = jaFirmwares.get(i).getAsJsonObject();
|
||||
if( jsVersion.get("fw").getAsString().equals(firmwareVersion) )
|
||||
{
|
||||
VerifyCodeRecord result=new VerifyCodeRecord();
|
||||
result.hashAlg = "sha-256";
|
||||
JsonArray jsHashes = jsVersion.get(result.hashAlg).getAsJsonArray();
|
||||
result.challenge = Util.hexToBytes(jsVersion.get("challenge").getAsString());
|
||||
int caseIndex= Util.byteArrayToInt(Util.generateRandomBytes(4))%jsHashes.size();
|
||||
JsonObject jsRecord = jsHashes.get(caseIndex).getAsJsonObject();
|
||||
result.blockIndex = jsRecord.get("block").getAsInt();
|
||||
result.blockCount = jsRecord.get("count").getAsInt();
|
||||
result.digest = Util.hexToBytes(jsRecord.get("digest").getAsString());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.tangem.presentation.dialog.NFCEnableDialog;
|
||||
|
||||
|
|
@ -76,6 +77,27 @@ public class NfcManager {
|
|||
// }
|
||||
}
|
||||
|
||||
int errorCount=0;
|
||||
public void notifyReadResult(boolean success)
|
||||
{
|
||||
if(success)
|
||||
{
|
||||
errorCount=0;
|
||||
}else{
|
||||
errorCount++;
|
||||
}
|
||||
if( errorCount>=3 )
|
||||
{
|
||||
disableReaderMode();
|
||||
mNfcAdapter=null;
|
||||
Toast.makeText(mActivity,"NFC restarted!",Toast.LENGTH_SHORT).show();
|
||||
mActivity.runOnUiThread(()->{
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity);
|
||||
enableReaderMode();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showNFCEnableDialog() {
|
||||
mEnableNfcDialog = new NFCEnableDialog();
|
||||
mEnableNfcDialog.show(mActivity.getFragmentManager(), NFCEnableDialog.TAG);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class ResponseApdu {
|
|||
responseApdu.mSw2 = ((int) data[1] & 0xFF);
|
||||
return responseApdu;
|
||||
}else if( data.length>=18 ){
|
||||
byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2));
|
||||
byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2),true);
|
||||
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(decryptedData);
|
||||
byte[] baLength = new byte[2];
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ public class TLV {
|
|||
TAG_Token_Decimal(0xA2),
|
||||
TAG_Denomination(0xC0),
|
||||
TAG_ValidatedBalance(0xC1),
|
||||
TAG_LastSign_Date(0xC2);
|
||||
TAG_LastSign_Date(0xC2),
|
||||
TAG_DenominationText(0xC3);
|
||||
|
||||
|
||||
Tag(int Code) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue