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

org.jmrtd.PassportService 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: PassportService.java 1833 2020-01-13 14:37:37Z martijno $
 */

package org.jmrtd;

import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;

import javax.crypto.SecretKey;

import org.jmrtd.cert.CVCPrincipal;
import org.jmrtd.cert.CardVerifiableCertificate;
import org.jmrtd.protocol.AAAPDUSender;
import org.jmrtd.protocol.AAProtocol;
import org.jmrtd.protocol.AAResult;
import org.jmrtd.protocol.BACAPDUSender;
import org.jmrtd.protocol.BACProtocol;
import org.jmrtd.protocol.BACResult;
import org.jmrtd.protocol.EACCAAPDUSender;
import org.jmrtd.protocol.EACCAProtocol;
import org.jmrtd.protocol.EACCAResult;
import org.jmrtd.protocol.EACTAAPDUSender;
import org.jmrtd.protocol.EACTAProtocol;
import org.jmrtd.protocol.EACTAResult;
import org.jmrtd.protocol.PACEAPDUSender;
import org.jmrtd.protocol.PACEProtocol;
import org.jmrtd.protocol.PACEResult;
import org.jmrtd.protocol.ReadBinaryAPDUSender;
import org.jmrtd.protocol.SecureMessagingWrapper;

import net.sf.scuba.smartcards.APDUEvent;
import net.sf.scuba.smartcards.APDUListener;
import net.sf.scuba.smartcards.CardFileInputStream;
import net.sf.scuba.smartcards.CardService;
import net.sf.scuba.smartcards.CardServiceException;
import net.sf.scuba.smartcards.CommandAPDU;
import net.sf.scuba.smartcards.ResponseAPDU;

/**
 * Card service for reading files (such as data groups) and using the various
 * access control protocols (BAC, PACE, EAC-TA), clone-detection verification
 * protocols (AA, EAC-CA), and the resulting secure messaging as implemented
 * by the MRTD ICC.
 *
 * Based on ICAO Doc 9303 2015.
 * Originally based on ICAO-TR-PKI and ICAO-TR-LDS.
 *
 * @author The JMRTD team ([email protected])
 *
 * @version $Revision:352 $
 */
public class PassportService extends AbstractMRTDCardService {

  /** Shared secret type for non-PACE key. */
  public static final byte NO_PACE_KEY_REFERENCE = 0x00;

  /** Shared secret type for PACE according to BSI TR-03110 v2.03 B.11.1. */
  public static final byte MRZ_PACE_KEY_REFERENCE = 0x01;

  /** Shared secret type for PACE according to BSI TR-03110 v2.03 B.11.1. */
  public static final byte CAN_PACE_KEY_REFERENCE = 0x02;

  /** Shared secret type for PACE according to BSI TR-03110 v2.03 B.11.1. */
  public static final byte PIN_PACE_KEY_REFERENCE = 0x03;

  /** Shared secret type for PACE according to BSI TR-03110 v2.03 B.11.1. */
  public static final byte PUK_PACE_KEY_REFERENCE = 0x04;

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

  /** Card Access. */
  public static final short EF_CARD_ACCESS = 0x011C;

  /** Card Security. */
  public static final short EF_CARD_SECURITY = 0x011D;

  /** File identifier for data group 1. Data group 1 contains the MRZ. */
  public static final short EF_DG1 = 0x0101;

  /** File identifier for data group 2. Data group 2 contains face image data. */
  public static final short EF_DG2 = 0x0102;

  /** File identifier for data group 3. Data group 3 contains finger print data. */
  public static final short EF_DG3 = 0x0103;

  /** File identifier for data group 4. Data group 4 contains iris data. */
  public static final short EF_DG4 = 0x0104;

  /** File identifier for data group 5. Data group 5 contains displayed portrait. */
  public static final short EF_DG5 = 0x0105;

  /** File identifier for data group 6. Data group 6 is RFU. */
  public static final short EF_DG6 = 0x0106;

  /** File identifier for data group 7. Data group 7 contains displayed signature. */
  public static final short EF_DG7 = 0x0107;

  /** File identifier for data group 8. Data group 8 contains data features. */
  public static final short EF_DG8 = 0x0108;

