22 Apr 2011

Unusual usage of exceptions in JavaCard development (Updated)

If you are reading this blog most probably you already know that development of JavaCard applets has own specificities. Sometimes we need to save few bytes to fit applet to card memory and speed of applet is always important. In this post I’d like to show you unusual usage of exceptions to optimize speed and size of the applet.
Let’s imagine you need to read transparent file on the card but you don’t know its size in advance. Usual practice is analyzing response to select and extracting size information. In our test case we have to read PLMNSel file which contains Mobile Network Code and Mobile Country Code. Each PLMN information length is 3 bytes. Afterwards we can do something with this information but I skip it to show you the main idea. Let's have a look to the code:

package testappletwithbuffer;

import sim.access.*;
import javacard.framework.*;

public class TestAppletWithBuffer extends javacard.framework.Applet {
    
    private SIMView gsmFile;
    private byte[] plmn;
    private byte[] response;
    
    protected TestAppletWithBuffer() {
        gsmFile = SIMSystem.getTheSIMView ();
        plmn = new byte[3];
        response = new byte[15];
    }

    public static void install(byte[] bArray, short bOffset, byte bLength){
        TestAppletWithBuffer refApplet = new TestAppletWithBuffer();
        refApplet.register(bArray, (short) (bOffset + 1), (byte) bArray[bOffset]);
        refApplet.readPLMNSel();
    }

    public void process(APDU apdu) throws ISOException {
          // ignore the applet select command dispatched to the process
        if (selectingApplet()) {
            return;
        }
    }
    
    public void readPLMNSel() {
        gsmFile.select ((short) SIMView.FID_DF_GSM);
        gsmFile.select ((short) SIMView.FID_EF_PLMNSEL
                        , response
                        , (short) 0
                        , (short) response.length);
        short fileOffset = 0;
        short fileLength = Util.makeShort(response[2], response[3]);
        
        // reads the PLMN information to plmn buffer
        for (fileOffset = 0; fileOffset < fileLength; fileOffset += plmn.length) {
            gsmFile.readBinary (fileOffset
                                , plmn
                                , (short) 0
                                , (short) plmn.length);
        }

    }
}

Let me show you how we can optimize this applet:

package testappletwithexception;

import sim.access.*;
import javacard.framework.*;

public class TestAppletWithException extends javacard.framework.Applet {

    private SIMView gsmFile;
    private byte[] plmn;

    public TestAppletWithException() {
        gsmFile = SIMSystem.getTheSIMView();
        plmn = new byte[3];
    }

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        TestAppletWithException refApplet = new TestAppletWithException();
        refApplet.register(bArray, (short) (bOffset + 1), (byte) bArray[bOffset]);
        refApplet.readPLMNSel();
    }

    public void process(APDU apdu) {
        // ignore the applet select command dispatched to the process
        if (selectingApplet()) {
            return;
        }
    }
    
    public void readPLMNSel() {
        short fileOffset = 0;
        gsmFile.select ((short) SIMView.FID_DF_GSM);
        gsmFile.select ((short) SIMView.FID_EF_PLMNSEL);
        
        try {
            // reads the PLMN information to plmn buffer
            for (fileOffset = 0; fileOffset < (short) 0xFFFF; fileOffset += plmn.length) {
                gsmFile.readBinary(fileOffset
                                   , plmn
                                   , (short) 0
                                   , (short) plmn.length);   
            }
        } catch (SIMViewException e) {
            // normal case
        }
    }
}

Such kind of usage of exception could be weird to regular Java developers. But compare generated bytecodes:

