예제 #1
0
 /**
  * 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);
 }
예제 #2
0
  /** 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);
  }
예제 #3
0
  private cardTest() {

    // Instantiate all object the applet will ever need
    // pin= new OwnerPIN(MAX_LENGTH, MAX_ATTEMPTS);
    // if(bArray==null){//check
    //			If no pin is passed as parameter at installation time use default 0000
    // pin.update(new byte[] {0x00,0x00,0x00,0x00}, (short) 0, (byte) 0x04);
    //	}
    // else {
    // pin.update(bArray, bOffset,  bLength);
    // }

    try {
      // Set signature algorithm
      sig = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
      // Generate the card keys
      keys.genKeyPair();
      // Get the public key
      k = (RSAPublicKey) keys.getPublic();
      // Get the private key
      k2 = (RSAPrivateKey) keys.getPrivate();
      // Initialize the signature object with card private key
      sig.init(k2, Signature.MODE_SIGN);
    } catch (CryptoException ex) {
      ISOException.throwIt((short) (ex.getReason()));
    } catch (SecurityException ex) {
      ISOException.throwIt((short) (0x6F10));
    } catch (Exception ex) {
      ISOException.throwIt((short) (0x6F20));
    }
  }
예제 #4
0
 /** 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);
 }
  void writeData(short fid, short file_offset, byte[] data, short data_offset, short length) {
    byte[] file = getFile(fid);
    short fileSize = getFileSize(fid);

    if (file == null) {
      ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND);
    }

    if (fileSize < (short) (file_offset + length)) ISOException.throwIt(ISO7816.SW_FILE_FULL);
    Util.arrayCopyNonAtomic(data, data_offset, file, file_offset, length);
    // Extract the pointers to where the Root and Alternate root CVCA certificate
    // identifiers are stored. Properly this should be done with a BERTLVScanner or
    // similar.
    if (fid == EF_COM_FID) {
      short cvcaRootIndex = -1;
      short cvcaAltIndex = -1;
      for (short i = 0; i < length; i++) {
        if (data[i] == 0x04 && data[(short) (i + 1)] == 0x11) {
          if (cvcaRootIndex == -1) {
            cvcaRootIndex = (short) ((short) (file_offset + i) + 2);
          } else {
            cvcaAltIndex = (short) ((short) (file_offset + i) + 2);
          }
        }
      }
      SmartIDApplet.certificate.setCOMFileData(file, cvcaRootIndex, cvcaAltIndex);
    }
  }
  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);
    }
  }
예제 #7
0
 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;
 }
예제 #8
0
  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);
    }
  }
예제 #9
0
 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);
 }
예제 #10
0
 // Checks the DO in the buffer, report any inconsitencies
 // Return the length of the data
 private short checkDataObject(byte[] buffer, short offset, short lastOffset, byte expectedTag) {
   if (offset >= lastOffset || lastOffset > buffer.length) {
     ISOException.throwIt(SW_WRONG_LENGTH);
   }
   if (buffer[offset++] != expectedTag) {
     ISOException.throwIt(SW_WRONG_DATA);
   }
   short len = unsigned(buffer[offset++]);
   if (offset > (short) (lastOffset - len)) {
     ISOException.throwIt(SW_WRONG_LENGTH);
   }
   return len;
 }
예제 #11
0
  /** 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);
  }
예제 #12
0
  /** 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);
  }
예제 #13
0
 /** 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);
   }
 }
  // 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);
      }
    }
  }
예제 #15
0
 /** 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()));
   }
 }
예제 #16
0
 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);
   }
 }
 // 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 setConnection() {

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

    if (connectionStatus[0] == CONNECTION_INUSE) {
      ISOException.throwIt(SW_CONNECTION_BUSY);
    }

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

    // The first time unit is charged at connection setup
    if (AccountAccessor.getAccount().debit(activeAreaCode[0], false)) {
      connectionStatus[0] = CONNECTION_INUSE;
    } else {
      ISOException.throwIt(SW_NEGATIVE_BALANCE);
    }
  }
  void createFile(short fid, short size, boolean eapProtection) {
    short idx = getFileIndex(fid);

    // first create determines maximum file size
    if (files[idx] == null) files[idx] = new byte[size];

    if (((byte[]) files[idx]).length < size) ISOException.throwIt(ISO7816.SW_FILE_FULL);

    fileSizes[idx] = size;
    if (eapProtection) {
      filePerms[idx] = SmartIDApplet.TERMINAL_AUTHENTICATED;
    }
  }
예제 #20
0
 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);
 }
예제 #21
0
 /** 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);
   }
 }
예제 #22
0
 /**
  * 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);
   }
 }
예제 #23
0
 /**
  * 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();
   }
 }
 private short getFileIndex(short fid) throws ISOException {
   short result = -1;
   switch (fid) {
     case EF_DG1_FID:
       result = EF_DG1_INDEX;
       break;
     case EF_DG2_FID:
       result = EF_DG2_INDEX;
       break;
     case EF_DG3_FID:
       result = EF_DG3_INDEX;
       break;
     case EF_DG4_FID:
       result = EF_DG4_INDEX;
       break;
     case EF_DG5_FID:
       result = EF_DG5_INDEX;
       break;
     case EF_DG6_FID:
       result = EF_DG6_INDEX;
       break;
     case EF_DG7_FID:
       result = EF_DG7_INDEX;
       break;
     case EF_DG8_FID:
       result = EF_DG8_INDEX;
       break;
     case EF_DG9_FID:
       result = EF_DG9_INDEX;
       break;
     case EF_DG10_FID:
       result = EF_DG10_INDEX;
       break;
     case EF_DG11_FID:
       result = EF_DG11_INDEX;
       break;
     case EF_DG12_FID:
       result = EF_DG12_INDEX;
       break;
     case EF_DG13_FID:
       result = EF_DG13_INDEX;
       break;
     case EF_DG14_FID:
       result = EF_DG14_INDEX;
       break;
     case EF_DG15_FID:
       result = EF_DG15_INDEX;
       break;
     case EF_DG16_FID:
       result = EF_DG16_INDEX;
       break;
     case EF_SOD_FID:
       result = EF_SOD_INDEX;
       break;
     case EF_COM_FID:
       result = EF_COM_INDEX;
       break;
     default:
       result = -1;
       break;
   }
   if (result != -1 && SmartIDApplet.isLocked() && SmartIDApplet.hasMutualAuthenticationKeys()) {
     // We are in the personalized state and BAC is active,
     // we need to control the access
     // a. check that the current authorization level is sufficient to
     // access
     // the given file
     // b. if we are passed the EAC protocol we also need to check
     // whether the current certificate authorization allows us to read the file.
     byte perm = filePerms[result];
     if ((byte) (perm & SmartIDApplet.volatileState[0]) != perm) {
       ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
     }
     if (result <= EF_DG16_INDEX
         && SmartIDApplet.hasTerminalAuthenticated()
         && perm == SmartIDApplet.TERMINAL_AUTHENTICATED) {
       short m = (short) (0x1 << result);
       if ((Util.getShort(currentAuthorization, (short) 1) & m) != m) {
         ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
       }
     }
   }
   return result;
 }
예제 #25
0
  /** 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);
    }
  }
예제 #26
0
 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);
   }
 }
예제 #27
0
  /** 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);
  }
예제 #28
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);
  }
예제 #29
0
  /**
   * 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;
    }
  }