  /** File identifier for data group 9. Data group 9 contains structure features. */
  public static final short EF_DG9 = 0x0109;

  /** File identifier for data group 10. Data group 10 contains substance features. */
  public static final short EF_DG10 = 0x010A;

  /** File identifier for data group 11. Data group 11 contains additional personal details. */
  public static final short EF_DG11 = 0x010B;

  /** File identifier for data group 12. Data group 12 contains additional document details. */
  public static final short EF_DG12 = 0x010C;

  /** File identifier for data group 13. Data group 13 contains optional details. */
  public static final short EF_DG13 = 0x010D;

  /** File identifier for data group 14. Data group 14 contains security infos. */
  public static final short EF_DG14 = 0x010E;

  /** File identifier for data group 15. Data group 15 contains the public key used for Active Authentication. */
  public static final short EF_DG15 = 0x010F;

  /** File identifier for data group 16. Data group 16 contains person(s) to notify. */
  public static final short EF_DG16 = 0x0110;

  /** The security document. */
  public static final short EF_SOD = 0x011D;

  /** The data group presence list. */
  public static final short EF_COM = 0x011E;

  /**
   * Contains EAC CVA references. Note: this can be overridden by a file
   * identifier in the DG14 file (in a TerminalAuthenticationInfo). Check DG14
   * first. Also, this file does not have a header tag, like the others.
   */
  public static final short EF_CVCA = 0x011C;

  /** Short file identifier for card access file. */
  public static final byte SFI_CARD_ACCESS = 0x1C;

  /** Short file identifier for card security file. */
  public static final byte SFI_CARD_SECURITY = 0x1D;

  /** Short file identifier for file. */
  public static final byte SFI_DG1 = 0x01;

  /** Short file identifier for file. */
  public static final byte SFI_DG2 = 0x02;

  /** Short file identifier for file. */
  public static final byte SFI_DG3 = 0x03;

  /** Short file identifier for file. */
  public static final byte SFI_DG4 = 0x04;

  /** Short file identifier for file. */
  public static final byte SFI_DG5 = 0x05;

  /** Short file identifier for file. */
  public static final byte SFI_DG6 = 0x06;

  /** Short file identifier for file. */
  public static final byte SFI_DG7 = 0x07;

  /** Short file identifier for file. */
  public static final byte SFI_DG8 = 0x08;

  /** Short file identifier for file. */
  public static final byte SFI_DG9 = 0x09;

  /** Short file identifier for file. */
  public static final byte SFI_DG10 = 0x0A;

  /** Short file identifier for file. */
  public static final byte SFI_DG11 = 0x0B;

  /** Short file identifier for file. */
  public static final byte SFI_DG12 = 0x0C;

  /** Short file identifier for file. */
  public static final byte SFI_DG13 = 0x0D;

  /** Short file identifier for file. */
  public static final byte SFI_DG14 = 0x0E;

  /** Short file identifier for file. */
  public static final byte SFI_DG15 = 0x0F;

  /** Short file identifier for file. */
  public static final byte SFI_DG16 = 0x10;

  /** Short file identifier for file. */
  public static final byte SFI_COM = 0x1E;

  /** Short file identifier for file. */
  public static final byte SFI_SOD = 0x1D;

  /** Short file identifier for file. */
  public static final byte SFI_CVCA = 0x1C;

  /** The default maximal blocksize used for unencrypted APDUs. */
  public static final int DEFAULT_MAX_BLOCKSIZE = 224;

  /** The normal maximal tranceive length of APDUs. */
  public static final int NORMAL_MAX_TRANCEIVE_LENGTH = 256;

  /** The extended maximal tranceive length of APDUs. */
  public static final int EXTENDED_MAX_TRANCEIVE_LENGTH = 65536;

  /** The applet we select when we start a session. */
  protected static final byte[] APPLET_AID = { (byte)0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01 };

  /**
   * The file read block size, some passports cannot handle large values.
   */
  private int maxBlockSize;

  private boolean isOpen;

  private SecureMessagingWrapper wrapper;

  private int maxTranceiveLength;

  private boolean shouldCheckMAC;

  private boolean isAppletSelected;

