initial import
This commit is contained in:
275
modules/core/src/sasc/iso7816/TLVUtil.java
Normal file
275
modules/core/src/sasc/iso7816/TLVUtil.java
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user