All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jmrtd.lds.LDSFileUtil Maven / Gradle / Ivy

/*
 * JMRTD - A Java API for accessing machine readable travel documents.
 *
 * Copyright (C) 2006 - 2018  The JMRTD team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * $Id: LDSFileUtil.java 1781 2018-05-25 11:41:48Z martijno $
 */

package org.jmrtd.lds;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jmrtd.PassportService;
import org.jmrtd.lds.icao.COMFile;
import org.jmrtd.lds.icao.DG11File;
import org.jmrtd.lds.icao.DG12File;
import org.jmrtd.lds.icao.DG14File;
import org.jmrtd.lds.icao.DG15File;
import org.jmrtd.lds.icao.DG1File;
import org.jmrtd.lds.icao.DG2File;
import org.jmrtd.lds.icao.DG3File;
import org.jmrtd.lds.icao.DG4File;
import org.jmrtd.lds.icao.DG5File;
import org.jmrtd.lds.icao.DG6File;
import org.jmrtd.lds.icao.DG7File;

/**
 * Static LDS file methods.
 *
 * @author The JMRTD team ([email protected])
 *
 * @version $Revision: 1781 $
 */
public final class LDSFileUtil {

  private static final Logger LOGGER = Logger.getLogger("org.jmrtd");

  public static final Map FID_TO_SFI = createFIDToSFIMap();

  /**
   * Objects of this class should not be constructed.
   */
  private LDSFileUtil() {
  }

  /**
   * Factory method for creating LDS files for a given input stream.
   *
   * @param fid file identifier
   * @param inputStream a given input stream
   *
   * @return a specific file
   *
   * @throws IOException on reading error from the input stream
   */
  public static AbstractLDSFile getLDSFile(short fid, InputStream inputStream) throws IOException {
    switch (fid) {
      case PassportService.EF_COM:
        return new COMFile(inputStream);
      case PassportService.EF_DG1:
        return new DG1File(inputStream);
      case PassportService.EF_DG2:
        return new DG2File(inputStream);
      case PassportService.EF_DG3:
        return new DG3File(inputStream);
      case PassportService.EF_DG4:
        return new DG4File(inputStream);
      case PassportService.EF_DG5:
        return new DG5File(inputStream);
      case PassportService.EF_DG6:
        return new DG6File(inputStream);
      case PassportService.EF_DG7:
        return new DG7File(inputStream);
      case PassportService.EF_DG8:
        throw new IllegalArgumentException("DG8 files are not yet supported");
      case PassportService.EF_DG9:
        throw new IllegalArgumentException("DG9 files are not yet supported");
      case PassportService.EF_DG10:
        throw new IllegalArgumentException("DG10 files are not yet supported");
      case PassportService.EF_DG11:
        return new DG11File(inputStream);
      case PassportService.EF_DG12:
        return new DG12File(inputStream);
      case PassportService.EF_DG13:
        throw new IllegalArgumentException("DG13 files are not yet supported");
      case PassportService.EF_DG14:
        return new DG14File(inputStream);
      case PassportService.EF_DG15:
        return new DG15File(inputStream);
      case PassportService.EF_DG16:
        throw new IllegalArgumentException("DG16 files are not yet supported");
      case PassportService.EF_SOD:
        return new SODFile(inputStream);
      case PassportService.EF_CVCA:
        return new CVCAFile(inputStream);
      default:
        BufferedInputStream bufferedIn = new BufferedInputStream(inputStream, 37);
        try {
          bufferedIn.mark(37);
          /* Just try, will read 36 bytes at most, and we can reset bufferedIn. */
          return new CVCAFile(fid, bufferedIn);
        } catch (Exception e) {
          LOGGER.log(Level.WARNING, "Unknown file " + Integer.toHexString(fid), e);
          bufferedIn.reset();
          throw new NumberFormatException("Unknown file " + Integer.toHexString(fid));
        }
    }
  }