  private DefaultFileSystem rootFileSystem;

  private DefaultFileSystem appletFileSystem;

  private BACAPDUSender bacSender;
  private PACEAPDUSender paceSender;
  private AAAPDUSender aaSender;
  private EACCAAPDUSender eacCASender;
  private EACTAAPDUSender eacTASender;
  private ReadBinaryAPDUSender readBinarySender;

  private CardService service;

  /**
   * Creates a new passport service for accessing the passport.
   *
   * @param service another service which will deal with sending the APDUs to the card
   * @param maxTranceiveLength maximum length for APDUs
   * @param maxBlockSize maximum buffer size for plain text APDUs
   * @param isSFIEnabled whether short file identifiers should be used for read binaries when possible
   * @param shouldCheckMAC whether the secure messaging channels, resulting from BAC, PACE, EAC-CA, should
   *                       check MACs on response APDUs
   */
  public PassportService(CardService service, int maxTranceiveLength, int maxBlockSize, boolean isSFIEnabled, boolean shouldCheckMAC) {
    this.service = service;

    this.bacSender = new BACAPDUSender(service);
    this.paceSender = new PACEAPDUSender(service);
    this.aaSender = new AAAPDUSender(service);
    this.eacCASender = new EACCAAPDUSender(service);
    this.eacTASender = new EACTAAPDUSender(service);
    this.readBinarySender = new ReadBinaryAPDUSender(service);

    this.maxTranceiveLength = maxTranceiveLength;
    this.maxBlockSize = maxBlockSize;
    this.shouldCheckMAC = shouldCheckMAC;
    this.isAppletSelected = false;
    this.isOpen = false;

    this.rootFileSystem = new DefaultFileSystem(readBinarySender, false); // Some passports (UK?) don't support SFI for EF.CardAccess. -- MO
    this.appletFileSystem = new DefaultFileSystem(readBinarySender, isSFIEnabled);
  }

  /**
   * Opens a session to the card. As of 0.4.10 this no longer auto selects the passport application,
   * caller is responsible to call #sendSelectApplet(boolean) now.
   *
   * @throws CardServiceException on error
   */
  @Override
  public void open() throws CardServiceException {
    if (isOpen()) {
      return;
    }
    synchronized(this) {
      service.open();
      isOpen = true;
    }
  }

  /**
   * Selects the card side applet. If PACE has been executed successfully previously, then the ICC has authenticated
   * us and a secure messaging channel has already been established. If not, then the caller should request BAC execution as a next
   * step.
   *
   * @param hasPACESucceeded indicates whether PACE has been executed successfully (in which case a secure messaging channel has been established)
   *
   * @throws CardServiceException on error
   */
  public void sendSelectApplet(boolean hasPACESucceeded) throws CardServiceException {
    if (isAppletSelected) {
      LOGGER.info("Re-selecting ICAO applet");
    }

    if (hasPACESucceeded) {
      /* Use SM as set up by doPACE() */
      readBinarySender.sendSelectApplet(wrapper, APPLET_AID);
    } else {
      /* Use plain messaging to select the applet, caller will have to do doBAC. */
      readBinarySender.sendSelectApplet(null, APPLET_AID);
    }

    isAppletSelected = true;
  }

  /**
   * Returns a boolean that indicates whether this service is open.
   *
   * @return a boolean that indicates whether this service is open
   */
  @Override
  public boolean isOpen() {
    return isOpen;
  }

  /**
   * Performs the Basic Access Control protocol.
   *
   * @param bacKey the key based on the document number,
   *               the card holder's birth date,
   *               and the document's expiration date
   *
   * @return the BAC result
   *
   * @throws CardServiceException if authentication failed
   */
  public synchronized BACResult doBAC(AccessKeySpec bacKey) throws CardServiceException {
    if (!(bacKey instanceof BACKeySpec)) {
      throw new IllegalArgumentException("Unsupported key type");
    }
    BACResult bacResult = (new BACProtocol(bacSender, maxTranceiveLength, shouldCheckMAC)).doBAC((BACKeySpec)bacKey);
    wrapper = bacResult.getWrapper();
    appletFileSystem.setWrapper(wrapper);
    return bacResult;
  }

