All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.devnied.emvnfccard.utils.TlvUtil Maven / Gradle / Ivy

The newest version!
package com.github.devnied.emvnfccard.utils;

/*
 * Copyright 2010 sasc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.github.devnied.emvnfccard.enums.SwEnum;
import com.github.devnied.emvnfccard.enums.TagValueTypeEnum;
import com.github.devnied.emvnfccard.exception.TlvException;
import com.github.devnied.emvnfccard.iso7816emv.EmvTags;
import com.github.devnied.emvnfccard.iso7816emv.ITag;
import com.github.devnied.emvnfccard.iso7816emv.TLV;
import com.github.devnied.emvnfccard.iso7816emv.TagAndLength;
import fr.devnied.bitlib.BytesUtils;
import net.sf.scuba.tlv.TLVInputStream;
import net.sf.scuba.tlv.TLVUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * List of utils methods to manipulate TLV
 *
 * @author MILLAU Julien
 *
 */
public final class TlvUtil {

	// Class logger
	private static final Logger LOGGER = LoggerFactory.getLogger(TlvUtil.class);

	/**
	 * Method used to find Tag with ID
	 *
	 * @param tagIdBytes
	 *            the tag to find
	 * @return the tag found
	 */
	private static ITag searchTagById(final int tagId) {
		return EmvTags.getNotNull(TLVUtil.getTagAsBytes(tagId));
	}

	// This is just a list of Tag And Lengths (eg DOLs)
	public static String getFormattedTagAndLength(final byte[] data, final int indentLength) {
		StringBuilder buf = new StringBuilder();
		String indent = getSpaces(indentLength);
		TLVInputStream stream = new TLVInputStream(new ByteArrayInputStream(data));

		boolean firstLine = true;
		try {
			while (stream.available() > 0) {
				if (firstLine) {
					firstLine = false;
				} else {
					buf.append("\n");
				}
				buf.append(indent);

				ITag tag = searchTagById(stream.readTag());
				int length = stream.readLength();

				buf.append(prettyPrintHex(tag.getTagBytes()));
				buf.append(" ");
				buf.append(String.format("%02x", length));
				buf.append(" -- ");
				buf.append(tag.getName());
			}
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(stream);
		}
		return buf.toString();
	}

	public static TLV getNextTLV(final TLVInputStream stream) {
		TLV tlv = null;
		try {
			int left = stream.available();
			if (left <= 2) {
				return tlv;
			}
			ITag tag = searchTagById(stream.readTag());
			int length = stream.readLength();
			if (stream.available() >= length) {
				tlv = new TLV(tag, length, TLVUtil.getLengthAsBytes(length), stream.readValue());
			}
		} catch (EOFException eof) {
			LOGGER.debug(eof.getMessage(), eof);
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(stream);
		}
		return tlv;
	}

	/**
	 * Method used get Tag value as String
	 *
	 * @param tag
	 *            tag type
	 * @param value
	 *            tag value
	 * @return
	 */
	private static String getTagValueAsString(final ITag tag, final 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(getSafePrintChars(value));
			break;
		case DOL:
			buf.append("");
			break;
		default:
			break;
		}

