Interface FileAccessCardService

  • All Superinterfaces:
    CardServiceInterface, SecureService
    All Known Subinterfaces:
    FileSystemCardService, IsoFileSystemCardService
    All Known Implementing Classes:
    IsoCardService, SmartCardHSMCardService

    public interface FileAccessCardService
    extends CardServiceInterface, SecureService
    Interface to a card service for ISO 7816-4 file access functionality. This service provides read and write access to transparent as well as to structured files. In addition, there are methods to check whether a file exists and to access information stored in a file header. Both methods can be mapped onto the ISO-specified SELECT command.
    This is a low-level interface that requires careful programming when used directly. Especially the CardFilePath objects used for identifying files on the smartcard are somehow tricky, as described below. Higher level access is provided by classes like CardFile, CardFileInputStream, CardFileOutputStream, CardRandomByteAccess, or CardRandomRecordAccess. They can be created on top of a FileAccessCardService. The rest of this comment specifies the conditions that have to be satisfied if this interface is implemented or used directly.


    CardFilePath objects, which are used as arguments in almost all methods, are mutable. Since they are first class candidates for keys in hashtables, there are some conditions that must be satisfied by applications as well as by card services. For the card service side, there are two restrictions:
    1. A path given as an argument must not be modified by the service. If modifications to the path are necessary, it has to be copied, and the copy can be modified. Therefore, an application can rely on the path to be the same before and after an invocation of a card service method.
    2. A path given as an argument may be modified by the application after the invoked card service method returned. If a card service needs to store a path across invocations, for example for caching purposes, the path has to be copied and the copy can be stored. Therefore, an application is allowed to modify a path, regardless of whether it has been used as an argument to a card service or not.
    The restrictions imposed on applications are more complex. However, these are common sense restrictions that will typically be satisfied by any reasonable application program. As a rule of thumb, path supplied as arguments must be as simple as possible, but not simpler. These kind of paths are referred to as canonical paths.
    1. A path given as an argument to a card service must be absolute. If the path consists only of file ids and short file ids, the first component of the path must be the id of the master file (MF). A path to the MF can be obtained by invoking getRoot. This path can then be copied and extended.
      A path that contains an application identifier is implicitly absolute, since application identifiers are supported only as the first component in a path. Hierarchical applications are not supported by OCF, even if a smartcard does. There may be additional restrictions imposed on the layout of smartcards to enforce a correct behavior of card services.
      This restriction guarantees to the card service that selection of the full path will select the correct file on the smartcard, no matter what file has been selected before.
    2. A path given as an argument to a card service must be straight. It is not allowed for a path to switch to parent directories, even if the smartcard would support this feature. It is also unacceptable to include the master file in a path, unless it is the first component.
      This restriction almost guarantees to the card service that any file on the smartcard is identified by a unique path. The path can therefore be used as a key in hashtables or other dictionaries that store information related to a file. It is also possible to cut off the leading components of a path if the path to the currently selected directory (DF) is a prefix of the path to select. Besides, this restriction reduces memory consumption and speeds up operations.
    3. If a file on the smartcard is referenced by a path with an application name, it should not be referenced by another path without application name. Together with the preceeding restriction, this basically means: a file on the smartcard should be referenced by a unique identifier. The reason for this has been mentioned in the preceeding restriction.
      In this last restriction, the term should is used instead of must. This is because an application programmer cannot know about the potential programmers of other applications that refer to the same file on the card. One could decide to use a path with an application name, while another one could decide to use a path with file ids only.
      In this case, if both applications run simultaneously, and the card services created for them use a shared data structure like a file cache, the same file may be referenced by two different paths. This could lead to inconsistencies in the shared data, for example if one of the applications deletes the file. However, this case is too unlikely to worry about.
      If different card services, or different instances of one card service, use the same smartcard without cooperating on shared data, inconsistencies in cached data cannot be avoided at all, no matter what restrictions are put on path names.
    Author:
    Roland Weber (rolweber@de.ibm.com)
    See Also:
    getRoot(), CardFilePath, CardFile, CardFileInputStream, CardFileOutputStream, CardRandomByteAccess, CardRandomRecordAccess
    • Method Detail

      • getRoot

        CardFilePath getRoot()
        Returns the absolute path to the master file (MF) of the smartcard. For ISO compliant cards, the master file has the fixed id 0x3f00, so this method will typically be implemented in the following way:

         
        private final static CardFilePath master_file = new CardFilePath(":3f00"); public final CardFilePath getRoot() { return master_file; }

        The value returned is not allowed to be modified. When taking a look at the sample implementation above, it should be obvious why. There are no exceptions thrown by this method, since it does not require interaction with the smartcard.

        Returns:
        the path to the master file
      • read

        byte[] read​(CardFilePath file,
                    int offset,
                    int length)
             throws CardServiceException,
                    CardTerminalException
        Reads a given amount of data from a transparent file. Transparent files are similiar to files in traditional file systems. They provide random access to an array of bytes.
        Instead of specifying a number of bytes to read, the constant READ_SEVERAL can be passed. The service will then read at least one byte, probably more. Only one read command will be sent to the card in this case, that means a maximum of about 255 bytes can be returned. If the specified offset points to the end of the file, that is if not even one byte can be read, null is returned, but no exception will be thrown.
        Parameters:
        file - the path to the file to read from
        offset - the index of the first byte to read (0 for first)
        length - the number of bytes to read, or READ_SEVERAL. If 0 is passed, the behavior is implementation dependent.
        Returns:
        an array holding the data read from the file, or null if a read with length READ_SEVERAL has been performed at the end of the file
        Throws:
        CardServiceException - if the service encountered an error
        CardTerminalException - if the terminal encountered an error
        See Also:
        READ_SEVERAL
      • readRecord

        byte[] readRecord​(CardFilePath file,
                          int recordNumber)
                   throws CardServiceException,
                          CardTerminalException
        Reads a record from a structured file. Structured files consist of records. Each record is an array of bytes. Data is addressed only in terms of records, and records are always read completely. The record size will be determined by the card service. ISO 7816-4 specifies the following structured file types:

        linear fixed
        An array of records, with absolute addressing. All records have the same, fixed size.
        linear variable
        An array of records, with absolute addressing. Every record may have a different size.
        cyclic fixed
        A ring buffer of records, with relative addressing. All records have the same, fixed size. Cyclic files are typically used for keeping logs on transactions.

        Files with a cyclic structure may not be easily accessible with this method, since the absolute addressing may be interpreted in different ways by different cards. For example, the first record may be the record that is physically stored first on the card, or it may be the record that was last written into the ring buffer. The method readRecords is the preferred way to read cyclic files.

        Parameters:
        file - the path to the file to read from
        recordNumber - the index of the record to read (0 for first)
        Returns:
        an array holding the record read. If the record has length 0, which may happen with linear variable files, an array of length 0 is returned.
        Throws:
        CardServiceException - if the service encountered an error
        CardTerminalException - if the terminal encountered an error
        See Also:
        readRecords(opencard.opt.iso.fs.CardFilePath, int)
      • readRecords

        byte[][] readRecords​(CardFilePath file,
                             int number)
                      throws CardServiceException,
                             CardTerminalException
        Reads consecutive records from a structured file. For a discussion of structured file types, see readRecord. The first record read will always be the first in the structured file. For linear files with fixed or variable record size, first is interpreted as an absolute record number. For cyclic files, first refers to the record most recently written. Starting with that first record, the specified number of consecutive records will be read. In the case of a cyclic file, the second record will be the second most recently written record, and so on.
        Typically, smartcards will implement absolute addressing for cyclic files, where the first record is the least recently written, and the following are sorted by decreasing time of writing. In this case, this method can be implemented by repeated invocations of readRecord.
        The magic number READ_SEVERAL may be passed as the number of records to read. In this case, all records in the file are read. This is especially useful with linear variable files, where the number of records in the file cannot be determined via file attributes.
        Parameters:
        file - the path to the file to read from
        number - the number of records to read, or READ_SEVERAL. If 0 is passed, the behavior is implementation dependent.
        Returns:
        an array holding the records read, where the records are arrays themselves
        Throws:
        CardServiceException - if the service encountered an error
        CardTerminalException - if the terminal encountered an error
        See Also:
        readRecord(opencard.opt.iso.fs.CardFilePath, int), READ_SEVERAL
      • write

        void write​(CardFilePath file,
                   int foffset,
                   byte[] source,
                   int soffset,
                   int length)
            throws CardServiceException,
                   CardTerminalException
        Writes data to a transparent file, using part of an array. This method corresponds to the UPDATE BINARY command defined in ISO 7816-4. The term write has been chosen since it is more natural for programmers that are used to traditional file systems. For an explanation of the term transparent file, see read. To write an array completely, the convenience method write with three arguments can be used.
        Parameters:
        file - the path to the file to write to
        foffset - the file index of the first byte to overwrite (0 for first byte in file)
        source - an array holding the data to write
        soffset - the array index of the first byte to write
        length - the number of bytes to write
        Throws:
        CardServiceException - if the service encountered an error
        CardTerminalException - if the terminal encountered an error
        See Also:
        read(opencard.opt.iso.fs.CardFilePath, int, int), write(opencard.opt.iso.fs.CardFilePath, int, byte[])
      • write

        void write​(CardFilePath file,
                   int offset,
                   byte[] data)
            throws CardServiceException,
                   CardTerminalException
        Writes data to a transparent file, using a complete array. This is a convenience method for write with five arguments. It does not allow to specify an array index and the number of bytes to write. Instead, it always writes the complete array passed. Typically, this method will be implemented as follows:

         
        final public void write(CardFilePath file, int offset, byte[] data) { write(file, offset, data, 0, data.length); }
        Parameters:
        file - the path to the file to write to
        offset - the file index of the first byte to overwrite (0 for first byte in file)
        data - the data to write to the file
        Throws:
        CardServiceException - if the service encountered an error
        CardTerminalException - if the terminal encountered an error
        See Also:
        write(opencard.opt.iso.fs.CardFilePath, int, byte[], int, int)
      • writeRecord

        void writeRecord​(CardFilePath file,
                         int recordNumber,
                         byte[] data)
                  throws CardServiceException,
                         CardTerminalException
        Writes data to a structured file. This method corresponds to the UPDATE RECORD command defined in ISO 7816-4. The term write has been chosen since it is more natural for programmers that are used to traditional file systems. For a discussion of structured file types, see readRecord.
        A record is always written completely. For linear fixed files, the size of the input record must be exactly the file's record size. For files with variable record sizes, the size of the input record must not exceed the maximum size for the record that will be overwritten. That maximum size is typically the initial size of the record when the smartcard was initialized. For cyclic files, this method is not necessarily supported. Use appendRecord instead.
        Parameters:
        file - the path to the file to write to
        recordNumber - the index of the record to overwrite (0 for first)
        data - the data to write to the file
        Throws:
        CardServiceException - if the service encountered an error
        CardTerminalException - if the terminal encountered an error
        See Also:
        readRecord(opencard.opt.iso.fs.CardFilePath, int), appendRecord(opencard.opt.iso.fs.CardFilePath, byte[])
      • appendRecord

        void appendRecord​(CardFilePath file,
                          byte[] data)
                   throws CardServiceException,
                          CardTerminalException
        Appends data to a structured file. For a discussion of structured file types, see readRecord. For linear files with variable record size, this method appends a new record at the end of the file. Typically, the space for appending a record must have been allocated at the time the file was created. For cyclic files, this method overwrites the oldest record in the ring buffer, which then becomes the newest. The size of the record to append has to match the file's record size exactly. For linear files with a fixed record size, this method is not necessarily supported. Use writeRecord instead.
        Parameters:
        file - the path to the file to append to
        data - the data to write to the new record
        Throws:
        CardServiceException - if the service encountered an error
        CardTerminalException - if the terminal encountered an error
        See Also:
        readRecord(opencard.opt.iso.fs.CardFilePath, int), writeRecord(opencard.opt.iso.fs.CardFilePath, int, byte[])