28 Nov 2017

How to remove all local snapshots in macOS High Sierra

If you are using macOS High Sierra and want to delete all your local snapshots, use the following command:

sudo tmutil listlocalsnapshots / | awk -F. '{ system("sudo tmutil deletelocalsnapshots "  $4); }'

Where:

    sudo tmutil listlocalsnapshots /


List of all local snapshots in form:

    com.apple.TimeMachine.2017-11-27-142931
    com.apple.TimeMachine.2017-11-27-152919
    com.apple.TimeMachine.2017-11-27-163113
    com.apple.TimeMachine.2017-11-27-173243
    com.apple.TimeMachine.2017-11-27-183511
    com.apple.TimeMachine.2017-11-27-193725
    com.apple.TimeMachine.2017-11-27-203137

        ...

The command:

    awk -F. { print $4; }

Will print date part.

And command:

    sudo tmutil deletelocalsnapshots

will delete given date.

 And don't forget to reboot after deleting.

30 May 2013

Interaction of PoS Terminal and Banking cards with chip


My former colleague wrote very interesting post about interaction of PoS terminal and banking cards with chip. So sharing with my readers. Enjoy and peace on you!

19 Apr 2013

Colorify less in Terminal.app

I don't know about you but I'm using less command in terminal window quite often. Especially it is very useful when you need to check quickly some parts of a source code. But the problem is that less command shows source code in plain text.
Recently I found this post about usage of pygments and less command. The given recipe tested on Linux and didn't work on my Mac with ZSH. After digging a little bit I drastically simplified it. Here it is:

  • We need to install pygments (http://pygments.org/). In my case it was simple: 
  pip install pygments
  • Add the following lines to .zshrc (in your case it could be .bashrc if you use Bash): 
export LESS="-R"
export LESSOPEN="|pygmentize -g %s"
The main difference from initial way is to rely on "-g" parameter of pygmentize instead of explicitly specification of lexer. Now my less command looks as follows:


Enjoy! Peace on you!

21 Oct 2012

JavaCard STK usage example: One Time Password


Today I'd like to show you one of the ways of JavaCard STK applet usage. In the world of information systems a good authentication procedure is one of the most important topics. And one of the most reliable ways to do it is two factor authentication usage. The most known two factor authentication is One Time Password (OTP).

OTP - is a password which is valid only for one authentication session. Password validity could be also limited in time. The advantage of One Time Password is inability to
use the same password twice. Hence if password is intercepted somehow it will be useless.

The aim of this post is not to give you all pros and cons of OTP. I'll let my reader dig it on his own :) Instead I want to show you how OTP could be implemented in JavaCard STK applet.

The main problem of OTP for end-users is inability to keep in mind all One Time Passwords therefore we need some additional device to generate it on the fly - JavaCard STK applet.

Let's have a look how we can do it. First of all we will implement 2 menu items for Init key and Get next password as:

/**
 * Constructor of the applet
 */
public otp() {
    // Get the reference of the applet ToolkitRegistry object
    reg = ToolkitRegistry.getEntry ();

    menuGetNextPasswd = new byte[] { (byte) 'G', (byte) 'e', (byte) 't',
            (byte) ' ', (byte) 'n', (byte) 'e', (byte) 'x', (byte) 't',
            (byte) ' ', (byte) 'p', (byte) 'a', (byte) 's', (byte) 's',
            (byte) 'w', (byte) 'o', (byte) 'r', (byte) 'd' };
    menuInitKey = new byte[] { (byte) 'I', (byte) 'n', (byte) 'i',
            (byte) 't', (byte) ' ', (byte) 'k', (byte) 'e', (byte) 'y' };
    // Define the applet Menu Entry
    idMenuGetNextPasswd = reg
            .initMenuEntry (menuGetNextPasswd, (short) 0,
                            (short) menuGetNextPasswd.length,
                            PRO_CMD_SELECT_ITEM, false, (byte) 0, (short) 0);
    idMenuInitKey = reg
            .initMenuEntry (menuInitKey, (short) 0,
                            (short) menuInitKey.length, (byte) 0, false,
                            (byte) 0, (short) 0);

    // during instantiation of the applet key isn't initialized yet
    isKeyInitialized = false;
    // fixing key length to 20 bytes
    key = new byte[KEY_LENGTH];

    tmpBuf = new byte[KEY_LENGTH_ASCII];
}

Then we have to implement processToolkit() method to treat menu items selections:


public void processToolkit(byte event) {
    EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler ();

    // Manage the request following the MENU SELECTION event type
    if (event == EVENT_MENU_SELECTION) {
        // Get the selected item
        byte selectedItemId = envHdlr.getItemIdentifier ();

        // Perform the required service following the menuGetNextPasswd
        // selected item
        if (selectedItemId == idMenuGetNextPasswd) {
            getNextPasswd ();
        }

        // Perform the required service following the menuInitKey selected
        // item
        if (selectedItemId == idMenuInitKey) {
            initKey ();
        }
    }
}

The initKey() method will request initial key which will be used for next password calculation. The method getNextPasswd() could be implemented in the following way:


/**
 * Manage the menuGetNextPasswd selection
 */
private void getNextPasswd() {
    if (isKeyInitialized) {
        try {
            hash = MessageDigest.getInstance (MessageDigest.ALG_SHA, true);
            hash.doFinal (key, (short) 0, (short) key.length
                         , tmpBuf, (short) 0);
            Util.arrayCopyNonAtomic (tmpBuf, (short) 0, key, (short) 0,
                                     (short) key.length);
            displayHexBuffer (key, (short) key.length);
        } catch (CryptoException e) {
        }
    } else {
        displayText(msgKeyNotInited);
    }
    return;
}

After password generation we need to verify it somehow on the server side. Let's implement authentication server simulator. I'll show you only proof-of-concept implementation, a simple GUI application wich requires password and verifies it. It looks like this:



I've used Clojure for it:


(def md (MessageDigest/getInstance "SHA-1"))

(defn ascii2hex [ascii-str]
  (map #(Integer/parseInt % 16) (map #(apply str %) (partition 2 ascii-str))))

(defn bytes2hexStr [bytes]
  (apply str
         (map #(.toUpperCase %)
              (map #(format "%02x" %)
                   (map #(bit-and 0xFF %) (seq bytes))))))

(defn get-next-password [key-str]
  (. md reset)
  (bytes2hexStr
   (seq
    (. md digest
       (into-array Byte/TYPE
                   (map #(.byteValue %)
                        (ascii2hex key-str)))))))

The full source code of the applet and authentication server simulator could be found on my GitHub page.

Peace on you! :)


14 Sept 2012

MIFARE password calculator (Clojure edition)

Wow! I didn't updated my blog almost a year already. I have to fix it. :)
After switched to Macbook at home I decided to review my development tools and also programming languages. As an exercise I redeveloped my MIFARE password calculator in Clojure. Now you don't need dotNET framework to run it. Just download standalone version of JAR file and run it as:
java -jar <jarfilename>
I hope it will be more useful.
You can download it from GitHub.
New posts on different topics are coming soon. Stay tuned! ;)

Update: Add links to previous posts on the same topic:

17 Oct 2011

Again about emacs gnus and gmail

Sometimes ago my gnus stopped sending emails through gmail SMTP server. The message was something like "connection refused". I didn't have time to dig the problem as in the office I'm using web mail due to company proxy and quite used to use it.

But after switching completely to Emacs 24 and starting using el-get with nognus I've decided to figure out what is the problem. Quick debugging of emacs smtpmail.el and network-stream.el showed me that problem comes from gnutls-cli which stopped connecting to port 587 of smtp.gmail.com with message "Connection refused". I've also recognized that in emacs 24 developers have introduced new parameter smtpmail-stream-type.
It has the following documentation:

Connection type SMTP connections.
This may be either nil (possibly upgraded to STARTTLS if possible), or `starttls' (refuse to send if STARTTLS isn't available), or `plain' (never use STARTTLS)..

Changing value to nil, starttls didn't help. After checking usage of the variable in open-network-stream function in network-stream.el I've recognized that it could have much more values:

:type specifies the connection type, one of the following:
  nil or `network'
             -- Begin with an ordinary network connection, and if
                the parameters :success and :capability-command
                are also supplied, try to upgrade to an encrypted
                connection via STARTTLS.  Even if that
                fails (e.g. if HOST does not support TLS), retain
                an unencrypted connection.
  `plain'    -- An ordinary, unencrypted network connection.
  `starttls' -- Begin with an ordinary connection, and try
                upgrading via STARTTLS.  If that fails for any
                reason, drop the connection; in that case the
                returned object is a killed process.
  `tls'      -- A TLS connection.
  `ssl'      -- Equivalent to `tls'.
  `shell'    -- A shell connection.
Also I found that we can use port 465 instead of 587 with smtp.gmail.com. After changing my configuration in .gnus to the following I can send emails from gnus again (hooray!!!):

(setq message-send-mail-function 'smtpmail-send-it
      smtpmail-starttls-credentials '(("smtp.gmail.com" 465 nil nil))
      smtpmail-auth-credentials '(("smtp.gmail.com" 465 "name@gmail.com" nil))
      smtpmail-default-smtp-server "smtp.gmail.com"
      smtpmail-smtp-server "smtp.gmail.com"
      smtpmail-smtp-service 465
;     smtpmail-debug-verb t
;     smtpmail-debug-info t
      smtpmail-local-domain nil
      smtpmail-stream-type 'ssl)




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;
    }
}