SecureChannel.unwrap fails in JCIDE simulator when no data present
Posted: Sun Sep 10, 2017 10:47 pm
Hi,
I've implemented GP SecureChannel handling in an applet that enforces encryption and mac (CEnc+CMac) on the incoming command data. This is generally working fine but in the JCIDE simulator, the call to unwrap() fails when there is no CDATA in the APDU. I've then tested this on several real cards and it works as expected.
The source code to demonstrate this is below. It shows a simple check for the CEnc+CMac attributes then has two valid INS commands.
00000000 should work always
00010000 should work only when in an encrypted/mac'd session
I suspect that this might be a bug in the simulator?
Thanks,
Ko
EDIT: Fixed a compilation bug in the example.
I've implemented GP SecureChannel handling in an applet that enforces encryption and mac (CEnc+CMac) on the incoming command data. This is generally working fine but in the JCIDE simulator, the call to unwrap() fails when there is no CDATA in the APDU. I've then tested this on several real cards and it works as expected.
The source code to demonstrate this is below. It shows a simple check for the CEnc+CMac attributes then has two valid INS commands.
00000000 should work always
00010000 should work only when in an encrypted/mac'd session
I suspect that this might be a bug in the simulator?
Thanks,
Ko
EDIT: Fixed a compilation bug in the example.
Code: Select all
package SecureChannelTest;
import javacard.framework.*;
import org.globalplatform.*;
import javacardx.crypto.*;
public class SecureChannelTest extends Applet
{
// Holds our secure channel
private SecureChannel secureChannel;
// GlobalPlatform instructions for establishing a Secure Channel
private static final byte INS_GP_INITIALIZE_UPDATE = (byte) 0x50;
private static final byte INS_GP_EXTERNAL_AUTHENTICATE = (byte) 0x82;
public static void install(byte[] bArray, short bOffset, byte bLength)
{
new SecureChannelTest().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
public void process(APDU apdu)
{
if (selectingApplet()) return;
byte[] buffer = apdu.getBuffer();
// Get a reference to the GlobalPlatform SecureChannel
if (secureChannel == null) secureChannel = GPSystem.getSecureChannel();
short length = apdu.setIncomingAndReceive();
boolean isAuthenticated = false;
// Decrypt and Verify any commands that are wrapped in a GP SecureChannel with CEncCMac
final byte SC_MASK = SecureChannel.AUTHENTICATED | SecureChannel.C_DECRYPTION | SecureChannel.C_MAC;
if ((secureChannel.getSecurityLevel() & SC_MASK) == SC_MASK) {
// Unwrap, including the entire APDU which is required for verifying the MAC
secureChannel.unwrap(buffer, (short)0, (short)(ISO7816.OFFSET_CDATA + length));
// Now flag the command as being authenticated
isAuthenticated = true;
}
switch (buffer[ISO7816.OFFSET_INS])
{
// GP commands
case INS_GP_INITIALIZE_UPDATE:
secureChannel.resetSecurity();
length = secureChannel.processSecurity(apdu);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, length);
break;
case INS_GP_EXTERNAL_AUTHENTICATE:
length = secureChannel.processSecurity(apdu);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, length);
break;
// This INS can be executed by anyone
case (byte)0x00:
// TODO: Do something here
break;
// This INS requires an authenticated session
case (byte)0x01:
// Check if we have an appropriately secured session
if (!isAuthenticated) ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
// TODO: Do something here
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
public void deselect() {
// Reset any security domain session (this is suggested in the documentation for resetSecurity)
if (secureChannel != null) secureChannel.resetSecurity();
}
}