276 lines
6.4 KiB
Java
276 lines
6.4 KiB
Java
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;
|
|
}
|
|
}
|