/** Process the WRITE BINARY Instruction (0xD0). ISO7816-4 Section 7.2.4 */
 private void processWriteBinary(APDU apdu) throws ISOException {
   if (state != STATE_INITIAL) {
     ISOException.throwIt(SW_INS_NOT_SUPPORTED);
   }
   byte[] buf = apdu.getBuffer();
   byte p1 = buf[OFFSET_P1];
   byte p2 = buf[OFFSET_P2];
   short offset = 0;
   short ef = -1;
   if ((byte) (p1 & MASK_SFI) == MASK_SFI) {
     byte sfi = (byte) (p1 | ~MASK_SFI);
     if (sfi >= 0x1F) {
       ISOException.throwIt(SW_INCORRECT_P1P2);
     }
     ef = fileSystem.findCurrentSFI(sfi);
     if (ef == -1) {
       ISOException.throwIt(SW_FILE_NOT_FOUND);
     }
     ef = fileSystem.fileStructure[ef];
     offset = unsigned(p2);
   } else {
     ef = fileSystem.getCurrentIndex();
     if (fileSystem.getFile(ef) == null) {
       ISOException.throwIt(SW_COMMAND_NOT_ALLOWED);
     }
     offset = Util.makeShort(p1, p2);
   }
   byte[] file = fileSystem.getFile(ef);
   short lc = unsigned(buf[OFFSET_LC]);
   if ((short) (offset + lc) > file.length) {
     ISOException.throwIt(SW_WRONG_LENGTH);
   }
   apdu.setIncomingAndReceive();
   Util.arrayCopyNonAtomic(buf, OFFSET_CDATA, file, offset, lc);
 }
 /**
  * Generate an assymetric RSA key pair according to ISO7816-8, Section 5.1. We only support RSA
  * 1024 bit at the moment, and return data in simple TLV data objects, tags 81 and 82.
  *
  * <p>Successful MSE command has to be performed prior to this one.
  */
 private void processGenerateAssymetricKeyPair(APDU apdu) {
   // This is only valid in state initial (at the moment)
   if (state != STATE_INITIAL) {
     ISOException.throwIt(SW_INS_NOT_SUPPORTED);
   }
   byte[] buf = apdu.getBuffer();
   byte p1 = buf[OFFSET_P1];
   byte p2 = buf[OFFSET_P2];
   if (p1 != (byte) 0x80 || p2 != (byte) 0x00) {
     ISOException.throwIt(SW_INCORRECT_P1P2);
   }
   if (currentPrivateKey[0] == null) {
     ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
   }
   KeyPair pair = new KeyPair(tempKeyPublic, (RSAPrivateCrtKey) currentPrivateKey[0]);
   pair.genKeyPair();
   // Sanity check, the KeyPair class should regenerate the keys "in place".
   if (pair.getPrivate() != currentPrivateKey[0] || pair.getPublic() != tempKeyPublic) {
     ISOException.throwIt(SW_DATA_INVALID);
   }
   apdu.setOutgoing();
   short len = (short) 0;
   short offset = 0;
   buf[offset++] = (byte) 0x81;
   len = tempKeyPublic.getModulus(buf, (short) (offset + 2));
   buf[offset++] = (byte) 0x81;
   buf[offset++] = (byte) len;
   offset += len;
   buf[offset++] = (byte) 0x82;
   len = tempKeyPublic.getExponent(buf, (short) (offset + 1));
   buf[offset++] = (byte) len;
   offset += len;
   apdu.setOutgoingLength(offset);
   apdu.sendBytes((short) 0, offset);
 }
  public void process(APDU apdu) {

    byte[] buffer = apdu.getBuffer();

    if (apdu.isISOInterindustryCLA()) {
      if (buffer[ISO7816.OFFSET_INS] == (byte) (0xA4)) {
        return;
      } else {
        ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
      }
    }

    switch (buffer[ISO7816.OFFSET_INS]) {
      case VALIDATEKEY:
        validateKey(apdu);
        return;
      case TIMETICK:
        timeTick(apdu);
        return;
      case SETCONNECTION:
        setConnection();
        return;
      case RESETCONNECTION:
        resetConnection();
        return;
      default:
        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
    }
  }
  /** 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);
  }
 private void processSetHistoricalBytes(APDU apdu) {
   if (state != STATE_INITIAL) {
     ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
   }
   byte[] buf = apdu.getBuffer();
   byte lc = buf[OFFSET_LC];
   if (lc <= 0) {
     ISOException.throwIt(SW_WRONG_LENGTH);
   }
   apdu.setIncomingAndReceive();
   // Was GPSystem
   OPSystem.setATRHistBytes(buf, OFFSET_CDATA, lc);
 }
 /**
  * Process the READ BINARY instruction (0xB0) ISO7816-4 Section 7.2.3
  *
  * <p>We handle only the INS == 0xB0 case.
  */
 private void processReadBinary(APDU apdu) {
   if (state != STATE_PERSONALISED) {
     ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
   }
   byte[] buf = apdu.getBuffer();
   byte p1 = buf[OFFSET_P1];
   byte p2 = buf[OFFSET_P2];
   short offset = 0;
   short ef = -1;
   if ((byte) (p1 & MASK_SFI) == MASK_SFI) {
     byte sfi = (byte) (p1 & ~MASK_SFI);
     if (sfi >= 0x1F) {
       ISOException.throwIt(SW_INCORRECT_P1P2);
     }
     ef = fileSystem.findCurrentSFI(sfi);
     if (ef == -1) {
       ISOException.throwIt(SW_FILE_NOT_FOUND);
     }
     ef = fileSystem.fileStructure[ef];
     offset = unsigned(p2);
   } else {
     ef = fileSystem.getCurrentIndex();
     if (fileSystem.getFile(ef) == null) {
       ISOException.throwIt(SW_COMMAND_NOT_ALLOWED);
     }
     offset = Util.makeShort(p1, p2);
   }
   byte[] file = fileSystem.getFile(ef);
   if (offset > file.length) {
     ISOException.throwIt(SW_INCORRECT_P1P2);
   }
   if (fileSystem.getPerm(ef) == FileSystem.PERM_PIN && !pin.isValidated()) {
     ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
   }
   short le = apdu.setOutgoing();
   if (le == 0 || le == 256) {
     le = (short) (file.length - offset);
     if (le > 256) le = 256;
   }
   boolean eof = false;
   if ((short) (file.length - offset) < le) {
     le = (short) (file.length - offset);
     eof = true;
   }
   apdu.setOutgoingLength(le);
   apdu.sendBytesLong(file, offset, le);
   if (eof) {
     ISOException.throwIt(SW_END_OF_FILE);
   }
 }
 /**
  * Process the CHANGE REFERENCE DATA instruction (0x24) ISO7816-4 Section 7.5.7
  *
  * <p>We have two options here: (a) in a procudction state we can set the PUC with this, (b) in
  * the distribution state and operational state we change the PIN
  */
 private void processChangeReferenceData(APDU apdu) {
   byte[] buf = apdu.getBuffer();
   short lc = unsigned(buf[OFFSET_LC]);
   byte p1 = buf[OFFSET_P1];
   byte p2 = buf[OFFSET_P2];
   if (state > STATE_INITIAL) {
     // We are changing the PIN, PUC has to be provided
     // check that P1 is 0x00: verification data (puc) followed by new reference data (pin)
     if (p1 != 0x00 || p2 != (byte) 0x00) {
       ISOException.throwIt(SW_INCORRECT_P1P2);
     }
     short pinSize = (short) (lc - PUC_SIZE);
     if (pinSize < MIN_PIN_SIZE || pinSize > MAX_PIN_SIZE) {
       ISOException.throwIt(SW_WRONG_LENGTH);
     }
     apdu.setIncomingAndReceive();
     short offset = (short) (OFFSET_CDATA + PUC_SIZE);
     for (short i = 0; i < pinSize; i++) {
       byte b = buf[(short) (offset + i)];
       if (b < (byte) 0x30 || b > (byte) 0x39) {
         ISOException.throwIt(SW_WRONG_DATA);
       }
     }
     // Pad the pin with 0x00 to overwrite any garbage, e.g. le
     Util.arrayFillNonAtomic(
         buf, (short) (offset + pinSize), (short) (MAX_PIN_SIZE - pinSize), (byte) 0x00);
     if (!puc.check(buf, OFFSET_CDATA, PUC_SIZE)) {
       ISOException.throwIt((short) (SW_PIN_INCORRECT_TRIES_LEFT | puc.getTriesRemaining()));
     }
     pin.update(buf, offset, MAX_PIN_SIZE);
     pin.resetAndUnblock();
     if (state == STATE_PREPERSONALISED) {
       state = STATE_PERSONALISED;
     }
   } else {
     // State is production, we set the puc
     if (p1 != 0x01 || p2 != 0x00) {
       ISOException.throwIt(SW_INCORRECT_P1P2);
     }
     if (lc != PUC_SIZE) {
       ISOException.throwIt(SW_WRONG_LENGTH);
     }
     apdu.setIncomingAndReceive();
     puc.update(buf, OFFSET_CDATA, (byte) lc);
     puc.resetAndUnblock();
   }
 }
  /** 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);
  }
  /** Process the GET CHALLENGE instruction (0x84) ISO 7816-4, Section 7.5.3 */
  private void processGetChallenge(APDU apdu) {
    if (state != STATE_PERSONALISED) {
      ISOException.throwIt(SW_INS_NOT_SUPPORTED);
    }
    byte[] buf = apdu.getBuffer();

    if (buf[OFFSET_P1] != 0x00 || buf[OFFSET_P2] != 0x00) {
      ISOException.throwIt(SW_INCORRECT_P1P2);
    }
    short le = apdu.setOutgoing();
    if (le == 0) {
      ISOException.throwIt(SW_WRONG_LENGTH);
    }
    apdu.setOutgoingLength(le);
    rd.generateData(buf, (short) 0, le);
    apdu.sendBytes((short) 0, le);
  }
 private void processSetState(APDU apdu) throws ISOException {
   if (state == STATE_PERSONALISED) {
     ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
   }
   byte p2 = apdu.getBuffer()[OFFSET_P2];
   if (p2 != STATE_INITIAL && p2 != STATE_PREPERSONALISED) {
     ISOException.throwIt(SW_WRONG_DATA);
   }
   state = p2;
 }
  // Connection management methods
  private void timeTick(APDU apdu) {
    // Updates the area code according to the passed parameter.
    byte[] buffer = apdu.getBuffer();
    byte numBytes = (byte) (buffer[ISO7816.OFFSET_LC]);
    byte byteRead = (byte) (apdu.setIncomingAndReceive());

    if ((numBytes != 2) || (byteRead != 2)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);

    // get area code
    short newAreaCode =
        (short)
            ((short) (buffer[ISO7816.OFFSET_CDATA] << (short) 8)
                | (short) (buffer[ISO7816.OFFSET_CDATA + 1] & 0x00FF));

    if (newAreaCode != INACTIVE_AREA) {
      activeAreaCode[0] = newAreaCode;
    } else {
      resetConnection();
      ISOException.throwIt(SW_NO_NETWORK);
    }

    short connectionType = apdu.getProtocol();
    byte b = apdu.getCLAChannel();
    boolean contactless = false;
    if ((connectionType & 0xf0) == 0x80 // APDU.PROTOCOL_MEDIA_CONTACTLESS_TYPE_A
        || (connectionType & 0xf0) == 0x90) { // APDU.PROTOCOL_MEDIA_CONTACTLESS_TYPE_B ){
      contactless = true;
    }

    // If a connection is active, the user account is debited.
    // If user runs out of credits, the connection is terminated.
    if (connectionStatus[0] == CONNECTION_INUSE) {

      if (AccountAccessor.getAccount() == null) {
        ISOException.throwIt(SW_NO_ACCOUNT);
      }

      if (AccountAccessor.getAccount().debit(activeAreaCode[0], contactless) == false) {
        resetConnection();
        ISOException.throwIt(SW_NEGATIVE_BALANCE);
      }
    }
  }
  public void process(APDU apdu) {

    byte[] buf = apdu.getBuffer();
    byte cla = buf[OFFSET_CLA];
    byte ins = buf[OFFSET_INS];

    // No secure messaging for the PKI applet
    if ((byte) (cla & CLA_SM) == CLA_SM) {
      ISOException.throwIt(SW_SECURE_MESSAGING_NOT_SUPPORTED);
    }
    // Only PSO can be chained
    if (!(cla == CLA_ISO7816 || (cla == CLA_CHAIN && ins == INS_PSO))) {
      ISOException.throwIt(SW_CLA_NOT_SUPPORTED);
    }
    switch (ins) {
      case INS_SELECT:
        processSelectFile(apdu);
        break;
      case INS_READBINARY:
        processReadBinary(apdu);
        break;
      case INS_WRITEBINARY:
        processWriteBinary(apdu);
        break;
      case INS_VERIFY:
        processVerify(apdu);
        break;
      case INS_CHANGEREFERENCEDATA:
        processChangeReferenceData(apdu);
        break;
      case INS_PUTDATA:
        processPutData(apdu);
        break;
      case INS_GENERATE_KEY_PAIR:
        processGenerateAssymetricKeyPair(apdu);
        break;
      case INS_CREATEFILE:
        processCreateFile(apdu);
        break;
      case INS_GETCHALLENGE:
        processGetChallenge(apdu);
        break;
      case INS_MSE:
        processManageSecurityEnvironment(apdu);
        break;
      case INS_PSO:
        processPerformSecurityOperation(apdu);
        break;
      case INS_INTERNALAUTHENTICATE:
        processInternalAuthenticate(apdu);
        break;
      default:
        ISOException.throwIt(SW_INS_NOT_SUPPORTED);
    }
  }
 private void processCreateFile(APDU apdu) {
   if (state != STATE_INITIAL) {
     ISOException.throwIt(SW_INS_NOT_SUPPORTED);
   }
   byte[] buf = apdu.getBuffer();
   short lc = unsigned(buf[OFFSET_LC]);
   apdu.setIncomingAndReceive();
   if (lc != 5) {
     ISOException.throwIt(SW_WRONG_LENGTH);
   }
   short offset = OFFSET_CDATA;
   short id = Util.getShort(buf, offset);
   offset += 2;
   short len = Util.getShort(buf, offset);
   offset += 2;
   byte perm = buf[offset];
   if (!fileSystem.createFile(id, len, perm)) {
     ISOException.throwIt(SW_WRONG_DATA);
   }
 }
 /** Process the VERIFY instruction (0x20) ISO7816-4 Section 7.5.6 */
 private void processVerify(APDU apdu) {
   if (state != STATE_PERSONALISED) {
     ISOException.throwIt(SW_INS_NOT_SUPPORTED);
   }
   byte[] buf = apdu.getBuffer();
   if (buf[OFFSET_P1] != 0x00 || buf[OFFSET_P2] != 0x00) {
     ISOException.throwIt(SW_INCORRECT_P1P2);
   }
   short lc = unsigned(buf[OFFSET_LC]);
   if (lc < MIN_PIN_SIZE || lc > MAX_PIN_SIZE) {
     ISOException.throwIt(SW_WRONG_LENGTH);
   }
   apdu.setIncomingAndReceive();
   // Pad the PIN to overwrite any possible garbage in the APDU (e.g. Le)
   Util.arrayFillNonAtomic(
       buf, (short) (OFFSET_CDATA + lc), (short) (MAX_PIN_SIZE - lc), (byte) 0x00);
   if (!pin.check(buf, OFFSET_CDATA, MAX_PIN_SIZE)) {
     ISOException.throwIt((short) (SW_PIN_INCORRECT_TRIES_LEFT | pin.getTriesRemaining()));
   }
 }
 // Check that the key sent is valid
 private void validateKey(APDU apdu) {
   // fake key test
   byte[] buffer = apdu.getBuffer();
   // process extended length apdu
   short testKey =
       (short)
           ((short) (buffer[ISO7816.OFFSET_EXT_CDATA] << (short) 8)
               | (short) (buffer[ISO7816.OFFSET_EXT_CDATA + 3] & 0x00FF));
   if (testKey != (short) 3) ISOException.throwIt(SW_INVALID_KEY);
   return;
   // System.out.println("here here");
 }
 private void processCreateFileSystemStructure(APDU apdu) {
   if (state != STATE_INITIAL) {
     ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
   }
   byte[] buf = apdu.getBuffer();
   short lc = unsigned(buf[OFFSET_LC]);
   apdu.setIncomingAndReceive();
   // Hack:
   // Search for a non-existing file,
   // if the structure is correct, then only the FileNotFoundException would be
   // thrown.
   try {
     fileSystem.searchId(
         buf, OFFSET_CDATA, OFFSET_CDATA, (short) (OFFSET_CDATA + lc), (short) 0x0000);
     ISOException.throwIt(SW_WRONG_DATA);
   } catch (FileNotFoundException e) {
   } catch (ArrayIndexOutOfBoundsException aioobe) {
     ISOException.throwIt(SW_WRONG_DATA);
   }
   fileSystem.fileStructure = new byte[lc];
   Util.arrayCopy(buf, OFFSET_CDATA, fileSystem.fileStructure, (short) 0, lc);
 }
 /** Process the PUT DATA instruction (0xDA) P1 and P2 are custom */
 private void processPutData(APDU apdu) {
   byte p1 = apdu.getBuffer()[OFFSET_P1];
   if (p1 >= (byte) 0x61 && p1 <= (byte) 0x66) {
     processSetupKey(apdu);
   } else if (p1 == (byte) 0x67) {
     processSetHistoricalBytes(apdu);
   } else if (p1 == (byte) 0x68) {
     processSetState(apdu);
   } else if (p1 == (byte) 0x69) {
     processCreateFileSystemStructure(apdu);
   } else {
     ISOException.throwIt(SW_INCORRECT_P1P2);
   }
 }
 /** Process the PERFORM SECURITY OPERATION instruction (0x2A). ISO 7816-8 Section 5.2 */
 private void processPerformSecurityOperation(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();
   byte p1 = buf[OFFSET_P1];
   byte p2 = buf[OFFSET_P2];
   if (p1 == (byte) 0x80 && (p2 == (byte) 0x82 || p2 == (byte) 0x84 || p2 == (byte) 0x86)) {
     processDecipher(apdu);
   } else if (p1 == (byte) 0x9E && p2 == (byte) 0x9A) {
     processComputeDigitalSignature(apdu);
   } else {
     ISOException.throwIt(SW_INCORRECT_P1P2);
   }
 }
  /**
   * Processes an incoming APDU.
   *
   * @see APDU
   * @param apdu the incoming APDU
   * @exception ISOException with the response bytes per ISO 7816-4
   */
  public void process(APDU apdu) {
    byte buffer[] = apdu.getBuffer();

    short bytesRead = apdu.setIncomingAndReceive();
    short echoOffset = (short) 0;

    while (bytesRead > 0) {
      Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, echoBytes, echoOffset, bytesRead);
      echoOffset += bytesRead;
      bytesRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA);
    }

    apdu.setOutgoing();
    apdu.setOutgoingLength((short) (echoOffset + 5));

    // echo header
    apdu.sendBytes((short) 0, (short) 5);
    // echo data
    apdu.sendBytesLong(echoBytes, (short) 0, echoOffset);
  }