  /**
   * Performs the Basic Access Control protocol.
   * It does BAC using kEnc and kMac keys, usually calculated
   * from the document number, the card holder's date of birth,
   * and the card's date of expiry.
   *
   * A secure messaging channel is set up as a result.
   *
   * @param kEnc static 3DES key required for BAC
   * @param kMac static 3DES key required for BAC
   *
   * @return the result
   *
   * @throws CardServiceException if authentication failed
   * @throws GeneralSecurityException on security primitives related problems
   */
  public synchronized BACResult doBAC(SecretKey kEnc, SecretKey kMac) throws CardServiceException, GeneralSecurityException {
    BACResult bacResult = (new BACProtocol(bacSender, maxTranceiveLength, shouldCheckMAC)).doBAC(kEnc, kMac);
    wrapper = bacResult.getWrapper();
    appletFileSystem.setWrapper(wrapper);
    return bacResult;
  }

  /**
   * Performs the PACE 2.0 / SAC protocol.
   * A secure messaging channel is set up as a result.
   *
   * @param keySpec the MRZ
   * @param oid as specified in the PACEInfo, indicates GM or IM or CAM, DH or ECDH, cipher, digest, length
   * @param params explicit static domain parameters the domain params for DH or ECDH
   * @param parameterId parameter identifier or {@code null}
   *
   * @return the result
   *
   * @throws CardServiceException on error
   */
  public synchronized PACEResult doPACE(AccessKeySpec keySpec, String oid, AlgorithmParameterSpec params, BigInteger parameterId) throws CardServiceException {
    PACEResult paceResult = (new PACEProtocol(paceSender, wrapper, maxTranceiveLength, shouldCheckMAC)).doPACE(keySpec, oid, params, parameterId);
    wrapper = paceResult.getWrapper();
    appletFileSystem.setWrapper(wrapper);
    return paceResult;
  }

  /**
   * Perform CA (Chip Authentication) part of EAC (version 1). For details see TR-03110
   * ver. 1.11. In short, we authenticate the chip with (EC)DH key agreement
   * protocol and create new secure messaging keys.
   * A new secure messaging channel is set up as a result.
   *
   * @param keyId passport's public key id (stored in DG14), {@code null} if none
   * @param oid the object identifier indicating the Chip Authentication protocol
   * @param publicKeyOID the object identifier indicating the public key algorithm used
   * @param publicKey passport's public key (stored in DG14)
   *
   * @return the Chip Authentication result
   *
   * @throws CardServiceException if CA failed or some error occurred
   */
  public synchronized EACCAResult doEACCA(BigInteger keyId, String oid, String publicKeyOID, PublicKey publicKey) throws CardServiceException {
    EACCAResult caResult = (new EACCAProtocol(eacCASender, wrapper, maxTranceiveLength, shouldCheckMAC)).doCA(keyId, oid, publicKeyOID, publicKey);
    wrapper = caResult.getWrapper();
    appletFileSystem.setWrapper(wrapper);
    return caResult;
  }

