237 lines
7 KiB
Java
237 lines
7 KiB
Java
package com.tangem.cardcommon.reader;
|
|
|
|
import com.tangem.cardcommon.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);
|
|
return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), SettingsMask.getDescription(iValue));
|
|
}
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
}
|