/** Process the INTERNAL AUTHENTICATE instruction (0x88) ISO 7816-4 Section 7.5.2 */ private void processInternalAuthenticate(APDU apdu) { if (state != STATE_PERSONALISED) { ISOException.throwIt(SW_INS_NOT_SUPPORTED); } if (!pin.isValidated()) { ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); } byte[] buf = apdu.getBuffer(); short lc = unsigned(buf[OFFSET_LC]); if (lc == 0) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) currentPrivateKey[0]; byte alg = tmp[TMP_SIGNALG_OFFSET]; if (privateKey != authKeyPrivate || alg != ALG_AUTH_DEC_RSA) { ISOException.throwIt(SW_WRONG_DATA); } short offset = OFFSET_CDATA; short maxLength = (short) ((short) (privateKey.getSize() / 8) - 11); if (lc > maxLength) { ISOException.throwIt(SW_WRONG_LENGTH); } pkcs1Cipher.init(privateKey, Cipher.MODE_ENCRYPT); short len = pkcs1Cipher.doFinal(buf, offset, lc, tmp, TMP_OFFSET); Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, len); apdu.setOutgoingAndSend((short) 0, len); }
/** Process the PSO COMPUTE DIGITAL SIGNATURE instruction (0x2A) ISO 7816-8 Section 5.4 */ private void processComputeDigitalSignature(APDU apdu) { pin.reset(); byte[] buf = apdu.getBuffer(); short lc = unsigned(buf[OFFSET_LC]); if (lc == 0) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) currentPrivateKey[0]; byte alg = tmp[TMP_SIGNALG_OFFSET]; if (privateKey != signKeyPrivate || (alg != ALG_SIGN_RSA_PKCS1_SHA1 && alg != ALG_SIGN_RSA_PKCS1_SHA256 && alg != ALG_SIGN_RSA_PSS && alg != ALG_SIGN_RSA_PKCS1_SHA1MD5)) { ISOException.throwIt(SW_WRONG_DATA); } short offset = OFFSET_CDATA; short expectedLength = 0; if (alg == ALG_SIGN_RSA_PKCS1_SHA256) { expectedLength = (short) (SHA256_LEN + 17); } else if (alg == ALG_SIGN_RSA_PKCS1_SHA1) { expectedLength = (short) (SHA1_LEN + 13); } else if (alg == ALG_SIGN_RSA_PSS) { expectedLength = SHA1_LEN; } else if (alg == ALG_SIGN_RSA_PKCS1_SHA1MD5) { expectedLength = SHA1MD5_LEN; } if (lc != expectedLength) { ISOException.throwIt(SW_WRONG_LENGTH); } short sigLen = 0; if (alg == ALG_SIGN_RSA_PKCS1_SHA1 || alg == ALG_SIGN_RSA_PKCS1_SHA256 || alg == ALG_SIGN_RSA_PKCS1_SHA1MD5) { pkcs1Cipher.init(privateKey, Cipher.MODE_ENCRYPT); sigLen = pkcs1Cipher.doFinal(buf, offset, lc, tmp, TMP_OFFSET); Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, sigLen); } else { short emLen = (short) (privateKey.getSize() / 8); pssPad(buf, offset, lc, tmp, TMP_OFFSET, emLen, signKeyFirstModulusByte); nopadCipher.init(privateKey, Cipher.MODE_ENCRYPT); sigLen = nopadCipher.doFinal(tmp, (short) 0, emLen, buf, (short) 0); } apdu.setOutgoingAndSend((short) 0, sigLen); }
/** 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); }
// 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); }