|
| 1 | +package com.mysmartlogon.gidsApplet; |
| 2 | + |
| 3 | +import javacard.framework.JCSystem; |
| 4 | +import javacard.framework.Util; |
| 5 | + |
| 6 | +public class BerTlvFile extends ElementaryFile { |
| 7 | + |
| 8 | + private static final short ELEMENT_COUNT_START = 10; |
| 9 | + private static final short ELEMENT_COUNT_MAX = 30; // set to max. 16383 |
| 10 | + |
| 11 | + private Record[] children; |
| 12 | + private byte currentNumChildren; |
| 13 | + |
| 14 | + /** |
| 15 | + * \brief Instantiate a new BER-TLV EF. No data is being added at this point. |
| 16 | + * |
| 17 | + * \param fileControlInformation The array of bytes containing the valid (!) File Control Information. |
| 18 | + * It must contain the File ID (Tag 83). No Copy is made. |
| 19 | + * |
| 20 | + * \param maxRecords The maximum amount of saved records. |
| 21 | + * |
| 22 | + * \attention No copy of the FCI is made. Do not pass any buffer that is altered |
| 23 | + * later (e.g. the apdu buffer). Max length 257 bytes as the length |
| 24 | + * of the FCI Tag (6F) must be a byte. |
| 25 | + * |
| 26 | + * \attention To be safe, use IsoFileSystem.getSafeFile() to instantiate files. |
| 27 | + * |
| 28 | + * \throw IllegalArgumentException If necessary tags in the FCI are missing. |
| 29 | + */ |
| 30 | + public BerTlvFile(short fileID, byte[] fileControlInformation) { |
| 31 | + super(fileID, fileControlInformation); |
| 32 | + this.children = new Record[ELEMENT_COUNT_START]; |
| 33 | + this.currentNumChildren = 0; |
| 34 | + } |
| 35 | + |
| 36 | + @Override |
| 37 | + void clearContents() { |
| 38 | + short i; |
| 39 | + |
| 40 | + for(i = 0; i < currentNumChildren; i++) { |
| 41 | + children[i].clearContents(); |
| 42 | + children[i] = null; |
| 43 | + } |
| 44 | + |
| 45 | + } |
| 46 | + |
| 47 | + /** |
| 48 | + * \brief Delete a DO |
| 49 | + * |
| 50 | + * This method requests garbage collection. |
| 51 | + * |
| 52 | + * \param childNum internal index |
| 53 | + */ |
| 54 | + protected void deleteChildren(short childNum) { |
| 55 | + |
| 56 | + children[childNum] = null; |
| 57 | + currentNumChildren--; // We have one less children now. |
| 58 | + |
| 59 | + // Fill up empty field in children array. |
| 60 | + // The last children is one ahead, so it is at currentNumChildren. |
| 61 | + if(childNum < currentNumChildren) { |
| 62 | + children[childNum] = children[currentNumChildren]; |
| 63 | + } |
| 64 | + |
| 65 | + // Clean up the old file object. |
| 66 | + if(JCSystem.isObjectDeletionSupported()) { |
| 67 | + JCSystem.requestObjectDeletion(); |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + /** |
| 72 | + * \brief remove a DO |
| 73 | + * |
| 74 | + * \param children The children to add. |
| 75 | + * |
| 76 | + * \throw NotEnoughSpaceException If CHILDREN_COUNT_MAX is reached. |
| 77 | + * @param size |
| 78 | + * @param offset_cdata |
| 79 | + */ |
| 80 | + public Record addChildren(byte[] buffer, short offset, short wholelength, short lengthavailable) throws NotEnoughSpaceException { |
| 81 | + // try to find a previous TLV |
| 82 | + short i; |
| 83 | + short lengthToCopy = (lengthavailable > wholelength ? wholelength: lengthavailable); |
| 84 | + for(i = 0; i < currentNumChildren; i++) { |
| 85 | + byte[] value = children[i].GetData(); |
| 86 | + |
| 87 | + if (UtilTLV.IsBERTLVTagEqual(buffer, offset, (short) (offset + lengthavailable), value)) { |
| 88 | + // found => replace or erase ? |
| 89 | + |
| 90 | + // erase if empty DO pushed and already empty DO stored |
| 91 | + short oldlen = UtilTLV.GetBERTLVDataLen(value, (short) 0, (short) value.length); |
| 92 | + short newlen = UtilTLV.GetBERTLVDataLen(buffer, offset, (short) (offset + lengthavailable)); |
| 93 | + if (oldlen == 0) { |
| 94 | + if (newlen == 0) { |
| 95 | + deleteChildren(i); |
| 96 | + return null; |
| 97 | + } |
| 98 | + } |
| 99 | + // replace |
| 100 | + if (oldlen == newlen) { |
| 101 | + // no need to add / remove data, just replace the buffer |
| 102 | + Util.arrayCopyNonAtomic(buffer, offset, value, (short) 0, lengthToCopy); |
| 103 | + } else { |
| 104 | + // remove previous data, add new |
| 105 | + byte[] data = new byte[wholelength]; |
| 106 | + Util.arrayCopyNonAtomic(buffer, offset, data, (short) 0, lengthToCopy); |
| 107 | + children[i] = null; |
| 108 | + if(JCSystem.isObjectDeletionSupported()) { |
| 109 | + JCSystem.requestObjectDeletion(); |
| 110 | + } |
| 111 | + children[i] = new Record(data); |
| 112 | + } |
| 113 | + return children[i]; |
| 114 | + } |
| 115 | + |
| 116 | + } |
| 117 | + |
| 118 | + |
| 119 | + // First we have to check for enough space. |
| 120 | + if(currentNumChildren >= (short)children.length) { |
| 121 | + Record[] newChildren = null; |
| 122 | + // The array is full - we try to increase the size. |
| 123 | + if((short)(children.length * 2) <= ELEMENT_COUNT_MAX) { |
| 124 | + // Doubling the size is possible. |
| 125 | + newChildren = new Record[(short)(children.length * 2)]; |
| 126 | + copyFileArrayRefs(children, newChildren); |
| 127 | + } else { |
| 128 | + // Doubling not possible - try to at least increase to CHILDREN_COUNT_MAX. |
| 129 | + if(currentNumChildren < ELEMENT_COUNT_MAX) { |
| 130 | + newChildren = new Record[ELEMENT_COUNT_MAX]; |
| 131 | + copyFileArrayRefs(children, newChildren); |
| 132 | + } else { |
| 133 | + // CHILDREN_COUNT_MAX exceeded. No "space" left. Fail. |
| 134 | + throw NotEnoughSpaceException.getInstance(); |
| 135 | + } |
| 136 | + } |
| 137 | + children = newChildren; // Initial children array is now garbage. |
| 138 | + if(JCSystem.isObjectDeletionSupported()) { |
| 139 | + JCSystem.requestObjectDeletion(); |
| 140 | + } |
| 141 | + } // We have enough space (now). |
| 142 | + byte[] data = new byte[wholelength]; |
| 143 | + Util.arrayCopyNonAtomic(buffer, offset, data, (short) 0, lengthToCopy); |
| 144 | + children[currentNumChildren++] = new Record(data); |
| 145 | + return children[(short) (currentNumChildren-1)]; |
| 146 | + } |
| 147 | + |
| 148 | + /** |
| 149 | + * \brief Copies the references from one File array to the other. |
| 150 | + * |
| 151 | + * \attention Although only references are copied, this is probably still quite expensive because |
| 152 | + * writing to the EEPROM is. Only use this for operations that are not called often (Creating and deleting files etc.). |
| 153 | + * |
| 154 | + * \param src The source File array to copy from. |
| 155 | + * |
| 156 | + * \param dest The destination File array to copy to. It MUST be at least of size of the src array. |
| 157 | + */ |
| 158 | + private static void copyFileArrayRefs(Record[] src, Record[] dest) { |
| 159 | + short i = 0; |
| 160 | + short length = src.length > dest.length ? (short)dest.length : (short)src.length; |
| 161 | + |
| 162 | + for(i=0; i < length; i++) { |
| 163 | + dest[i] = src[i]; |
| 164 | + } |
| 165 | + return; |
| 166 | + } |
| 167 | + |
| 168 | + public Record getData(byte[] tag, short offset, short len) throws NotFoundException { |
| 169 | + short i; |
| 170 | + |
| 171 | + for(i = 0; i < currentNumChildren; i++) { |
| 172 | + byte[] value = children[i].GetData(); |
| 173 | + if(UtilTLV.IsBERTLVTagEqual(tag, offset, len, value)) { |
| 174 | + return children[i]; |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + throw NotFoundException.getInstance(); |
| 179 | + } |
| 180 | + |
| 181 | + public Record[] getAllData() { |
| 182 | + return children; |
| 183 | + } |
| 184 | + |
| 185 | +} |
0 commit comments