  /**
   * Finds a file identifier for an ICAO tag.
   *
   * Corresponds to Table A1 in ICAO-TR-LDS_1.7_2004-05-18.
   *
   * @param tag an ICAO tag (the first byte of the EF)
   *
   * @return a file identifier.
   */
  public static short lookupFIDByTag(int tag) {
    switch(tag) {
      case LDSFile.EF_COM_TAG:
        return PassportService.EF_COM;
      case LDSFile.EF_DG1_TAG:
        return PassportService.EF_DG1;
      case LDSFile.EF_DG2_TAG:
        return PassportService.EF_DG2;
      case LDSFile.EF_DG3_TAG:
        return PassportService.EF_DG3;
      case LDSFile.EF_DG4_TAG:
        return PassportService.EF_DG4;
      case LDSFile.EF_DG5_TAG:
        return PassportService.EF_DG5;
      case LDSFile.EF_DG6_TAG:
        return PassportService.EF_DG6;
      case LDSFile.EF_DG7_TAG:
        return PassportService.EF_DG7;
      case LDSFile.EF_DG8_TAG:
        return PassportService.EF_DG8;
      case LDSFile.EF_DG9_TAG:
        return PassportService.EF_DG9;
      case LDSFile.EF_DG10_TAG:
        return PassportService.EF_DG10;
      case LDSFile.EF_DG11_TAG:
        return PassportService.EF_DG11;
      case LDSFile.EF_DG12_TAG:
        return PassportService.EF_DG12;
      case LDSFile.EF_DG13_TAG:
        return PassportService.EF_DG13;
      case LDSFile.EF_DG14_TAG:
        return PassportService.EF_DG14;
      case LDSFile.EF_DG15_TAG:
        return PassportService.EF_DG15;
      case LDSFile.EF_DG16_TAG:
        return PassportService.EF_DG16;
      case LDSFile.EF_SOD_TAG:
        return PassportService.EF_SOD;
      default:
        throw new NumberFormatException("Unknown tag " + Integer.toHexString(tag));
    }
  }

  /**
   * Finds a data group number for an ICAO tag.
   *
   * @param tag an ICAO tag (the first byte of the EF)
   *
   * @return a data group number (1-16)
   */
  public static int lookupDataGroupNumberByTag(int tag) {
    switch (tag) {
      case LDSFile.EF_DG1_TAG:
        return 1;
      case LDSFile.EF_DG2_TAG:
        return 2;
      case LDSFile.EF_DG3_TAG:
        return 3;
      case LDSFile.EF_DG4_TAG:
        return 4;
      case LDSFile.EF_DG5_TAG:
        return 5;
      case LDSFile.EF_DG6_TAG:
        return 6;
      case LDSFile.EF_DG7_TAG:
        return 7;
      case LDSFile.EF_DG8_TAG:
        return 8;
      case LDSFile.EF_DG9_TAG:
        return 9;
      case LDSFile.EF_DG10_TAG:
        return 10;
      case LDSFile.EF_DG11_TAG:
        return 11;
      case LDSFile.EF_DG12_TAG:
        return 12;
      case LDSFile.EF_DG13_TAG:
        return 13;
      case LDSFile.EF_DG14_TAG:
        return 14;
      case LDSFile.EF_DG15_TAG:
        return 15;
      case LDSFile.EF_DG16_TAG:
        return 16;
      default:
        throw new NumberFormatException("Unknown tag " + Integer.toHexString(tag));
    }
  }

  /**
   * Finds an ICAO tag for a data group number.
   *
   *
   * @param number a data group number (1-16)
   *
   * @return an ICAO tag (the first byte of the EF)
   */
  public static int lookupTagByDataGroupNumber(int number) {
    switch (number) {
      case 1:
        return LDSFile.EF_DG1_TAG;
      case 2:
        return LDSFile.EF_DG2_TAG;
      case 3:
        return LDSFile.EF_DG3_TAG;
      case 4:
        return LDSFile.EF_DG4_TAG;
      case 5:
        return LDSFile.EF_DG5_TAG;
      case 6:
        return LDSFile.EF_DG6_TAG;
      case 7:
        return LDSFile.EF_DG7_TAG;
      case 8:
        return LDSFile.EF_DG8_TAG;
      case 9:
        return LDSFile.EF_DG9_TAG;
      case 10:
        return LDSFile.EF_DG10_TAG;
      case 11:
        return LDSFile.EF_DG11_TAG;
      case 12:
        return LDSFile.EF_DG12_TAG;
      case 13:
        return LDSFile.EF_DG13_TAG;
      case 14:
        return LDSFile.EF_DG14_TAG;
      case 15:
        return LDSFile.EF_DG15_TAG;
      case 16:
        return LDSFile.EF_DG16_TAG;
      default:
        throw new NumberFormatException("Unknown number " + number);
    }
  }

