Hello everyone,
this message is intended as a "request for ideas/opinions/comments" on
how the CardEdge applet manages its memory.
Recently, Sylvain Ferey and Sébastien Lorquet pointed out in the list
that using EEPROM for operations like signing and ciphering in the
CardEdge applet (i.e. operations that currently write very frequently
to the EEPROM memory) should really be avoided (Sébastien even
suggested that it shouldn't be allowed).
One (of several) possible solution was mentioned: the RAM should, if
available, be used for these kinds of operations, since writing to the
EEPROM too frequently will/may shorten considerably the card's lifetime.
Sylvain Ferey pointed out that, on all newer cards, it should be safe
to assume that they provide, at least, 1024 bytes of RAM memory.
Sylvain also pointed out that, even if the card has sufficient RAM,
one must be careful while using it, since it is possible that other
applets (if present on the card) may already have allocated it for its
own purposes...
With this in mind (and since I'm not rich, it would be nice that my
card lasted a little bit more :P), I've written a small patch that
avoids EEPROM memory usage altogether if the applet is compiled with
the extended APDU option. The applet will need to have 526 bytes of
RAM memory available (268 bytes for the incoming buffer, 258 bytes for
the outgoing buffer) - this can be much more optimized, but I think it
should be part of a bigger effort...)
The main advantages are:
- It's faster - "pkcs11-tool --login --test" appears to be ~20% faster;
- It will not kill the card's memory.
The disvantages are:
- It's less secure (?) - the original memory allocation with
ObjectManager creates ACLs on the allocated memory so only the creator
of the object can access it. To try to mitigate this, I've added code
to clear the buffer after it is used, but is this secure enough? What
are the security implications?;
- RAM is needed... - shouldn't be a problem on cards that support
extended APDUs. For example, my card has 8KB of RAM.
Again, this patch only affects the applet if the WITH_EXT_APDU option
is enabled. If not, the old behavior is used. But, maybe, it could be
a good idea to use a smaller buffer even without the WITH_EXT_APDU
option? For example, a smaller buffer on the RAM, since the current
behavior is not very nice on the card...
If anyone can comment on this it would be great.
Thanks everyone for the discussion on the last days. It's a privilege
(and a pleasure) to learn about smart card development with people
like you.
Best regards,
Joao
--- CardEdge.src.orig 2009-05-30 11:52:40.000000000 +0100
+++ CardEdge.src 2009-05-30 21:11:00.000000000 +0100
@@ -69,15 +69,19 @@
// Maximum number of keys allowed for ExtAuth
private final static byte MAX_NUM_AUTH_KEYS = (byte) 6;
-
#ifdef WITH_EXT_APDU
- // Maximum size for the extended APDU buffer for a 2048 bit key:
+ // Maximum size for the incoming extended APDU buffer [supports 2048 bit keys]:
// CLA [1 byte] + INS [1 byte] + P1 [1 byte] + P2 [1 byte] +
- // LC [3 bytes] + cipher_mode[1 byte] + cipher_direction [1 byte] +
+ // LC [3 bytes] + cipher_mode [1 byte] + cipher_direction [1 byte] +
// data_location [1 byte] + data_size [2 bytes] + data [256 bytes]
// = 268 bytes
- private final static short EXT_APDU_BUFFER_SIZE = (short) 268;
+ private final static short EXT_APDU_IBUFFER_SIZE = (short) 268;
+
+ // Maximum size of the extended outgoing APDU buffer [supports 2048 bit keys]:
+ // data_size [2 bytes] + data [256 bytes] = 258 bytes
+ private final static short EXT_APDU_OBUFFER_SIZE = (short) 258;
#endif
+
/* Pin policies constants (OR-ed in var pinPolicies) */
/** Enable pin size check */
private final static byte PIN_POLICY_SIZE = (byte) 0x01;
@@ -331,8 +335,12 @@
// OwnerPIN objects, allocated on demand
private OwnerPIN[] pins, ublk_pins;
- // Buffer for storing extended APDUs
- private byte[] recvBuffer;
+#ifdef WITH_EXT_APDU
+ // Buffer for storing incoming extended APDUs
+ private byte[] recvBuffer;
+ // Buffer for storing outgoing extended APDUs
+ private byte[] sendBuffer;
+#endif
/* Logged identities: this is used for faster access *
* control, so we don't have to ping each PIN object */
@@ -609,18 +617,31 @@
STD_PUBLIC_ACL = new byte[KEY_ACL_SIZE];
for (byte i = (byte) 0; i < (byte) KEY_ACL_SIZE; i += (short) 2)
Util.setShort(STD_PUBLIC_ACL, i, (short)0x0000);
+
#ifdef WITH_EXT_APDU
- // Initialize the extended APDU buffer
- try {
- // Try to allocate the extended APDU buffer on RAM memory
- recvBuffer = JCSystem.makeTransientByteArray((short)EXT_APDU_BUFFER_SIZE,
+ // Initialize the incoming extended APDU buffer
+ try {
+ // Try to allocate the extended APDU buffer on RAM memory
+ recvBuffer = JCSystem.makeTransientByteArray((short) EXT_APDU_IBUFFER_SIZE,
JCSystem.CLEAR_ON_DESELECT);
- } catch (SystemException e) {
- // Allocate the extended APDU buffer on EEPROM memory
- // This is the fallback method, but its usage is really not recommended
- // as after ~ 100000 writes it will kill the EEPROM cells...
- recvBuffer = new byte[EXT_APDU_BUFFER_SIZE];
- }
+ } catch (SystemException e) {
+ // Allocate the extended APDU buffer on EEPROM memory
+ // This is the fallback method, but its usage is really not recommended
+ // as after ~ 100000 writes it will kill the EEPROM cells...
+ recvBuffer = new byte[EXT_APDU_IBUFFER_SIZE];
+ }
+
+ // Initialize the outgoing extended APDU buffer
+ try {
+ // Try to allocate the extended APDU buffer on RAM memory
+ sendBuffer = JCSystem.makeTransientByteArray((short) EXT_APDU_OBUFFER_SIZE,
+ JCSystem.CLEAR_ON_DESELECT);
+ } catch (SystemException e) {
+ // Allocate the extended APDU buffer on EEPROM memory
+ // This is the fallback method, but its usage is really not recommended
+ // as after ~ 100000 writes it will kill the EEPROM cells...
+ sendBuffer = new byte[EXT_APDU_OBUFFER_SIZE];
+ }
#endif
setupDone = true;
@@ -632,7 +653,7 @@
* that could be necessary to be fully JavaCard compliant. */
private void sendData(APDU apdu, byte[] data, short offset, short size) {
#ifdef WITH_EXT_APDU
- if (size > EXT_APDU_BUFFER_SIZE)
+ if (size > EXT_APDU_OBUFFER_SIZE)
#else
if (size > 255)
#endif
@@ -929,7 +950,7 @@
short LC = apdu.getIncomingLength();
short dataOffset = apdu.getOffsetCdata();
- if((short) (LC + dataOffset) > EXT_APDU_BUFFER_SIZE)
+ if((short) (LC + dataOffset) > EXT_APDU_IBUFFER_SIZE)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
/* Is this an extended APDU? */
@@ -961,6 +982,7 @@
Key key = keys[key_nb];
byte ciph_dir;
byte data_location;
+ byte[] dst_buff;
byte[] src_buff;
short src_base;
short src_avail;
@@ -1133,22 +1155,40 @@
else {
// OP_FINALIZE
if (ciph_dir == CD_SIGN) {
+#ifdef WITH_EXT_APDU
+ if (sign.getLength() > (short) (EXT_APDU_OBUFFER_SIZE - 2))
+ ISOException.throwIt(SW_NO_MEMORY_LEFT);
+ // Should we clear the buffer?
+ dst_buff = sendBuffer;
+ short dst_base = 0;
+#else
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID,
(short) (sign.getLength() + 2),
getCurrentACL(), (short) 0);
if (dst_base == MemoryManager.NULL_OFFSET)
ISOException.throwIt(SW_NO_MEMORY_LEFT);
+ dst_buff = mem.getBuffer();
+#endif
short sign_size = sign.sign(src_buff, (short) (src_base + 2),
- size, mem.getBuffer(), (short) (dst_base + 2));
+ size, dst_buff, (short) (dst_base + 2));
if (sign_size > sign.getLength())
// We got a buffer overflow (unless we were in memory end and got an exception...)
ISOException.throwIt(SW_INTERNAL_ERROR);
+#ifdef WITH_EXT_APDU
+ Util.setShort(dst_buff, (short) 0, (short) sign_size);
+#else
mem.setShort(dst_base, sign_size);
+#endif
// Actually send data back (and clear output buffer) only if location is APDU
if (data_location == DL_APDU) {
- sendData(apdu, mem.getBuffer(), dst_base, (short) (sign_size + 2));
+ sendData(apdu, dst_buff, dst_base, (short) (sign_size + 2));
+#ifdef WITH_EXT_APDU
+ // Clear the outgoing buffer
+ Util.arrayFillNonAtomic(dst_buff, (short) 0, EXT_APDU_OBUFFER_SIZE, (byte) 0x00);
+#else
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
+#endif
}
} else { // ciph_dir == CD_VERIFY
if (src_avail < (short) (2 + size + 2))
@@ -1198,6 +1238,15 @@
size = Util.getShort(src_buff, src_base);
if (src_avail < (short) (2 + size))
ISOException.throwIt(SW_INVALID_PARAMETER);
+
+#ifdef WITH_EXT_APDU
+ if (size > (short) (EXT_APDU_OBUFFER_SIZE - 2))
+ ISOException.throwIt(SW_NO_MEMORY_LEFT);
+ // Should we clear the buffer?
+ dst_buff = buffer = sendBuffer;
+ short dst_base = 0;
+ Util.setShort(dst_buff, (short) 0, (short) size);
+#else
// TODO: Don't destroy the out obj every time, but keep it
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
// Create object with 2 more bytes for DataChunk Size field
@@ -1208,18 +1257,26 @@
ISOException.throwIt(SW_NO_MEMORY_LEFT);
mem.setShort(dst_base, size);
+ dst_buff = mem.getBuffer();
+#endif
if (op == OP_PROCESS)
ciph.update(src_buff, (short) (src_base + 2),
- size, mem.getBuffer(), (short) (dst_base + 2));
+ size, dst_buff, (short) (dst_base + 2));
else /* op == OP_FINAL */
ciph.doFinal(src_buff, (short) (src_base + 2),
- size, mem.getBuffer(), (short) (dst_base + 2));
+ size, dst_buff, (short) (dst_base + 2));
if (data_location == DL_APDU) {
// Also copies the Short size information
- Util.arrayCopyNonAtomic(mem.getBuffer(), dst_base,
+#ifndef WITH_EXT_APDU
+ Util.arrayCopyNonAtomic(dst_buff, dst_base,
buffer, (short) 0, (short) (size + 2));
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
+#endif
sendData(apdu, buffer, (short) 0, (short) (size + 2));
+#ifdef WITH_EXT_APDU
+ // Clear the outgoing buffer
+ Util.arrayFillNonAtomic(dst_buff, (short) 0, EXT_APDU_OBUFFER_SIZE, (byte) 0x00);
+#endif
}
break;
#else_______________________________________________
Muscle mailing list
[email protected]
http://lists.drizzle.com/mailman/listinfo/muscle