		return buf.toString();
	}

	/**
	 * Method used to parser Tag and length
	 *
	 * @param data
	 *            data to parse
	 * @return tag and length
	 */
	public static List parseTagAndLength(final byte[] data) {
		List tagAndLengthList = new ArrayList();
		if (data != null) {
			TLVInputStream stream = new TLVInputStream(new ByteArrayInputStream(data));

			try {
				while (stream.available() > 0) {
					if (stream.available() < 2) {
						throw new TlvException("Data length < 2 : " + stream.available());
					}

					ITag tag = searchTagById(stream.readTag());
					int tagValueLength = stream.readLength();

					tagAndLengthList.add(new TagAndLength(tag, tagValueLength));
				}
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			} finally {
				IOUtils.closeQuietly(stream);
			}
		}
		return tagAndLengthList;
	}

	public static String prettyPrintAPDUResponse(final byte[] data) {
		return prettyPrintAPDUResponse(data, 0);
	}

	/**
	 * Method used to get the list of TLV inside the parameter tag specified in parameter
	 *
	 * @param pData
	 *            data to parse
	 * @param pTag
	 *            tag to find
	 * @param pAdd
	 *           boolean to indicate if we nned to add the tlv to the return list
	 * @return the list of TLV tag inside
	 */
	public static List getlistTLV(final byte[] pData, final ITag pTag, final boolean pAdd) {

		List list = new ArrayList();

		TLVInputStream stream = new TLVInputStream(new ByteArrayInputStream(pData));

		try {
			while (stream.available() > 0) {

				TLV tlv = TlvUtil.getNextTLV(stream);
				if (tlv == null) {
					break;
				}
				if (pAdd) {
					list.add(tlv);
				} else if (tlv.getTag().isConstructed()) {
					list.addAll(TlvUtil.getlistTLV(tlv.getValueBytes(), pTag, tlv.getTag() == pTag));
				}
			}
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(stream);
		}

		return list;
	}

	/**
	 * Method used to get the list of TLV corresponding to tags specified in parameters
	 *
	 * @param pData
	 *            data to parse
	 * @param pTag
	 *            tags to find
	 * @return the list of TLV
	 */
	public static List getlistTLV(final byte[] pData, final ITag... pTag) {

		List list = new ArrayList();

		TLVInputStream stream = new TLVInputStream(new ByteArrayInputStream(pData));

		try {
			while (stream.available() > 0) {

				TLV tlv = TlvUtil.getNextTLV(stream);
				if (tlv == null) {
					break;
				}
				if (ArrayUtils.contains(pTag, tlv.getTag())) {
					list.add(tlv);
				} else if (tlv.getTag().isConstructed()) {
					list.addAll(TlvUtil.getlistTLV(tlv.getValueBytes(), pTag));
				}
			}
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(stream);
		}

		return list;
	}

	/**
	 * Method used to get Tag value
	 *
	 * @param pData
	 *            data
	 * @param pTag
	 *            tag to find
	 * @return tag value or null
	 */
	public static byte[] getValue(final byte[] pData, final ITag... pTag) {

		byte[] ret = null;

		if (pData != null) {
			TLVInputStream stream = new TLVInputStream(new ByteArrayInputStream(pData));

			try {
				while (stream.available() > 0) {

					TLV tlv = TlvUtil.getNextTLV(stream);
					if (tlv == null) {
						break;
					}
					if (ArrayUtils.contains(pTag, tlv.getTag())) {
						return tlv.getValueBytes();
					} else if (tlv.getTag().isConstructed()) {
						ret = TlvUtil.getValue(tlv.getValueBytes(), pTag);
						if (ret != null) {
							break;
						}
					}

				}
			} catch (IOException e) {
				LOGGER.error(e.getMessage(), e);
			} finally {
				IOUtils.closeQuietly(stream);
			}
		}

		return ret;
	}

	public static String prettyPrintAPDUResponse(final byte[] data, final int indentLength) {
		StringBuilder buf = new StringBuilder();
		TLVInputStream stream = new TLVInputStream(new ByteArrayInputStream(data));

		try {
			while (stream.available() > 0) {
				buf.append("\n");
				if (stream.available() == 2) {
					stream.mark(0);
					byte[] value = new byte[2];
					try {
						stream.read(value);
					} catch (IOException e) {
					}
					SwEnum sw = SwEnum.getSW(value);
					if (sw != null) {
						buf.append(getSpaces(0));
						buf.append(BytesUtils.bytesToString(value)).append(" -- ");
						buf.append(sw.getDetail());
						continue;
					}
					stream.reset();
				}

				buf.append(getSpaces(indentLength));

				TLV tlv = TlvUtil.getNextTLV(stream);

				if (tlv == null) {
					buf.setLength(0);
					LOGGER.debug("TLV format error");
					break;
				}

				byte[] tagBytes = tlv.getTagBytes();
				byte[] lengthBytes = tlv.getRawEncodedLengthBytes();
				byte[] valueBytes = tlv.getValueBytes();

				ITag tag = tlv.getTag();

				buf.append(prettyPrintHex(tagBytes));
				buf.append(" ");
				buf.append(prettyPrintHex(lengthBytes));
				buf.append(" -- ");
				buf.append(tag.getName());

				int extraIndent = (lengthBytes.length + tagBytes.length) * 3;

				if (tag.isConstructed()) {
					// indentLength += extraIndent; //TODO check this
					// Recursion
					buf.append(prettyPrintAPDUResponse(valueBytes, indentLength + extraIndent));
				} else {
					buf.append("\n");
					if (tag.getTagValueType() == TagValueTypeEnum.DOL) {
						buf.append(TlvUtil.getFormattedTagAndLength(valueBytes, indentLength + extraIndent));
					} else {
						buf.append(getSpaces(indentLength + extraIndent));
						buf.append(prettyPrintHex(BytesUtils.bytesToStringNoSpace(valueBytes), indentLength + extraIndent));
						buf.append(" (");
						buf.append(TlvUtil.getTagValueAsString(tag, valueBytes));
						buf.append(")");
					}
				}
			}
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		} catch (TlvException exce) {
			buf.setLength(0);
			LOGGER.debug(exce.getMessage(), exce);
		} finally {
			IOUtils.closeQuietly(stream);
		}
		return buf.toString();
	}

	public static String getSpaces(final int length) {
		return StringUtils.leftPad(StringUtils.EMPTY, length);
	}

	public static String prettyPrintHex(final String in, final int indent) {
		return prettyPrintHex(in, indent, true);
	}

	public static String prettyPrintHex(final byte[] data) {
		return prettyPrintHex(BytesUtils.bytesToStringNoSpace(data), 0, true);
	}

	public static String prettyPrintHex(final String in, final int indent, final boolean wrapLines) {
		StringBuilder buf = new StringBuilder();

		for (int i = 0; i < in.length(); i++) {
			char c = in.charAt(i);
			buf.append(c);

			int nextPos = i + 1;
			if (wrapLines && nextPos % 32 == 0 && nextPos != in.length()) {
				buf.append("\n").append(getSpaces(indent));
			} else if (nextPos % 2 == 0 && nextPos != in.length()) {
				buf.append(" ");
			}
		}
		return buf.toString();
	}

	public static String getSafePrintChars(final byte[] byteArray) {
		if (byteArray == null) {
			return "";
		}
		return getSafePrintChars(byteArray, 0, byteArray.length);
	}

	public static String getSafePrintChars(final byte[] byteArray, final int startPos, final int length) {
		if (byteArray == null) {
			return "";
		}
		if (byteArray.length < startPos + length) {
			throw new IllegalArgumentException("startPos(" + startPos + ")+length(" + length + ") > byteArray.length("
					+ byteArray.length + ")");
		}
		StringBuilder buf = new StringBuilder();
		for (int i = startPos; i < startPos + length; i++) {
			if (byteArray[i] >= (byte) 0x20 && byteArray[i] < (byte) 0x7F) {
				buf.append((char) byteArray[i]);
			} else {
				buf.append(".");
			}
		}
		return buf.toString();
	}

	/**
	 * Method used to get length of all Tags
	 *
	 * @param pList
	 *            tag length list
	 * @return the sum of tag length
	 */
	public static int getLength(final List pList) {
		int ret = 0;
		if (pList != null) {
			for (TagAndLength tl : pList) {
				ret += tl.getLength();
			}
		}
		return ret;
	}

	/**
	 * Private constructor
	 */
	private TlvUtil() {
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy