/**
   * Process the MANAGE SECURITY ENVIRONMENT instruction (0x22). ISO7816-4, Section 7.5.11
   *
   * <p>This command can be also used to prepare key generation. In this case the algorithm
   * indication is not required, in fact, should not be present. Note that the key identifiers
   * should be already set up with put data before that.
   */
  private void processManageSecurityEnvironment(APDU apdu) {
    boolean forKeyGeneration = false;
    if (state == STATE_INITIAL) {
      forKeyGeneration = true;
    } else if (state == STATE_PREPERSONALISED) {
      ISOException.throwIt(SW_INS_NOT_SUPPORTED);
    }
    pin.reset();
    byte[] buf = apdu.getBuffer();
    byte p1 = buf[OFFSET_P1];
    byte p2 = buf[OFFSET_P2];
    // P1 should be:
    // (a) 0x40: computation, decipherment, internal authentication, ...
    // (b) 0x01: set
    if (p1 != (byte) 0x41) {
      ISOException.throwIt(SW_INCORRECT_P1P2);
    }
    byte[] expectedKeyId = null;
    // P2 should be one of the following, see ISO7816-4 Table 79
    if (p2 == (byte) 0xa4) {
      expectedKeyId = authKeyId;
    } else if (p2 == (byte) 0xb6) {
      expectedKeyId = signKeyId;
    } else if (p2 == (byte) 0xB8) {
      expectedKeyId = decKeyId;
    } else {
      ISOException.throwIt(SW_INCORRECT_P1P2);
    }
    short lc = unsigned(buf[OFFSET_LC]);
    if (lc == 0) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    apdu.setIncomingAndReceive();
    short offset = OFFSET_CDATA;
    lc += OFFSET_CDATA;
    // Tag for the private key:
    short len = checkDataObject(buf, offset, lc, (byte) 0x84);
    offset += 2;
    if (len != expectedKeyId[0]) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    if (Util.arrayCompare(buf, offset, expectedKeyId, (short) 1, len) != 0) {
      ISOException.throwIt(SW_KEY_NOT_FOUND);
    }
    offset += len;

    // Algorithm identfier tag
    if (!forKeyGeneration) {
      if (offset >= lc) {
        ISOException.throwIt(SW_WRONG_DATA);
      }
      len = checkDataObject(buf, offset, lc, (byte) 0x80);
      offset += 2;
      if (len != 1) {
        ISOException.throwIt(SW_WRONG_LENGTH);
      }
      byte sAlg = buf[offset++];
      if (offset != lc) {
        ISOException.throwIt(SW_WRONG_LENGTH);
      }
      if (sAlg < ALG_AUTH_DEC_RSA || sAlg > ALG_SIGN_RSA_PKCS1_SHA1MD5) {
        ISOException.throwIt(SW_WRONG_DATA);
      }
      tmp[TMP_SIGNALG_OFFSET] = sAlg;
    } else {
      if (offset != lc) {
        ISOException.throwIt(SW_WRONG_LENGTH);
      }
    }
    if (expectedKeyId == authKeyId) {
      currentPrivateKey[0] = authKeyPrivate;
    } else if (expectedKeyId == signKeyId) {
      currentPrivateKey[0] = signKeyPrivate;
    } else if (expectedKeyId == decKeyId) {
      currentPrivateKey[0] = decKeyPrivate;
    }
  }
  /** Process the SELECT (file) instruction (0xA4) ISO7816-4 Section 7.1.1 */
  private void processSelectFile(APDU apdu) {
    byte[] buf = apdu.getBuffer();
    byte p1 = buf[OFFSET_P1];
    // byte p2 = buf[OFFSET_P2];
    short lc = unsigned(buf[OFFSET_LC]);

    if (p1 == 0x04) {
      // Select the AID of the applet
      // do heavy verification, just for the fun of it ;)
      if (lc != (short) 0x0C) {
        ISOException.throwIt(SW_WRONG_LENGTH);
      }
      apdu.setIncomingAndReceive();
      if (Util.arrayCompare(buf, OFFSET_CDATA, myAID, (short) 0, lc) != 0) {
        ISOException.throwIt(SW_WRONG_DATA);
      }
      return;
    }

    short id = 0;
    switch (p1) {
      case (byte) 0x00:
        // Direct selection of MF, DF, or EF:
        if (lc != 0 && lc != 2) {
          ISOException.throwIt(SW_WRONG_LENGTH);
        }
        if (lc > 0) {
          apdu.setIncomingAndReceive();
          id = Util.makeShort(buf[OFFSET_CDATA], buf[(short) (OFFSET_CDATA + 1)]);
        } else {
          id = FileSystem.MASTER_FILE_ID;
        }
        if (!fileSystem.selectEntryAbsolute(id)) {
          ISOException.throwIt(SW_FILE_NOT_FOUND);
        }
        break;
      case (byte) 0x01:
      case (byte) 0x02:
        // Select the child under the current DF,
        // p1 0x01 DF identifier in data field
        // p1 0x02 EF identifier in data field
        if (lc != 2) {
          ISOException.throwIt(SW_WRONG_LENGTH);
        }
        apdu.setIncomingAndReceive();
        id = Util.makeShort(buf[OFFSET_CDATA], buf[(short) (OFFSET_CDATA + 1)]);
        if (!fileSystem.selectEntryUnderCurrent(id, p1 == (byte) 0x02)) {
          ISOException.throwIt(SW_FILE_NOT_FOUND);
        }
        break;
      case (byte) 0x03:
        // Select the parent of the current DF
        // no command data
        if (lc != 0) {
          ISOException.throwIt(SW_WRONG_LENGTH);
        }
        if (!fileSystem.selectEntryParent()) {
          ISOException.throwIt(SW_FILE_NOT_FOUND);
        }
        break;
      case (byte) 0x08:
      case (byte) 0x09:
        // Select by path
        // p1 0x08 from MF
        // p1 0x09 from current DF
        // data field: the path without the head
        if (lc == 0 || (short) (lc % 2) != 0) {
          ISOException.throwIt(SW_WRONG_LENGTH);
        }
        apdu.setIncomingAndReceive();
        if (!fileSystem.selectEntryByPath(buf, OFFSET_CDATA, lc, p1 == (byte) 0x08)) {
          ISOException.throwIt(SW_FILE_NOT_FOUND);
        }
        break;
      default:
        ISOException.throwIt(SW_INCORRECT_P1P2);
    }
  }