/* * IsoApplet: A Java Card PKI applet aimiing for ISO 7816 compliance. * Copyright (C) 2014 Philip Wendland (wendlandphilip@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.pwendland.javacard.pki.isoapplet; import javacard.framework.Util; /** * \brief Utility class for TLV-realted operations. */ public class UtilTLV { /** \brief Find the position of the tag at level 1. * * \attention This method only searches level 1 of TLV encoded arrays (i.e. no nested TLVs are searched). * * \param tlv The array containing the TLV-encoded object to search. * * \param tlvOffset The position at which the TLV structure begins. * * \param tlvLength The length of the TLV structure. * * \param tag The tag to search for. * * \return The position of the tag if found or -1. */ public static short findTag(byte[] tlv, short tlvOffset, short tlvLength, byte tag) { short tagPos = tlvOffset; short len; while(tagPos < (short)(tlvLength+tlvOffset-1)) { if(tlv[tagPos] == tag) { return tagPos; } len = decodeLengthField(tlv, (short)(tagPos+1)); // Increase the position by: T length (1), L length, V length. // I.e. look at the next Tag, jump over current L and V field. // This saves execution time and ensures that no byte from V is misinterpreted. tagPos += 1 + getLengthFieldLength(len) + len; } return -1; } /** * \brief Check the consistency of the TLV structure. * * Basically, we jump from one tag to the next. At the end, we must be at the position * where the next tag would be, if it was there. If the position is any other than that, * the TLV structure is not consistent. * * \param tlv The array containing the TLV-encoded object to search. * * \param offset The position at which the TLV structure begins. * * \param length The length of the TLV structure. * * \return True if the TLV structure is valid, else false. */ public static boolean isTLVconsistent(byte[] tlv, short offset, short length) { short pos = offset; short len; while(pos < (short)(length+offset-1)) { len = decodeLengthField(tlv, (short)(pos+1)); if(len < 0) { return false; } pos += 1 + getLengthFieldLength(len) + len; } return (pos == (short)(offset+length)); } /** * \brief Decode the length field of a TLV-entry. * * The length field itself can be 1, 2 or 3 bytes long: * - If the length is between 0 and 127, it is 1 byte long. * - If the length is between 128 and 255, it is 2 bytes long. * The first byte is 0x81 to indicate this. * - If the length is between 256 and 65535, it is 3 bytes long. * The first byte is 0x82, the following 2 contain the actual length. * Note: Only lengths up to 0x7FFF (32767) are supported here, because a short in Java is signed. * * \param buf The buffer containing the length field. * * \param offset The offset at where the length field starts. * * \param length The length of the buffer (buf). This is to prevent that the index gets out of bounds. * * \return The (positive) length encoded by the length field, or in case of an error, -1. */ public static short decodeLengthField(byte[] buf, short offset) { if(buf[offset] == (byte)0x82) { // 256..65535 // Check for short overflow // (In Java, a short is signed: positive values are 0000..7FFF) if(buf[(short)(offset+1)] > (byte)0x7F) { return -1; } return Util.getShort(buf, (short)(offset+1)); } else if(buf[offset] == (byte)0x81) { return (short) ( 0x00FF & buf[(short)(offset+1)]); } else if(buf[offset] <= (byte)0x7F) { return (short) ( 0x00FF & buf[offset]); } else { return -1; } } /** * \brief Get the length of the length field of a TLV-entry. * * Note: Not the length of the value-field is returned, * but the length of the length field itself. * * \see decodeLengthField() * * \param length The decoded length from the TLV-entry. * * \return -1 in case of an error, or the length of the length field. */ public static short getLengthFieldLength(short length) { if(length < 0) { return -1; } else if(length < 128) { return 1; } else if(length < 256) { return 2; } else { return 3; } } /** * \brief Write the tag and length to a buffer. * * Only the tag and length field are written. The writing of the value field is left to the caller. * * \param tag A one- or two-byte tag. * * \param len The length that should be written to the length field. * * \param out The buffer to write the result to. * * \param outLen The length of out. * * \param outOffset The offset at which to start writing the tag. * * \return -1 in case of an error, or the length that was written. */ public static short writeTagAndLen(short tag, short len, byte[] out, short outOffset) { byte tagLen; short pos = outOffset; short outLen = (short)out.length; if((short)(tag & (short)0xFF00) != 0) { if((short)(tag & (short)0x1F00) != (short)0x1F00) { /* Missing escape marker */ return -1; } tagLen = 2; } else { tagLen = 1; } if(len < 0) { return -1; } if((short)(tagLen + getLengthFieldLength(len)) > (short)(outLen - outOffset)) { return -1; } if(tagLen == 1) { out[pos] = (byte)(tag & (short)0x00FF); } else { Util.setShort(out, pos, tag); } pos += tagLen; if(len < 128) { out[pos++] = (byte)(len & (short)0x007F); } else if(len < 256) { out[pos++] = (byte)0x81; out[pos++] = (byte)(len & (short)0x00FF); } else { out[pos++] = (byte)0x82; Util.setShort(out, pos, len); pos += 2; } return (short)(pos - outOffset); } }