Applet with exception Applet with buffer
.method public readPLMNSel()V 8 {
    .stack 5;
    .locals 1;

    L0:  sconst_0;
         sstore_1;
         getfield_a_this 0;  // ref gsmFile
      sspush 32544;
         invokeinterface 2 10 7; // SIMView
         getfield_a_this 0;  // ref gsmFile
         sspush 28464;
         invokeinterface 2 10 7; // SIMView
    L1:  sconst_0;
         sstore_1;
         goto L3;
    L2:  getfield_a_this 0; // ref gsmFile
         sload_1;
         getfield_a_this 1; // ref plmn
         sconst_0;
         getfield_a_this 1; // ref plmn
         arraylength;
         invokeinterface 5 10 9; // SIMView
         pop;
         sload_1;
         getfield_a_this 1; // ref plmn
         arraylength;
         sadd;
         sstore_1;
    L3:  sload_1;
         sconst_m1;
         if_scmplt L2;
    L4:  goto L6;
    L5:  pop;
    L6:  return;
    .exceptionTable {
         // start_block end_block
         // handler_block catch_type_index
         L1 L4 L5 9;
    }
}
.method public readPLMNSel()V 8 {
    .stack 5;
    .locals 2;

    L0:  getfield_a_this 0;  // ref gsmFile
         sspush 32544;
         invokeinterface 2 10 7; // SIMView
         getfield_a_this 0;  // ref gsmFile
         sspush 28464;
         getfield_a_this 2;  // ref response
         sconst_0;
         getfield_a_this 2;  // ref response
         arraylength;
         invokeinterface 5 10 6; // SIMView
         pop;
         sconst_0;
         sstore_1;
         getfield_a_this 2;  // ref response
         sconst_2;
         baload;
         getfield_a_this 2;  // ref response
         sconst_3;
         baload;
         invokestatic 11;  // makeShort
         sstore_2;
         sconst_0;
         sstore_1;
         goto L2;
    L1:  getfield_a_this 0;  // ref gsmFile
         sload_1;
         getfield_a_this 1;  // ref plmn
         sconst_0;
         getfield_a_this 1;  // ref plmn
         arraylength;
         invokeinterface 5 10 9; // SIMView
         pop;
         sload_1;
         getfield_a_this 1;  // ref plmn
         arraylength;
         sadd;
         sstore_1;
    L2:  sload_1;
         sload_2;
         if_scmplt L1;
    L3:  return;
}

As you can see exception bytecode shorter and (here you have to trust me :)) it will work faster.

In given example bytecode size isn’t noticeably less but if you have more complex logic of verification it could make sense. If you compile to above two applets and compare size of IJC files it will be 339 and 363 bytes accordingly. So you will save 24 bytes and will win a little on speed.

Update:
After publishing post I figured out how to more optimize the version with exception. Actually we don't need to check anything at all and if we will replace for loop with while like below we will win even more on the size and speed:

while(true) {
    gsmFile.readBinary(fileOffset
                       , plmn
                       , (short) 0
                       , (short) plmn.length);
    fileOffset += plmn.length;
}

And bytecode in this case will be very short:

.method public readPLMNSel()V 8 {
    .stack 5;
    .locals 1;

    L0:  sconst_0;
         sstore_1;
         getfield_a_this 0;  // ref gsmFile
         sspush 32544;
         invokeinterface 2 10 7;  // sim/access/SIMView
         getfield_a_this 0;       // ref gsmFile
         sspush 28464;
         invokeinterface 2 10 7;  // sim/access/SIMView
    L1:  getfield_a_this 0;       // ref gsmFile
         sload_1;
         getfield_a_this 1;       // ref plmn
         sconst_0;
         getfield_a_this 1;       // ref plmn
         arraylength;
         invokeinterface 5 10 9;  // sim/access/SIMView
         pop;
         sload_1;
         getfield_a_this 1;       // ref plmn
         arraylength;
         sadd;
         sstore_1;
         goto L1;
    L2:  pop;
         return;
    .exceptionTable {
        // start_block end_block handler_block catch_type_index
        L1 L2 L2 9;
    }
}
So there is no limit for perfection :)
Update 2:
I've decided do not leave you with the statement "just trust me" concerning the speed. Let me explain why it is faster:
  • There is no implicit garbage collection on JavaCard. You have to call it explicitly hence no need to unwind stack during exception
  • Exceptions are not fully object as in terms of "big" Java. Usually it is just error code from native layer
As matter of fact usage of exceptions is cheap in JavaCard

6 Apr 2011

How to develop SCWS Servlet?

I'd like to continue the subject of Smart Card Web Server (SCWS) and explain in this post how to develop servlets for SCWS. Servlet for SCWS from one point is regular JavaCard Applet but at the same it has some differences. Let me explain.

Servlet has the following properties:
  • Triggers upon HTTP request reception
  • Has to be developed using dedicated ETSI 102.588 API
  • Mapped on one or several URIs (e.g.“/operator/app”)
There are two kind of servlets:
  • Dynamic content generation
  • Interception (usage tracking)
Servlet supports the following HTTP Request methods:

GETRequests the specified resource
HEADRequests meta-information on a resource (no body)
PUTUploads (add or replace) a resource on the server
POSTSubmits data to be processed to the identified resource
DELETEDeletes the specified resource on the server
OPTIONSReturns the HTTP methods supported for the specified URI (debug purpose)
TRACEEchos back the received requests (debug purposes)

