initial import

This commit is contained in:
2026-05-11 19:59:10 +07:00
commit 582353e277
479 changed files with 32418 additions and 0 deletions

View File

@ -0,0 +1,275 @@
package sasc.iso7816;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import sasc.emv.EMVTags;
import sasc.util.Util;
public class TLVUtil
{
private static Tag searchTagById(byte[] tagIdBytes) { return EMVTags.getNotNull(tagIdBytes); }
private static Tag searchTagById(ByteArrayInputStream stream) { return searchTagById(readTagIdBytes(stream)); }
public static String getFormattedTagAndLength(byte[] data, int indentLength) {
StringBuilder buf = new StringBuilder();
String indent = Util.getSpaces(indentLength);
ByteArrayInputStream stream = new ByteArrayInputStream(data);
boolean firstLine = true;
while (stream.available() > 0) {
if (firstLine) {
firstLine = false;
} else {
buf.append("\n");
}
buf.append(indent);
Tag tag = searchTagById(stream);
int length = readTagLength(stream);
buf.append(Util.prettyPrintHex(tag.getTagBytes()));
buf.append(" ");
buf.append(Util.byteArrayToHexString(Util.intToByteArray(length)));
buf.append(" -- ");
buf.append(tag.getName());
}
return buf.toString();
}
public static byte[] readTagIdBytes(ByteArrayInputStream stream) {
ByteArrayOutputStream tagBAOS = new ByteArrayOutputStream();
byte tagFirstOctet = (byte)stream.read();
tagBAOS.write(tagFirstOctet);
byte MASK = 31;
if ((tagFirstOctet & MASK) == MASK) {
byte tlvIdNextOctet;
do {
int nextOctet = stream.read();
if (nextOctet < 0) {
break;
}
tlvIdNextOctet = (byte)nextOctet;
tagBAOS.write(tlvIdNextOctet);
}
while (Util.isBitSet(tlvIdNextOctet, 8) && (!Util.isBitSet(tlvIdNextOctet, 8) || (tlvIdNextOctet & 0x7F) != 0));
}
return tagBAOS.toByteArray();
}
public static int readTagLength(ByteArrayInputStream stream) {
int length, tmpLength = stream.read();
if (tmpLength < 0) {
throw new TLVException("Negative length: " + tmpLength);
}
if (tmpLength <= 127) {
length = tmpLength;
} else if (tmpLength == 128) {
length = tmpLength;
} else {
int numberOfLengthOctets = tmpLength & 0x7F;
tmpLength = 0;
for (int i = 0; i < numberOfLengthOctets; i++) {
int nextLengthOctet = stream.read();
if (nextLengthOctet < 0) {
throw new TLVException("EOS when reading length bytes");
}
tmpLength <<= 8;
tmpLength |= nextLengthOctet;
}
length = tmpLength;
}
return length;
}
public static BERTLV getNextTLV(ByteArrayInputStream stream) {
byte[] valueBytes;
if (stream.available() < 2) {
throw new TLVException("Error parsing data. Available bytes < 2 . Length=" + stream.available());
}
stream.mark(0);
int peekInt = stream.read();
byte peekByte = (byte)peekInt;
while (peekInt != -1 && (peekByte == -1 || peekByte == 0)) {
stream.mark(0);
peekInt = stream.read();
peekByte = (byte)peekInt;
}
stream.reset();
if (stream.available() < 2) {
throw new TLVException("Error parsing data. Available bytes < 2 . Length=" + stream.available());
}
byte[] tagIdBytes = readTagIdBytes(stream);
stream.mark(0);
int posBefore = stream.available();
int length = readTagLength(stream);
int posAfter = stream.available();
stream.reset();
byte[] lengthBytes = new byte[posBefore - posAfter];
if (lengthBytes.length < 1 || lengthBytes.length > 4) {
throw new TLVException("Number of length bytes must be from 1 to 4. Found " + lengthBytes.length);
}
stream.read(lengthBytes, 0, lengthBytes.length);
int rawLength = Util.byteArrayToInt(lengthBytes);
Tag tag = searchTagById(tagIdBytes);
if (rawLength == 128) {
stream.mark(0);
int prevOctet = 1;
int len = 0;
while (true) {
len++;
int curOctet = stream.read();
if (curOctet < 0) {
throw new TLVException("Error parsing data. TLV length byte indicated indefinite length, but EOS was reached before 0x0000 was found" + stream
.available());
}
if (prevOctet == 0 && curOctet == 0) {
break;
}
prevOctet = curOctet;
}
len -= 2;
valueBytes = new byte[len];
stream.reset();
stream.read(valueBytes, 0, len);
length = len;
} else {
if (stream.available() < length) {
throw new TLVException("Length byte(s) indicated " + length + " value bytes, but only " + stream.available() + " " + ((stream.available() > 1) ? "are" : "is") + " available");
}
valueBytes = new byte[length];
stream.read(valueBytes, 0, length);
}
stream.mark(0);
peekInt = stream.read();
peekByte = (byte)peekInt;
while (peekInt != -1 && (peekByte == -1 || peekByte == 0)) {
stream.mark(0);
peekInt = stream.read();
peekByte = (byte)peekInt;
}
stream.reset();
return new BERTLV(tag, length, lengthBytes, valueBytes);
}
private static String getTagValueAsString(Tag tag, byte[] value) {
StringBuilder buf = new StringBuilder();
switch (tag.getTagValueType()) {
case TEXT:
buf.append("=");
buf.append(new String(value));
break;
case NUMERIC:
buf.append("NUMERIC");
break;
case BINARY:
buf.append("BINARY");
break;
case MIXED:
buf.append("=");
buf.append(Util.getSafePrintChars(value));
break;
case DOL:
buf.append("");
break;
}
return buf.toString();
}
public static List<TagAndLength> parseTagAndLength(byte[] data) {
ByteArrayInputStream stream = new ByteArrayInputStream(data);
List<TagAndLength> tagAndLengthList = new ArrayList<TagAndLength>();
while (stream.available() > 0) {
if (stream.available() < 2) {
throw new SmartCardException("Data length < 2 : " + stream.available());
}
byte[] tagIdBytes = readTagIdBytes(stream);
int tagValueLength = readTagLength(stream);
Tag tag = searchTagById(tagIdBytes);
tagAndLengthList.add(new TagAndLength(tag, tagValueLength));
}
return tagAndLengthList;
}
}