Beispiel #20
0
 // Simpler send method that assumes that APDU.buffer is updated with the output and sent instead.
 // Saves resources, but needs some checks on the
 // size of the incoming buffer
 private void sendBuff(APDU apdu) {
   apdu.setOutgoingAndSend((short) 0, size);
 }
 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 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);
    }
  }
  /**
   * 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;
    }
  }
Beispiel #24
0
 // Common method that sets the size of the output to the global variable size and sends the
 // content of the global variable output
 private void send(APDU apdu) {
   apdu.setOutgoing();
   apdu.setOutgoingLength(size);
   apdu.sendBytesLong(output, (short) 0, size);
 }
  /** 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);
  }
Beispiel #26
0
  public void process(APDU apdu) {
    // Good practice: Return 9000 on SELECT
    if (selectingApplet()) {
      return;
    }

    byte[] buff = apdu.getBuffer();
    // Get the incoming APDU
    // Util.arrayCopy(apdu.getBuffer(),(short) 0, buff,(short) 0,(short) apdu.getBuffer().length);//
    // apdu.getBuffer();
    // Check the CLA

    /*
    if(buff[ISO7816.OFFSET_CLA]!=CLA){
    	ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
    }
    */
    // Switch on the instruction code INS
    switch (buff[ISO7816.OFFSET_INS]) {
        //		Create a test signature using test
      case SEND_TEST_SIGNATURE:
        // Sign the test byte and get the signature size
        size = sig.sign(test, (short) 0, (short) test.length, output, (short) 0);
        break;
        //			return modulus of public key
      case SEND_PUB_MOD:
        // Retrieve the modulus, store it in the output byte array and set the output length
        size = k.getModulus(output, (short) 0);
        break;
        //		  return exponent of public key
      case SEND_PUB_EXP:
        //			Retrieve the public exponent, store it in the output byte array and set the output
        // length
        size = k.getExponent(output, (short) 0);
        break;
        //			return exponent of private key given correct pin authentication
      case SEND_PRV_EXP:
        // Check that the user is authenticated (correct command 0x80 0x03 0x01 0x00 0x04 0x00 0x00
        // 0x00 0x00 0x00)
        if (buff[ISO7816.OFFSET_P1] == ((byte) 0x01)) {
          if (buff[ISO7816.OFFSET_LC] != (byte) 0x00) {
            if (pin.check(buff, (short) (ISO7816.OFFSET_LC + 1), buff[ISO7816.OFFSET_LC])) {
              size = k2.getExponent(output, (short) 0);
              pin.reset();
            } else {
              // wrong pin (system should have taken care of decrementing the counter and checking
              // boundary conditions)
              ISOException.throwIt(ISO7816.SW_WRONG_DATA);
            }
          } else {
            ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); // no pin was sent
          }
        } else {
          ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); // wrong command code
        }
        //			Retrieve the private exponent, store it in the output byte array and set the output
        // length
        size = k2.getExponent(output, (short) 0);
        break;
        //			return size of signature and modulus for testing purposes (they should be the same)
      case (byte) SEND_KEY_LENGTH:
        shortToByteArray(keysize);
        size = (short) 2;
        Util.arrayCopy(buff, (short) 0, output, (short) 0, size);
        break;
        //			Sign arbitrary text sent to card
      case (byte) SIGN_INPUT_DATA:
        size = (short) buff[ISO7816.OFFSET_LC];
        size = sig.sign(buff, (short) (ISO7816.OFFSET_LC + 1), size, output, (short) 0);
        break;
        //			return the modulus of public keywith a random value sent from the host
      case (byte) SEND_AUTHENTICATED_PUB_EXP:
        // Find the size of the random value
        size = (short) buff[ISO7816.OFFSET_LC];
        // If the current key is 2048 bit =256 bytes we need a big array to store all data to sign
        // TODO limit the size of the input value and do some checks on it
        bigArray =
            JCSystem.makeTransientByteArray((short) (size + keysize), JCSystem.CLEAR_ON_RESET);
        // Update the signature object with that value
        Util.arrayCopy(buff, (short) (ISO7816.OFFSET_LC + 1), bigArray, (short) 0, size);
        k.getModulus(bigArray, (short) (size));
        // Util.arrayCopy(buff, (short) 0, bigArray, (short) (ISO7816.OFFSET_LC+size+1), len);
        size = sig.sign(bigArray, (short) 0, (short) bigArray.length, output, (short) 0);
        break;

      case (byte) 0x07:
        size = (short) 2;
        output[0] = (byte) 0x09;
        output[1] = (byte) 0x08;
        break;
      case (byte) 0x08:
        short length = 256;
        // short length = (short) buff.length;
        size = (short) length;
        // output[0]=(byte) 0x08;
        // output[1]=(byte) 0x08;

        for (short i = 0; i < length; i++) {
          output[i] = (byte) buff[i];
        }
        break;
      default:
        // good practice: If you don't know the INStruction, say so:
        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
    }

    send(apdu);
  }