  /* From BSI-03110 v1.1, B.2:
   *
   * 
   * The following sequence of commands SHALL be used to implement Terminal Authentication:
   *   1. MSE:Set DST
   *   2. PSO:Verify Certificate
   *   3. MSE:Set AT
   *   4. Get Challenge
   *   5. External Authenticate
   * Steps 1 and 2 are repeated for every CV certificate to be verified
   * (CVCA Link Certificates, DV Certificate, IS Certificate).
   * 
*/ /** * Performs Terminal Authentication (TA) part of EAC (version 1). For details see * TR-03110 ver. 1.11. * * In short, we feed the sequence of terminal certificates to the card for verification, * get a challenge from the card, sign it with the terminal private key, and send the result * back to the card for verification. * * @param caReference reference issuer * @param terminalCertificates terminal certificate chain * @param terminalKey terminal private key * @param taAlg algorithm * @param chipAuthenticationResult the chip authentication result * @param documentNumber the document number * * @return the Terminal Authentication result * * @throws CardServiceException on error */ public synchronized EACTAResult doEACTA(CVCPrincipal caReference, List terminalCertificates, PrivateKey terminalKey, String taAlg, EACCAResult chipAuthenticationResult, String documentNumber) throws CardServiceException { return (new EACTAProtocol(eacTASender, wrapper)).doEACTA(caReference, terminalCertificates, terminalKey, taAlg, chipAuthenticationResult, documentNumber); } /** * Performs Terminal Authentication (TA) part of EAC (version 1). For details see * TR-03110 ver. 1.11. * * In short, we feed the sequence of terminal certificates to the card for verification, * get a challenge from the card, sign it with the terminal private key, and send the result * back to the card for verification. * * @param caReference reference issuer * @param terminalCertificates terminal certificate chain * @param terminalKey terminal private key * @param taAlg algorithm * @param chipAuthenticationResult the chip authentication result * @param paceResult the PACE result * * @return the Terminal Authentication result * * @throws CardServiceException on error */ public synchronized EACTAResult doEACTA(CVCPrincipal caReference, List terminalCertificates, PrivateKey terminalKey, String taAlg, EACCAResult chipAuthenticationResult, PACEResult paceResult) throws CardServiceException { return (new EACTAProtocol(eacTASender, wrapper)).doTA(caReference, terminalCertificates, terminalKey, taAlg, chipAuthenticationResult, paceResult); } /** * Performs the Active Authentication protocol. * * @param publicKey the public key to use (usually read from the card) * @param digestAlgorithm the digest algorithm to use, or null * @param signatureAlgorithm signature algorithm * @param challenge challenge * * @return a boolean indicating whether the card was authenticated * * @throws CardServiceException on error */ public AAResult doAA(PublicKey publicKey, String digestAlgorithm, String signatureAlgorithm, byte[] challenge) throws CardServiceException { return (new AAProtocol(aaSender, wrapper)).doAA(publicKey, digestAlgorithm, signatureAlgorithm, challenge); } /** * Closes this service. */ @Override public void close() { try { service.close(); wrapper = null; } finally { isOpen = false; } } /** * Returns the maximum tranceive length of (protected) APDUs. * * @return the maximum APDU tranceive length */ public int getMaxTranceiveLength() { return maxTranceiveLength; } /** * Returns the secure messaging wrapper currently in use. * Returns {@code null} until access control has been performed. * * @return the wrapper */ public SecureMessagingWrapper getWrapper() { return wrapper; } @Override public ResponseAPDU transmit(CommandAPDU commandAPDU) throws CardServiceException { return service.transmit(commandAPDU); } /** * Returns the answer to reset. * * @return the answer to reset * * @throws CardServiceException on error */ @Override public byte[] getATR() throws CardServiceException { return service.getATR(); } /** * Determines whether an exception indicates a tag is lost event. * * @param e an exception * * @return whether the exception indicates a tag is lost event */ @Override public boolean isConnectionLost(Exception e) { return service.isConnectionLost(e); } /** * Whether secure channels should check the MAC on response APDUs sent by the ICC. * * @return a boolean indicating whether the MAC should be checked */ public boolean shouldCheckMAC() { return shouldCheckMAC; } /** * Returns the file indicated by the file identifier as an input stream. * The resulting input stream will send APDUs to the card as it is being read. * * @param fid the file identifier * * @return the file as an input stream * * @throws CardServiceException if the file cannot be read */ public synchronized CardFileInputStream getInputStream(short fid) throws CardServiceException { if (!isAppletSelected) { synchronized(rootFileSystem) { rootFileSystem.selectFile(fid); return new CardFileInputStream(maxBlockSize, rootFileSystem); } } else { synchronized(appletFileSystem) { appletFileSystem.selectFile(fid); return new CardFileInputStream(maxBlockSize, appletFileSystem); } } } @Override public void addAPDUListener(APDUListener l) { service.addAPDUListener(l); } @Override public void removeAPDUListener(APDUListener l) { service.removeAPDUListener(l); } @Override public Collection getAPDUListeners() { return service.getAPDUListeners(); } @Override protected void notifyExchangedAPDU(APDUEvent event) { Collection apduListeners = getAPDUListeners(); if (apduListeners == null || apduListeners.isEmpty()) { return; } for (APDUListener apduListener: apduListeners) { apduListener.exchangedAPDU(event); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy