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

Reply via email to