  /**
   * Finds an ICAO tag for a data group number.
   *
   *
   * @param number a data group number (1-16)
   *
   * @return a file identifier
   */
  public static short lookupFIDByDataGroupNumber(int number) {
    switch (number) {
      case 1:
        return PassportService.EF_DG1;
      case 2:
        return PassportService.EF_DG2;
      case 3:
        return PassportService.EF_DG3;
      case 4:
        return PassportService.EF_DG4;
      case 5:
        return PassportService.EF_DG5;
      case 6:
        return PassportService.EF_DG6;
      case 7:
        return PassportService.EF_DG7;
      case 8:
        return PassportService.EF_DG8;
      case 9:
        return PassportService.EF_DG9;
      case 10:
        return PassportService.EF_DG10;
      case 11:
        return PassportService.EF_DG11;
      case 12:
        return PassportService.EF_DG12;
      case 13:
        return PassportService.EF_DG13;
      case 14:
        return PassportService.EF_DG14;
      case 15:
        return PassportService.EF_DG15;
      case 16:
        return PassportService.EF_DG16;
      default:
        throw new NumberFormatException("Unknown number " + number);
    }
  }

  /**
   * Finds an ICAO tag for a file identifier.
   *
   * Corresponds to Table A1 in ICAO-TR-LDS_1.7_2004-05-18.
   *
   * @param fid a file identifier
   *
   * @return a an ICAO tag (first byte of EF)
   */
  public static short lookupTagByFID(short fid) {
    switch(fid) {
      case PassportService.EF_COM:
        return LDSFile.EF_COM_TAG;
      case PassportService.EF_DG1:
        return LDSFile.EF_DG1_TAG;
      case PassportService.EF_DG2:
        return LDSFile.EF_DG2_TAG;
      case PassportService.EF_DG3:
        return LDSFile.EF_DG3_TAG;
      case PassportService.EF_DG4:
        return LDSFile.EF_DG4_TAG;
      case PassportService.EF_DG5:
        return LDSFile.EF_DG5_TAG;
      case PassportService.EF_DG6:
        return LDSFile.EF_DG6_TAG;
      case PassportService.EF_DG7:
        return LDSFile.EF_DG7_TAG;
      case PassportService.EF_DG8:
        return LDSFile.EF_DG8_TAG;
      case PassportService.EF_DG9:
        return LDSFile.EF_DG9_TAG;
      case PassportService.EF_DG10:
        return LDSFile.EF_DG10_TAG;
      case PassportService.EF_DG11:
        return LDSFile.EF_DG11_TAG;
      case PassportService.EF_DG12:
        return LDSFile.EF_DG12_TAG;
      case PassportService.EF_DG13:
        return LDSFile.EF_DG13_TAG;
      case PassportService.EF_DG14:
        return LDSFile.EF_DG14_TAG;
      case PassportService.EF_DG15:
        return LDSFile.EF_DG15_TAG;
      case PassportService.EF_DG16:
        return LDSFile.EF_DG16_TAG;
      case PassportService.EF_SOD:
        return LDSFile.EF_SOD_TAG;
      default:
        throw new NumberFormatException("Unknown fid " + Integer.toHexString(fid));
    }
  }

  /**
   * Finds a data group number by file identifier.
   *
   * @param fid a file id
   *
   * @return a data group number
   */
  public static int lookupDataGroupNumberByFID(short fid) {
    switch(fid) {
      case PassportService.EF_DG1:
        return 1;
      case PassportService.EF_DG2:
        return 2;
      case PassportService.EF_DG3:
        return 3;
      case PassportService.EF_DG4:
        return 4;
      case PassportService.EF_DG5:
        return 5;
      case PassportService.EF_DG6:
        return 6;
      case PassportService.EF_DG7:
        return 7;
      case PassportService.EF_DG8:
        return 8;
      case PassportService.EF_DG9:
        return 9;
      case PassportService.EF_DG10:
        return 10;
      case PassportService.EF_DG11:
        return 11;
      case PassportService.EF_DG12:
        return 12;
      case PassportService.EF_DG13:
        return 13;
      case PassportService.EF_DG14:
        return 14;
      case PassportService.EF_DG15:
        return 15;
      case PassportService.EF_DG16:
        return 16;
      default:
        throw new NumberFormatException("Unknown fid " + Integer.toHexString(fid));
    }
  }

