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; } }