CONNECT method isn't supported by ETSI TS 102.588 standard.

SCWS API has defined in uicc.scws hence you have to import it:

import uicc.scws.*;

Class has to be declared as follows:

class ServletHelloWorld extends Applet
      implements AppletEvent, ScwsExtension
  • Class should extend Applet as it is JavaCard standard applet
  • It has to implement ScwsExtension interface to be able to trigger by SCWS
  • It has to implement AppletEvent interface to define uninstall() method
  • It could implement ToolkitInterface interface if the servlet should be triggered as STK applet
Methods which have to be implemented:
  • JavaCard mandatory methods: process(), install()
  • ScwsExtension mandatory methods: doDelete(), doGet(), doHead(), doOptions(), doPost(), doPut(), doTrace()
Ok, let's have a look to real source code:

package firstservletpackage;

/*
 * Imported packages
 */
import javacard.framework.*;
import uicc.scws.HttpRequest;
import uicc.scws.HttpResponse;
import uicc.scws.ScwsConstants;
import uicc.scws.ScwsException;
import uicc.scws.ScwsExtension;
import uicc.scws.ScwsExtensionRegistry;

public class FirstServlet extends javacard.framework.Applet
                          implements AppletEvent, ScwsExtension {
    /** the servlet url */
    public final static byte[] url = { '/', 'F', 'S' };
    public final static byte[] appId = { 'F', 'i', 'r', 's', 't', 'S', 'e'
                                        ,'r', 'v', 'l', 'e', 't' };
    public final static byte[] CRLF = { '\r', '\n' };
    public static final byte[] SPACE = { ' ' };
    /** error status word when install parameter are invalid */
    public final static short SW_INVALID_INSTALL_PARAMETER = (short) 0x6F06;
    /** install tag: should find the http server aid inside */
    public final static byte INSTALL_TAG = (byte) 0x71;

    public final static byte[] METHOD_GET = { 'G', 'E', 'T' };
    public final static byte[] TEXT_PLAIN = { 't', 'e', 'x', 't', '/', 'p'
                                             ,'l', 'a', 'i', 'n' };

    // Temporary operation buffer
    public byte[] temporaryBuffer;
    public final static short TEMPORARY_BUFFER_LENGTH = (short) 100;

    public final static byte[] REQUEST_BODY = { 'R', 'e', 'q', 'u', 'e', 's'
                                             ,'t', ' ', 'B', 'o', 'd', 'y', ':' };
    public final static byte[] PROTOCOL = { 'P', 'r', 'o', 't', 'o', 'c', 'o'
                                           ,'l', ':' };
    public final static byte[] CONTENT_TYPE = { 'C', 'o', 'n', 't', 'e', 'n'
                                             ,'t', ' ', 'T', 'y', 'p', 'e', ':' };
    public final static byte[] HEADER_UA = { 'H', 'e', 'a', 'd', 'e', 'r', ' '
                                            ,'U', 's', 'e', 'r', ' '
                                            , 'A', 'g', 'e', 'n', 't', ':' };
    public final static byte[] METHOD = { 'M', 'e', 't', 'h', 'o', 'd', ':' };
    public final static byte[] QUERY_STRING = { 'Q', 'u', 'e', 'r', 'y', ' '
                                             ,'S', 't', 'r', 'i', 'n', 'g', ':' };
    public final static byte[] REQUEST_URI = { 'R', 'e', 'q', 'u', 'e', 's'
                                              ,'t', ' ', 'U', 'R', 'I', ':' };
    public final static byte[] TRACE = { 'T', 'R', 'A', 'C', 'E' };
    public final static byte[] ACCEPT = { 'A', 'c', 'c', 'e', 'p', 't', ':' };
    public final static byte[] ACCEPT_LANG = { 'A', 'c', 'c', 'e', 'p', 't'
                              ,'-', 'L', 'a', 'n', 'g', 'u', 'a', 'g', 'e', ':' };
    public final static byte[] CONNECTION = { 'C', 'o', 'n', 'n', 'e', 'c'
                                             ,'t', 'i', 'o', 'n', ':' };
    public final static byte[] HOST = { 'H', 'o', 's', 't', ':' };
    public final static byte[] REFERER = { 'R', 'e', 'f', 'e', 'r', 'e', 'r', ':' };
    public final static byte[] USER_AGENT = { 'U', 's', 'e', 'r', '-', 'A'
                                             ,'g', 'e', 'n', 't', ':' };
    public final static byte[] ACCEPT_ENCODING = { 'A', 'c', 'c', 'e', 'p', 't'
                                                  , '-', 'E', 'n', 'c', 'o', 'd'
                                                  , 'i', 'n', 'g', ':' };
    public final static byte[] ALLOW = { 'A', 'l', 'l', 'o', 'w' };
    public final static byte[] ALLOW_OPTIONS = { 'G', 'E', 'T', ','
                                       , 'T', 'R', 'A', 'C', 'E'
                                       , ',', 'O', 'P', 'T', 'I', 'O', 'N', 'S' };
    public final static byte[] HTTP_10 = { 'H', 'T', 'T', 'P', ':'
                                          , '/', '1', '.', '0' };
    public final static byte[] HTTP_11 = { 'H', 'T', 'T', 'P', ':'
                                          , '/', '1', '.', '1' };

    public final static byte[] HELLO = { 'H', 'e', 'l', 'l', 'o' };

    /**
     * Constructor of servlet
     */
    public FirstServlet(byte[] buffer, short offset, byte length) {
        // First LV is instance AID
        short aid = offset;
        offset += buffer[offset] + (byte) 1;
        // Second LV is Privilege
        offset += buffer[offset] + (byte) 1;
        // Third LV is specific install parameter (extract from TAG C9)
        offset++; // skip C9 Length
        // Register the new applet instance to the JCRE
        register(buffer, (short) (aid + (short) 1), buffer[aid]);
        // Register application id,there is corresponding appId in the Run/Debug
        // configuration for URL Mapping
        // if you update this appId in current code,you may need update
        // corresponding value there for URL Mapping
        ScwsExtensionRegistry.register(this, appId, (short) 0,
                (short) appId.length);
        try {
            // Create a temporary buffer for read/write
            temporaryBuffer = JCSystem.makeTransientByteArray(
                    TEMPORARY_BUFFER_LENGTH, JCSystem.CLEAR_ON_RESET);
        } catch (SystemException se) {
            // create buffer in persistent memory as not enough transient is
            // available
            temporaryBuffer = new byte[TEMPORARY_BUFFER_LENGTH];
        }

    }

    /**
     * Method called by the JCRE at the installation of the applet
     * 
     * @param bArray
     *            the byte array containing the AID bytes
     * @param bOffset
     *            the start of AID bytes in bArray
     * @param bLength
     *            the length of the AID bytes in bArray
     */
    public static void install(byte[] bArray, short sOffset, byte bLength)
            throws ISOException {
        new FirstServlet(bArray, sOffset, bLength);
    }

    /**
     * Method called by the JCRE, uninstall the applet
     */
    public void uninstall() {
        ScwsExtensionRegistry.deregister(this);
    }

    /**
     * Method called by the JCRE, once selected
     * 
     * @param apdu
     *            the incoming APDU object
     */

    public void process(APDU apdu) throws ISOException {
        // ignore the applet select command dispached to the process
        if (selectingApplet()) {
            return;
        }
        // actually no IO methods needed for the server.
        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
    }

    /**
     * Called by the SCWS to allow a servlet to handle a DELETE request.
     * <p>
     * If the HTTP DELETE request is incorrectly formatted, doDelete returns an
     * HTTP "Bad Request" message.
     * </p>
     * 
     * @param req
     *            - the HttpRequest object that contains the request the client
     *            made of the servlet
     * @param resp
     *            - the HttpResponse object that contains the response the
     *            servlet returns to the client
     * @throws ScwsException
     *             - if the request for the DELETE cannot be handled
     */
    public void doDelete(HttpRequest req, HttpResponse resp)
            throws ScwsException {
    }

    /**
     * Called by SCWS to allow a servlet to handle a GET request.
     * <p>
     * When using HTTP 1.1 chunked encoding (which means that the response has a
     * Transfer-Encoding header), do not set the Content-Length header.
     * </p>
     * If the HTTP POST request is incorrectly formatted, doPost returns an HTTP
     * "Bad Request" message.
     * 
     * @param req
     *            - an HttpRequest object that contains the request the client
     *            has made of the servlet
     * @param resp
     *            - an HttpResponse object that contains the response the
     *            servlet sends to the client
     * @throws ScwsException
     *             - if the request for the GET could not be handled
     */
    public void doGet(HttpRequest req, HttpResponse resp) throws ScwsException {
        // TODO Replace your doGet method below
        // ----------------------------------------------------------------------
        // return on response body:
        // status: 200 (OK)
        // Content Type: text/html
        // Protocol: HTTP/1.1
        // Method: GET
        // Header User Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
        // Content Type: text/html
        // ...
        try {
            short httpVersion = req.getRequestHttpVersion();

            // set header
            resp.writeStatusCode(ScwsConstants.SC_OK);
            resp.setContentType(ScwsConstants.CONTENT_TYPE_TEXT_HTML);
            resp.enableChunkMode();

            // write body
            resp.appendContent(HELLO, (short) 0, (short) HELLO.length);
            
        } catch (Exception e) {
            resp.writeStatusCode(ScwsConstants.SC_BAD_REQUEST);
        }

        resp.flush();

    }

    /**
     * Receives an HTTP HEAD request from the protected service method and
     * handles the request.
     * <p>
     * The client sends a HEAD request when it wants to see only the headers of
     * a response, such as <tt>Content-Type</tt> or <tt>Content-Length</tt>.<br>
     * <p>
     * The HTTP HEAD method counts the output bytes in the response to set the
     * Content-Length header accurately.
     * <p>
     * If the HTTP HEAD request is incorrectly formatted, doHead returns an HTTP
     * "Bad Request" message.
     * 
     * @param req
     *            - the request object that is passed to the servlet
     * @param resp
     *            - the response object that the servlet uses to return the
     *            headers to the clien
     * @throws ScwsException
     *             - if the request for the HEAD could not be handled
     */
    public void doHead(HttpRequest req, HttpResponse resp) throws ScwsException {

    }

    /**
     * The OPTIONS method represents a request for information about the
     * communication options available on the request/response chain identified
     * by the Servlet.
     * <p>
     * Called by the SCWS to allow a servlet to handle a OPTIONS request. <br>
     * The OPTIONS request determines which HTTP methods the server supports and
     * returns an appropriate header.<br>
     * For example, if a servlet overrides doGet, this method returns the
     * following header:
     * 
     * <pre>
     * Allow: <tt>GET</tt>, <tt>HEAD</tt>, <tt>OPTIONS</tt>
     * </pre>
     * 
     * </p>
     * 
     * @param req
     *            - the HttpRequest object that contains the request the client
     *            made of the servlet
     * @param resp
     *            - the HttpResponse object that contains the response the
     *            servlet returns to the client
     * @throws ScwsException
     *             - if the request for the OPTIONS cannot be handled
     */
    public void doOptions(HttpRequest req, HttpResponse resp)
            throws ScwsException {
        resp.appendHeaderVariable(ALLOW, (short) 0, (short) ALLOW.length,
                ALLOW_OPTIONS, (short) 0, (short) ALLOW_OPTIONS.length);
        resp.flush();

    }

    /**
     * Called by the SCWS to allow the servlet to handle a POST request.
     * <p>
     * When using HTTP 1.1 chunked encoding (which means that the response has a
     * Transfer-Encoding header), do not set the Content-Length header.
     * </p>
     * If the HTTP POST request is incorrectly formatted, doPost returns an HTTP
     * "Bad Request" message.
     * 
     * @param req
     *            - an HttpRequest object that contains the request the client
     *            has made of the servlet
     * @param resp
     *            - an HttpResponse object that contains the response the
     *            servlet sends to the client
     * @throws ScwsException
     *             - if the request for the POST could not be handled
     */
    public void doPost(HttpRequest req, HttpResponse resp) throws ScwsException {
    }

    /**
     * Called by the SCWS (via the service method) to allow a servlet to handle
     * a PUT request.
     * <p>
     * If the HTTP PUT request is incorrectly formatted, doPut returns an HTTP
     * "Bad Request" message.
     * 
     * @param req
     *            - the HttpRequest object that contains the request the client
     *            made of the servlet
     * @param resp
     *            - the HttpResponse object that contains the response the
     *            servlet returns to the client
     * @throws HttpCardServletException
     *             - if the request for the PUT cannot be handled
     * @throws java.lang.Exception
     */
    public void doPut(HttpRequest req, HttpResponse resp) throws ScwsException {
    }

    /**
     * Called by the server (via the service method) to allow a servlet to
     * handle a TRACE request.
     * <p>
     * The TRACE method is used to invoke a remote, application-layer loop-back
     * of the request message.
     * </p>
     * 
     * @param req
     *            - the HttpRequest object that contains the request the client
     *            made of the servlet
     * @param resp
     *            - the HttpResponse object that contains the response the
     *            servlet returns to the client
     * @throws ScwsException
     *             - if the request for the TRACE cannot be handled
     */
    public void doTrace(HttpRequest req, HttpResponse resp)
            throws ScwsException {
        // TODO Auto-generated method stub
        // --------------------------
        /*
         * A TRACE returns the headers sent with the TRACE request to the
         * client, so that they can be used in debugging.
         */
        try {

            // set header
            resp.writeStatusCode(ScwsConstants.SC_OK);
            resp.setContentType(ScwsConstants.CONTENT_TYPE_TEXT_PLAIN);
            resp.enableChunkMode();

            // TRACE /MyServlet1 HTTP/1.1
            resp.appendContent(TRACE, (short) 0, (short) TRACE.length);
            resp.appendContent(SPACE, (short) 0, (short) SPACE.length);
            getAndOutputContent(req, resp, ScwsConstants.URI_PATH_TAG);

            resp.appendContent(SPACE, (short) 0, (short) SPACE.length);
            short httpVersion = req.getRequestHttpVersion();
            if (httpVersion == ScwsConstants.HTTP_PROTOCOL_VERSION_10)
                resp.appendContent(HTTP_10, (short) 0, (short) HTTP_10.length);
            else
                resp.appendContent(HTTP_11, (short) 0, (short) HTTP_11.length);
            resp.appendContent(CRLF, (short) 0, (short) CRLF.length);

            // ex: Accept: */*
            resp.appendContent(ACCEPT, (short) 0, (short) ACCEPT.length);
            getAndOutputContent(req, resp, ScwsConstants.HEADER_ACCEPT);
            resp.appendContent(CRLF, (short) 0, (short) CRLF.length);

            // ex: Accept-Language: en-us
            resp.appendContent(ACCEPT_LANG, (short) 0,
                    (short) ACCEPT_LANG.length);
            getAndOutputContent(req, resp, ScwsConstants.HEADER_ACCEPT_LANGUAGE);
            resp.appendContent(CRLF, (short) 0, (short) CRLF.length);

            // ex: Host: localhost
            resp.appendContent(HOST, (short) 0, (short) HOST.length);
            getAndOutputContent(req, resp, ScwsConstants.HEADER_HOST);
            resp.appendContent(CRLF, (short) 0, (short) CRLF.length);

            // ex: User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT
            // 5.0)
            resp
                    .appendContent(USER_AGENT, (short) 0,
                            (short) USER_AGENT.length);
            getAndOutputContent(req, resp, ScwsConstants.HEADER_USER_AGENT);
            resp.appendContent(CRLF, (short) 0, (short) CRLF.length);

            // ex: Accept-Encoding: gzip, deflate
            resp.appendContent(ACCEPT_ENCODING, (short) 0,
                    (short) ACCEPT_ENCODING.length);
            getAndOutputContent(req, resp, ScwsConstants.HEADER_ACCEPT_ENCODING);
            resp.appendContent(CRLF, (short) 0, (short) CRLF.length);

        } catch (Exception e) {
            resp.writeStatusCode(ScwsConstants.SC_NOT_IMPLEMENTED);
        }

        resp.flush();

    }

    private void getAndOutputContent(HttpRequest req, HttpResponse resp,
            short keyword) {
        short contentLgth = req.findAndCopyKeywordValue(keyword,
                temporaryBuffer, (short) 1,
                (short) (temporaryBuffer.length - 1));
        if (contentLgth == ScwsConstants.KEYWORD_NOT_FOUND)
            contentLgth++; // Query string may not exist, so set it 0 if did
        temporaryBuffer[0] = (byte) (contentLgth);
        resp.appendContent(temporaryBuffer, (short) 1,
                (short) temporaryBuffer[0]);
        clearBuffer(); // Clear buffer for reuse
    }

    private void clearBuffer() {
        for (short i = 0; i < temporaryBuffer.length; i++) {
            temporaryBuffer[i] = 0;
        }
    }

    private short getLength(byte[] data) {
        for (short i = 0; i < data.length; i++) {
            if (data[i] == 0) {
                return i;
            }
        }
        return 0;
    }
}