  /**
   * Returns a mnemonic name corresponding to the file represented by the
   * given ICAO tag, such as "EF_COM", "EF_SOD", or "EF_DG1".
   *
   * @param tag an ICAO tag (the first byte of the EF)
   *
   * @return a mnemonic name corresponding to the file represented by the given ICAO tag
   */
  public static String lookupFileNameByTag(int tag) {
    switch (tag) {
      case LDSFile.EF_COM_TAG:
        return "EF_COM";
      case LDSFile.EF_DG1_TAG:
        return "EF_DG1";
      case LDSFile.EF_DG2_TAG:
        return "EF_DG2";
      case LDSFile.EF_DG3_TAG:
        return "EF_DG3";
      case LDSFile.EF_DG4_TAG:
        return "EF_DG4";
      case LDSFile.EF_DG5_TAG:
        return "EF_DG5";
      case LDSFile.EF_DG6_TAG:
        return "EF_DG6";
      case LDSFile.EF_DG7_TAG:
        return "EF_DG7";
      case LDSFile.EF_DG8_TAG:
        return "EF_DG8";
      case LDSFile.EF_DG9_TAG:
        return "EF_DG9";
      case LDSFile.EF_DG10_TAG:
        return "EF_DG10";
      case LDSFile.EF_DG11_TAG:
        return "EF_DG11";
      case LDSFile.EF_DG12_TAG:
        return "EF_DG12";
      case LDSFile.EF_DG13_TAG:
        return "EF_DG13";
      case LDSFile.EF_DG14_TAG:
        return "EF_DG14";
      case LDSFile.EF_DG15_TAG:
        return "EF_DG15";
      case LDSFile.EF_DG16_TAG:
        return "EF_DG16";
      case LDSFile.EF_SOD_TAG:
        return "EF_SOD";
      default: return "File with tag 0x" + Integer.toHexString(tag);
    }
  }

  /**
   * Returns a mnemonic name corresponding to the file represented by the
   * given file identifier, such as "EF_COM", "EF_SOD", or "EF_DG1".
   *
   * @param fid an LDS file identifiers
   *
   * @return a mnemonic name corresponding to the file represented by the given ICAO tag
   */
  public static String lookupFileNameByFID(int fid) {
    switch (fid) {
      case PassportService.EF_COM:
        return "EF_COM";
      case PassportService.EF_DG1:
        return "EF_DG1";
      case PassportService.EF_DG2:
        return "EF_DG2";
      case PassportService.EF_DG3:
        return "EF_DG3";
      case PassportService.EF_DG4:
        return "EF_DG4";
      case PassportService.EF_DG5:
        return "EF_DG5";
      case PassportService.EF_DG6:
        return "EF_DG6";
      case PassportService.EF_DG7:
        return "EF_DG7";
      case PassportService.EF_DG8:
        return "EF_DG8";
      case PassportService.EF_DG9:
        return "EF_DG9";
      case PassportService.EF_DG10:
        return "EF_DG10";
      case PassportService.EF_DG11:
        return "EF_DG11";
      case PassportService.EF_DG12:
        return "EF_DG12";
      case PassportService.EF_DG13:
        return "EF_DG13";
      case PassportService.EF_DG14:
        return "EF_DG14";
      case PassportService.EF_DG15:
        return "EF_DG15";
      case PassportService.EF_DG16:
        return "EF_DG16";
      case PassportService.EF_SOD:
        return "EF_SOD";
      default:
        return "File with FID 0x" + Integer.toHexString(fid);
    }
  }

  /**
   * Returns the short (one  byte) file identifier corresponding
   * to the given (two byte) file identifier.
   *
   * @param fid a file identifier
   *
   * @return the corresponding short file identifier
   */
  public static int lookupSFIByFID(short fid) {
    Byte sfiByte = FID_TO_SFI.get(fid);
    if (sfiByte == null) {
      throw new NumberFormatException("Unknown FID " + Integer.toHexString(fid));
    }

    return sfiByte & 0xFF;
  }

  /**
   * Looks up a file identifier for a given short file identifier.
   *
   * @param sfi the short file identifier
   *
   * @return a file identifier
   */
  public static short lookupFIDBySFI(byte sfi) {
    switch (sfi) {
      case PassportService.SFI_COM:
        return PassportService.EF_COM;
      case PassportService.SFI_DG1:
        return PassportService.EF_DG1;
      case PassportService.SFI_DG2:
        return PassportService.EF_DG2;
      case PassportService.SFI_DG3:
        return PassportService.EF_DG3;
      case PassportService.SFI_DG4:
        return PassportService.EF_DG4;
      case PassportService.SFI_DG5:
        return PassportService.EF_DG5;
      case PassportService.SFI_DG6:
        return PassportService.EF_DG6;
      case PassportService.SFI_DG7:
        return PassportService.EF_DG7;
      case PassportService.SFI_DG8:
        return PassportService.EF_DG8;
      case PassportService.SFI_DG9:
        return PassportService.EF_DG9;
      case PassportService.SFI_DG10:
        return PassportService.EF_DG10;
      case PassportService.SFI_DG11:
        return PassportService.EF_DG11;
      case PassportService.SFI_DG12:
        return PassportService.EF_DG12;
      case PassportService.SFI_DG13:
        return PassportService.EF_DG13;
      case PassportService.SFI_DG14:
        return PassportService.EF_DG14;
      case PassportService.SFI_DG15:
        return PassportService.EF_DG15;
      case PassportService.SFI_DG16:
        return PassportService.EF_DG16;
      case PassportService.SFI_SOD:
        return PassportService.EF_SOD;
      case PassportService.SFI_CVCA:
        return PassportService.EF_CVCA;
      default:
        throw new NumberFormatException("Unknown SFI " + Integer.toHexString(sfi));
    }
  }

