SmartPGP/src/dev/c0de/smartpgp/SmartPGPApplet.java

1648 lines
58 KiB
Java

/*
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
https://github.com/ANSSI-FR/SmartPGP
Copyright (C) 2016 ANSSI
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.c0de.smartpgp;
import javacard.framework.*;
import javacard.security.*;
import javacardx.crypto.*;
import javacardx.apdu.ExtendedLength;
public final class SmartPGPApplet extends Applet implements ExtendedLength {
private final Common common;
private final Persistent data;
private final Transients transients;
public SmartPGPApplet() {
common = new Common();
data = new Persistent();
transients = new Transients();
}
public static final void install(byte[] buf, short off, byte len) {
new SmartPGPApplet().register(buf, (short)(off + 1), buf[off]);
}
private final PGPKey currentTagOccurenceToKey() {
switch(transients.currentTagOccurrence()) {
case 0:
return data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT];
case 1:
return data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC];
case 2:
return data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG];
default:
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
return null;
}
}
private final void prepareChainingInput(final byte[] apdubuf) {
short tmp;
tmp = transients.outputLength();
if(tmp > 0) {
Util.arrayFillNonAtomic(transients.buffer, transients.outputStart(), tmp, (byte)0);
}
transients.setChainingOutput(false);
transients.setOutputStart((short)0);
transients.setOutputLength((short)0);
if(transients.chainingInput()) {
if((apdubuf[ISO7816.OFFSET_INS] != transients.chainingInputIns()) ||
(apdubuf[ISO7816.OFFSET_P1] != transients.chainingInputP1()) ||
(apdubuf[ISO7816.OFFSET_P2] != transients.chainingInputP2())) {
transients.setChainingInput(false);
transients.setChainingInputLength((short)0);
ISOException.throwIt(Constants.SW_CHAINING_ERROR);
return;
}
if((apdubuf[ISO7816.OFFSET_CLA] & Constants.CLA_MASK_CHAINING) != Constants.CLA_MASK_CHAINING) {
transients.setChainingInput(false);
}
} else {
tmp = transients.chainingInputLength();
if(tmp > 0) {
Util.arrayFillNonAtomic(transients.buffer, (short)0, tmp, (byte)0);
}
transients.setChainingInputLength((short)0);
if((apdubuf[ISO7816.OFFSET_CLA] & Constants.CLA_MASK_CHAINING) == Constants.CLA_MASK_CHAINING) {
transients.setChainingInputIns(apdubuf[ISO7816.OFFSET_INS]);
transients.setChainingInputP1(apdubuf[ISO7816.OFFSET_P1]);
transients.setChainingInputP2(apdubuf[ISO7816.OFFSET_P2]);
transients.setChainingInput(true);
}
}
}
private final void receiveData(final APDU apdu) {
final byte[] apdubuf = apdu.getBuffer();
short blen = apdu.setIncomingAndReceive();
final short lc = apdu.getIncomingLength();
final short offcdata = apdu.getOffsetCdata();
short off = transients.chainingInputLength();
if((short)(off + lc) > Constants.INTERNAL_BUFFER_MAX_LENGTH) {
transients.setChainingInput(false);
transients.setChainingInputLength((short)0);
ISOException.throwIt(Constants.SW_MEMORY_FAILURE);
return;
}
while(blen > 0) {
off = Util.arrayCopyNonAtomic(apdubuf, offcdata,
transients.buffer, off,
blen);
blen = apdu.receiveBytes(offcdata);
}
transients.setChainingInputLength(off);
}
private final void sensitiveData() {
}
private final void assertAdmin() {
if(!data.admin_pin.isValidated()) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
}
private final void assertUserMode81() {
if(!data.user_pin.isValidated() || !transients.userPinMode81()) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
}
private final void assertUserMode82() {
if(!data.user_pin.isValidated() || !transients.userPinMode82()) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
}
private final short writePwStatus(final byte[] buf, short off) {
buf[off++] = (byte)(data.user_pin_force_verify_signature ? 0x00 : 0x01);
if(data.keyDerivationIsActive()) {
final byte size = data.keyDerivationSize();
buf[off++] = size;
buf[off++] = size;
buf[off++] = size;
} else {
buf[off++] = Constants.USER_PIN_MAX_SIZE;
buf[off++] = Constants.USER_PUK_MAX_SIZE;
buf[off++] = Constants.ADMIN_PIN_MAX_SIZE;
}
buf[off++] = data.user_pin.getTriesRemaining();
buf[off++] = data.user_puk.getTriesRemaining();
buf[off++] = data.admin_pin.getTriesRemaining();
return off;
}
private final short writeKeyFingerprints(final byte[] buf, short off) {
for(byte i = 0; i < data.pgp_keys.length; ++i) {
off = data.pgp_keys[i].fingerprint.write(buf, off);
}
return off;
}
private final short writeCaFingerprints(final byte[] buf, short off) {
for(byte i = 0; i < data.fingerprints.length; ++i) {
off = data.fingerprints[i].write(buf, off);
}
return off;
}
private final short writeKeyGenerationDates(final byte[] buf, short off) {
for(byte i = 0; i < data.pgp_keys.length; ++i) {
off = Util.arrayCopyNonAtomic(data.pgp_keys[i].generation_date, (short)0,
buf, off,
Constants.GENERATION_DATE_SIZE);
}
return off;
}
private final void processSelectData(final short lc,
final byte p1, final byte p2) {
if((lc < 5) || (lc > 6)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
if((p1 < 0) || (p1 > 3) ||
(p2 != 0x04)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
final byte[] buf = transients.buffer;
if((buf[0] != (byte)0x60) ||
(buf[1] != (byte)(lc - 2)) ||
(buf[2] != (byte)0x5C) ||
(buf[3] != (byte)(lc - 2 - 2))) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return;
}
transients.setCurrentTagOccurrence(p1);
if(buf[3] == 1) {
transients.setCurrentTag(buf[4]);
} else if(buf[3] == 2) {
transients.setCurrentTag(Util.getShort(buf, (short)4));
}
}
private final short processGetData(final byte p1, final byte p2) {
final short tag = Util.makeShort(p1, p2);
short off = 0;
short tlen = 0;
if(transients.currentTag() == 0) {
transients.setCurrentTag(tag);
transients.setCurrentTagOccurrence((byte)0);
} else if(transients.currentTag() != tag) {
transients.setCurrentTagOccurrence((byte)0);
}
final byte[] buf = transients.buffer;
PGPKey k;
switch(tag) {
case Constants.TAG_AID:
off = (short)(off + JCSystem.getAID().getBytes(buf, off));
break;
case Constants.TAG_LOGIN:
off = Util.arrayCopyNonAtomic(data.login, (short)0, buf, off, data.login_length);
break;
case Constants.TAG_URL:
off = Util.arrayCopyNonAtomic(data.url, (short)0, buf, off, data.url_length);
break;
case Constants.TAG_PRIVATE_DO_0101:
off = Util.arrayCopyNonAtomic(data.do_0101, (short)0, buf, off, data.do_0101_length);
break;
case Constants.TAG_PRIVATE_DO_0102:
off = Util.arrayCopyNonAtomic(data.do_0102, (short)0, buf, off, data.do_0102_length);
break;
case Constants.TAG_PRIVATE_DO_0103:
assertUserMode82();
off = Util.arrayCopyNonAtomic(data.do_0103, (short)0, buf, off, data.do_0103_length);
break;
case Constants.TAG_PRIVATE_DO_0104:
assertAdmin();
off = Util.arrayCopyNonAtomic(data.do_0104, (short)0, buf, off, data.do_0104_length);
break;
case Constants.TAG_KEY_FINGERPRINTS:
off = writeKeyFingerprints(buf, off);
break;
case Constants.TAG_CA_FINGERPRINTS:
off = writeCaFingerprints(buf, off);
break;
case Constants.TAG_KEY_GENERATION_DATES:
off = writeKeyGenerationDates(buf, off);
break;
case Constants.TAG_HISTORICAL_BYTES_CARD_SERVICE_CARD_CAPABILITIES:
off = Util.arrayCopyNonAtomic(Constants.HISTORICAL_BYTES, (short)0,
buf, off,
(byte)Constants.HISTORICAL_BYTES.length);
break;
case Constants.TAG_CARDHOLDER_RELATED_DATA:
buf[off++] = (byte)0x5B;
off = Common.writeLength(buf, off, data.name_length);
off = Util.arrayCopyNonAtomic(data.name, (short)0, buf, off, data.name_length);
off = Util.setShort(buf, off, (short)0x5f2d);
off = Common.writeLength(buf, off, data.lang_length);
off = Util.arrayCopyNonAtomic(data.lang, (short)0, buf, off, data.lang_length);
off = Util.setShort(buf, off, (short)0x5f35);
buf[off++] = (byte)0x01;
buf[off++] = data.sex;
break;
case Constants.TAG_EXTENDED_LENGTH_INFORMATION:
off = Util.setShort(buf, off, Constants.TAG_EXTENDED_LENGTH_INFORMATION);
off = Common.writeLength(buf, off, (short)8);
buf[off++] = (byte)0x02;
buf[off++] = (byte)0x02;
off = Util.setShort(buf, off, Constants.APDU_MAX_LENGTH);
buf[off++] = (byte)0x02;
buf[off++] = (byte)0x02;
off = Util.setShort(buf, off, Constants.APDU_MAX_LENGTH);
break;
case Constants.TAG_ALGORITHM_ATTRIBUTES_SIG:
buf[off++] = (byte)0xc1;
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG];
off = Common.writeLength(buf, off, k.attributes_length);
off = Util.arrayCopyNonAtomic(k.attributes, (short)0,
buf, off,
k.attributes_length);
break;
case Constants.TAG_ALGORITHM_ATTRIBUTES_DEC:
buf[off++] = (byte)0xc2;
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC];
off = Common.writeLength(buf, off, k.attributes_length);
off = Util.arrayCopyNonAtomic(k.attributes, (short)0,
buf, off,
k.attributes_length);
break;
case Constants.TAG_ALGORITHM_ATTRIBUTES_AUT:
buf[off++] = (byte)0xc3;
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT];
off = Common.writeLength(buf, off, k.attributes_length);
off = Util.arrayCopyNonAtomic(k.attributes, (short)0,
buf, off,
k.attributes_length);
break;
case Constants.TAG_APPLICATION_RELATED_DATA:
tlen = (short)(1 + 1 + Constants.EXTENDED_CAPABILITIES.length +
1 + 1 + data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG].attributes_length +
1 + 1 + data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC].attributes_length +
1 + 1 + data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT].attributes_length +
1 + 1 + 7 +
1 + 1 + (3 * Constants.FINGERPRINT_SIZE) +
1 + 1 + (3 * Constants.FINGERPRINT_SIZE) +
1 + 1 + (3 * Constants.GENERATION_DATE_SIZE));
final byte aid_length = JCSystem.getAID().getBytes(buf, off);
buf[off++] = (byte)Constants.TAG_APPLICATION_RELATED_DATA;
off = Common.writeLength(buf, off, (short)(tlen + 1 + aid_length + 2 + 1 + Constants.HISTORICAL_BYTES.length));
buf[off++] = (byte)Constants.TAG_AID;
off = Common.writeLength(buf, off, aid_length);
off += JCSystem.getAID().getBytes(buf, off);
off = Util.setShort(buf, off, Constants.TAG_HISTORICAL_BYTES_CARD_SERVICE_CARD_CAPABILITIES);
off = Common.writeLength(buf, off, (short)Constants.HISTORICAL_BYTES.length);
off = Util.arrayCopyNonAtomic(Constants.HISTORICAL_BYTES, (short)0,
buf, off,
(byte)Constants.HISTORICAL_BYTES.length);
buf[off++] = (byte)0x73;
off = Common.writeLength(buf, off, tlen);
buf[off++] = (byte)0xc0;
off = Common.writeLength(buf, off, (short)Constants.EXTENDED_CAPABILITIES.length);
off = Util.arrayCopyNonAtomic(Constants.EXTENDED_CAPABILITIES, (short)0,
buf, off,
(short)Constants.EXTENDED_CAPABILITIES.length);
buf[off++] = (byte)0xc1;
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG];
off = Common.writeLength(buf, off, k.attributes_length);
off = Util.arrayCopyNonAtomic(k.attributes, (short)0,
buf, off,
k.attributes_length);
buf[off++] = (byte)0xc2;
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC];
off = Common.writeLength(buf, off, k.attributes_length);
off = Util.arrayCopyNonAtomic(k.attributes, (short)0,
buf, off,
k.attributes_length);
buf[off++] = (byte)0xc3;
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT];
off = Common.writeLength(buf, off, k.attributes_length);
off = Util.arrayCopyNonAtomic(k.attributes, (short)0,
buf, off,
k.attributes_length);
buf[off++] = (byte)0xc4;
buf[off++] = 7;
off = writePwStatus(buf, off);
buf[off++] = (byte)0xc5;
off = Common.writeLength(buf, off, (short)(3 * Constants.FINGERPRINT_SIZE));
off = writeKeyFingerprints(buf, off);
buf[off++] = (byte)0xc6;
off = Common.writeLength(buf, off, (short)(3 * Constants.FINGERPRINT_SIZE));
off = writeCaFingerprints(buf, off);
buf[off++] = (byte)0xcd;
off = Common.writeLength(buf, off, (short)(3 * Constants.GENERATION_DATE_SIZE));
off = writeKeyGenerationDates(buf, off);
Common.writeLength(buf, (short)1, (short)(off - 3));
break;
case Constants.TAG_PW_STATUS:
off = writePwStatus(buf, off);
break;
case Constants.TAG_SECURITY_SUPPORT_TEMPLATE:
buf[off++] = (byte)0x93;
buf[off++] = (byte)data.digital_signature_counter.length;
off = Util.arrayCopyNonAtomic(data.digital_signature_counter, (short)0,
buf, off,
(byte)data.digital_signature_counter.length);
break;
case Constants.TAG_CARDHOLDER_CERTIFICATE:
k = currentTagOccurenceToKey();
if(k == null) {
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
return 0;
}
off = Util.arrayCopyNonAtomic(k.certificate, (short)0,
buf, off,
k.certificate_length);
break;
case Constants.TAG_KEY_INFORMATION:
buf[off++] = (byte)0xde;
buf[off++] = (byte)0x06; /* len */
buf[off++] = (byte)0x01;
buf[off++] = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG].keyInformation();
buf[off++] = (byte)0x02;
buf[off++] = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC].keyInformation();
buf[off++] = (byte)0x03;
buf[off++] = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT].keyInformation();
break;
case Constants.TAG_KEY_DERIVATION_FUNCTION:
off = Util.arrayCopyNonAtomic(data.key_derivation_function, (short)0,
buf, off,
data.key_derivation_function_length);
break;
case Constants.TAG_ALGORITHM_INFORMATION:
off = Common.writeAlgorithmInformation((byte)0xc1, buf, off); /* SIG */
off = Common.writeAlgorithmInformation((byte)0xc2, buf, off); /* DEC */
off = Common.writeAlgorithmInformation((byte)0xc3, buf, off); /* AUT */
break;
default:
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
return 0;
}
return off;
}
private final short processGetNextData(final byte p1, final byte p2) {
if(Util.makeShort(p1, p2) != Constants.TAG_CARDHOLDER_CERTIFICATE) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return 0;
}
final PGPKey k = currentTagOccurenceToKey();
if(k == null) {
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
return 0;
}
transients.setCurrentTagOccurrence((byte)(transients.currentTagOccurrence() + 1));
return Util.arrayCopyNonAtomic(k.certificate, (short)0,
transients.buffer, (short)0,
k.certificate_length);
}
private final void processVerify(short lc, final byte p1, final byte p2) {
sensitiveData();
if(p1 == 0) {
if(lc == 0) {
byte remaining = 0;
switch(p2) {
case (byte)0x81:
if(data.user_pin.isValidated() && transients.userPinMode81()) {
return;
}
remaining = data.user_pin.getTriesRemaining();
break;
case (byte)0x82:
if(data.user_pin.isValidated() && transients.userPinMode82()) {
return;
}
remaining = data.user_pin.getTriesRemaining();
break;
case (byte)0x83:
if(data.admin_pin.isValidated()) {
return;
}
remaining = data.admin_pin.getTriesRemaining();
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
remaining = (byte)((byte)0xf & remaining);
ISOException.throwIt(Util.makeShort((byte)0x63, (byte)(0xC0 | remaining)));
return;
} else {
switch(p2) {
case (byte)0x81:
case (byte)0x82:
if(data.keyDerivationIsActive()) {
if(lc != data.keyDerivationSize()) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
} else {
if((lc < Constants.USER_PIN_MIN_SIZE) ||
(lc > Constants.USER_PIN_MAX_SIZE)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
}
if(p2 == (byte)0x81) {
transients.setUserPinMode81(false);
} else {
transients.setUserPinMode82(false);
}
if(!data.user_pin.check(transients.buffer, (short)0, (byte)lc)) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
return;
}
if(p2 == (byte)0x81) {
transients.setUserPinMode81(true);
} else {
transients.setUserPinMode82(true);
}
return;
case (byte)0x83:
if(data.keyDerivationIsActive()) {
if(lc != data.keyDerivationSize()) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
} else {
if((lc < Constants.ADMIN_PIN_MIN_SIZE) ||
(lc > Constants.ADMIN_PIN_MAX_SIZE)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
}
if(!data.admin_pin.check(transients.buffer, (short)0, (byte)lc)) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
return;
}
return;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
}
} else if(p1 == (byte)0xff) {
if(lc != 0) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
switch(p2) {
case (byte)0x81:
transients.setUserPinMode81(false);
return;
case (byte)0x82:
transients.setUserPinMode82(false);
return;
case (byte)0x83:
if(data.admin_pin.isValidated()) {
data.admin_pin.reset();
}
return;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
}
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
private final void processChangeReferenceData(final short lc,
final byte p1, final byte p2) {
sensitiveData();
byte off;
byte minlen;
if(p1 != 0) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
switch(p2) {
case (byte)0x81:
minlen = Constants.USER_PIN_MIN_SIZE;
if(data.keyDerivationIsActive()) {
minlen += data.keyDerivationSize();
} else {
minlen += Constants.USER_PIN_MIN_SIZE;
}
if(lc < minlen) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
transients.setUserPinMode81(false);
transients.setUserPinMode82(false);
off = data.user_pin_length;
if(!data.user_pin.check(transients.buffer, (short)0, off)) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
return;
}
minlen = (byte)(lc - off);
if(data.keyDerivationIsActive()) {
if(data.keyDerivationSize() != minlen) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
} else {
if((minlen < Constants.USER_PIN_MIN_SIZE) ||
(minlen > Constants.USER_PIN_MAX_SIZE)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
}
JCSystem.beginTransaction();
data.user_pin_length = minlen;
data.user_pin.update(transients.buffer, off, data.user_pin_length);
JCSystem.commitTransaction();
data.user_pin.resetAndUnblock();
break;
case (byte)0x83:
minlen = Constants.ADMIN_PIN_MIN_SIZE;
if(data.keyDerivationIsActive()) {
minlen += data.keyDerivationSize();
} else {
minlen += Constants.ADMIN_PIN_MIN_SIZE;
}
if(lc < minlen) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
off = data.admin_pin_length;
if(!data.admin_pin.check(transients.buffer, (short)0, off)) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
return;
}
minlen = (byte)(lc - off);
if(data.keyDerivationIsActive()) {
if(data.keyDerivationSize() != minlen) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
} else {
if((minlen < Constants.ADMIN_PIN_MIN_SIZE) ||
(minlen > Constants.ADMIN_PIN_MAX_SIZE)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
}
JCSystem.beginTransaction();
data.admin_pin_length = minlen;
data.admin_pin.update(transients.buffer, off, data.admin_pin_length);
JCSystem.commitTransaction();
data.admin_pin.resetAndUnblock();
data.admin_pin.check(transients.buffer, off, data.admin_pin_length);
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
}
private final void processResetRetryCounter(final short lc,
final byte p1, final byte p2) {
sensitiveData();
byte off = 0;
byte minlen;
if(p2 != (byte)0x81) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
switch(p1) {
case (byte)0x00:
minlen = Constants.USER_PUK_MIN_SIZE;
if(data.keyDerivationIsActive()) {
minlen += data.keyDerivationSize();
} else {
minlen += Constants.USER_PIN_MIN_SIZE;
}
if(lc < minlen) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
transients.setUserPinMode81(false);
transients.setUserPinMode82(false);
off = data.user_puk_length;
if(!data.user_puk.check(transients.buffer, (short)0, off)) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
return;
}
minlen = (byte)(lc - off);
if(data.keyDerivationIsActive()) {
if(data.keyDerivationSize() != minlen) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
} else {
if((minlen < Constants.USER_PIN_MIN_SIZE) ||
(minlen > Constants.USER_PIN_MAX_SIZE)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
}
JCSystem.beginTransaction();
data.user_pin_length = minlen;
data.user_pin.update(transients.buffer, off, data.user_pin_length);
JCSystem.commitTransaction();
data.user_pin.resetAndUnblock();
break;
case (byte)0x02:
assertAdmin();
if(data.keyDerivationIsActive()) {
if(lc != data.keyDerivationSize()) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
} else {
if((lc < Constants.USER_PIN_MIN_SIZE) ||
(lc > Constants.USER_PIN_MAX_SIZE)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
}
transients.setUserPinMode81(false);
transients.setUserPinMode82(false);
JCSystem.beginTransaction();
data.user_pin_length = (byte)lc;
data.user_pin.update(transients.buffer, (short)0, data.user_pin_length);
JCSystem.commitTransaction();
data.user_pin.resetAndUnblock();
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
}
private final void processPutData(final short lc,
final byte p1, final byte p2,
final boolean isOdd) {
sensitiveData();
final byte[] buf = transients.buffer;
PGPKey k = null;
if(isOdd) {
assertAdmin();
if((p1 != (byte)0x3f) || (p2 != (byte)0xff)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
if(lc < 8) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
if(buf[0] != (byte)0x4D) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return;
}
final short len = Common.readLength(buf, (byte)1, (short)(lc - 1));
short off = Common.skipLength(buf, (byte)1, (short)(lc - 1));
if((short)(off + len) != lc) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
byte extended_expect = (byte)0;
switch(buf[off]) {
case Constants.CRT_TAG_SIGNATURE_KEY:
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG];
JCSystem.beginTransaction();
Util.arrayFillNonAtomic(data.digital_signature_counter,
(short)0, (byte)data.digital_signature_counter.length,
(byte)0);
JCSystem.commitTransaction();
extended_expect = (byte)0x01;
break;
case Constants.CRT_TAG_DECRYPTION_KEY:
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC];
extended_expect = (byte)0x02;
break;
case Constants.CRT_TAG_AUTHENTICATION_KEY:
k = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT];
extended_expect = (byte)0x03;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return;
}
++off;
if(buf[off] == (byte)0) {
++off;
} else if(buf[off] == (byte)3) {
++off;
if(buf[off++] != (byte)0x84) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return;
}
if(buf[off++] != (byte)1) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
if(buf[off++] != extended_expect) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return;
}
} else {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
k.importKey(buf, off, (short)(lc - off));
} else {
final short tag = Util.makeShort(p1, p2);
if(transients.currentTag() == 0) {
transients.setCurrentTag(tag);
transients.setCurrentTagOccurrence((byte)0);
} else if(transients.currentTag() != tag) {
transients.setCurrentTagOccurrence((byte)0);
}
switch(tag) {
case Constants.TAG_NAME:
assertAdmin();
if((lc < 0) ||
(lc > Constants.NAME_MAX_LENGTH)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.name_length > 0) {
Util.arrayFillNonAtomic(data.name, (short)0, data.name_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.name, (short)0, lc);
data.name_length = (byte)lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_LOGIN:
assertAdmin();
if((lc < 0) ||
(lc > Constants.specialDoMaxLength())) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.login_length > 0) {
Util.arrayFillNonAtomic(data.login, (short)0, data.login_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.login, (short)0, lc);
data.login_length = lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_LANG:
assertAdmin();
if((lc < Constants.LANG_MIN_LENGTH) ||
(lc > Constants.LANG_MAX_LENGTH)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.lang_length > 0) {
Util.arrayFillNonAtomic(data.lang, (short)0, data.lang_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.lang, (short)0, lc);
data.lang_length = (byte)lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_SEX:
assertAdmin();
if(lc != 1) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
switch(buf[0]) {
case Constants.SEX_NOT_KNOWN:
case Constants.SEX_MALE:
case Constants.SEX_FEMALE:
case Constants.SEX_NOT_APPLICABLE:
data.sex = buf[0];
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return;
}
break;
case Constants.TAG_URL:
assertAdmin();
if((lc < 0) ||
(lc > Constants.specialDoMaxLength())) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.url_length > 0) {
Util.arrayFillNonAtomic(data.url, (short)0, data.url_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.url, (short)0, lc);
data.url_length = lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_PRIVATE_DO_0101:
assertUserMode82();
if((lc < 0) ||
(lc > Constants.specialDoMaxLength())) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.do_0101_length > 0) {
Util.arrayFillNonAtomic(data.do_0101, (short)0, data.do_0101_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.do_0101, (short)0, lc);
data.do_0101_length = lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_PRIVATE_DO_0102:
assertAdmin();
if((lc < 0) ||
(lc > Constants.specialDoMaxLength())) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.do_0102_length > 0) {
Util.arrayFillNonAtomic(data.do_0102, (short)0, data.do_0102_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.do_0102, (short)0, lc);
data.do_0102_length = lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_PRIVATE_DO_0103:
assertUserMode82();
if((lc < 0) ||
(lc > Constants.specialDoMaxLength())) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.do_0103_length > 0) {
Util.arrayFillNonAtomic(data.do_0103, (short)0, data.do_0103_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.do_0103, (short)0, lc);
data.do_0103_length = lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_PRIVATE_DO_0104:
assertAdmin();
if((lc < 0) ||
(lc > Constants.specialDoMaxLength())) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.do_0104_length > 0) {
Util.arrayFillNonAtomic(data.do_0104, (short)0, data.do_0104_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.do_0104, (short)0, lc);
data.do_0104_length = lc;
JCSystem.commitTransaction();
break;
case Constants.TAG_AES_KEY:
assertAdmin();
if((lc != (short)16) && (lc != (short)32)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.aes_key != null) {
data.aes_key.clearKey();
}
data.aes_key = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES,
(short)(lc * 8),
false);
data.aes_key.setKey(buf, (short)0);
JCSystem.commitTransaction();
break;
case Constants.TAG_CARDHOLDER_CERTIFICATE:
assertAdmin();
k = currentTagOccurenceToKey();
if(k == null) {
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
return;
}
k.setCertificate(buf, (short)0, lc);
break;
case Constants.TAG_ALGORITHM_ATTRIBUTES_SIG:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG].setAttributes(buf, (short)0, lc);
JCSystem.beginTransaction();
Util.arrayFillNonAtomic(data.digital_signature_counter, (short)0,
(byte)data.digital_signature_counter.length, (byte)0);
JCSystem.commitTransaction();
break;
case Constants.TAG_ALGORITHM_ATTRIBUTES_DEC:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC].setAttributes(buf, (short)0, lc);
break;
case Constants.TAG_ALGORITHM_ATTRIBUTES_AUT:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT].setAttributes(buf, (short)0, lc);
break;
case Constants.TAG_PW_STATUS:
assertAdmin();
if(lc != 0x01) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
if((buf[0] != 0x00) && (buf[0] != 0x01)) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return;
}
data.user_pin_force_verify_signature = (buf[0] == 0);
break;
case Constants.TAG_FINGERPRINT_SIG:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG].fingerprint.set(buf, (short)0, lc);
break;
case Constants.TAG_FINGERPRINT_DEC:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC].fingerprint.set(buf, (short)0, lc);
break;
case Constants.TAG_FINGERPRINT_AUT:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT].fingerprint.set(buf, (short)0, lc);
break;
case Constants.TAG_FINGERPRINT_CA:
assertAdmin();
data.fingerprints[Persistent.FINGERPRINTS_OFFSET_CA].set(buf, (short)0, lc);
break;
case Constants.TAG_FINGERPRINT_CB:
assertAdmin();
data.fingerprints[Persistent.FINGERPRINTS_OFFSET_CB].set(buf, (short)0, lc);
break;
case Constants.TAG_FINGERPRINT_CC:
assertAdmin();
data.fingerprints[Persistent.FINGERPRINTS_OFFSET_CC].set(buf, (short)0, lc);
break;
case Constants.TAG_GENERATION_DATE_SIG:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG].setGenerationDate(buf, (short)0, lc);
break;
case Constants.TAG_GENERATION_DATE_DEC:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC].setGenerationDate(buf, (short)0, lc);
break;
case Constants.TAG_GENERATION_DATE_AUT:
assertAdmin();
data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT].setGenerationDate(buf, (short)0, lc);
break;
case Constants.TAG_RESETTING_CODE:
assertAdmin();
if(data.keyDerivationIsActive()) {
if(lc != data.keyDerivationSize()) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
} else {
if((lc < Constants.USER_PUK_MIN_SIZE) ||
(lc > Constants.USER_PUK_MAX_SIZE)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
}
JCSystem.beginTransaction();
data.user_puk_length = (byte)lc;
data.user_puk.update(buf, (short)0, data.user_puk_length);
JCSystem.commitTransaction();
data.user_puk.resetAndUnblock();
break;
case Constants.TAG_KEY_DERIVATION_FUNCTION:
assertAdmin();
if((lc < 0) ||
(lc > Constants.specialDoMaxLength())) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
JCSystem.beginTransaction();
if(data.key_derivation_function_length > 0) {
Util.arrayFillNonAtomic(data.key_derivation_function, (short)0, data.key_derivation_function_length, (byte)0);
}
Util.arrayCopyNonAtomic(buf, (short)0, data.key_derivation_function, (short)0, lc);
data.key_derivation_function_length = (byte)lc;
JCSystem.commitTransaction();
break;
default:
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
return;
}
}
}
private final short processGenerateAsymmetricKeyPair(final short lc,
final byte p1, final byte p2) {
final byte[] buf = transients.buffer;
if(((p1 != (byte)0x80) && (p1 != (byte)0x81)) ||
(p2 != 0)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return 0;
}
if(lc < 2) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
boolean do_reset = false;
PGPKey pkey;
byte extended_expect = (byte)0;
switch(buf[0]) {
case Constants.CRT_TAG_SIGNATURE_KEY:
do_reset = true;
pkey = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG];
extended_expect = (byte)0x01;
break;
case Constants.CRT_TAG_DECRYPTION_KEY:
pkey = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC];
extended_expect = (byte)0x02;
break;
case Constants.CRT_TAG_AUTHENTICATION_KEY:
pkey = data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT];
extended_expect = (byte)0x03;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return 0;
}
if(lc == (short)2) {
if(buf[1] != (byte)0) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
} else if(lc == (short)5) {
if((buf[1] != (byte)3) || (buf[3] != (byte)1)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
if((buf[2] != (byte)0x84) || buf[4] != extended_expect) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return 0;
}
} else {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
if(p1 == (byte)0x80) {
assertAdmin();
pkey.generate();
if(do_reset) {
JCSystem.beginTransaction();
Util.arrayFillNonAtomic(data.digital_signature_counter, (short)0,
(byte)data.digital_signature_counter.length, (byte)0);
JCSystem.commitTransaction();
}
}
return pkey.writePublicKeyDo(buf, (short)0);
}
private final short processPerformSecurityOperation(final short lc,
final byte p1, final byte p2) {
sensitiveData();
/* PSO : COMPUTE DIGITAL SIGNATURE */
if((p1 == (byte)0x9e) && (p2 == (byte)0x9a)) {
assertUserMode81();
if(data.user_pin_force_verify_signature) {
transients.setUserPinMode81(false);
}
byte i = 0;
JCSystem.beginTransaction();
while(data.digital_signature_counter[(byte)(data.digital_signature_counter.length - i - 1)] == (byte)0xff) {
++i;
}
if(i < data.digital_signature_counter.length) {
++data.digital_signature_counter[(byte)(data.digital_signature_counter.length - i - 1)];
if(i > 0) {
--i;
Util.arrayFillNonAtomic(data.digital_signature_counter,
(short)(data.digital_signature_counter.length - i - 1),
(byte)(i + 1), (byte)0);
}
}
JCSystem.commitTransaction();
return data.pgp_keys[Persistent.PGP_KEYS_OFFSET_SIG].sign(common, transients.buffer, lc, false);
}
/* PSO : DECIPHER */
if((p1 == (byte)0x80) && (p2 == (byte)0x86)) {
assertUserMode82();
if(lc <= 1) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
if(transients.buffer[0] == (byte)0x02) {
if(((short)(lc - 1) % Constants.AES_BLOCK_SIZE) != 0) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
if((data.aes_key == null) || !data.aes_key.isInitialized()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
return 0;
}
common.cipher_aes_cbc_nopad.init(data.aes_key, Cipher.MODE_DECRYPT);
final short res = common.cipher_aes_cbc_nopad.doFinal(transients.buffer, (short)1, (short)(lc - 1),
transients.buffer, lc);
Util.arrayCopyNonAtomic(transients.buffer, lc,
transients.buffer, (short)0, res);
Util.arrayFillNonAtomic(transients.buffer, lc, res, (byte)0);
return res;
}
return data.pgp_keys[Persistent.PGP_KEYS_OFFSET_DEC].decipher(common, transients.buffer, lc);
}
/* PSO : ENCIPHER */
if((p1 == (byte)0x86) && (p2 == (byte)0x80)) {
assertUserMode82();
if((lc <= 0) || ((lc % Constants.AES_BLOCK_SIZE) != 0)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
if((data.aes_key == null) || !data.aes_key.isInitialized()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
return 0;
}
common.cipher_aes_cbc_nopad.init(data.aes_key, Cipher.MODE_ENCRYPT);
final short res = common.cipher_aes_cbc_nopad.doFinal(transients.buffer, (short)0, lc,
transients.buffer, (short)(lc + 1));
transients.buffer[lc] = (byte)0x02;
Util.arrayCopyNonAtomic(transients.buffer, lc,
transients.buffer, (short)0, (short)(res + 1));
Util.arrayFillNonAtomic(transients.buffer, (short)(lc + 1), res, (byte)0);
return (short)(res + 1);
}
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return 0;
}
private final short processInternalAuthenticate(final short lc,
final byte p1, final byte p2) {
if(p2 == (byte)0x00) {
switch(p1) {
case (byte)0x00:
sensitiveData();
assertUserMode82();
return data.pgp_keys[Persistent.PGP_KEYS_OFFSET_AUT].sign(common, transients.buffer, lc, true);
default:
break;
}
}
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return 0;
}
private final short processGetChallenge(short le,
final byte p1, final byte p2) {
if((p1 != (byte)0) || (p2 != (byte)0)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return 0;
}
if(le < 0) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return 0;
}
if(le > Constants.challengeMaxLength()) {
le = Constants.challengeMaxLength();
}
if(le != 0) {
common.random.generateData(transients.buffer, (short)0, le);
}
return le;
}
private final void processTerminateDf(final byte p1, final byte p2) {
if((p1 != (byte)0) || (p2 != (byte)0)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
if(data.admin_pin.getTriesRemaining() <= 0) {
data.isTerminated = true;
return;
}
assertAdmin();
data.isTerminated = true;
}
private final void processActivateFile(final byte p1, final byte p2) {
if(p1 != (byte)0) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
if(data.isTerminated) {
switch(p2) {
case (byte)0:
transients.clear();
data.reset(false);
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
}
}
private final void clearConnection() {
data.user_pin.reset();
data.user_puk.reset();
data.admin_pin.reset();
transients.clear();
}
public final void process(final APDU apdu) {
final byte[] apdubuf = apdu.getBuffer();
if(apdu.isISOInterindustryCLA() && selectingApplet()) {
clearConnection();
if(data.isTerminated) {
ISOException.throwIt(Constants.SW_TERMINATED);
}
return;
}
if(data.isTerminated) {
if((apdubuf[ISO7816.OFFSET_CLA] & Constants.CLA_MASK_CHANNEL) != 0) {
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
return;
}
if(apdubuf[ISO7816.OFFSET_INS] == Constants.INS_ACTIVATE_FILE) {
processActivateFile(apdubuf[ISO7816.OFFSET_P1], apdubuf[ISO7816.OFFSET_P2]);
return;
}
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
return;
}
final byte p1 = apdubuf[ISO7816.OFFSET_P1];
final byte p2 = apdubuf[ISO7816.OFFSET_P2];
short available_le = 0;
short sw = (short)0x9000;
if(((apdubuf[ISO7816.OFFSET_CLA] & Constants.CLA_MASK_CHAINING) != Constants.CLA_MASK_CHAINING) &&
(apdubuf[ISO7816.OFFSET_INS] == Constants.INS_GET_RESPONSE)) {
if(transients.chainingInput() || !transients.chainingOutput()) {
ISOException.throwIt(Constants.SW_CHAINING_ERROR);
return;
}
if((p1 != 0) || (p2 != 0)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
available_le = transients.outputLength();
} else if((apdubuf[ISO7816.OFFSET_CLA] & Constants.CLA_MASK_CHAINING) == Constants.CLA_MASK_CHAINING) {
prepareChainingInput(apdubuf);
receiveData(apdu);
} else {
prepareChainingInput(apdubuf);
receiveData(apdu);
short lc = transients.chainingInputLength();
try {
switch(apdubuf[ISO7816.OFFSET_INS]) {
case Constants.INS_SELECT_DATA:
processSelectData(lc, p1, p2);
break;
case Constants.INS_GET_DATA:
available_le = processGetData(p1, p2);
break;
case Constants.INS_GET_NEXT_DATA:
available_le = processGetNextData(p1, p2);
break;
case Constants.INS_VERIFY:
processVerify(lc, p1, p2);
break;
case Constants.INS_CHANGE_REFERENCE_DATA:
processChangeReferenceData(lc, p1, p2);
break;
case Constants.INS_RESET_RETRY_COUNTER:
processResetRetryCounter(lc, p1, p2);
break;
case Constants.INS_PUT_DATA_DA:
processPutData(lc, p1, p2, false);
break;
case Constants.INS_PUT_DATA_DB:
processPutData(lc, p1, p2, true);
break;
case Constants.INS_GENERATE_ASYMMETRIC_KEY_PAIR:
available_le = processGenerateAsymmetricKeyPair(lc, p1, p2);
break;
case Constants.INS_PERFORM_SECURITY_OPERATION:
available_le = processPerformSecurityOperation(lc, p1, p2);
break;
case Constants.INS_INTERNAL_AUTHENTICATE:
available_le = processInternalAuthenticate(lc, p1, p2);
break;
case Constants.INS_GET_CHALLENGE:
available_le = processGetChallenge(apdu.setOutgoing(), p1, p2);
break;
case Constants.INS_TERMINATE_DF:
processTerminateDf(p1, p2);
break;
case Constants.INS_ACTIVATE_FILE:
processActivateFile(p1, p2);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
return;
}
} catch (ISOException e) {
sw = e.getReason();
}
transients.setOutputLength(available_le);
}
if(available_le > 0) {
short resp_le = available_le;
if(apdu.getCurrentState() != APDU.STATE_OUTGOING) {
resp_le = apdu.setOutgoing();
if((resp_le == (short)0) || (available_le < resp_le)) {
resp_le = available_le;
}
}
if(resp_le > Constants.APDU_MAX_LENGTH) {
resp_le = Constants.APDU_MAX_LENGTH;
}
short off = transients.outputStart();
Util.arrayCopyNonAtomic(transients.buffer, off,
apdubuf, (short)0, resp_le);
apdu.setOutgoingLength(resp_le);
apdu.sendBytes((short)0, resp_le);
Util.arrayFillNonAtomic(transients.buffer, off, resp_le, (byte)0);
available_le -= resp_le;
off += resp_le;
if(available_le > 0) {
transients.setChainingOutput(true);
transients.setOutputLength(available_le);
transients.setOutputStart(off);
if(available_le > (short)0x00ff) {
available_le = (short)0x00ff;
}
sw = (short)(ISO7816.SW_BYTES_REMAINING_00 | available_le);
} else {
transients.setChainingOutput(false);
transients.setOutputLength((short)0);
transients.setOutputStart((short)0);
}
}
ISOException.throwIt(sw);
}
}