Updated on 2026-08-14

This commit is contained in:
Tangem 2018-07-16 13:03:51 +03:00
commit ab13ff8636
16 changed files with 530 additions and 175 deletions

View file

@ -179,12 +179,12 @@ public class CardCrypto {
Log.e("cardCrypto","enc:" +Util.bytesToHex(enc));
throw new Exception("unsupported s-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc));
}
if(!VerifySignature(GeneratePublicKey(privateKeyArray), data, res))
{
throw new Exception("Signature self verify failed - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)+",res:"+Util.bytesToHex(res));
}
return res;
}
@ -206,7 +206,7 @@ public class CardCrypto {
* @return the PBDKF2 hash of the password
*/
public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations)
throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
throws InvalidKeyException {
return PBKDF2.deriveKey(password, salt, iterations);
}
@ -219,22 +219,20 @@ public class CardCrypto {
return mEncryptedData;
}
public static byte[] Decrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
try {
public static byte[] Decrypt(byte[] key, byte[] data, boolean UsePKCS7)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
if (UsePKCS7) {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length ));
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
return decryptedData;
}
catch (Exception e)
{
} else {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length ));
Log.e("decrypt",Util.bytesToHex(decryptedData));
throw e;
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
return decryptedData;
}
}
}

View file

@ -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);

View file

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

View file

@ -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);

View file

@ -59,7 +59,7 @@ public class ResponseApdu {
responseApdu.mSw2 = ((int) data[1] & 0xFF);
return responseApdu;
}else if( data.length>=18 ){
byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2));
byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2),true);
ByteArrayInputStream inputStream = new ByteArrayInputStream(decryptedData);
byte[] baLength = new byte[2];

View file

@ -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) {