  /**
   * Returns the data group list from the security object (SOd).
   *
   * @param sodFile the security object
   *
   * @return the list of data group numbers
   */
  public static List getDataGroupNumbers(SODFile sodFile) {
    /* Get the list of DGs from EF.SOd, we don't trust EF.COM. */
    List dgNumbers = new ArrayList();
    if (sodFile == null) {
      return dgNumbers;
    }

    dgNumbers.addAll(sodFile.getDataGroupHashes().keySet());
    Collections.sort(dgNumbers); /* NOTE: need to sort it, since we get keys as a set. */
    return dgNumbers;
  }

  /**
   * Returns the data group list from the document index file (COM).
   *
   * @param comFile the document index file
   *
   * @return the list with data group number according to the document index file
   */
  public static List getDataGroupNumbers(COMFile comFile) {
    List dgNumbers = new ArrayList();
    if (comFile == null) {
      return dgNumbers;
    }

    int[] tagList = comFile.getTagList();
    dgNumbers.addAll(toDataGroupList(tagList));
    Collections.sort(dgNumbers); // NOTE: sort it, just in case.
    return dgNumbers;
  }

  /**
   * Converts a list with ICAO tags into a list of ICAO data group numbers.
   *
   * @param tagList a list of tags specified in ICAO Doc 9303
   *
   * @return the list with data group number according to the security object
   */
  private static List toDataGroupList(int[] tagList) {
    if (tagList == null) {
      return Collections.emptyList();
    }
    List dgNumberList = new ArrayList(tagList.length);
    for (int tag: tagList) {
      try {
        int dgNumber = LDSFileUtil.lookupDataGroupNumberByTag(tag);
        dgNumberList.add(dgNumber);
      } catch (NumberFormatException nfe) {
        LOGGER.log(Level.WARNING, "Could not find DG number for tag: " + Integer.toHexString(tag), nfe);
      }
    }
    return dgNumberList;
  }

  /**
   * Creates a map for looking up short file identifiers based on file identifiers.
   *
   * @return the lookup map
   */
  private static Map createFIDToSFIMap() {
    Map fidToSFI = new HashMap(20);
    fidToSFI.put(PassportService.EF_COM, PassportService.SFI_COM);
    fidToSFI.put(PassportService.EF_DG1, PassportService.SFI_DG1);
    fidToSFI.put(PassportService.EF_DG2, PassportService.SFI_DG2);
    fidToSFI.put(PassportService.EF_DG3, PassportService.SFI_DG3);
    fidToSFI.put(PassportService.EF_DG4, PassportService.SFI_DG4);
    fidToSFI.put(PassportService.EF_DG5, PassportService.SFI_DG5);
    fidToSFI.put(PassportService.EF_DG6, PassportService.SFI_DG6);
    fidToSFI.put(PassportService.EF_DG7, PassportService.SFI_DG7);
    fidToSFI.put(PassportService.EF_DG8, PassportService.SFI_DG8);
    fidToSFI.put(PassportService.EF_DG9,  PassportService.SFI_DG9);
    fidToSFI.put(PassportService.EF_DG10, PassportService.SFI_DG10);
    fidToSFI.put(PassportService.EF_DG11, PassportService.SFI_DG11);
    fidToSFI.put(PassportService.EF_DG12, PassportService.SFI_DG12);
    fidToSFI.put(PassportService.EF_DG13, PassportService.SFI_DG13);
    fidToSFI.put(PassportService.EF_DG14, PassportService.SFI_DG14);
    fidToSFI.put(PassportService.EF_DG15, PassportService.SFI_DG15);
    fidToSFI.put(PassportService.EF_DG16, PassportService.SFI_DG16);
    fidToSFI.put(PassportService.EF_SOD, PassportService.SFI_SOD);
    fidToSFI.put(PassportService.EF_CVCA, PassportService.SFI_CVCA);
    return Collections.unmodifiableMap(fidToSFI);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy