Program: 40097
Project: 40097
Buggy source:
/** 
 * The GENERAL AUTHENTICATE card command performs a cryptographic operation, such as an authentication protocol, using the data provided in the data field of the command and returns the result of the cryptographic operation in the response data field.
 * @param buffer The incoming APDU buffer
 * @param offset The offset of the CDATA element
 * @param length The length of the CDATA element
 * @return The length of the return data
 */
public short generalAuthenticate(byte[] buffer,short offset,short length){
  final byte CONST_TAG_TEMPLATE=(byte)0x7C;
  final byte CONST_TAG_WITNESS=(byte)0x80;
  final byte CONST_TAG_CHALLENGE=(byte)0x81;
  final byte CONST_TAG_RESPONSE=(byte)0x82;
  length=chainBuffer.processIncomingAPDU(buffer,offset,length,scratch,(short)0);
  if (length == 0)   return length;
  tlvReader.init(scratch,(short)0,length);
  PIVKeyObject key=cspPIV.selectKey(buffer[ISO7816.OFFSET_P2],buffer[ISO7816.OFFSET_P1]);
  if (key == null) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
  }
  if (!key.isInitialised()) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
  }
  if (!cspPIV.checkAccessModeObject(key)) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
  }
  if (!tlvReader.find(CONST_TAG_TEMPLATE)) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_DATA_INVALID);
  }
  tlvReader.moveInto();
  short challengeOffset=0, witnessOffset=0, responseOffset=0;
  boolean challengeEmpty=false, witnessEmpty=false, responseEmpty=false;
  offset=tlvReader.getOffset();
  do {
    if (tlvReader.match(CONST_TAG_CHALLENGE)) {
      challengeOffset=tlvReader.getOffset();
      challengeEmpty=tlvReader.isNull();
    }
 else     if (tlvReader.match(CONST_TAG_RESPONSE)) {
      responseOffset=tlvReader.getOffset();
      responseEmpty=tlvReader.isNull();
    }
 else     if (tlvReader.match(CONST_TAG_WITNESS)) {
      witnessOffset=tlvReader.getOffset();
      witnessEmpty=tlvReader.isNull();
    }
 else {
    }
  }
 while (tlvReader.moveNext());
  tlvReader.setOffset(offset);
  length=key.getBlockLength();
  if ((challengeOffset != 0 && !challengeEmpty) && (responseOffset != 0 && responseEmpty)) {
    authenticateReset();
    if (!key.hasRole(PIVKeyObject.ROLE_AUTH_INTERNAL)) {
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(challengeOffset);
    if (tlvReader.getLength() != length) {
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_WRONG_DATA);
    }
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_RESPONSE);
    tlvWriter.writeLength(length);
    offset=tlvWriter.getOffset();
    offset+=cspPIV.encrypt(key,scratch,tlvReader.getDataOffset(),length,scratch,offset);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else   if (challengeOffset != 0 && challengeEmpty) {
    authenticateReset();
    key.resetSecurityStatus();
    if (!key.hasRole(PIVKeyObject.ROLE_AUTH_EXTERNAL)) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_CHALLENGE);
    tlvWriter.writeLength(length);
    offset=tlvWriter.getOffset();
    cspPIV.generateRandom(scratch,offset,length);
    offset+=cspPIV.encrypt(key,scratch,offset,length,authenticationContext,OFFSET_AUTH_CHALLENGE);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    authenticationContext[OFFSET_AUTH_STATE]=AUTH_STATE_EXTERNAL;
    authenticationContext[OFFSET_AUTH_ID]=key.getId();
    authenticationContext[OFFSET_AUTH_MECHANISM]=key.getMechanism();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else   if (responseOffset != 0 && !responseEmpty) {
    if (authenticationContext[OFFSET_AUTH_STATE] != AUTH_STATE_EXTERNAL) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (authenticationContext[OFFSET_AUTH_ID] != key.getId() || authenticationContext[OFFSET_AUTH_MECHANISM] != key.getMechanism()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(responseOffset);
    if (length != tlvReader.getLength()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (Util.arrayCompare(scratch,tlvReader.getDataOffset(),authenticationContext,OFFSET_AUTH_CHALLENGE,length) != 0) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    key.setSecurityStatus();
    authenticateReset();
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    return (short)0;
  }
 else   if (witnessOffset != 0 && witnessEmpty) {
    authenticateReset();
    key.resetSecurityStatus();
    if (!key.hasRole(PIVKeyObject.ROLE_AUTH_EXTERNAL)) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    cspPIV.generateRandom(authenticationContext,OFFSET_AUTH_CHALLENGE,length);
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_WITNESS);
    tlvWriter.writeLength(length);
    offset=tlvWriter.getOffset();
    offset+=cspPIV.encrypt(key,authenticationContext,OFFSET_AUTH_CHALLENGE,length,scratch,offset);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    authenticationContext[OFFSET_AUTH_STATE]=AUTH_STATE_MUTUAL;
    authenticationContext[OFFSET_AUTH_ID]=key.getId();
    authenticationContext[OFFSET_AUTH_MECHANISM]=key.getMechanism();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else   if (witnessOffset != 0 && !witnessEmpty && challengeOffset != 0 && !challengeEmpty) {
    if (authenticationContext[OFFSET_AUTH_STATE] != AUTH_STATE_MUTUAL) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (authenticationContext[OFFSET_AUTH_ID] != key.getId() || authenticationContext[OFFSET_AUTH_MECHANISM] != key.getMechanism()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(witnessOffset);
    if (length != tlvReader.getLength()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (Util.arrayCompare(scratch,tlvReader.getDataOffset(),authenticationContext,OFFSET_AUTH_CHALLENGE,length) != 0) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(challengeOffset);
    length=tlvReader.getLength();
    if (key.getBlockLength() != length) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
    }
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_RESPONSE);
    tlvWriter.writeLength(length);
    offset=tlvWriter.getOffset();
    offset+=cspPIV.encrypt(key,scratch,tlvReader.getDataOffset(),key.getBlockLength(),scratch,offset);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    key.setSecurityStatus();
    authenticateReset();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else {
    authenticateReset();
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_WRONG_DATA);
    return (short)0;
  }
}

Fixed version (ground truth):
/** 
 * The GENERAL AUTHENTICATE card command performs a cryptographic operation, such as an authentication protocol, using the data provided in the data field of the command and returns the result of the cryptographic operation in the response data field.
 * @param buffer The incoming APDU buffer
 * @param offset The offset of the CDATA element
 * @param length The length of the CDATA element
 * @return The length of the return data
 */
public short generalAuthenticate(byte[] buffer,short offset,short length){
  final byte CONST_TAG_TEMPLATE=(byte)0x7C;
  final byte CONST_TAG_WITNESS=(byte)0x80;
  final byte CONST_TAG_CHALLENGE=(byte)0x81;
  final byte CONST_TAG_RESPONSE=(byte)0x82;
  length=chainBuffer.processIncomingAPDU(buffer,offset,length,scratch,(short)0);
  if (length == 0)   return length;
  tlvReader.init(scratch,(short)0,length);
  PIVKeyObject key=cspPIV.selectKey(buffer[ISO7816.OFFSET_P2],buffer[ISO7816.OFFSET_P1]);
  if (key == null) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
  }
  if (!key.isInitialised()) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
  }
  if (!cspPIV.checkAccessModeObject(key)) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
  }
  if (!tlvReader.find(CONST_TAG_TEMPLATE)) {
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_DATA_INVALID);
  }
  tlvReader.moveInto();
  short challengeOffset=0, witnessOffset=0, responseOffset=0;
  boolean challengeEmpty=false, witnessEmpty=false, responseEmpty=false;
  offset=tlvReader.getOffset();
  do {
    if (tlvReader.match(CONST_TAG_CHALLENGE)) {
      challengeOffset=tlvReader.getOffset();
      challengeEmpty=tlvReader.isNull();
    }
 else     if (tlvReader.match(CONST_TAG_RESPONSE)) {
      responseOffset=tlvReader.getOffset();
      responseEmpty=tlvReader.isNull();
    }
 else     if (tlvReader.match(CONST_TAG_WITNESS)) {
      witnessOffset=tlvReader.getOffset();
      witnessEmpty=tlvReader.isNull();
    }
 else {
    }
  }
 while (tlvReader.moveNext());
  tlvReader.setOffset(offset);
  length=key.getBlockLength();
  if ((challengeOffset != 0 && !challengeEmpty) && (responseOffset != 0 && responseEmpty)) {
    authenticateReset();
    if (!key.hasRole(PIVKeyObject.ROLE_AUTH_INTERNAL)) {
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(challengeOffset);
    if (tlvReader.getLength() != length) {
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_WRONG_DATA);
    }
    length=cspPIV.encrypt(key,scratch,tlvReader.getDataOffset(),length,buffer,(short)0);
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_RESPONSE);
    tlvWriter.writeLength(length);
    offset=Util.arrayCopyNonAtomic(buffer,(short)0,scratch,tlvWriter.getOffset(),length);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else   if (challengeOffset != 0 && challengeEmpty) {
    authenticateReset();
    key.resetSecurityStatus();
    if (!key.hasRole(PIVKeyObject.ROLE_AUTH_EXTERNAL)) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_CHALLENGE);
    tlvWriter.writeLength(length);
    offset=tlvWriter.getOffset();
    cspPIV.generateRandom(scratch,offset,length);
    offset+=cspPIV.encrypt(key,scratch,offset,length,authenticationContext,OFFSET_AUTH_CHALLENGE);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    authenticationContext[OFFSET_AUTH_STATE]=AUTH_STATE_EXTERNAL;
    authenticationContext[OFFSET_AUTH_ID]=key.getId();
    authenticationContext[OFFSET_AUTH_MECHANISM]=key.getMechanism();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else   if (responseOffset != 0 && !responseEmpty) {
    if (authenticationContext[OFFSET_AUTH_STATE] != AUTH_STATE_EXTERNAL) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (authenticationContext[OFFSET_AUTH_ID] != key.getId() || authenticationContext[OFFSET_AUTH_MECHANISM] != key.getMechanism()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(responseOffset);
    if (length != tlvReader.getLength()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (Util.arrayCompare(scratch,tlvReader.getDataOffset(),authenticationContext,OFFSET_AUTH_CHALLENGE,length) != 0) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    key.setSecurityStatus();
    authenticateReset();
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    return (short)0;
  }
 else   if (witnessOffset != 0 && witnessEmpty) {
    authenticateReset();
    key.resetSecurityStatus();
    if (!key.hasRole(PIVKeyObject.ROLE_AUTH_EXTERNAL)) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    cspPIV.generateRandom(authenticationContext,OFFSET_AUTH_CHALLENGE,length);
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_WITNESS);
    tlvWriter.writeLength(length);
    offset=tlvWriter.getOffset();
    offset+=cspPIV.encrypt(key,authenticationContext,OFFSET_AUTH_CHALLENGE,length,scratch,offset);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    authenticationContext[OFFSET_AUTH_STATE]=AUTH_STATE_MUTUAL;
    authenticationContext[OFFSET_AUTH_ID]=key.getId();
    authenticationContext[OFFSET_AUTH_MECHANISM]=key.getMechanism();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else   if (witnessOffset != 0 && !witnessEmpty && challengeOffset != 0 && !challengeEmpty) {
    if (authenticationContext[OFFSET_AUTH_STATE] != AUTH_STATE_MUTUAL) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (authenticationContext[OFFSET_AUTH_ID] != key.getId() || authenticationContext[OFFSET_AUTH_MECHANISM] != key.getMechanism()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(witnessOffset);
    if (length != tlvReader.getLength()) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    if (Util.arrayCompare(scratch,tlvReader.getDataOffset(),authenticationContext,OFFSET_AUTH_CHALLENGE,length) != 0) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
    }
    tlvReader.setOffset(challengeOffset);
    length=tlvReader.getLength();
    if (key.getBlockLength() != length) {
      authenticateReset();
      cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
    }
    length=cspPIV.encrypt(key,scratch,tlvReader.getDataOffset(),length,buffer,(short)0);
    tlvWriter.init(scratch,(short)0,length,CONST_TAG_TEMPLATE);
    tlvWriter.writeTag(CONST_TAG_RESPONSE);
    tlvWriter.writeLength(length);
    offset=Util.arrayCopyNonAtomic(buffer,(short)0,scratch,tlvWriter.getOffset(),length);
    tlvWriter.setOffset(offset);
    length=tlvWriter.finish();
    key.setSecurityStatus();
    authenticateReset();
    chainBuffer.setOutgoing(scratch,(short)0,length,true);
    return length;
  }
 else {
    authenticateReset();
    cspPIV.zeroise(scratch,(short)0,LENGTH_SCRATCH);
    ISOException.throwIt(ISO7816.SW_WRONG_DATA);
    return (short)0;
  }
}