5 Apr 2011

What is Smart Card Web Server (SCWS)?

Smart Card Web Server is web server application which works in the SIM Card. It allows HTTP/1.0 and HTTP/1.1 connection between mobile phone and SIM card. It could enrich visualizations of content by using xHTML, javascript and flash animations.
As any web server SCWS is not only performing static content but also capable to serve dynamic content using SCWS servlets. Let's have a look to generic architecture of SCWS:



All parts of SCWS at the present time have been standardized by Open Mobile Alliance (OMA) and ETSI:

Smart Card Web Server:
  • OMA-TS-Smartcard_Web_Server-V1_1-20090512-A
  • Covers the technical specificities of a HTTP server in a SIM card plus the new Remote Management architecture and protocol
  • 1.1 Approved by OMA in 2008
BIP TCP Server:
  • Standardised bearer (TS 102 223 Rel-7) for ISO (APDU) interface minor modification to BIP/TCP Client (Rel-99)
USB-IC ("IP in UICC"):
  • High speed interface (HSI) based on USB: TS 102 600
  • Direct TCP/IP connectivity, locally or remotely: TS 102 483, on top of USB
SCWS Servlet API:
  • TS 102 588 Rel-7
SCWS supports Basic Authentication by default. SCWS Servlet API allows developing any type of application. Some examples:
  • Home screen personalization based on operator design
  • Blogging application which could work even offline and upload posts when it will be online
  • Operator portal which will replace ancient STK menu application
  • etc

