/** * 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); }
/** 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 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)); } }
/** 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); } }
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; }
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 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); }
// 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; }
/** 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); }
/** 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); } } }
/** 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())); } }
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; } }
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 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(); } }
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; }
/** 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); } }
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); }
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); }
/** * 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; } }