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

org.jpos.tlv.TLVMsg Maven / Gradle / Ivy

Go to download

jPOS is an ISO-8583 based financial transaction library/framework that can be customized and extended in order to implement financial interchanges.

There is a newer version: 3.0.0
Show newest version
/*
 * jPOS Project [http://jpos.org]
 * Copyright (C) 2000-2023 jPOS Software SRL
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */

package org.jpos.tlv;

import java.io.PrintStream;
import java.math.BigInteger;
import java.util.Arrays;
import org.jpos.iso.ISOUtil;
import org.jpos.util.Loggeable;

/**
 * @author bharavi
 */
public class TLVMsg implements Loggeable {

    /**
     * Value not used as tag id in accordance with ISO/IEC 7816.
     */
    private static final int SKIP_BYTE1     = 0x00;

    /**
     * Value not used as tag id in accordance with ISO/IEC 7816.
     */
    private static final int SKIP_BYTE2     = 0xFF;

    private static final int EXT_TAG_MASK   = 0x1F;

    /**
     * Enforces fixed tag size.
     * 

* Zero means that the tag size will be determined in accordance with * ISO/IEC 7816. */ private final int tagSize; /** * Enforces fixed length size. *

* Zero means that the length size will be determined in accordance with * ISO/IEC 7816. */ private final int lengthSize; private final int tag; private final byte[] value; /** * Constructs a TLV message from tag and value. * * @param tag id * @param value tag value * @deprecated In most cases, a message is created to attach it to the list. *
* It can be done by: *

{@code
     *   TLVList tl = ...;
     *   tl.append(tag, value);
     * }
* If for some reason this is not possible then a message can be created: *
{@code
     *   TLVList tl = TLVListBuilder.createInstance().build(); // or just new TLVList();
     *   tl.append(tag, value);
     *   TLVMsg tm = tl.find(tag);
     * }
* The intention is to not promote the use of TLVMsg outside. Due to * the lack of compatibility of various TLV types at TLVList.append(TLVMsg) */ @Deprecated public TLVMsg(int tag, byte[] value) throws IllegalArgumentException { this(tag, value, 0, 0); } protected TLVMsg(int tag, byte[] value, int tagSize, int lengthSize) throws IllegalArgumentException { this.tag = tag; this.value = value; this.tagSize = tagSize; this.lengthSize = lengthSize; if (tagSize == 0) verifyTag(tag); else verifyTagLength(tag); if (lengthSize > 0) verifyValue(value); } private boolean isExtTagByte(int b) { return (b & EXT_TAG_MASK) == EXT_TAG_MASK; } /** * Verify tag identifier. *

* Tag number in accordance with ISO/IEC 7816 *

    *
  1. The tag field consists of one or more consecutive bytes. It is a number. *
  2. ISO/IEC 7816 uses neither ’00’ nor ‘FF’ as tag value. *
  3. If the bits B5-B1 of the leading byte are not all set to 1, then * may they shall encode an integer equal to the tag number. Then the * tag field consists of a single byte. * Otherwise (B5-B1 set to 1 in the leading byte), the tag * field shall continue on one or more subsequent bytes. *
  4. *
* * See ISO 7816-4 Annex D.2: Tag field * * @param tag tag identifier * @throws IllegalArgumentException if tag identifier is zero or less or * it is included in the illegal ranges. */ private void verifyTag(int tag) throws IllegalArgumentException { if (tag <= 0) throw new IllegalArgumentException("Tag id must be greater than zero"); BigInteger bi = BigInteger.valueOf(tag); byte[] ba = bi.toByteArray(); if (ba[0] == 0x00) { // strip byte array if starts with 0x00 ba = Arrays.copyOfRange(ba, 1, ba.length); } int idx = 0; do { boolean byteFollows = (idx == 0 && isExtTagByte(ba[idx])) || (idx > 0 && (ba[idx] & 0x80) == 0x80); if ((ba[idx] & 0xff) == SKIP_BYTE1) { throw new IllegalArgumentException("Tag id: 0x" + Integer.toString(tag, 0x10).toUpperCase() + " cannot contain in any 0x00 byte" ); } else if ((ba[idx] & 0xff) == SKIP_BYTE2) { throw new IllegalArgumentException("Tag id: 0x" + Integer.toString(tag, 0x10).toUpperCase() + " cannot contain in any 0xff byte" ); } else if (byteFollows && ba.length <= idx + 1) { throw new IllegalArgumentException("Tag id: 0x" + Integer.toString(tag, 0x10).toUpperCase() + " shall contain subsequent byte" ); } else if (!byteFollows && idx+1 < ba.length) { throw new IllegalArgumentException("Tag id: 0x" + Integer.toString(tag, 0x10).toUpperCase() + " cannot contain subsequent byte" ); } idx++; } while (idx < ba.length); } private void verifyTagLength(int tag) throws IllegalArgumentException { if (tag < 0) throw new IllegalArgumentException("The tag id must be greater than or equals zero"); int maxTag = 1 << (tagSize << 3); maxTag -= 1; if (tag > maxTag) throw new IllegalArgumentException("The tag id cannot be greater that: " + maxTag); } private void verifyValue(byte[] value) throws IllegalArgumentException { if (value == null) return; int maxLength = 1 << (lengthSize << 3); maxLength -= 1; if (value.length > maxLength) throw new IllegalArgumentException("The tag value length cannot exceed: " + maxLength); } /** * @return tag */ public int getTag() { return tag; } /** * @return tag value */ public byte[] getValue() { return value; } /** * @return tag + length + value of the TLV Message */ public byte[] getTLV() { String hexTag = Integer.toHexString(tag); byte[] bTag = ISOUtil.hex2byte(hexTag); if (tagSize > 0) bTag = fitInArray(bTag, tagSize); byte[] bLen = getL(); byte[] bVal = getValue(); if (bVal == null) //Value can be null bVal = new byte[0]; int tLength = bTag.length + bLen.length + bVal.length; byte[] out = new byte[tLength]; System.arraycopy(bTag, 0, out, 0, bTag.length); System.arraycopy(bLen, 0, out, bTag.length, bLen.length); System.arraycopy(bVal, 0, out, bTag.length + bLen.length, bVal.length ); return out; } private byte[] fitInArray(byte[] bytes, int length) { byte[] ret = new byte[length]; if (bytes.length <= length) // copy bytes at end of ret System.arraycopy(bytes, 0, ret, ret.length - bytes.length, bytes.length); else // copy last tagSize bytes of bytes System.arraycopy(bytes, bytes.length - ret.length, ret, 0, ret.length); return ret; } private byte[] getLengthArray() { int length = 0; if (value != null) length = value.length; byte[] ret = BigInteger.valueOf(length).toByteArray(); return fitInArray(ret, lengthSize); } /** * Value up to 127 can be encoded in single byte and multiple bytes are * required for length bigger than 127 * * @return encoded length */ public byte[] getL() { if (lengthSize > 0) return getLengthArray(); if (value == null) return new byte[1]; // if Length is less than 128 // set the 8bit as 0 indicating next 7 bits is the length // of the message // if length is more than 127 then, set the first bit as 1 indicating // next 7 bits will indicate the length of following bytes used for // length BigInteger bi = BigInteger.valueOf(value.length); /* Value to be encoded on multiple bytes */ byte[] rBytes = bi.toByteArray(); /* If value can be encoded on one byte */ if (value.length < 0x80) return rBytes; //we need 1 byte to indicate the length //for that is used sign byte (first 8-bits equals 0), //if it is not present it is added if (rBytes[0] > 0) rBytes = ISOUtil.concat(new byte[1], rBytes); rBytes[0] = (byte) (0x80 | rBytes.length - 1); return rBytes; } /** * @return value */ public String getStringValue() { return ISOUtil.hexString(value); } @Override public String toString(){ String t = Integer.toHexString(tag); if (t.length() % 2 > 0) t = "0" + t; return String.format("[tag: 0x%s, %s]", t ,value == null ? null : getStringValue() ); } @Override public void dump(PrintStream p, String indent) { p.print(indent); p.print(""); } private boolean isPrimitive (byte firstByte) { return (firstByte & 0x20) == 0x00; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy