/** Process the INTERNAL AUTHENTICATE instruction (0x88) ISO 7816-4 Section 7.5.2 */
  private void processInternalAuthenticate(APDU apdu) {
    if (state != STATE_PERSONALISED) {
      ISOException.throwIt(SW_INS_NOT_SUPPORTED);
    }
    if (!pin.isValidated()) {
      ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    byte[] buf = apdu.getBuffer();
    short lc = unsigned(buf[OFFSET_LC]);
    if (lc == 0) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    apdu.setIncomingAndReceive();

    RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) currentPrivateKey[0];
    byte alg = tmp[TMP_SIGNALG_OFFSET];
    if (privateKey != authKeyPrivate || alg != ALG_AUTH_DEC_RSA) {
      ISOException.throwIt(SW_WRONG_DATA);
    }
    short offset = OFFSET_CDATA;
    short maxLength = (short) ((short) (privateKey.getSize() / 8) - 11);
    if (lc > maxLength) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    pkcs1Cipher.init(privateKey, Cipher.MODE_ENCRYPT);
    short len = pkcs1Cipher.doFinal(buf, offset, lc, tmp, TMP_OFFSET);
    Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, len);
    apdu.setOutgoingAndSend((short) 0, len);
  }
  /** Process the PSO COMPUTE DIGITAL SIGNATURE instruction (0x2A) ISO 7816-8 Section 5.4 */
  private void processComputeDigitalSignature(APDU apdu) {
    pin.reset();
    byte[] buf = apdu.getBuffer();
    short lc = unsigned(buf[OFFSET_LC]);
    if (lc == 0) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    apdu.setIncomingAndReceive();

    RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) currentPrivateKey[0];
    byte alg = tmp[TMP_SIGNALG_OFFSET];
    if (privateKey != signKeyPrivate
        || (alg != ALG_SIGN_RSA_PKCS1_SHA1
            && alg != ALG_SIGN_RSA_PKCS1_SHA256
            && alg != ALG_SIGN_RSA_PSS
            && alg != ALG_SIGN_RSA_PKCS1_SHA1MD5)) {
      ISOException.throwIt(SW_WRONG_DATA);
    }
    short offset = OFFSET_CDATA;
    short expectedLength = 0;
    if (alg == ALG_SIGN_RSA_PKCS1_SHA256) {
      expectedLength = (short) (SHA256_LEN + 17);
    } else if (alg == ALG_SIGN_RSA_PKCS1_SHA1) {
      expectedLength = (short) (SHA1_LEN + 13);
    } else if (alg == ALG_SIGN_RSA_PSS) {
      expectedLength = SHA1_LEN;
    } else if (alg == ALG_SIGN_RSA_PKCS1_SHA1MD5) {
      expectedLength = SHA1MD5_LEN;
    }
    if (lc != expectedLength) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    short sigLen = 0;
    if (alg == ALG_SIGN_RSA_PKCS1_SHA1
        || alg == ALG_SIGN_RSA_PKCS1_SHA256
        || alg == ALG_SIGN_RSA_PKCS1_SHA1MD5) {
      pkcs1Cipher.init(privateKey, Cipher.MODE_ENCRYPT);
      sigLen = pkcs1Cipher.doFinal(buf, offset, lc, tmp, TMP_OFFSET);
      Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, sigLen);
    } else {
      short emLen = (short) (privateKey.getSize() / 8);
      pssPad(buf, offset, lc, tmp, TMP_OFFSET, emLen, signKeyFirstModulusByte);
      nopadCipher.init(privateKey, Cipher.MODE_ENCRYPT);
      sigLen = nopadCipher.doFinal(tmp, (short) 0, emLen, buf, (short) 0);
    }
    apdu.setOutgoingAndSend((short) 0, sigLen);
  }
 private void processSetupKey(APDU apdu) throws ISOException {
   if (state != STATE_INITIAL) {
     ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
   }
   byte[] buf = apdu.getBuffer();
   byte p1 = buf[OFFSET_P1];
   byte p2 = buf[OFFSET_P2];
   short lc = unsigned(buf[OFFSET_LC]);
   apdu.setIncomingAndReceive();
   if (p1 == (byte) 0x61 || p1 == (byte) 0x62 || p1 == (byte) 0x63) {
     if (lc > 16) {
       ISOException.throwIt(SW_WRONG_LENGTH);
     }
     byte[] keyId = null;
     if (p1 == (byte) 0x61) {
       keyId = authKeyId;
     } else if (p1 == (byte) 0x62) {
       keyId = signKeyId;
     } else if (p1 == (byte) 0x63) {
       keyId = decKeyId;
     }
     Util.arrayCopy(buf, OFFSET_CDATA, keyId, (short) 1, lc);
     keyId[0] = (byte) lc;
     return;
   }
   RSAPrivateCrtKey privKey = null;
   if (p1 == (byte) 0x64) {
     privKey = authKeyPrivate;
   } else if (p1 == (byte) 0x65) {
     privKey = signKeyPrivate;
   } else if (p1 == (byte) 0x66) {
     privKey = decKeyPrivate;
   } else {
     ISOException.throwIt(SW_INCORRECT_P1P2);
   }
   try {
     switch (p2) {
       case (byte) 0x81: // Modulus, ignore, but record the first byte if key is sign key
         if (privKey == signKeyPrivate) {
           signKeyFirstModulusByte = buf[OFFSET_CDATA];
         }
         break;
       case (byte) 0x82: // Exponent, ignore
         break;
       case (byte) 0x83:
         privKey.setP(buf, OFFSET_CDATA, lc);
         break;
       case (byte) 0x84:
         privKey.setQ(buf, OFFSET_CDATA, lc);
         break;
       case (byte) 0x85:
         privKey.setDP1(buf, OFFSET_CDATA, lc);
         break;
       case (byte) 0x86:
         privKey.setDQ1(buf, OFFSET_CDATA, lc);
         break;
       case (byte) 0x87:
         privKey.setPQ(buf, OFFSET_CDATA, lc);
         break;
       default:
     }
   } catch (Exception e) {
     ISOException.throwIt(SW_WRONG_DATA);
   }
 }
  /** Process the PSO DECIPHER instruction. ISO 7816-8 Section 5.10 */
  private void processDecipher(APDU apdu) {
    byte[] buf = apdu.getBuffer();
    byte cla = buf[OFFSET_CLA];
    boolean chain = ((byte) (cla & CLA_CHAIN) == CLA_CHAIN);
    short lc = unsigned(buf[OFFSET_LC]);

    // We need at least 1 byte of data to feed into the cipher,
    // so that a progression is made
    if (lc == 0) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    apdu.setIncomingAndReceive();
    short offset = OFFSET_CDATA;

    // The first in chain, intialized and check:
    if (tmp[TMP_DECSEQ_OFFSET] == (byte) 0x00) {
      RSAPrivateCrtKey key = (RSAPrivateCrtKey) currentPrivateKey[0];
      if (key == null) {
        ISOException.throwIt(SW_KEY_NOT_FOUND);
      }
      byte alg = tmp[TMP_SIGNALG_OFFSET];
      if (key != decKeyPrivate || alg != ALG_AUTH_DEC_RSA) {
        ISOException.throwIt(SW_WRONG_DATA);
      }
      pkcs1Cipher.init(key, Cipher.MODE_DECRYPT);
      tmp[TMP_DECSEQ_OFFSET]++;
      expectedDecipherDataLength[0] = (short) (key.getSize() / 8);
    }

    short decipheredLen = 0;
    try {
      decipheredLen =
          pkcs1Cipher.update(buf, offset, lc, tmp, (short) (TMP_OFFSET + decipheredLen));
    } catch (CryptoException ce) {
      ISOException.throwIt(SW_WRONG_DATA);
    }
    expectedDecipherDataLength[0] -= lc;
    offset += lc;

    // Data still to come:
    if (expectedDecipherDataLength[0] != 0 && !chain) {
      ISOException.throwIt(SW_WRONG_DATA);
    }
    // No more data:
    if (expectedDecipherDataLength[0] == 0 && chain) {
      ISOException.throwIt(SW_LAST_COMMAND_EXPECTED);
    }

    if (chain) {
      // It should also be the case the deciphereLen == 0, check?
      return;
    }
    pin.reset();
    tmp[TMP_DECSEQ_OFFSET] = 0x00;
    try {
      decipheredLen =
          pkcs1Cipher.doFinal(buf, offset, (short) 0, tmp, (short) (TMP_OFFSET + decipheredLen));
    } catch (CryptoException ce) {
      ISOException.throwIt(SW_WRONG_DATA);
    }
    Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, decipheredLen);
    apdu.setOutgoingAndSend((short) 0, decipheredLen);
  }