14 Mar 2011

Emacs el-get on windows

Emacs el-get allows you to install and manage elisp code for Emacs. It supports lots of differents types of sources and is able to install, update and remove them, but more importantly it will init them for you.
It allows you to keep up-to-date you elisp packages like cedet, emacs-w3m and hundred others. But if you tried to use it on windows you will figure out that a lot of them fail to install. It is related to the fact that most of packages are using UNIX command line tools which are not working on windows out-of-box.
Fortunately most of them could be installed on windows with slight modifications. Below small how-to:
1. Download and install cygwin tools and and cygwin/bin folder to your PATH. For the most of the packages you will need:
- curl (I'll explain later why it is necessary)
- ssh
- openssl
- bzr/git/subversion etc - it depends on which version control system has been used for package. It could be easily identified from recipe file. Below simple example of yasnippet recipe:
(:name yasnippet
      :type svn
      :url "http://yasnippet.googlecode.com/svn/trunk/"
      :features "yasnippet"
      :post-init (lambda ()
           (yas/initialize)
           (add-to-list 'yas/snippet-dirs (concat el-get-dir "yasnippet/snippets"))
            (yas/reload-all)))

Pay attention to type. It is specifying used version control system. In the given example it is subversion. So to be able to install this package you need svn client in your path. It is not mandatory to have it from cygwin but I prefer to use cygwin one as everything could be installed by its setup from one place.

- make utility

- autotools (autoconf, automake and co.)

- etc. (please check what exactly recipe is using.)

2. Open bash from cygwin installation (simply run cygwin.bat file) and enter the following commands:
$ cd /usr/ssl/certs
$ curl http://curl.haxx.se/ca/cacert.pem | awk 'split_after==1{n++;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > "cert" n ".pem"}'
$ c_rehash
If you won't do that you will have certificate error during installation of el-get from github. These commands will imports many certificates for different sites which are collected by curl developers team.

3. Follow the instruction specified on el-get site.

4. If your favourite package gives error check the following:

- If you've installed client for version control mentioned in recipe and is path specified in your PATH?

- If site is available by web browser.

- If it fails during installation check what kind of commands have been used. Try to run them from CMD prompt (not bash). If needed correct them.

I'll give you example of my patches to emacs-w3m and cedet.

emacs-w3m

Original recipe was:
(:name emacs-w3m
      :type cvs
      :module "emacs-w3m"
      :url ":pserver:anonymous@cvs.namazu.org:/storage/cvsroot"
      :build `("autoconf" ("./configure" ,(concat "--with-emacs=" el-get-emacs)) "make")
      :info "doc")

autoconf, ./configure commands are shell scripts hence they are not executable from CMD prompt and this build will fail. Fixed version of recipe:
(:name emacs-w3m
      :type cvs
      :module "emacs-w3m"
      :url ":pserver:anonymous@cvs.namazu.org:/storage/cvsroot"
      :build `("autoconf" ("./configure" ,(concat "--with-emacs=" el-get-emacs)) "make")
      :build/windows-nt ("sh /usr/bin/autoconf" "sh ./configure" "make")
      :info "doc")

CEDET

Original recipe was:
(:name cedet
      :type bzr
      :url "bzr://cedet.bzr.sourceforge.net/bzrroot/cedet/code/trunk"
      :build ("touch `find . -name Makefile`" "make")
      :load-path ("./common"))
Patch for this recipe was more difficult as “`” symbol is not valid for CMD and you cannot pass whole this command to shell as touch binary. Moreover Makefile for CEDET use UNIX version of find command. Below how I fixed it:
(:name cedet
      :type bzr
      :url "bzr://cedet.bzr.sourceforge.net/bzrroot/cedet/code/trunk"
      :build ("touch `find . -name Makefile`" "make")
      :build/windows-nt ("echo #!/bin/sh > tmp.sh & echo touch `/usr/bin/find . -name Makefile` >> tmp.sh & echo make FIND=/usr/bin/find >> tmp.sh" 
          "sed 's/^M$//' tmp.sh  > tmp2.sh"
          "sh ./tmp2.sh" "rm ./tmp.sh ./tmp2.sh")
      :load-path ("./common"))
By this way you can fix almost any recipe. If you do it, don’t forget to send patch to original author and share with us ;)

8 Mar 2011

Utility for calculation MIFARE Password

The ones who read my post about how to calculate MIFARE password found it very tough to calculate manually. Hence I've developed tool to automate it. The full source codes are available in github. If you don't care about source code, executable file is also there. You need .Net 4.0 and F# runtime to run it.

9 Feb 2011

And then there's Haskell...

> Odd that this has come up -- I was just looking at Common Lisp and
> Scheme implementations, as well as a smattering of Haskellish tools

And then there's Haskell...

Haskell is like "that girl."  You know the one...

You never really went steady, but you'd run into her from time to  
time while knocking around in disreputable joints, usually late at  
night, every several months or so.  She looked so hot, so sleek, so  
sexy, so expressive, so exotic.  You'd end up back at her place and  
the night would just...  take off.  A complete blur of hot, sweaty,  
feverish, delirious, fumbling passion.  You'd do things to each  
other...  you'd do things to her, she'd do things to you...  things  
that you're not even sure have names, that you're pretty sure are  
illegal almost anywhere.  Even her kinks have kinks --- and after one  
of these nights, you'd realize that you yourself had a lot more kinks  
than you.  And it wasn't just physical, it was --- cerebral.   
Ethereal.  Transcendent.  But it would all whiz by in a blur, and by  
morning you'd find yourself lightheaded, a bit confused, and  
stumbling homeward to your regular gal.

Over the next few days and weeks you'd find yourself occasionally  
drifting away, thinking about her.  Haskell.  You'd be there, banging  
away at your regular girl, and find yourself thinking "you know, if I  
was with Haskell, I'd be doing this completely differently."  You'd  
think "I could be doing so much bigger and better stuff with  
Haskell."  Now, your regular girl, she's not as exotic as Haskell.   
Pretty, maybe, if you're lucky.  (Perhaps your regular girlfriend's  
name is Python. ;-)  But not nearly as --- weird.  Wild.  Cool.   
Exciting.  Don't get me wrong --- your girl, she's wonderful.  You've  
got a wonderful relationship.  She's --- comfortable.  You can bang  
away at her all day and night.  She's accommodating.  Easy going.   
You work well together.  But --- confidentially --- she's, well,  
maybe just a little bit boring.  You'd catch yourself thinking these  
things, and the guilty pangs would get to you...  You'd quash the  
thoughts, buckle down, and get back to banging away.  Comfortable...   
there's a lot to be said for that, ya know?  Comfortable...  just  
keep telling yourself that.

Months would go by.  Late some night you'd find yourself out,  
disreputable places again.  Maybe that hacker bar, LtU.  Somebody'd  
slip you an URL for some renegade paper, you know, one of *those*  
papers.  You'd run into Haskell again.  And the whole thing starts over.

Eventually, you're going to get the ultimatum.  Haskell's ultimately  
just like any other girl on some level;  she needs commitment.   
Eventually, after one night of wild, feverish, kinky, abstract  
passion, she's going to say to you:  "All these times, and you don't  
understand me at all!  You know, you're going to have to get serious,  
mister!  I've got needs, too.  You're going to have to get serious  
about my monads, or that's the last time you're going to play with  
them!  Got it?"

...and then, you've got to make The Choice.

Chances are, you're going to go back to your regular gal.  Haskell's  
just too much for any one man, probably.  She leaves a trail of  
broken, brainy, embittered PhDs and former programmers behind her.   
She ruins you for the RealWorld.  You can ride a while, but you  
probably can't go the distance with her.  Go back to your regular gal  
and try not to think too much about what you've seen.  Done.  Felt.   
Thought.

Maybe you can salvage a little happiness;  but it'll be hard.  After  
all...  you've tasted Haskell.

She's not like anything else.