/* * GidsApplet: A Java Card implementation of the GIDS (Generic Identity * Device Specification) specification * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642100%28v=vs.85%29.aspx * Copyright (C) 2016 Vincent Le Toux(vincent.letoux@mysmartlogon.com) * * It has been based on the IsoApplet * 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 com.mysmartlogon.gidsApplet; import javacard.framework.JCSystem; import javacard.framework.Util; /** * \brief class used to store DO */ public class BerTlvFile extends ElementaryFile { private static final short ELEMENT_COUNT_START = 10; private static final short ELEMENT_COUNT_MAX = 30; // set to max. 16383 private Record[] children; private byte currentNumChildren; /** * \brief Instantiate a new BER-TLV EF. No data is being added at this point. * * \param fileControlInformation The array of bytes containing the valid (!) File Control Information. * It must contain the File ID (Tag 83). No Copy is made. * * \param maxRecords The maximum amount of saved records. * * \attention No copy of the FCI is made. Do not pass any buffer that is altered * later (e.g. the apdu buffer). Max length 257 bytes as the length * of the FCI Tag (6F) must be a byte. * * \attention To be safe, use IsoFileSystem.getSafeFile() to instantiate files. * * \throw IllegalArgumentException If necessary tags in the FCI are missing. */ public BerTlvFile(short fileID, byte[] fileControlInformation) { super(fileID, fileControlInformation); this.children = new Record[ELEMENT_COUNT_START]; this.currentNumChildren = 0; } void clearContents() { short i; for(i = 0; i < currentNumChildren; i++) { children[i].clearContents(); children[i] = null; } } /** * \brief Delete a DO * * This method requests garbage collection. * * \param childNum internal index */ protected void deleteChildren(short childNum) { // Fill up empty field in children array. if(!JCSystem.isObjectDeletionSupported()) { children[childNum].clearContents(); } children[childNum] = null; currentNumChildren--; // We have one less children now. // The last children is one ahead, so it is at currentNumChildren. if(childNum < currentNumChildren) { children[childNum] = children[currentNumChildren]; } // Clean up the old file object. if(JCSystem.isObjectDeletionSupported()) { JCSystem.requestObjectDeletion(); } } /** * \brief add or replace a DO * * \param children The children to add. * * \throw NotEnoughSpaceException If CHILDREN_COUNT_MAX is reached. * @param size * @param offset_cdata */ public Record addChildren(byte[] buffer, short offset, short wholelength, short lengthavailable) throws NotEnoughSpaceException { // try to find a previous TLV short i; short lengthToCopy = (lengthavailable > wholelength ? wholelength: lengthavailable); for(i = 0; i < currentNumChildren; i++) { byte[] value = children[i].GetData(); if (UtilTLV.IsBERTLVTagEqual(buffer, offset, (short) (offset + lengthavailable), value)) { // found => replace or erase ? // erase if empty DO pushed and already empty DO stored short oldlen = UtilTLV.GetBERTLVDataLen(value, (short) 0, (short) value.length); short newlen = UtilTLV.GetBERTLVDataLen(buffer, offset, (short) (offset + lengthavailable)); if (oldlen == 0) { if (newlen == 0) { deleteChildren(i); return null; } } // replace if (oldlen == newlen) { // no need to add / remove data, just replace the buffer Util.arrayCopyNonAtomic(buffer, offset, value, (short) 0, lengthToCopy); } else { // remove previous data, add new byte[] data = new byte[wholelength]; Record record = new Record(data); Util.arrayCopyNonAtomic(buffer, offset, data, (short) 0, lengthToCopy); deleteChildren(i); children[currentNumChildren++] = record; } return children[i]; } } // First we have to check for enough space. if(currentNumChildren >= (short)children.length) { Record[] newChildren = null; // The array is full - we try to increase the size. if((short)(children.length * 2) <= ELEMENT_COUNT_MAX) { // Doubling the size is possible. newChildren = new Record[(short)(children.length * 2)]; copyFileArrayRefs(children, newChildren); } else { // Doubling not possible - try to at least increase to CHILDREN_COUNT_MAX. if(currentNumChildren < ELEMENT_COUNT_MAX) { newChildren = new Record[ELEMENT_COUNT_MAX]; copyFileArrayRefs(children, newChildren); } else { // CHILDREN_COUNT_MAX exceeded. No "space" left. Fail. throw NotEnoughSpaceException.getInstance(); } } children = newChildren; // Initial children array is now garbage. if(JCSystem.isObjectDeletionSupported()) { JCSystem.requestObjectDeletion(); } } // We have enough space (now). byte[] data = new byte[wholelength]; Util.arrayCopyNonAtomic(buffer, offset, data, (short) 0, lengthToCopy); children[currentNumChildren++] = new Record(data); return children[(short) (currentNumChildren-1)]; } /** * \brief Copies the references from one File array to the other. * * \attention Although only references are copied, this is probably still quite expensive because * writing to the EEPROM is. Only use this for operations that are not called often (Creating and deleting files etc.). * * \param src The source File array to copy from. * * \param dest The destination File array to copy to. It MUST be at least of size of the src array. */ private static void copyFileArrayRefs(Record[] src, Record[] dest) { short i = 0; short length = src.length > dest.length ? (short)dest.length : (short)src.length; for(i=0; i < length; i++) { dest[i] = src[i]; } return; } public Record getData(byte[] tag, short offset, short len) throws NotFoundException { short i; for(i = 0; i < currentNumChildren; i++) { byte[] value = children[i].GetData(); if(UtilTLV.IsBERTLVTagEqual(tag, offset, len, value)) { return children[i]; } } throw NotFoundException.getInstance(); } public Record[] getAllData() { return children; } }