/// <summary> /// Process ERASE instruction (CC3) /// <para> /// C-APDU: <code>00 0E {offset} {Lc} {length} </code> /// </para> /// <list type="table"> /// <item> /// <term>offset</term> /// <description>offset of first word of the file coded by P1 P2 (WORDS) to be erased.</description> /// </item> /// <item> /// <term>length</term> /// <description>number of words to be erased.</description> /// </item> /// </list> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessErase(CommandAPDU apdu) { // TODO: check ==> security of current EF byte[] apduBuffer = apdu.GetBuffer(); apdu.SetIncomingAndReceive(); // Check if Lc ==2 short lc = APDUHelpers.getIncomingLength(apdu); if (lc != 2) { ISOException.throwIt(JavaCard.ISO7816.SW_WRONG_LENGTH); } short offset = Util.getShort(apduBuffer, JavaCard.ISO7816.OFFSET_P1); // in WORDS short udcOffset = APDUHelpers.getOffsetCdata(apdu); short length = Util.getShort(apduBuffer, udcOffset); // in WORDS VerifyOutOfFile(offset, length); _currentEF.Erase(offset, length); return(FakeCardFeedback.FromSuccess(unchecked ((short)0x9000))); }
/// <summary> /// Process DELETE FILE instruction (E4) /// <para> /// C-APDU: <code>00 E4 00 00 02 {fid}</code> /// </para> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessDeleteFile(CommandAPDU apdu) { byte[] buffer = apdu.GetBuffer(); _ = apdu.SetIncomingAndReceive(); short udcOffset = APDUHelpers.getOffsetCdata(apdu); short lc = APDUHelpers.getIncomingLength(apdu); if (lc != 2) { ISOException.throwIt(JavaCard.ISO7816.SW_WRONG_P1P2); } short fid = Util.getShort(buffer, udcOffset); if (_currentDF.DeleteFile(fid) == false) { ISOException.throwIt(JavaCard.ISO7816.SW_FILE_NOT_FOUND); } if (_currentEF != null && fid == _currentEF.GetFileId()) { _currentEF = null; } return(apdu.SetOutgoingAndSend(0, 0)); }
/// <summary> /// Process SELECT instruction (CC4). /// <para> /// C-APDU: <code>00 A4 00 00 02 {FID} {Le}</code> /// </para> /// <para> /// R-APDU: <code>{offset} {size} {header}</code> /// </para> /// <list type="table"> /// <item> /// <term>offset</term> /// <description>offset of first word of the file, 2 bytes (WORDS).</description> /// </item> /// <item> /// <term>size</term> /// <description>size of the body of the file (WORDS).</description> /// </item> /// </list> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessSelect(CommandAPDU apdu) { byte[] buffer = apdu.GetBuffer(); apdu.SetIncomingAndReceive(); short udcOffset = APDUHelpers.getOffsetCdata(apdu); short lc = (short)apdu.Lc; if (lc != 2) { ISOException.throwIt(JavaCard.ISO7816.SW_WRONG_LENGTH); } short fid = Util.getShort(buffer, udcOffset); File file; if (fid == 0x3F00) { file = _masterFile; } else { file = _currentDF.FindFileByFileId(fid); } if (file == null) { ISOException.throwIt(JavaCard.ISO7816.SW_FILE_NOT_FOUND); } // Update current DF / EF if (file.IsDF()) { _currentDF = (DedicatedFile)file; _currentEF = null; } else { _currentEF = (ElementaryFile)file; } // Build and send R-APDU short headerSize = file.GetHeaderSize(); Util.setShort(buffer, 0, (short)(file._inParentBodyOffset >> 2)); Util.setShort(buffer, 2, (short)(file.GetLength() - headerSize)); file.GetHeader(buffer, 4); // TODO Automagically adds 9000 return(apdu.SetOutgoingAndSend(0, (short)(4 + (headerSize << 2)))); }
/// <summary> /// Process FSEARCH instruction (CC2). /// <para> /// C-APDU: <code>00 B0 {P1-P2: offset} 04</code> /// </para> /// <para> /// R-APDU when an EF is selected: <code>{offset} {number of word in current EF} {looked-up word}</code> /// </para> /// <para> /// R-APDU when no EF is selected: <code>{offset} {number of word in current DF} {looked-up word}</code> /// </para> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessFSearch(CommandAPDU apdu) { // get offset byte[] buffer = apdu.GetBuffer(); short offset = Util.getShort(buffer, JavaCard.ISO7816.OFFSET_P1); // in WORDS // get le short le = apdu.SetOutgoing(); // in BYTES if (le != 0) { ISOException.throwIt(JavaCard.ISO7816.SW_WRONG_LENGTH); } short wordCount; short offsetFound = offset; // check that there is a current EF if (_currentEF != null) { wordCount = (short)(_currentEF.GetLength() - _currentEF.GetHeaderSize()); while (!(_currentEF.IsAvailable(offsetFound, 1) || offsetFound == wordCount)) { offsetFound++; } } else { wordCount = (short)(_currentDF.GetLength() - _currentDF.GetHeaderSize()); while (!(_currentDF.IsAvailable(offsetFound, 1) || offsetFound == wordCount)) { offsetFound++; } } // check that there is still some empty space if (offsetFound == wordCount) { ISOException.throwIt(Constants.SW_DATA_NOT_FOUND); } // copy answer in buffer Util.setShort(buffer, 0, offsetFound); Util.setShort(buffer, 2, wordCount); Util.arrayFillNonAtomic(buffer, 4, 4, (byte)MemoryState.Free); // and send it! return(apdu.SetOutgoingAndSend(0, 8)); }
/// <summary> /// Process CREATE FILE instruction (E0) /// <para> /// C-APDU: <code>00 E0 {offset} {Lc} {header}</code> /// </para> /// <list type="table"> /// <item> /// <term>offset</term> /// <description>offset of first word of the file coded by P1 P2 (WORDS).</description> /// </item> /// <item> /// <term>header</term> /// <description>header of the new file, must be word aligned.</description> /// </item> /// </list> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessCreateFile(CommandAPDU apdu) { byte[] buffer = apdu.GetBuffer(); apdu.SetIncomingAndReceive(); short headerOffset = APDUHelpers.getOffsetCdata(apdu); short headerLength = APDUHelpers.getIncomingLength(apdu); if (headerLength < 4) { ISOException.throwIt(JavaCard.ISO7816.SW_DATA_INVALID); } short offset = Util.getShort(buffer, JavaCard.ISO7816.OFFSET_P1); if (!_headerParser.Parse(buffer, headerOffset, (short)(headerOffset + headerLength))) { ISOException.throwIt(JavaCard.ISO7816.SW_DATA_INVALID); } short size = (short)(_headerParser.bodyLength + (short)(_headerParser.headerLength >> 2)); File file = null; switch (_headerParser.fileType) { case HeaderParser.FILETYPE_DF: file = _currentDF.CreateDedicatedFile(offset, size, buffer, headerOffset, headerLength); break; case HeaderParser.FILETYPE_EFSZ: case HeaderParser.FILETYPE_EFWZ: file = _currentDF.CreateElementaryFile(offset, size, buffer, headerOffset, headerLength); break; default: ISOException.throwIt(JavaCard.ISO7816.SW_CONDITIONS_NOT_SATISFIED); break; } if (file == null) { ISOException.throwIt(JavaCard.ISO7816.SW_DATA_INVALID); } return(apdu.SetOutgoingAndSend(0, 0)); }
/// <summary> /// Process GENERATE RANDOM instruction (CC2) /// <para> /// C-APDU: <code>00 C4 00 00 08</code> /// </para> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessGenerateRandom(CommandAPDU apdu) { byte[] apduBuffer = apdu.GetBuffer(); short le = apdu.SetOutgoing(); // verify that Le='08' if (le != 8) { // if not => error ISOException.throwIt(JavaCard.ISO7816.SW_WRONG_LENGTH); } // generate 8 random bytes var rndGen = new RNGCryptoServiceProvider(); rndGen.GetBytes(apduBuffer, 0, 8); // and send it! return(apdu.SetOutgoingAndSend(0, 8)); }
/// <summary> /// Process READ BINARY instruction (CC2) /// <para> /// C-APDU: <code>00 B0 {P1-P2: offset} {Le}</code> /// </para> /// <para> /// R-APDU when an EF is selected: <code>{data in EF}</code> /// </para> /// <para> /// R-APDU when no EF is selected: <code>{headers of EF in current DF}</code> /// </para> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessRead(CommandAPDU apdu) { short le = apdu.SetOutgoing(); // in BYTES short wordCount = (short)((short)(le + 3) / 4); byte[] buffer = JCSystem.makeTransientByteArray((short)(wordCount * 4), JCSystem.CLEAR_ON_DESELECT); short offset; if (_currentEF != null) { // an EF is selected ==> read binary offset = Util.getShort(apdu.GetBuffer(), JavaCard.ISO7816.OFFSET_P1); // in WORDS byte[] header = JCSystem.makeTransientByteArray(8, JCSystem.CLEAR_ON_DESELECT); _currentEF.GetHeader(header, 0); _headerParser.Parse(header, 0, 8); offset += (short)(_currentEF.Read(offset, buffer, 0, wordCount, _headerParser.fileType == HeaderParser.FILETYPE_EFSZ) << 2); } else { // no EF selected ==> DIR offset = 0; byte currentChildNumber = 0; File fileChild = _currentDF.GetChild(currentChildNumber); // get header of each file in current DF while (fileChild != null && offset < le) { fileChild.GetHeader(buffer, offset); offset += (short)(fileChild.GetHeaderSize() << 2); fileChild = _currentDF.GetChild(++currentChildNumber); } } // Pad with FF Util.arrayFillNonAtomic(buffer, offset, (short)(le - offset), (byte)MemoryState.Free); // and send data! apdu.SetOutgoingLength(le); return(apdu.SendBytesLong(buffer, 0, le)); }
/// <summary> /// Process WRITE BINARY instruction (CC3) /// <para> /// C-APDU: <code>00 B0 {offset} {Lc} {data} </code> /// </para> /// <list type="table"> /// <item> /// <term>offset</term> /// <description>offset of first word of the file coded by P1 P2 (WORDS) to be written.</description> /// </item> /// <item> /// <term>data</term> /// <description>data to be written in file.</description> /// </item> /// </list> /// </summary> /// <param name="apdu"></param> /// <returns></returns> private IFakeCardFeedback ProcessWrite(CommandAPDU apdu) { // TODO: check ==> security of current EF byte[] apduBuffer = apdu.GetBuffer(); apdu.SetIncomingAndReceive(); short offset = Util.getShort(apduBuffer, JavaCard.ISO7816.OFFSET_P1); // in WORDS short length = APDUHelpers.getIncomingLength(apdu); // in BYTES short wordCount = (short)((short)(length + 3) / 4); // length in WORDS // availability check VerifyOutOfFile(offset, wordCount); if (!_currentEF.IsAvailable(offset, wordCount)) { ISOException.throwIt(JavaCard.ISO7816.SW_WRONG_LENGTH); } // copy data in a buffer byte[] buffer = JCSystem.makeTransientByteArray((short)(wordCount * 4), JCSystem.CLEAR_ON_DESELECT); short udcOffset = APDUHelpers.getOffsetCdata(apdu); Util.arrayCopyNonAtomic(apduBuffer, udcOffset, buffer, 0, length); // complete words with FF in buffer short iMax = (short)(wordCount * 4); for (short i = length; i < iMax; i++) { buffer[i] = 0xFF; } // and write data to file _currentEF.Write(buffer, 0, offset, wordCount); return(FakeCardFeedback.FromSuccess(unchecked ((short)0x9000))); }