From b72ee79ef8947f5696f902b40df3bfd364b4fdd1 Mon Sep 17 00:00:00 2001
From: Jack Magne <jmagne@redhat.com>
Date: Mon, 13 Oct 2014 13:40:59 -0700
Subject: [PATCH] Ticket: TPS Rewrite: Implement Secure Channel Protocol 02
 (#883).

First cut of gp211 and scp protocol 02 for tokens.

Allow token operations using a GP211 token over secure channel protocol 02.

This patch supports the following:

1. Token operations with a GP211 card and SCP02 protocol, implementation 15.
2. Token still supports GP201 cards with SCP01.
3. SCP02 tested with SC650 gp211/scp02 card.

Things still to do:

1. Right now the SCP02 support has been tested with the current gp201 applet and
enrollment and formatting works just fine. We need to modify and compile the applet
against the GP211 spec and retest to see if any further changes are needed.

2. The nistSP800 key derivation stuff is not completed for the SCP02 protocol. Some
of the routines are self contained vs similar SCP01 ones. We have another ticket to
complete the nistSP800 support from end to end. This work will be done for that ticket.

3. One of the new scp02 deriviation functions can make use of a new NSS derive mechanism.
As of now this work is done by simple encryption, this can be done later.

4. The security APDU level of "RMAC" is not supported because the card does not support it.
It could have been done to the spec, but it having the card to test is more convenient and there
were more crucial issues to this point.
---
 base/common/src/org/dogtagpki/tps/apdu/APDU.java   |   69 +
 .../dogtagpki/tps/apdu/DeleteFileGP211APDU.java    |   32 +
 .../tps/apdu/ExternalAuthenticateAPDUGP211.java    |  125 ++
 .../src/org/dogtagpki/tps/apdu/GetDataAPDU.java    |   11 +
 .../dogtagpki/tps/apdu/InstallAppletAPDUGP211.java |  103 ++
 .../dogtagpki/tps/apdu/InstallLoadGP211APDU.java   |   52 +
 .../org/dogtagpki/tps/apdu/LoadFileAPDUGP211.java  |   49 +
 .../src/org/dogtagpki/tps/main/TPSBuffer.java      |   10 +
 .../src/org/dogtagpki/tps/main/TPSException.java   |    4 +-
 base/common/src/org/dogtagpki/tps/main/Util.java   |  165 +++
 .../com/netscape/cms/servlet/tks/TokenServlet.java | 1400 ++++++++++++++------
 .../dogtagpki/server/connector/IRemoteRequest.java |    4 +
 base/symkey/src/com/netscape/symkey/Buffer.cpp     |    6 +-
 .../symkey/src/com/netscape/symkey/EncryptData.cpp |    5 +-
 base/symkey/src/com/netscape/symkey/SessionKey.cpp |  604 ++++++++-
 .../symkey/src/com/netscape/symkey/SessionKey.java |   17 +-
 base/symkey/src/com/netscape/symkey/SymKey.cpp     |  420 ++++--
 base/symkey/src/com/netscape/symkey/SymKey.h       |    5 +-
 .../channel/PlatformAndSecChannelProtoInfo.java    |  108 ++
 .../server/tps/channel/SecureChannel.java          |  534 +++++++-
 .../server/tps/cms/TKSRemoteRequestHandler.java    |  159 ++-
 .../org/dogtagpki/server/tps/engine/TPSEngine.java |   66 +-
 .../server/tps/processor/TPSEnrollProcessor.java   |   14 +-
 .../server/tps/processor/TPSProcessor.java         |  562 ++++++--
 24 files changed, 3848 insertions(+), 676 deletions(-)
 create mode 100644 base/common/src/org/dogtagpki/tps/apdu/DeleteFileGP211APDU.java
 create mode 100644 base/common/src/org/dogtagpki/tps/apdu/ExternalAuthenticateAPDUGP211.java
 create mode 100644 base/common/src/org/dogtagpki/tps/apdu/InstallAppletAPDUGP211.java
 create mode 100644 base/common/src/org/dogtagpki/tps/apdu/InstallLoadGP211APDU.java
 create mode 100644 base/common/src/org/dogtagpki/tps/apdu/LoadFileAPDUGP211.java
 create mode 100644 base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java

diff --git a/base/common/src/org/dogtagpki/tps/apdu/APDU.java b/base/common/src/org/dogtagpki/tps/apdu/APDU.java
index c1aa517..cac99a0 100644
--- a/base/common/src/org/dogtagpki/tps/apdu/APDU.java
+++ b/base/common/src/org/dogtagpki/tps/apdu/APDU.java
@@ -67,6 +67,9 @@ public abstract class APDU {
     protected TPSBuffer data = null;
     protected TPSBuffer plainText = null;
     protected TPSBuffer mac = null;
+    protected TPSBuffer trailer = null;
+
+    protected boolean calculateSingleDesBeforeMac = false;
 
     public APDU() {
         data = new TPSBuffer();
@@ -76,6 +79,14 @@ public abstract class APDU {
         data = new TPSBuffer(otherAPDU.getData());
     }
 
+    public void setCalculateSingleDesBeforeMac(boolean value) {
+        calculateSingleDesBeforeMac = value;
+    }
+
+    public boolean getCalculateSingleDesBeforeMac() {
+        return calculateSingleDesBeforeMac;
+    }
+
     void setCLA(byte theCla) {
         cla = theCla;
     }
@@ -101,6 +112,10 @@ public abstract class APDU {
         mac = theMac;
     }
 
+    public void setTrailer(TPSBuffer theTrailer) {
+        trailer = theTrailer;
+    }
+
     /**
      * Retrieves APDU's encoding.
      * The encoding of APDU is as follows:
@@ -140,6 +155,10 @@ public abstract class APDU {
             encoding.add(mac);
         }
 
+        if (trailer != null) {
+            encoding.add(trailer);
+        }
+
         return encoding;
     }
 
@@ -176,6 +195,7 @@ public abstract class APDU {
         int rem = dataSize % 8;
 
         if (rem == 0) {
+
             padNeeded = 0;
         } else if (dataSize < 8) {
             padNeeded = 8 - dataSize;
@@ -198,6 +218,55 @@ public abstract class APDU {
         data.set(dataEncrypted);
     }
 
+    public void secureMessageSCP02(PK11SymKey encKey) throws EBaseException {
+
+        if (encKey == null) {
+            throw new EBaseException("APDU.secureDataSCP02: Invalid input data!");
+        }
+
+        int padNeeded = 0;
+
+        TPSBuffer dataToEnc = null;
+        TPSBuffer padding = null;
+        TPSBuffer dataEncrypted = null;
+
+        dataToEnc = new TPSBuffer();
+        // dataToEnc.add((byte) data.size());
+        dataToEnc.add(data);
+
+        int dataSize = dataToEnc.size();
+        int rem = dataSize % 8;
+
+        if (rem == 0) {
+
+            padNeeded = 8;
+        } else if (dataSize < 8) {
+            padNeeded = 8 - dataSize;
+        } else {
+            padNeeded = 8 - rem;
+        }
+
+        if (padNeeded > 0) {
+            dataToEnc.add((byte) 0x80);
+            padNeeded--;
+
+            if (padNeeded > 0) {
+                padding = new TPSBuffer(padNeeded);
+                dataToEnc.add(padding);
+            }
+        }
+
+        //  dataToEnc.setAt(0, (byte)  (dataToEnc.size() -1));
+        dataEncrypted = Util.encryptData(dataToEnc, encKey);
+
+        data.reset();
+
+    //    data.add((byte) (dataEncrypted.size() + 8));
+
+        data.add(dataEncrypted);
+
+    }
+
     public Type getType() {
         return Type.APDU_UNDEFINED;
     }
diff --git a/base/common/src/org/dogtagpki/tps/apdu/DeleteFileGP211APDU.java b/base/common/src/org/dogtagpki/tps/apdu/DeleteFileGP211APDU.java
new file mode 100644
index 0000000..3273d4e
--- /dev/null
+++ b/base/common/src/org/dogtagpki/tps/apdu/DeleteFileGP211APDU.java
@@ -0,0 +1,32 @@
+package org.dogtagpki.tps.apdu;
+
+import org.dogtagpki.tps.main.TPSBuffer;
+
+public class DeleteFileGP211APDU extends APDU {
+    public DeleteFileGP211APDU(TPSBuffer aid) {
+        calculateSingleDesBeforeMac = true;
+        trailer = new TPSBuffer();
+        trailer.add((byte) 0x0);
+
+        setCLA((byte) 0x84);
+        setINS((byte) 0xE4);
+        setP1((byte) 0x00);
+        setP2((byte) 0x80);
+
+        TPSBuffer AIDTLV = new TPSBuffer();
+
+        AIDTLV.add((byte) 0x4f);
+        AIDTLV.add((byte) aid.size());
+
+        AIDTLV.add(aid);
+
+        setData(AIDTLV);
+
+    }
+
+    public static void main(String[] args) {
+        // TODO Auto-generated method stub
+
+    }
+
+}
diff --git a/base/common/src/org/dogtagpki/tps/apdu/ExternalAuthenticateAPDUGP211.java b/base/common/src/org/dogtagpki/tps/apdu/ExternalAuthenticateAPDUGP211.java
new file mode 100644
index 0000000..bd68784
--- /dev/null
+++ b/base/common/src/org/dogtagpki/tps/apdu/ExternalAuthenticateAPDUGP211.java
@@ -0,0 +1,125 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package org.dogtagpki.tps.apdu;
+
+import org.dogtagpki.tps.main.TPSBuffer;
+
+public class ExternalAuthenticateAPDUGP211 extends APDU {
+
+    public enum SecurityLevel {
+        SECURE_MSG_NONE, //not yet supported
+        CMAC,
+        CDEC_CMAC,
+        RMAC,
+        CMAC_RMAC,
+        CDEC_CMAC_RMAC
+
+    }
+
+    public ExternalAuthenticateAPDUGP211(TPSBuffer theData, SecurityLevel securityLevel) {
+        setCLA((byte) 0x84);
+        setINS((byte) 0x82);
+
+        setP1(securityLevelToByte(securityLevel));
+        setP2((byte) 0x0);
+
+        setData(theData);
+    }
+
+    public TPSBuffer getHostCryptogram()
+    {
+        return getData();
+    }
+
+    @Override
+    public APDU.Type getType()
+    {
+        return APDU.Type.APDU_EXTERNAL_AUTHENTICATE;
+    }
+
+    public static byte securityLevelToByte(SecurityLevel level) {
+        byte result = 0;
+
+        switch (level) {
+        case SECURE_MSG_NONE:
+            result = 0x0;
+            break;
+        case CMAC:
+            result = 0x1;
+            break;
+        case CDEC_CMAC:
+            result = 0x03;
+            break;
+        case RMAC:
+            result = 0x10;
+            break;
+
+        case CMAC_RMAC:
+            result = 0x11;
+            break;
+
+        case CDEC_CMAC_RMAC:
+            result = 0x13;
+            break;
+
+        default:
+            result = 0;
+            break;
+
+        }
+
+        return result;
+    }
+
+    public static SecurityLevel byteToSecurityLevel(byte level) {
+
+        SecurityLevel result = SecurityLevel.SECURE_MSG_NONE;
+
+        switch (level) {
+
+        case 0:
+            result = SecurityLevel.SECURE_MSG_NONE;
+            break;
+        case 1:
+            result = SecurityLevel.CMAC;
+            break;
+        case 0x03:
+            result = SecurityLevel.CDEC_CMAC;
+            break;
+        case 0x10:
+            result = SecurityLevel.RMAC;
+            break;
+
+        case 0x13:
+            result = SecurityLevel.CDEC_CMAC_RMAC;
+                break;
+        default:
+            result = SecurityLevel.SECURE_MSG_NONE;
+            break;
+        }
+
+        return result;
+
+    }
+
+    public static void main(String[] args) {
+        // TODO Auto-generated method stub
+
+    }
+
+}
diff --git a/base/common/src/org/dogtagpki/tps/apdu/GetDataAPDU.java b/base/common/src/org/dogtagpki/tps/apdu/GetDataAPDU.java
index 7cd52fc..6a2e8ed 100644
--- a/base/common/src/org/dogtagpki/tps/apdu/GetDataAPDU.java
+++ b/base/common/src/org/dogtagpki/tps/apdu/GetDataAPDU.java
@@ -32,6 +32,17 @@ public class GetDataAPDU extends APDU {
         setP2((byte) 0x7F);
     }
 
+    public GetDataAPDU(byte[] identifier) {
+
+        this();
+
+        if(identifier != null && identifier.length == 2) {
+            setP1(identifier[0]);
+            setP2(identifier[1]);
+        }
+
+    }
+
     @Override
     public Type getType()
     {
diff --git a/base/common/src/org/dogtagpki/tps/apdu/InstallAppletAPDUGP211.java b/base/common/src/org/dogtagpki/tps/apdu/InstallAppletAPDUGP211.java
new file mode 100644
index 0000000..f07d696
--- /dev/null
+++ b/base/common/src/org/dogtagpki/tps/apdu/InstallAppletAPDUGP211.java
@@ -0,0 +1,103 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ * All rights reserved.
+ * --- END COPYRIGHT BLOCK ---
+ */
+
+package org.dogtagpki.tps.apdu;
+
+import org.dogtagpki.tps.main.TPSBuffer;
+
+public class InstallAppletAPDUGP211 extends APDU {
+
+    public InstallAppletAPDUGP211(TPSBuffer packageAID, TPSBuffer appletAID,
+            byte appPrivileges, int instanceSize, int appletMemorySize)
+    {
+        calculateSingleDesBeforeMac = true;
+        trailer = new TPSBuffer();
+        trailer.add((byte)0x0);
+        setCLA((byte) 0x84);
+        setINS((byte) 0xE6);
+        setP1((byte) 0x0C);
+        setP2((byte) 0x00);
+
+        data = new TPSBuffer();
+        data.add((byte) packageAID.size());
+        data.add(packageAID);
+        data.add((byte) appletAID.size());
+        data.add(appletAID);
+        data.add((byte) appletAID.size());
+        data.add(appletAID);
+
+        data.add((byte) 0x01); // length of application privileges byte
+        data.add(appPrivileges);
+
+        TPSBuffer installParams = new TPSBuffer();
+        installParams.add((byte) 0xEF);
+        installParams.add((byte) 0x04);
+        installParams.add((byte) 0xC8);
+        installParams.add((byte) 0x02);
+
+        installParams.add((byte) ((instanceSize >> 8) & 0xff));
+        installParams.add((byte) (instanceSize & 0xff));
+        installParams.add((byte) 0xC9);
+
+        //Now add some applet specific init data that the applet supports
+        //Length of applet specific data
+
+        installParams.add((byte) 0x04);
+
+        //Issuer info length.
+        //Leave this to zero since TPS already writes phone home info to card.
+        installParams.add((byte) 0x00);
+
+        //Length of applet memory size
+        installParams.add((byte) 0x02);
+
+        // Applet memory block size
+
+        installParams.add((byte) ((appletMemorySize >> 8) & 0xff));
+        installParams.add((byte) (appletMemorySize & 0xff));
+
+        data.add((byte) installParams.size());
+        data.add(installParams);
+        data.add((byte) 0x00); // size of token return data
+    }
+
+    /**
+     * Constructs Install Applet APDU.
+     */
+    public InstallAppletAPDUGP211(TPSBuffer theData)
+    {
+        calculateSingleDesBeforeMac = true;
+        trailer = new TPSBuffer();
+        trailer.add((byte)0x0);
+        setCLA((byte) 0x84);
+        setINS((byte) 0xE6);
+        setP1((byte) 0x0C);
+        setP2((byte) 0x00);
+        setData(theData);
+    }
+
+    @Override
+    public Type getType()
+    {
+        return Type.APDU_INSTALL_APPLET;
+    }
+
+}
diff --git a/base/common/src/org/dogtagpki/tps/apdu/InstallLoadGP211APDU.java b/base/common/src/org/dogtagpki/tps/apdu/InstallLoadGP211APDU.java
new file mode 100644
index 0000000..3418dfa
--- /dev/null
+++ b/base/common/src/org/dogtagpki/tps/apdu/InstallLoadGP211APDU.java
@@ -0,0 +1,52 @@
+package org.dogtagpki.tps.apdu;
+
+import org.dogtagpki.tps.main.TPSBuffer;
+
+import com.netscape.certsrv.apps.CMS;
+
+public class InstallLoadGP211APDU extends APDU {
+
+    public InstallLoadGP211APDU(TPSBuffer packageAID, TPSBuffer sdAID,
+            int fileLen) {
+        calculateSingleDesBeforeMac = true;
+        setCLA((byte) 0x84);
+        setINS((byte) 0xE6);
+        setP1((byte) 0x02);
+        setP2((byte) 0x00);
+
+
+        CMS.debug("InstlalLoadGP211APDU: packageAID " + packageAID.toHexString() + " aid size: " + packageAID.size() + " fileLen: " + fileLen);
+
+        TPSBuffer inputData = new TPSBuffer();
+
+      //  inputData.add((byte) 0x0);
+
+        inputData.add((byte) packageAID.size());
+        inputData.add(packageAID);
+        inputData.add((byte) sdAID.size());
+        inputData.add(sdAID);
+
+        //work in file size here
+
+        inputData.add((byte) 0x00);
+        inputData.add((byte) 0x06);
+
+        inputData.add((byte) 0xEF);
+        inputData.add((byte) 0x04);
+        inputData.add((byte) 0xc6);
+
+
+        inputData.add((byte) 0x02);
+        int finalLen = fileLen + 24 + sdAID.size();
+        inputData.addInt2Bytes(finalLen);
+
+        //assume no load file data block hash
+        inputData.add((byte) 0x0);
+
+        setData(inputData);
+
+        trailer = new TPSBuffer();
+        trailer.add((byte)0x0);
+    }
+
+}
diff --git a/base/common/src/org/dogtagpki/tps/apdu/LoadFileAPDUGP211.java b/base/common/src/org/dogtagpki/tps/apdu/LoadFileAPDUGP211.java
new file mode 100644
index 0000000..ae53ae5
--- /dev/null
+++ b/base/common/src/org/dogtagpki/tps/apdu/LoadFileAPDUGP211.java
@@ -0,0 +1,49 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ * All rights reserved.
+ * --- END COPYRIGHT BLOCK ---
+ */
+package org.dogtagpki.tps.apdu;
+
+import org.dogtagpki.tps.main.TPSBuffer;
+
+public class LoadFileAPDUGP211 extends APDU {
+    /**
+     * Constructs Load File APDU.
+     */
+    public LoadFileAPDUGP211(byte refControl, byte blockNum, TPSBuffer theData)
+    {
+        calculateSingleDesBeforeMac = true;
+        trailer = new TPSBuffer();
+        setCLA((byte) 0x84);
+        setINS((byte) 0xE8);
+        setP1(refControl);
+        setP2(blockNum);
+
+        setData(theData);
+
+    }
+
+    @Override
+    public Type getType()
+    {
+        return Type.APDU_LOAD_FILE;
+    }
+
+}
+
diff --git a/base/common/src/org/dogtagpki/tps/main/TPSBuffer.java b/base/common/src/org/dogtagpki/tps/main/TPSBuffer.java
index 242f082..24c0b49 100644
--- a/base/common/src/org/dogtagpki/tps/main/TPSBuffer.java
+++ b/base/common/src/org/dogtagpki/tps/main/TPSBuffer.java
@@ -156,6 +156,12 @@ public class TPSBuffer {
         buf = newContents.toBytesArray();
     }
 
+    public void set(byte [] newContents) {
+        if (newContents == null)
+            return;
+        buf = newContents;
+    }
+
     /**
      * Append operators.
      */
@@ -361,6 +367,10 @@ public class TPSBuffer {
         return l1 + l2 + l3 + l4;
     }
 
+    public void reset() {
+        buf = new byte[0];
+    }
+
     public static void main(String[] args) {
 
         byte[] first = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a };
diff --git a/base/common/src/org/dogtagpki/tps/main/TPSException.java b/base/common/src/org/dogtagpki/tps/main/TPSException.java
index 0d89aae..940e89a 100644
--- a/base/common/src/org/dogtagpki/tps/main/TPSException.java
+++ b/base/common/src/org/dogtagpki/tps/main/TPSException.java
@@ -26,8 +26,8 @@ public class TPSException extends EBaseException {
     private static final long serialVersionUID = -678878301521643436L;
     private TPSStatus status;
 
-    public TPSException(String msg) {
-        super(msg);
+    public TPSException(String e) {
+        super(e);
         status = TPSStatus.STATUS_ERROR_CONTACT_ADMIN;
     }
 
diff --git a/base/common/src/org/dogtagpki/tps/main/Util.java b/base/common/src/org/dogtagpki/tps/main/Util.java
index c102622..2973bb8 100644
--- a/base/common/src/org/dogtagpki/tps/main/Util.java
+++ b/base/common/src/org/dogtagpki/tps/main/Util.java
@@ -43,6 +43,7 @@ import org.mozilla.jss.pkcs11.PK11SymKey;
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.cmsutil.util.Utils;
+import com.netscape.symkey.SessionKey;
 
 public class Util {
 
@@ -171,6 +172,166 @@ public class Util {
         return Utils.SpecialEncode(data.toBytesArray());
     }
 
+    public static TPSBuffer computeEncEcbDes(PK11SymKey symKey, TPSBuffer input) throws EBaseException {
+
+        //Asssume 8 bytes message
+        if (symKey == null || input == null || input.size() != 8) {
+            throw new EBaseException("Util.computeEncEcbDes: invalid input data!");
+        }
+
+        CMS.debug("Util.computeEncEcbDes entering... ");
+
+        TPSBuffer result = null;
+        CryptoToken token = null;
+
+        int inputLen = input.size();
+
+        TPSBuffer message = new TPSBuffer(input);
+
+        CMS.debug("Util.computeEncEcbDes: input data. " + message.toHexString() + " input len: " + inputLen);
+
+        try {
+
+            token = CryptoManager.getInstance().getInternalKeyStorageToken();
+
+            PK11SymKey des = SessionKey.DeriveDESKeyFrom3DesKey(token.getName(), symKey, 0x00000121 /*CKM_DES_ECB */);
+
+            if (des == null) {
+                throw new EBaseException("Util.computeEncEcbDes: Can't derive single des key from triple des key!");
+            }
+
+            TPSBuffer desDebug = new TPSBuffer(des.getEncoded());
+
+            CMS.debug("des key debug bytes: " + desDebug.toHexString());
+
+            Cipher cipher = token.getCipherContext(EncryptionAlgorithm.DES_ECB);
+
+            result = new TPSBuffer();
+
+            cipher.initEncrypt(des);
+            byte[] ciphResult = cipher.doFinal(message.toBytesArray());
+
+            if (ciphResult.length != 8) {
+                throw new EBaseException("Invalid cipher in Util.computeEncEcbDes");
+            }
+
+            result.set(ciphResult);
+
+            CMS.debug("Util.computeEncEcbDes: encrypted bloc: " + result.toHexString());
+
+        } catch (Exception e) {
+            throw new EBaseException("Util.computeMACdes3des: Cryptographic problem encountered! " + e.toString());
+        }
+
+        return result;
+    }
+
+    public static TPSBuffer computeMACdes3des(PK11SymKey symKey, TPSBuffer input, TPSBuffer initialIcv)
+            throws EBaseException {
+
+        if (symKey == null || input == null || initialIcv == null || initialIcv.size() != 8) {
+            throw new EBaseException("Util.coputeMACdes3des: invalid input data!");
+        }
+
+        CMS.debug("Util.computeMACdes3des entering... Initial icv: " + initialIcv.toHexString());
+
+        TPSBuffer output = null;
+        TPSBuffer mac = null;
+        CryptoToken token = null;
+
+        int inputLen = input.size();
+
+        TPSBuffer message = new TPSBuffer(input);
+        CMS.debug("Util.computeMACdes3des entering... Input message: " + message.toHexString() + " message.size(): "
+                + message.size());
+
+        //Add the padding, looks like we need this even if the remainder is 0
+        int remainder = inputLen % 8;
+
+        CMS.debug("Util.computeMACdes3des remainder: " + remainder);
+
+        TPSBuffer macPad = new TPSBuffer(8);
+        macPad.setAt(0, (byte) 0x80);
+
+        TPSBuffer padBuff = macPad.substr(0, 8 - remainder);
+
+        message.add(padBuff);
+        inputLen += (8 - remainder);
+
+        CMS.debug("Util.computeMACdes3des: padded input data. " + message.toHexString() + " input len: " + inputLen);
+
+        try {
+
+            token = CryptoManager.getInstance().getInternalKeyStorageToken();
+
+            PK11SymKey des = SessionKey.DeriveDESKeyFrom3DesKey(token.getName(), symKey, 0x00000122 /* CKM_DES_CBC */);
+
+            if (des == null) {
+                throw new EBaseException("Util.computeMACdes3des: Can't derive single des key from tripe des key!");
+            }
+
+            TPSBuffer desDebug = new TPSBuffer(des.getEncoded());
+
+            CMS.debug("des key debug bytes: " + desDebug.toHexString());
+
+            Cipher cipher = token.getCipherContext(EncryptionAlgorithm.DES_CBC);
+            Cipher cipher3des = token.getCipherContext(EncryptionAlgorithm.DES3_CBC);
+            mac = new TPSBuffer(initialIcv);
+
+            AlgorithmParameterSpec algSpec = new IVParameterSpec(initialIcv.toBytesArray());
+
+            int inputOffset = 0;
+
+            while (inputLen > 8)
+            {
+
+                mac.set(message.substr(inputOffset, 8));
+
+                // CMS.debug("About to encrypt1des: " + mac.toHexString());
+                cipher.initEncrypt(des, algSpec);
+                byte[] ciphResult = cipher.doFinal(mac.toBytesArray());
+
+                if (ciphResult.length != mac.size()) {
+                    throw new EBaseException("Invalid cipher in Util.computeMAC");
+                }
+
+                mac.set(ciphResult);
+                algSpec = new IVParameterSpec(ciphResult);
+
+                // CMS.debug("Util.computeMACdes3des: des encrypted bloc: " + mac.toHexString());
+
+                inputLen -= 8;
+                inputOffset += 8;
+            }
+
+            // Now do the 3DES portion of the operation
+
+            TPSBuffer newICV = new TPSBuffer(mac);
+
+            CMS.debug("Util.computeMACdes3des: inputOffset: " + inputOffset);
+
+            mac.set(message.substr(inputOffset, 8));
+
+            CMS.debug("About to encrypt 3des: " + mac.toHexString() + " icv: " + newICV.toHexString());
+
+            cipher3des.initEncrypt(symKey, new IVParameterSpec(newICV.toBytesArray()));
+            byte[] ciphResultFinal = cipher3des.doFinal(mac.toBytesArray());
+
+            if (ciphResultFinal.length != mac.size()) {
+                throw new EBaseException("Invalid cipher in Util.computeMAC");
+            }
+
+            output = new TPSBuffer(ciphResultFinal);
+
+            CMS.debug("Util.computeMACdes3des: final mac results: " + output.toHexString());
+
+        } catch (Exception e) {
+            throw new EBaseException("Util.computeMACdes3des: Cryptographic problem encountered! " + e.toString());
+        }
+
+        return output;
+    }
+
     public static TPSBuffer computeMAC(PK11SymKey symKey, TPSBuffer input, TPSBuffer icv) throws EBaseException {
         TPSBuffer output = null;
         TPSBuffer result = null;
@@ -271,6 +432,8 @@ public class Util {
             throw new EBaseException("Util.encryptData: called with no sym key or no data!");
         }
 
+        CMS.debug("Util.encryptData: dataToEnc: " + dataToEnc.toHexString());
+
         CryptoToken token = null;
         try {
 
@@ -280,9 +443,11 @@ public class Util {
             AlgorithmParameterSpec algSpec = null;
 
             int len = EncryptionAlgorithm.DES3_CBC.getIVLength();
+
             byte[] iv = new byte[len]; // Assume iv set to 0's as in current TPS
 
             algSpec = new IVParameterSpec(iv);
+
             cipher.initEncrypt(encKey, algSpec);
 
             byte[] encryptedBytes = cipher.doFinal(dataToEnc.toBytesArray());
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java b/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java
index 2cca81b..711701b 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java
@@ -29,7 +29,9 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.dogtagpki.server.connector.IRemoteRequest;
 import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.CryptoManager.NotInitializedException;
 import org.mozilla.jss.crypto.CryptoToken;
 import org.mozilla.jss.crypto.KeyWrapAlgorithm;
 import org.mozilla.jss.crypto.KeyWrapper;
@@ -48,8 +50,6 @@ import com.netscape.cms.servlet.base.CMSServlet;
 import com.netscape.cms.servlet.common.CMSRequest;
 import com.netscape.symkey.SessionKey;
 
-import org.dogtagpki.server.connector.IRemoteRequest;
-
 /**
  * A class representings an administration servlet for Token Key
  * Service Authority. This servlet is responsible to serve
@@ -107,6 +107,12 @@ public class TokenServlet extends CMSServlet {
     private final static String LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE =
             "LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE_4";
 
+    // Derivation Constants for SCP02
+    public final static byte[] C_MACDerivationConstant = { (byte) 0x01, (byte) 0x01 };
+    public final static byte[] ENCDerivationConstant = { 0x01, (byte) 0x82 };
+    public final static byte[] DEKDerivationConstant = { 0x01, (byte) 0x81 };
+    public final static byte[] R_MACDerivationConstant = { 0x01, 0x02 };
+
     /**
      * Constructs tks servlet.
      */
@@ -216,86 +222,92 @@ public class TokenServlet extends CMSServlet {
 
     }
 
-    // AC: KDF SPEC CHANGE - read new setting value from config file 
+    // AC: KDF SPEC CHANGE - read new setting value from config file
     // (This value allows configuration of which master keys use the NIST SP800-108 KDF and which use the original KDF for backwards compatibility)
     // CAREFUL:  Result returned may be negative due to java's lack of unsigned types.
     //           Negative values need to be treated as higher key numbers than positive key numbers.
-    private static byte read_setting_nistSP800_108KdfOnKeyVersion(String keySet) throws Exception{
+    private static byte read_setting_nistSP800_108KdfOnKeyVersion(String keySet) throws Exception {
         String nistSP800_108KdfOnKeyVersion_map = "tks." + keySet + ".nistSP800-108KdfOnKeyVersion";
         // KDF phase1: default to 00
         String nistSP800_108KdfOnKeyVersion_value =
-            CMS.getConfigStore().getString(nistSP800_108KdfOnKeyVersion_map, "00" /*null*/);
+                CMS.getConfigStore().getString(nistSP800_108KdfOnKeyVersion_map, "00" /*null*/);
         short nistSP800_108KdfOnKeyVersion_short = 0;
         // if value does not exist in file
-        if (nistSP800_108KdfOnKeyVersion_value == null){
+        if (nistSP800_108KdfOnKeyVersion_value == null) {
             // throw
             //  (we want admins to pay attention to this configuration item rather than guessing for them)
-            throw new Exception("Required configuration value \"" + nistSP800_108KdfOnKeyVersion_map + "\" missing from configuration file.");
+            throw new Exception("Required configuration value \"" + nistSP800_108KdfOnKeyVersion_map
+                    + "\" missing from configuration file.");
         }
         // convert setting value (in ASCII-hex) to short
-        try{
+        try {
             nistSP800_108KdfOnKeyVersion_short = Short.parseShort(nistSP800_108KdfOnKeyVersion_value, 16);
-            if ((nistSP800_108KdfOnKeyVersion_short < 0) || (nistSP800_108KdfOnKeyVersion_short > (short)0x00FF)){
+            if ((nistSP800_108KdfOnKeyVersion_short < 0) || (nistSP800_108KdfOnKeyVersion_short > (short) 0x00FF)) {
                 throw new Exception("Out of range.");
             }
-        } catch(Throwable t){
-            throw new Exception("Configuration value \"" + nistSP800_108KdfOnKeyVersion_map + "\" is in incorrect format. " + 
-                                "Correct format is \"" + nistSP800_108KdfOnKeyVersion_map + "=xx\" where xx is key version specified in ASCII-HEX format.", t);
+        } catch (Throwable t) {
+            throw new Exception("Configuration value \"" + nistSP800_108KdfOnKeyVersion_map
+                    + "\" is in incorrect format. " +
+                    "Correct format is \"" + nistSP800_108KdfOnKeyVersion_map
+                    + "=xx\" where xx is key version specified in ASCII-HEX format.", t);
         }
         // convert to byte (anything higher than 0x7F is represented as a negative)
-        byte nistSP800_108KdfOnKeyVersion_byte = (byte)nistSP800_108KdfOnKeyVersion_short;
+        byte nistSP800_108KdfOnKeyVersion_byte = (byte) nistSP800_108KdfOnKeyVersion_short;
         return nistSP800_108KdfOnKeyVersion_byte;
     }
-    
-    // AC: KDF SPEC CHANGE - read new setting value from config file 
+
+    // AC: KDF SPEC CHANGE - read new setting value from config file
     // (This value allows configuration of the NIST SP800-108 KDF:
     //   If "true" we use the CUID parameter within the NIST SP800-108 KDF.
     //   If "false" we use the KDD parameter within the NIST SP800-108 KDF.
-    private static boolean read_setting_nistSP800_108KdfUseCuidAsKdd(String keySet) throws Exception{
+    private static boolean read_setting_nistSP800_108KdfUseCuidAsKdd(String keySet) throws Exception {
         String setting_map = "tks." + keySet + ".nistSP800-108KdfUseCuidAsKdd";
         // KDF phase1: default to "false"
         String setting_str =
-            CMS.getConfigStore().getString(setting_map, "false" /*null*/);
+                CMS.getConfigStore().getString(setting_map, "false" /*null*/);
         boolean setting_boolean = false;
         // if value does not exist in file
-        if (setting_str == null){
+        if (setting_str == null) {
             // throw
             //  (we want admins to pay attention to this configuration item rather than guessing for them)
             throw new Exception("Required configuration value \"" + setting_map + "\" missing from configuration file.");
         }
         // convert setting value to boolean
-        try{
+        try {
             setting_boolean = Boolean.parseBoolean(setting_str);
-        }catch(Throwable t){
-            throw new Exception("Configuration value \"" + setting_map + "\" is in incorrect format.  Should be either \"true\" or \"false\".", t);
+        } catch (Throwable t) {
+            throw new Exception("Configuration value \"" + setting_map
+                    + "\" is in incorrect format.  Should be either \"true\" or \"false\".", t);
         }
         return setting_boolean;
     }
-    
+
     // AC: KDF SPEC CHANGE - Audit logging helper functions.
     // Converts a byte array to an ASCII-hex string.
-    //   We implemented this ourselves rather than using this.pp.toHexArray() because 
-    //   the team preferred CUID and KDD strings to be without ":" separators every byte. 
+    //   We implemented this ourselves rather than using this.pp.toHexArray() because
+    //   the team preferred CUID and KDD strings to be without ":" separators every byte.
     final char[] bytesToHex_hexArray = "0123456789ABCDEF".toCharArray();
-    private String bytesToHex(byte[] bytes){
+
+    private String bytesToHex(byte[] bytes) {
         char[] hexChars = new char[bytes.length * 2];
-        for (int i = 0; i < bytes.length; i++){
+        for (int i = 0; i < bytes.length; i++) {
             int thisChar = bytes[i] & 0x000000FF;
-            hexChars[i * 2] = bytesToHex_hexArray[thisChar >>> 4];    // div 16
-            hexChars[i*2 + 1] = bytesToHex_hexArray[thisChar & 0x0F];
+            hexChars[i * 2] = bytesToHex_hexArray[thisChar >>> 4]; // div 16
+            hexChars[i * 2 + 1] = bytesToHex_hexArray[thisChar & 0x0F];
         }
         return new String(hexChars);
     }
-    
+
     // AC: KDF SPEC CHANGE - Audit logging helper functions.
     // Safely converts a keyInfo byte array to a Key version hex string in the format: 0xa
-    // Since key version is always the first byte, this function returns the unsigned hex string representation of parameter[0]. 
+    // Since key version is always the first byte, this function returns the unsigned hex string representation of parameter[0].
     //   Returns "null" if parameter is null.
     //   Returns "invalid" if parameter.length < 1
-    private String log_string_from_keyInfo(byte[] xkeyInfo){
-        return (xkeyInfo == null) ? "null" : (xkeyInfo.length < 1 ? "invalid" : "0x" + Integer.toHexString((int)(xkeyInfo[0]) & 0x000000FF) );
+    private String log_string_from_keyInfo(byte[] xkeyInfo) {
+        return (xkeyInfo == null) ? "null" : (xkeyInfo.length < 1 ? "invalid" : "0x"
+                + Integer.toHexString((xkeyInfo[0]) & 0x000000FF));
     }
-    
+
     // AC: KDF SPEC CHANGE - Audit logging helper functions.
     // Safely converts a byte array containing specialDecoded information to an ASCII-hex string.
     // Parameters:
@@ -303,21 +315,551 @@ public class TokenServlet extends CMSServlet {
     // Returns:
     //   if specialDecoded is blank, returns "null"
     //   if specialDecoded != null, returns <ASCII-HEX string representation of specialDecoded>
-    private String log_string_from_specialDecoded_byte_array(byte[] specialDecoded){
-        if (specialDecoded == null){
+    private String log_string_from_specialDecoded_byte_array(byte[] specialDecoded) {
+        if (specialDecoded == null) {
             return "null";
-        }else{
+        } else {
             return bytesToHex(specialDecoded);
         }
     }
-    
+
+    /* Compute Session Key for SCP02
+     *  For simplicity compute just one session key,unless it is the DEK key case.
+     */
+
+    private void processComputeSessionKeySCP02(HttpServletRequest req, HttpServletResponse resp) throws EBaseException {
+
+        CMS.debug("TokenServlet.processComputeSessionKeySCP02 entering..");
+        String auditMessage = null;
+        String errorMsg = "";
+        String badParams = "";
+        String transportKeyName = "";
+        boolean missingParam = false;
+        String selectedToken = null;
+        String keyNickName = null;
+        byte[] drm_trans_wrapped_desKey = null;
+
+        byte[] xKDD = null;
+        byte nistSP800_108KdfOnKeyVersion = (byte) 0xff;
+        boolean nistSP800_108KdfUseCuidAsKdd = false;
+
+        IConfigStore sconfig = CMS.getConfigStore();
+
+        boolean isCryptoValidate = false;
+        byte[] keyInfo, CUID = null, session_key = null;
+
+        Exception missingSettingException = null;
+
+        String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID);
+
+        String rKDD = req.getParameter("KDD");
+        if ((rKDD == null) || (rKDD.length() == 0)) {
+            // KDF phase1: default to rCUID if not present
+            CMS.debug("TokenServlet: KDD not supplied, set to CUID before TPS change");
+            rKDD = rCUID;
+        }
+
+        String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO);
+
+        if ((rKeyInfo == null) || (rKeyInfo.equals(""))) {
+            badParams += " KeyInfo,";
+            CMS.debug("TokenServlet: processComputeSessionKeySCP02(): missing request parameter: key info");
+            missingParam = true;
+        }
+
+        keyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo);
+
+        String keySet = req.getParameter(IRemoteRequest.TOKEN_KEYSET);
+
+        if (keySet == null || keySet.equals("")) {
+            keySet = "defKeySet";
+        }
+        CMS.debug("TokenServlet.processComputeSessionKeySCP02: keySet selected: " + keySet + " keyInfo: " + rKeyInfo);
+
+        boolean serversideKeygen = false;
+
+        String rDerivationConstant = req.getParameter(IRemoteRequest.DERIVATION_CONSTANT);
+        String rSequenceCounter = req.getParameter(IRemoteRequest.SEQUENCE_COUNTER);
+
+        if ((rDerivationConstant == null) || (rDerivationConstant.equals(""))) {
+            badParams += " derivation_constant,";
+            CMS.debug("TokenServlet.processComputeSessionKeySCP02(): missing request parameter: derivation constant.");
+            missingParam = true;
+        }
+
+        if ((rSequenceCounter == null) || (rSequenceCounter.equals(""))) {
+            badParams += " sequence_counter,";
+            CMS.debug("TokenServlet.processComputeSessionKeySCP02(): missing request parameter: sequence counter.");
+            missingParam = true;
+        }
+
+        SessionContext sContext = SessionContext.getContext();
+
+        String agentId = "";
+        if (sContext != null) {
+            agentId =
+                    (String) sContext.get(SessionContext.USER_ID);
+        }
+
+        auditMessage = CMS.getLogMessage(
+                LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST,
+                rCUID,
+                rKDD, // AC: KDF SPEC CHANGE - Log both CUID and KDD.
+                ILogger.SUCCESS,
+                agentId);
+
+        audit(auditMessage);
+
+        if (!missingParam) {
+            CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID);
+
+            if (CUID == null || CUID.length != 10) {
+                badParams += " CUID length,";
+                CMS.debug("TokenServlet.processCompureSessionKeySCP02: Invalid CUID length");
+                missingParam = true;
+            }
+
+            xKDD = com.netscape.cmsutil.util.Utils.SpecialDecode(rKDD);
+            if (xKDD == null || xKDD.length != 10) {
+                badParams += " KDD length,";
+                CMS.debug("TokenServlet: Invalid KDD length");
+                missingParam = true;
+            }
+
+            keyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo);
+            if (keyInfo == null || keyInfo.length != 2) {
+                badParams += " KeyInfo length,";
+                CMS.debug("TokenServlet: Invalid key info length.");
+                missingParam = true;
+            }
+
+            try {
+                nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet);
+                nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet);
+
+                // log settings read in to debug log along with xkeyInfo
+                CMS.debug("TokenServlet: ComputeSessionKeySCP02():  keyInfo[0] = 0x"
+                        + Integer.toHexString((keyInfo[0]) & 0x0000000FF)
+                        + ",  xkeyInfo[1] = 0x"
+                        + Integer.toHexString((keyInfo[1]) & 0x0000000FF)
+                        );
+                CMS.debug("TokenServlet: ComputeSessionKeySCP02():  Nist SP800-108 KDF will be used for key versions >= 0x"
+                        + Integer.toHexString((nistSP800_108KdfOnKeyVersion) & 0x0000000FF)
+                        );
+                if (nistSP800_108KdfUseCuidAsKdd == true) {
+                    CMS.debug("TokenServlet: ComputeSessionKeySCP02():  Nist SP800-108 KDF (if used) will use CUID instead of KDD.");
+                } else {
+                    CMS.debug("TokenServlet: ComputeSessionKeySCP02():  Nist SP800-108 KDF (if used) will use KDD.");
+                }
+                // conform to the set-an-error-flag mentality
+            } catch (Exception e) {
+                missingSettingException = e;
+                CMS.debug("TokenServlet: ComputeSessionKeySCP02():  Exception reading Nist SP800-108 KDF config values: "
+                        + e.toString());
+            }
+
+        }
+
+        String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo; //#xx#xx
+        String mappingValue = CMS.getConfigStore().getString(keyInfoMap, null);
+        if (mappingValue == null) {
+            selectedToken =
+                    CMS.getConfigStore().getString("tks.defaultSlot", "internal");
+            keyNickName = rKeyInfo;
+        } else {
+            StringTokenizer st = new StringTokenizer(mappingValue, ":");
+            if (st.hasMoreTokens())
+                selectedToken = st.nextToken();
+            if (st.hasMoreTokens())
+                keyNickName = st.nextToken();
+        }
+
+        keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo; //#xx#xx
+        try {
+            mappingValue = CMS.getConfigStore().getString(keyInfoMap, null);
+        } catch (EBaseException e1) {
+
+            e1.printStackTrace();
+        }
+        if (mappingValue == null) {
+            try {
+                selectedToken =
+                        CMS.getConfigStore().getString("tks.defaultSlot", "internal");
+            } catch (EBaseException e) {
+
+                e.printStackTrace();
+            }
+            keyNickName = rKeyInfo;
+        } else {
+            StringTokenizer st = new StringTokenizer(mappingValue, ":");
+            if (st.hasMoreTokens())
+                selectedToken = st.nextToken();
+            if (st.hasMoreTokens())
+                keyNickName = st.nextToken();
+        }
+
+        CMS.debug("TokenServlet: processComputeSessionKeySCP02(): final keyNickname: " + keyNickName);
+        String useSoftToken_s = null;
+        try {
+            useSoftToken_s = CMS.getConfigStore().getString("tks.useSoftToken", "true");
+        } catch (EBaseException e1) {
+            // TODO Auto-generated catch block
+            e1.printStackTrace();
+        }
+        if (!useSoftToken_s.equalsIgnoreCase("true"))
+            useSoftToken_s = "false";
+
+        String rServersideKeygen = req.getParameter(IRemoteRequest.SERVER_SIDE_KEYGEN);
+        if (rServersideKeygen.equals("true")) {
+            CMS.debug("TokenServlet.processComputeSessionKeySCP02: serversideKeygen requested");
+            serversideKeygen = true;
+        } else {
+            CMS.debug("TokenServlet.processComputeSessionKeySCP02: serversideKeygen not requested");
+        }
+
+        transportKeyName = null;
+        try {
+            transportKeyName = getSharedSecretName(sconfig);
+        } catch (EBaseException e1) {
+            // TODO Auto-generated catch block
+            e1.printStackTrace();
+            CMS.debug("TokenServlet.processComputeSessionKeySCP02: Can't find transport key name!");
+
+        }
+
+        CMS.debug("TokenServlet: processComputeSessionKeySCP02(): tksSharedSymKeyName: " + transportKeyName);
+
+        CMS.debug("TokenServlet: ComputeSessionKeySCP02(): tksSharedSymKeyName: " + transportKeyName);
+
+        try {
+            isCryptoValidate = sconfig.getBoolean("cardcryptogram.validate.enable", true);
+        } catch (EBaseException eee) {
+        }
+
+        byte macKeyArray[] = null;
+        byte sequenceCounter[] = null;
+        byte derivationConstant[] = null;
+
+        boolean errorFound = false;
+
+        String dek_wrapped_desKeyString = null;
+        String keycheck_s = null;
+
+        if (selectedToken != null && keyNickName != null && transportKeyName != null && missingSettingException == null) {
+            try {
+                macKeyArray = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+                        + keySet + ".mac_key"));
+
+                sequenceCounter = com.netscape.cmsutil.util.Utils.SpecialDecode(rSequenceCounter);
+                derivationConstant = com.netscape.cmsutil.util.Utils.SpecialDecode(rDerivationConstant);
+
+                //Use old style for the moment.
+                //ToDo: We need to use the nistXP800 params we have collected and send them down to symkey
+                //Perform in next ticket to fully implement nistXP800
+
+                session_key = SessionKey.ComputeSessionKeySCP02(
+                        selectedToken, keyNickName,
+                        keyInfo, CUID, macKeyArray, sequenceCounter, derivationConstant, useSoftToken_s, keySet,
+                        transportKeyName);
+
+                if (session_key == null) {
+                    CMS.debug("TokenServlet.computeSessionKeySCP02:Tried ComputeSessionKey, got NULL ");
+                    throw new EBaseException("Can't compute session key for SCP02!");
+
+                }
+
+                //Only do this for the dekSessionKey and if we are in the server side keygen case.
+                if (derivationConstant[0] == DEKDerivationConstant[0]
+                        && derivationConstant[1] == DEKDerivationConstant[1] && serversideKeygen == true) {
+
+                    CMS.debug("TokenServlet.computeSessionKeySCP02: We have the server side keygen case while generating the dek session key, wrap and return symkeys for the drm and token.");
+
+                    /**
+                     * 0. generate des key
+                     * 1. encrypt des key with dek key
+                     * 2. encrypt des key with DRM transport key
+                     * These two wrapped items are to be sent back to
+                     * TPS. 2nd item is to DRM
+                     **/
+
+                    PK11SymKey desKey = null;
+                    PK11SymKey dekKey = null;
+
+                    /*generate it on whichever token the master key is at*/
+                    if (useSoftToken_s.equals("true")) {
+                        CMS.debug("TokenServlet.computeSessionKeySCP02: key encryption key generated on internal");
+
+                        desKey = SessionKey.GenerateSymkey("internal");
+
+                    } else {
+                        CMS.debug("TokenServlet.computeSessionKeySCP02: key encryption key generated on "
+                                + selectedToken);
+                        desKey = SessionKey.GenerateSymkey(selectedToken);
+                    }
+                    if (desKey != null)
+                        CMS.debug("TokenServlet.computeSessionKeySCP02: key encryption key generated for " + rCUID);
+                    else {
+                        CMS.debug("TokenServlet.computeSessionKeySCP02: key encryption key generation failed for "
+                                + rCUID);
+                        throw new EBaseException(
+                                "TokenServlet.computeSessionKeySCP02: can't generate key encryption key");
+                    }
+
+                    CryptoToken token = null;
+                    if (useSoftToken_s.equals("true")) {
+                        //token = CryptoManager.getInstance().getTokenByName(selectedToken);
+                        token = CryptoManager.getInstance().getInternalCryptoToken();
+                    } else {
+                        token = CryptoManager.getInstance().getTokenByName(selectedToken);
+                    }
+
+                    //Now we have to create a sym key object for the wrapped session_key (dekKey)
+                    // session_key wrapped by the shared Secret
+
+                    PK11SymKey sharedSecret = getSharedSecretKey();
+
+                    if (sharedSecret == null) {
+                        throw new EBaseException(
+                                "TokenServlet.computeSessionKeySCP02: Can't find share secret sym key!");
+                    }
+
+                    dekKey = SessionKey.UnwrapSessionKeyWithSharedSecret(token.getName(), sharedSecret,
+                            session_key);
+
+                    if (dekKey == null) {
+                        throw new EBaseException(
+                                "TokenServlet.computeSessionKeySCP02: Can't unwrap DEK key onto the token!");
+                    }
+
+                    /*
+                     * ECBencrypt actually takes the 24 byte DES2 key
+                     * and discard the last 8 bytes before it encrypts.
+                     * This is done so that the applet can digest it
+                     */
+                    byte[] encDesKey =
+                            SessionKey.ECBencrypt(dekKey,
+                                    desKey);
+
+                    if (encDesKey == null) {
+                        throw new EBaseException("TokenServlet.computeSessionKeySCP02: Can't encrypt DEK key!");
+                    }
+
+                    dek_wrapped_desKeyString =
+                            com.netscape.cmsutil.util.Utils.SpecialEncode(encDesKey);
+
+                    byte[] keycheck =
+                            SessionKey.ComputeKeyCheck(desKey);
+
+                    if (keycheck == null) {
+                        throw new EBaseException(
+                                "TokenServlet.computeSessionKeySCP02: Can't compute key check for encrypted DEK key!");
+                    }
+
+                    keycheck_s =
+                            com.netscape.cmsutil.util.Utils.SpecialEncode(keycheck);
+
+                    //use DRM transport cert to wrap desKey
+                    String drmTransNickname = CMS.getConfigStore().getString("tks.drm_transport_cert_nickname", "");
+
+                    if ((drmTransNickname == null) || (drmTransNickname == "")) {
+                        CMS.debug("TokenServlet.computeSessionKeySCP02:did not find DRM transport certificate nickname");
+                        throw new EBaseException("can't find DRM transport certificate nickname");
+                    } else {
+                        CMS.debug("TokenServlet.computeSessionKeySCP02:drmtransport_cert_nickname=" + drmTransNickname);
+                    }
+
+                    X509Certificate drmTransCert = null;
+                    drmTransCert = CryptoManager.getInstance().findCertByNickname(drmTransNickname);
+                    // wrap kek session key with DRM transport public key
+
+                    PublicKey pubKey = drmTransCert.getPublicKey();
+                    String pubKeyAlgo = pubKey.getAlgorithm();
+
+                    KeyWrapper keyWrapper = null;
+                    //For wrapping symmetric keys don't need IV, use ECB
+                    if (pubKeyAlgo.equals("EC")) {
+                        keyWrapper = token.getKeyWrapper(KeyWrapAlgorithm.AES_ECB);
+                        keyWrapper.initWrap(pubKey, null);
+                    } else {
+                        keyWrapper = token.getKeyWrapper(KeyWrapAlgorithm.RSA);
+                        keyWrapper.initWrap(pubKey, null);
+                    }
+
+                    drm_trans_wrapped_desKey = keyWrapper.wrap(desKey);
+                    CMS.debug("computeSessionKey.computeSessionKeySCP02:desKey wrapped with drm transportation key.");
+
+                    CMS.debug("computeSessionKey.computeSessionKeySCP02:desKey: Just unwrapped the dekKey onto the token to be wrapped on the way out.");
+
+                }
+
+            } catch (Exception e) {
+                CMS.debug("TokenServlet.computeSessionKeySCP02 Computing Session Key: " + e.toString());
+                errorFound = true;
+
+            }
+
+        }
+
+        String status = "0";
+        String value = "";
+        String outputString = "";
+
+        boolean statusDeclared = false;
+
+        if (session_key != null && session_key.length > 0 && errorFound == false) {
+            outputString =
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(session_key);
+        } else {
+
+            status = "1";
+            statusDeclared = true;
+        }
+
+        if (selectedToken == null || keyNickName == null) {
+            if (!statusDeclared) {
+                status = "4";
+                statusDeclared = true;
+            }
+        }
+
+        if (missingSettingException != null) {
+            if (!statusDeclared) {
+                status = "6";
+                statusDeclared = true;
+            }
+        }
+
+        if (missingParam) {
+            status = "3";
+        }
+
+        String drm_trans_wrapped_desKeyString = null;
+
+        if (!status.equals("0")) {
+            if (status.equals("1")) {
+                errorMsg = "Problem generating session key info.";
+            }
+
+            if (status.equals("4")) {
+                errorMsg = "Problem obtaining token information.";
+            }
+
+            if (status.equals("3")) {
+                if (badParams.endsWith(",")) {
+                    badParams = badParams.substring(0, badParams.length() - 1);
+                }
+                errorMsg = "Missing input parameters :" + badParams;
+            }
+
+            if (status.equals("6")) {
+                errorMsg = "Problem reading required configuration value.";
+            }
+
+        } else {
+
+            if (serversideKeygen == true) {
+
+                if (drm_trans_wrapped_desKey != null && drm_trans_wrapped_desKey.length > 0) {
+                    drm_trans_wrapped_desKeyString =
+                            com.netscape.cmsutil.util.Utils.SpecialEncode(drm_trans_wrapped_desKey);
+                }
+
+                StringBuffer sb = new StringBuffer();
+                sb.append(IRemoteRequest.RESPONSE_STATUS + "=0&");
+                sb.append(IRemoteRequest.TKS_RESPONSE_SessionKey + "=");
+                sb.append(outputString);
+
+                //Now add the trans wrapped des key
+
+                if (drm_trans_wrapped_desKeyString != null) {
+                    sb.append("&" + IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey + "=");
+                    sb.append(drm_trans_wrapped_desKeyString);
+                }
+
+                if (dek_wrapped_desKeyString != null) {
+                    sb.append("&" + IRemoteRequest.TKS_RESPONSE_KEK_DesKey + "=");
+                    sb.append(dek_wrapped_desKeyString);
+                }
+
+                if (keycheck_s != null) {
+                    sb.append("&" + IRemoteRequest.TKS_RESPONSE_KeyCheck + "=");
+                    sb.append(keycheck_s);
+                }
+
+                value = sb.toString();
+            } else {
+                StringBuffer sb = new StringBuffer();
+                sb.append(IRemoteRequest.RESPONSE_STATUS + "=0&");
+                sb.append(IRemoteRequest.TKS_RESPONSE_SessionKey + "=");
+                sb.append(outputString);
+                value = sb.toString();
+            }
+
+        }
+
+        CMS.debug("TokenServlet:outputString.encode " + value);
+
+        try {
+            resp.setContentLength(value.length());
+            CMS.debug("TokenServlet:outputString.length " + value.length());
+            OutputStream ooss = resp.getOutputStream();
+            ooss.write(value.getBytes());
+            ooss.flush();
+            mRenderResult = false;
+        } catch (IOException e) {
+            CMS.debug("TokenServlet: " + e.toString());
+        }
+
+        if (status.equals("0")) {
+
+            String[] logParams = { log_string_from_specialDecoded_byte_array(CUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.SUCCESS, // Outcome
+                    status, // status
+                    agentId, // AgentID
+                    isCryptoValidate ? "true" : "false", // IsCryptoValidate
+                    serversideKeygen ? "true" : "false", // IsServerSideKeygen
+                    selectedToken, // SelectedToken
+                    keyNickName, // KeyNickName
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(keyInfo), // KeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd) // NistSP800_108KdfUseCuidAsKdd
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS,
+                    logParams);
+
+        } else {
+
+            String[] logParams = { log_string_from_specialDecoded_byte_array(CUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.FAILURE, // Outcome
+                    status, // status
+                    agentId, // AgentID
+                    isCryptoValidate ? "true" : "false", // IsCryptoValidate
+                    serversideKeygen ? "true" : "false", // IsServerSideKeygen
+                    selectedToken, // SelectedToken
+                    keyNickName, // KeyNickName
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(keyInfo), // KeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd), // NistSP800_108KdfUseCuidAsKdd
+                    errorMsg // Error
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,
+                    logParams);
+        }
+
+        audit(auditMessage);
+
+    }
 
     private void processComputeSessionKey(HttpServletRequest req,
             HttpServletResponse resp) throws EBaseException {
-        byte[] card_challenge ,host_challenge,keyInfo, xCUID, session_key, xKDD;  // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD
+        byte[] card_challenge, host_challenge, keyInfo, xCUID, session_key, xKDD; // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD
 
         // AC: KDF SPEC CHANGE - new config file values (needed for symkey)
-        byte nistSP800_108KdfOnKeyVersion = (byte)0xff;
+        byte nistSP800_108KdfOnKeyVersion = (byte) 0xff;
         boolean nistSP800_108KdfUseCuidAsKdd = false;
 
         byte[] card_crypto, host_cryptogram, input_card_crypto;
@@ -327,7 +869,6 @@ public class TokenServlet extends CMSServlet {
         String errorMsg = "";
         String badParams = "";
         String transportKeyName = "";
-
         String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID);
 
         // AC: KDF SPEC CHANGE - read new KDD parameter from TPS
@@ -353,10 +894,10 @@ public class TokenServlet extends CMSServlet {
         IConfigStore sconfig = CMS.getConfigStore();
         boolean isCryptoValidate = true;
         boolean missingParam = false;
-        
+
         // AC: KDF SPEC CHANGE - flag for if there is an error reading our new setting
         Exception missingSetting_exception = null;
-        
+
         session_key = null;
         card_crypto = null;
         host_cryptogram = null;
@@ -373,11 +914,11 @@ public class TokenServlet extends CMSServlet {
 
         // AC: KDF SPEC CHANGE:  Need to log both KDD and CUID
         auditMessage = CMS.getLogMessage(
-                         LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST,
-                        rCUID,
-                        rKDD,            // AC: KDF SPEC CHANGE - Log both CUID and KDD.
-                        ILogger.SUCCESS,
-                        agentId);
+                LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST,
+                rCUID,
+                rKDD, // AC: KDF SPEC CHANGE - Log both CUID and KDD.
+                ILogger.SUCCESS,
+                agentId);
 
         audit(auditMessage);
 
@@ -415,7 +956,7 @@ public class TokenServlet extends CMSServlet {
             badParams += " CUID,";
             missingParam = true;
         }
-        
+
         // AC: KDF SPEC CHANGE - read new KDD parameter from TPS
         if ((rKDD == null) || (rKDD.length() == 0)) {
             CMS.debug("TokenServlet: ComputeSessionKey(): missing request parameter: KDD");
@@ -445,11 +986,11 @@ public class TokenServlet extends CMSServlet {
         String keyNickName = null;
         boolean sameCardCrypto = true;
 
-        // AC: KDF SPEC CHANGE 
-        xCUID = null;       // avoid errors about non-initialization
-        xKDD = null;        // avoid errors about non-initialization
-        xkeyInfo = null;    // avoid errors about non-initialization
-        
+        // AC: KDF SPEC CHANGE
+        xCUID = null; // avoid errors about non-initialization
+        xKDD = null; // avoid errors about non-initialization
+        xkeyInfo = null; // avoid errors about non-initialization
+
         if (!missingParam) {
 
             xCUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID);
@@ -458,15 +999,15 @@ public class TokenServlet extends CMSServlet {
                 CMS.debug("TokenServlet: Invalid CUID length");
                 missingParam = true;
             }
-            
+
             // AC: KDF SPEC CHANGE - read new KDD parameter from TPS
             xKDD = com.netscape.cmsutil.util.Utils.SpecialDecode(rKDD);
             if (xKDD == null || xKDD.length != 10) {
-                        badParams += " KDD length,";
-                        CMS.debug("TokenServlet: Invalid KDD length");
-                        missingParam = true;
+                badParams += " KDD length,";
+                CMS.debug("TokenServlet: Invalid KDD length");
+                missingParam = true;
             }
-            
+
             xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo);
             if (xkeyInfo == null || xkeyInfo.length != 2) {
                 badParams += " KeyInfo length,";
@@ -490,9 +1031,6 @@ public class TokenServlet extends CMSServlet {
 
         }
 
-        // AC: KDF SPEC CHANGE - Remove duplicative variable.
-        // CUID = null;  
-        
         if (!missingParam) {
             card_challenge =
                     com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_challenge);
@@ -500,32 +1038,31 @@ public class TokenServlet extends CMSServlet {
             host_challenge = com.netscape.cmsutil.util.Utils.SpecialDecode(rhost_challenge);
             keyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo);
 
-            // CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID);
-            // AC: KDF SPEC CHANGE: Removed duplicative variable/processing.
-
             // AC: KDF SPEC CHANGE - read new config file values (needed for symkey)
-            try{
+            //ToDo: Will use these values after completing next ticket
+            try {
                 nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet);
                 nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet);
 
                 // log settings read in to debug log along with xkeyInfo
                 CMS.debug("TokenServlet: ComputeSessionKey():  xkeyInfo[0] = 0x"
-                         + Integer.toHexString((int)(xkeyInfo[0]) & 0x0000000FF)
-                         + ",  xkeyInfo[1] = 0x"
-                         + Integer.toHexString((int)(xkeyInfo[1]) & 0x0000000FF)
-                         );
+                        + Integer.toHexString((xkeyInfo[0]) & 0x0000000FF)
+                        + ",  xkeyInfo[1] = 0x"
+                        + Integer.toHexString((xkeyInfo[1]) & 0x0000000FF)
+                        );
                 CMS.debug("TokenServlet: ComputeSessionKey():  Nist SP800-108 KDF will be used for key versions >= 0x"
-                         + Integer.toHexString((int)(nistSP800_108KdfOnKeyVersion) & 0x0000000FF)
-                         );
-                if (nistSP800_108KdfUseCuidAsKdd == true){
+                        + Integer.toHexString((nistSP800_108KdfOnKeyVersion) & 0x0000000FF)
+                        );
+                if (nistSP800_108KdfUseCuidAsKdd == true) {
                     CMS.debug("TokenServlet: ComputeSessionKey():  Nist SP800-108 KDF (if used) will use CUID instead of KDD.");
-                }else{
+                } else {
                     CMS.debug("TokenServlet: ComputeSessionKey():  Nist SP800-108 KDF (if used) will use KDD.");
                 }
-            // conform to the set-an-error-flag mentality
-            }catch(Exception e){
+                // conform to the set-an-error-flag mentality
+            } catch (Exception e) {
                 missingSetting_exception = e;
-                CMS.debug("TokenServlet: ComputeSessionKey():  Exception reading Nist SP800-108 KDF config values: " + e.toString());
+                CMS.debug("TokenServlet: ComputeSessionKey():  Exception reading Nist SP800-108 KDF config values: "
+                        + e.toString());
             }
 
             String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo; //#xx#xx
@@ -555,12 +1092,12 @@ public class TokenServlet extends CMSServlet {
                             + selectedToken + " keyNickName=" + keyNickName);
                     session_key = SessionKey.ComputeSessionKey(
                             selectedToken, keyNickName, card_challenge,
-                             host_challenge,keyInfo,
-                             nistSP800_108KdfOnKeyVersion,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                             nistSP800_108KdfUseCuidAsKdd,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                             xCUID,                           // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
-                             xKDD,                            // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
-                             macKeyArray, useSoftToken_s, keySet, transportKeyName );
+                            host_challenge, keyInfo,
+                            nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+                            xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+                            macKeyArray, useSoftToken_s, keySet, transportKeyName);
 
                     if (session_key == null) {
                         CMS.debug("TokenServlet:Tried ComputeSessionKey, got NULL ");
@@ -573,12 +1110,12 @@ public class TokenServlet extends CMSServlet {
                                     + keySet + ".auth_key"));
                     enc_session_key = SessionKey.ComputeEncSessionKey(
                             selectedToken, keyNickName, card_challenge,
-                      host_challenge,keyInfo,
-                      nistSP800_108KdfOnKeyVersion,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                      nistSP800_108KdfUseCuidAsKdd,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                      xCUID,                           // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
-                      xKDD,                            // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
-                      encKeyArray, useSoftToken_s, keySet);
+                            host_challenge, keyInfo,
+                            nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+                            xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+                            encKeyArray, useSoftToken_s, keySet);
 
                     if (enc_session_key == null) {
                         CMS.debug("TokenServlet:Tried ComputeEncSessionKey, got NULL ");
@@ -603,12 +1140,12 @@ public class TokenServlet extends CMSServlet {
 
                         kek_key = SessionKey.ComputeKekKey(
                                 selectedToken, keyNickName, card_challenge,
-                             host_challenge,keyInfo,
-                             nistSP800_108KdfOnKeyVersion,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                             nistSP800_108KdfUseCuidAsKdd,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                             xCUID,                           // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
-                             xKDD,                            // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
-                             kekKeyArray, useSoftToken_s,keySet);
+                                host_challenge, keyInfo,
+                                nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+                                nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+                                xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+                                xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+                                kekKeyArray, useSoftToken_s, keySet);
 
                         CMS.debug("TokenServlet: called ComputeKekKey");
 
@@ -647,15 +1184,15 @@ public class TokenServlet extends CMSServlet {
                         if (desKey != null) {
                             // AC: KDF SPEC CHANGE - Output using CUID and KDD
                             CMS.debug("TokenServlet: key encryption key generated for CUID=" +
-                                trim(pp.toHexString(xCUID)) +
-                                ", KDD=" +
-                                trim(pp.toHexString(xKDD)));
+                                    trim(pp.toHexString(xCUID)) +
+                                    ", KDD=" +
+                                    trim(pp.toHexString(xKDD)));
                         } else {
                             // AC: KDF SPEC CHANGE - Output using CUID and KDD
                             CMS.debug("TokenServlet: key encryption key generation failed for CUID=" +
-                                trim(pp.toHexString(xCUID)) +
-                                ", KDD=" +
-                                trim(pp.toHexString(xKDD)));
+                                    trim(pp.toHexString(xCUID)) +
+                                    ", KDD=" +
+                                    trim(pp.toHexString(xKDD)));
 
                             throw new Exception("can't generate key encryption key");
                         }
@@ -729,12 +1266,12 @@ public class TokenServlet extends CMSServlet {
                                     + keySet + ".auth_key"));
                     host_cryptogram = SessionKey.ComputeCryptogram(
                             selectedToken, keyNickName, card_challenge,
-                      host_challenge,keyInfo,
-                      nistSP800_108KdfOnKeyVersion,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                      nistSP800_108KdfUseCuidAsKdd,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                      xCUID,                           // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
-                      xKDD,                            // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
-                      0, authKeyArray, useSoftToken_s, keySet);
+                            host_challenge, keyInfo,
+                            nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+                            xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+                            0, authKeyArray, useSoftToken_s, keySet);
 
                     if (host_cryptogram == null) {
                         CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL ");
@@ -743,13 +1280,12 @@ public class TokenServlet extends CMSServlet {
                     }
                     card_crypto = SessionKey.ComputeCryptogram(
                             selectedToken, keyNickName, card_challenge,
-                      host_challenge,keyInfo,
-                      nistSP800_108KdfOnKeyVersion,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                      nistSP800_108KdfUseCuidAsKdd,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                      xCUID,                           // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
-                      xKDD,                            // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
-                      1, authKeyArray, useSoftToken_s, keySet);
-
+                            host_challenge, keyInfo,
+                            nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+                            xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+                            xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+                            1, authKeyArray, useSoftToken_s, keySet);
 
                     if (card_crypto == null) {
                         CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL ");
@@ -781,9 +1317,9 @@ public class TokenServlet extends CMSServlet {
                     CMS.getLogger().log(ILogger.EV_AUDIT,
                             ILogger.S_TKS,
                             ILogger.LL_INFO, "processComputeSessionKey for CUID=" +
-                            trim(pp.toHexString(xCUID)) +
-                            ", KDD=" +
-                            trim(pp.toHexString(xKDD)));
+                                    trim(pp.toHexString(xCUID)) +
+                                    ", KDD=" +
+                                    trim(pp.toHexString(xKDD)));
                 } catch (Exception e) {
                     CMS.debug(e);
                     CMS.debug("TokenServlet Computing Session Key: " + e.toString());
@@ -831,32 +1367,32 @@ public class TokenServlet extends CMSServlet {
                     com.netscape.cmsutil.util.Utils.SpecialEncode(host_cryptogram);
         } else {
             // AC: Bugfix: Don't override status's value if an error was already flagged
-            if (status.equals("0") == true){
+            if (status.equals("0") == true) {
                 status = "2";
             }
         }
 
         if (selectedToken == null || keyNickName == null) {
             // AC: Bugfix: Don't override status's value if an error was already flagged
-            if (status.equals("0") == true){
+            if (status.equals("0") == true) {
                 status = "4";
             }
         }
 
         if (!sameCardCrypto) {
             // AC: Bugfix: Don't override status's value if an error was already flagged
-            if (status.equals("0") == true){
+            if (status.equals("0") == true) {
                 // AC: Bugfix: Don't mis-represent host cryptogram mismatch errors as TPS parameter issues
                 status = "5";
             }
         }
 
         // AC: KDF SPEC CHANGE - check for settings file issue (flag)
-        if (missingSetting_exception != null){
+        if (missingSetting_exception != null) {
             // AC: Intentionally override previous errors if config file settings were missing.
             status = "6";
         }
-        
+
         if (missingParam) {
             // AC: Intentionally override previous errors if parameters were missing.
             status = "3";
@@ -874,7 +1410,7 @@ public class TokenServlet extends CMSServlet {
 
             // AC: Bugfix: Don't mis-represent card cryptogram mismatch errors as TPS parameter issues
             if (status.equals("5")) {
-               errorMsg = "Card cryptogram mismatch. Token likely has incorrect keys.";
+                errorMsg = "Card cryptogram mismatch. Token likely has incorrect keys.";
             }
 
             if (status.equals("4")) {
@@ -893,32 +1429,33 @@ public class TokenServlet extends CMSServlet {
                 errorMsg = "Missing input parameters :" + badParams;
             }
 
-            value = IRemoteRequest.RESPONSE_STATUS+"=" + status;
+            value = IRemoteRequest.RESPONSE_STATUS + "=" + status;
         } else {
             if (serversideKeygen == true) {
                 StringBuffer sb = new StringBuffer();
-                sb.append(IRemoteRequest.RESPONSE_STATUS+"=0&");
-                sb.append(IRemoteRequest.TKS_RESPONSE_SessionKey+"=");
+                sb.append(IRemoteRequest.RESPONSE_STATUS + "=0&");
+                sb.append(IRemoteRequest.TKS_RESPONSE_SessionKey + "=");
                 sb.append(outputString);
-                sb.append("&"+IRemoteRequest.TKS_RESPONSE_HostCryptogram+"=");
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_HostCryptogram + "=");
                 sb.append(cryptogram);
-                sb.append("&"+IRemoteRequest.TKS_RESPONSE_EncSessionKey+"=");
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_EncSessionKey + "=");
                 sb.append(encSessionKeyString);
-                sb.append("&"+IRemoteRequest.TKS_RESPONSE_KEK_DesKey+"=");
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_KEK_DesKey + "=");
                 sb.append(kek_wrapped_desKeyString);
-                sb.append("&"+IRemoteRequest.TKS_RESPONSE_KeyCheck+"=");
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_KeyCheck + "=");
                 sb.append(keycheck_s);
-                sb.append("&"+IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey+"=");
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey + "=");
                 sb.append(drm_trans_wrapped_desKeyString);
                 value = sb.toString();
             } else {
+
                 StringBuffer sb = new StringBuffer();
-                sb.append(IRemoteRequest.RESPONSE_STATUS+"=0&");
-                sb.append(IRemoteRequest.TKS_RESPONSE_SessionKey+"=");
+                sb.append(IRemoteRequest.RESPONSE_STATUS + "=0&");
+                sb.append(IRemoteRequest.TKS_RESPONSE_SessionKey + "=");
                 sb.append(outputString);
-                sb.append("&"+IRemoteRequest.TKS_RESPONSE_HostCryptogram+"=");
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_HostCryptogram + "=");
                 sb.append(cryptogram);
-                sb.append("&"+IRemoteRequest.TKS_RESPONSE_EncSessionKey+"=");
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_EncSessionKey + "=");
                 sb.append(encSessionKeyString);
                 value = sb.toString();
             }
@@ -941,41 +1478,44 @@ public class TokenServlet extends CMSServlet {
             // AC: KDF SPEC CHANGE - Log both CUID and KDD.
             //                       Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
             //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
-            String[] logParams = {  log_string_from_specialDecoded_byte_array(xCUID),                                   // CUID_decoded
-                                    log_string_from_specialDecoded_byte_array(xKDD),                                    // KDD_decoded
-                                    ILogger.SUCCESS,                                                                    // Outcome
-                                    status,                                                                             // status
-                                    agentId,                                                                            // AgentID
-                                    isCryptoValidate? "true":"false",                                                   // IsCryptoValidate
-                                    serversideKeygen? "true":"false",                                                   // IsServerSideKeygen
-                                    selectedToken,                                                                      // SelectedToken
-                                    keyNickName,                                                                        // KeyNickName
-                                    keySet,                                                                             // TKSKeyset
-                                    log_string_from_keyInfo(xkeyInfo),                                                  // KeyInfo_KeyVersion
-                                    "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF),         // NistSP800_108KdfOnKeyVersion
-                                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd)                                      // NistSP800_108KdfUseCuidAsKdd
-                                 };
-            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, logParams);
+            String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.SUCCESS, // Outcome
+                    status, // status
+                    agentId, // AgentID
+                    isCryptoValidate ? "true" : "false", // IsCryptoValidate
+                    serversideKeygen ? "true" : "false", // IsServerSideKeygen
+                    selectedToken, // SelectedToken
+                    keyNickName, // KeyNickName
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd) // NistSP800_108KdfUseCuidAsKdd
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS,
+                    logParams);
+
         } else {
             // AC: KDF SPEC CHANGE - Log both CUID and KDD
             //                       Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
             //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
-            String[] logParams = {  log_string_from_specialDecoded_byte_array(xCUID),                                   // CUID_decoded
-                                    log_string_from_specialDecoded_byte_array(xKDD),                                    // KDD_decoded
-                                    ILogger.FAILURE,                                                                    // Outcome
-                                    status,                                                                             // status
-                                    agentId,                                                                            // AgentID
-                                    isCryptoValidate? "true":"false",                                                   // IsCryptoValidate
-                                    serversideKeygen? "true":"false",                                                   // IsServerSideKeygen
-                                    selectedToken,                                                                      // SelectedToken
-                                    keyNickName,                                                                        // KeyNickName
-                                    keySet,                                                                             // TKSKeyset
-                                    log_string_from_keyInfo(xkeyInfo),                                                  // KeyInfo_KeyVersion
-                                    "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF),         // NistSP800_108KdfOnKeyVersion
-                                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd),                                     // NistSP800_108KdfUseCuidAsKdd
-                                    errorMsg                                                                            // Error
-                                 };
-            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE, logParams);
+            String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.FAILURE, // Outcome
+                    status, // status
+                    agentId, // AgentID
+                    isCryptoValidate ? "true" : "false", // IsCryptoValidate
+                    serversideKeygen ? "true" : "false", // IsServerSideKeygen
+                    selectedToken, // SelectedToken
+                    keyNickName, // KeyNickName
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd), // NistSP800_108KdfUseCuidAsKdd
+                    errorMsg // Error
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,
+                    logParams);
 
         }
 
@@ -1014,18 +1554,18 @@ public class TokenServlet extends CMSServlet {
 
     private void processDiversifyKey(HttpServletRequest req,
             HttpServletResponse resp) throws EBaseException {
-        byte[] KeySetData,KeysValues,xCUID,xKDD;  // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD
+        byte[] KeySetData, xCUID, xKDD; // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD
 
         // AC: BUGFIX:  Record the actual parameters to DiversifyKey in the audit log.
         String oldKeyNickName = null;
         String newKeyNickName = null;
 
         // AC: KDF SPEC CHANGE - new config file values (needed for symkey)
-        byte nistSP800_108KdfOnKeyVersion = (byte)0xff;
+        byte nistSP800_108KdfOnKeyVersion = (byte) 0xff;
         boolean nistSP800_108KdfUseCuidAsKdd = false;
 
         // AC: BUGFIX for key versions higher than 09:  We need to initialize these variables in order for the compiler not to complain when we pass them to DiversifyKey.
-        byte[] xkeyInfo = null,xnewkeyInfo = null;
+        byte[] xkeyInfo = null, xnewkeyInfo = null;
 
         // AC: KDF SPEC CHANGE - flag for if there is an error reading our new setting
         Exception missingSetting_exception = null;
@@ -1033,6 +1573,7 @@ public class TokenServlet extends CMSServlet {
         boolean missingParam = false;
         String errorMsg = "";
         String badParams = "";
+        byte[] xWrappedDekKey = null;
 
         IConfigStore sconfig = CMS.getConfigStore();
         String rnewKeyInfo = req.getParameter(IRemoteRequest.TOKEN_NEW_KEYINFO);
@@ -1048,6 +1589,9 @@ public class TokenServlet extends CMSServlet {
             rKDD = rCUID;
         }
 
+        String rProtocol = req.getParameter(IRemoteRequest.CHANNEL_PROTOCOL);
+        String rWrappedDekKey = req.getParameter(IRemoteRequest.WRAPPED_DEK_SESSION_KEY);
+        int protocol = 1;
         String auditMessage = "";
 
         String keySet = req.getParameter(IRemoteRequest.TOKEN_KEYSET);
@@ -1066,13 +1610,13 @@ public class TokenServlet extends CMSServlet {
 
         // AC: KDF SPEC CHANGE:  Need to log both KDD and CUID
         auditMessage = CMS.getLogMessage(
-                        LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST,
-                        rCUID,
-                        rKDD,            // AC: KDF SPEC CHANGE - Log both CUID and KDD.
-                        ILogger.SUCCESS,
-                        agentId,
-                        oldMasterKeyName,
-                        newMasterKeyName);
+                LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST,
+                rCUID,
+                rKDD, // AC: KDF SPEC CHANGE - Log both CUID and KDD.
+                ILogger.SUCCESS,
+                agentId,
+                oldMasterKeyName,
+                newMasterKeyName);
 
         audit(auditMessage);
 
@@ -1081,14 +1625,14 @@ public class TokenServlet extends CMSServlet {
             CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: CUID");
             missingParam = true;
         }
-        
+
         // AC: KDF SPEC CHANGE - read new KDD parameter from TPS
         if ((rKDD == null) || (rKDD.length() == 0)) {
             CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: KDD");
             badParams += " KDD,";
             missingParam = true;
         }
-        
+
         if ((rnewKeyInfo == null) || (rnewKeyInfo.equals(""))) {
             badParams += " newKeyInfo,";
             CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: newKeyInfo");
@@ -1100,10 +1644,10 @@ public class TokenServlet extends CMSServlet {
             missingParam = true;
         }
 
-        // AC: KDF SPEC CHANGE 
-        xCUID = null;       // avoid errors about non-initialization
-        xKDD = null;        // avoid errors about non-initialization
-        xkeyInfo = null;    // avoid errors about non-initialization
+        // AC: KDF SPEC CHANGE
+        xCUID = null; // avoid errors about non-initialization
+        xKDD = null; // avoid errors about non-initialization
+        xkeyInfo = null; // avoid errors about non-initialization
         xnewkeyInfo = null; // avoid errors about non-initialization
 
         if (!missingParam) {
@@ -1119,6 +1663,29 @@ public class TokenServlet extends CMSServlet {
                 CMS.debug("TokenServlet: Invalid new key info length");
                 missingParam = true;
             }
+
+            if (rProtocol != null) {
+                try {
+                    protocol = Integer.parseInt(rProtocol);
+                } catch (NumberFormatException e) {
+                    protocol = 1;
+                }
+            }
+            CMS.debug("process DiversifyKey: protocol value: " + protocol);
+
+            if (protocol == 2) {
+
+                if ((rWrappedDekKey == null) || (rWrappedDekKey.equals(""))) {
+                    badParams += " WrappedDekKey,";
+                    CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: WrappedDekKey, with SCP02.");
+                    missingParam = true;
+                } else {
+
+                    CMS.debug("process DiversifyKey: wrappedDekKey value: " + rWrappedDekKey);
+                    xWrappedDekKey = com.netscape.cmsutil.util.Utils.SpecialDecode(rWrappedDekKey);
+                }
+
+            }
         }
         String useSoftToken_s = CMS.getConfigStore().getString("tks.useSoftToken", "true");
         if (!useSoftToken_s.equalsIgnoreCase("true"))
@@ -1142,36 +1709,39 @@ public class TokenServlet extends CMSServlet {
             }
         }
         if (!missingParam) {
-          // CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); // AC: KDF SPEC CHANGE: Removed duplicative variable/processing.
+            // CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); // AC: KDF SPEC CHANGE: Removed duplicative variable/processing.
+
+            // AC: KDF SPEC CHANGE - read new config file values (needed for symkey)
 
-          // AC: KDF SPEC CHANGE - read new config file values (needed for symkey)
-          try{
-              nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet);
-              nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet);
+            //ToDo: Refactor this, this same block occurs several times in the file
+            try {
+                nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet);
+                nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet);
 
-              // log settings read in to debug log along with xkeyInfo and xnewkeyInfo
-              CMS.debug("TokenServlet: processDiversifyKey():  xkeyInfo[0] (old) = 0x"
-                        + Integer.toHexString((int)(xkeyInfo[0]) & 0x0000000FF)
+                // log settings read in to debug log along with xkeyInfo and xnewkeyInfo
+                CMS.debug("TokenServlet: processDiversifyKey():  xkeyInfo[0] (old) = 0x"
+                        + Integer.toHexString((xkeyInfo[0]) & 0x0000000FF)
                         + ",  xkeyInfo[1] (old) = 0x"
-                        + Integer.toHexString((int)(xkeyInfo[1]) & 0x0000000FF)
+                        + Integer.toHexString((xkeyInfo[1]) & 0x0000000FF)
                         + ",  xnewkeyInfo[0] = 0x"
-                        + Integer.toHexString((int)(xnewkeyInfo[0]) & 0x000000FF)
+                        + Integer.toHexString((xnewkeyInfo[0]) & 0x000000FF)
                         + ",  xnewkeyInfo[1] = 0x"
-                        + Integer.toHexString((int)(xnewkeyInfo[1]) & 0x000000FF)
-                       );
-              CMS.debug("TokenServlet: processDiversifyKey():  Nist SP800-108 KDF will be used for key versions >= 0x"
-                        + Integer.toHexString((int)(nistSP800_108KdfOnKeyVersion) & 0x0000000FF)
-                       );
-              if (nistSP800_108KdfUseCuidAsKdd == true){
-                  CMS.debug("TokenServlet: processDiversifyKey():  Nist SP800-108 KDF (if used) will use CUID instead of KDD.");
-              }else{
-                  CMS.debug("TokenServlet: processDiversifyKey():  Nist SP800-108 KDF (if used) will use KDD.");
-              }
-          // conform to the set-an-error-flag mentality
-          }catch(Exception e){
-              missingSetting_exception = e;
-              CMS.debug("TokenServlet: processDiversifyKey():  Exception reading Nist SP800-108 KDF config values: " + e.toString());
-          }
+                        + Integer.toHexString((xnewkeyInfo[1]) & 0x000000FF)
+                        );
+                CMS.debug("TokenServlet: processDiversifyKey():  Nist SP800-108 KDF will be used for key versions >= 0x"
+                        + Integer.toHexString((nistSP800_108KdfOnKeyVersion) & 0x0000000FF)
+                        );
+                if (nistSP800_108KdfUseCuidAsKdd == true) {
+                    CMS.debug("TokenServlet: processDiversifyKey():  Nist SP800-108 KDF (if used) will use CUID instead of KDD.");
+                } else {
+                    CMS.debug("TokenServlet: processDiversifyKey():  Nist SP800-108 KDF (if used) will use KDD.");
+                }
+                // conform to the set-an-error-flag mentality
+            } catch (Exception e) {
+                missingSetting_exception = e;
+                CMS.debug("TokenServlet: processDiversifyKey():  Exception reading Nist SP800-108 KDF config values: "
+                        + e.toString());
+            }
 
             if (mKeyNickName != null)
                 oldMasterKeyName = mKeyNickName;
@@ -1208,55 +1778,53 @@ public class TokenServlet extends CMSServlet {
                     newKeyNickName);
 
             byte kekKeyArray[] =
-                com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key"));
-
-          // AC: KDF SPEC CHANGE - check for error reading settings
-          if (missingSetting_exception == null){
-            KeySetData = SessionKey.DiversifyKey(oldSelectedToken,
-                     newSelectedToken, oldKeyNickName,
-                     newKeyNickName,
-                     xkeyInfo,                        // AC: KDF SPEC CHANGE - pass in old key info so symkey can make decision about which KDF version to use
-                     xnewkeyInfo,                     // AC: BUGFIX for key versions higher than 09:  We need to specialDecode keyInfo parameters before sending them into symkey!  This means the parameters must be byte[]
-                     nistSP800_108KdfOnKeyVersion,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                     nistSP800_108KdfUseCuidAsKdd,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                     xCUID,                           // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
-                     xKDD,                            // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
-                     kekKeyArray, useSoftToken_s, keySet);
-
-            if (KeySetData == null || KeySetData.length <= 1) {
+                    com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key"));
+
+            // AC: KDF SPEC CHANGE - check for error reading settings
+            if (missingSetting_exception == null) {
+                KeySetData = SessionKey.DiversifyKey(oldSelectedToken,
+                        newSelectedToken, oldKeyNickName,
+                        newKeyNickName,
+                        xkeyInfo, // AC: KDF SPEC CHANGE - pass in old key info so symkey can make decision about which KDF version to use
+                        xnewkeyInfo, // AC: BUGFIX for key versions higher than 09:  We need to specialDecode keyInfo parameters before sending them into symkey!  This means the parameters must be byte[]
+                        nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+                        nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+                        xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+                        xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+                        (protocol == 2) ? xWrappedDekKey : kekKeyArray, useSoftToken_s, keySet, (byte) protocol);
+
+                if (KeySetData == null || KeySetData.length <= 1) {
+                    CMS.getLogger().log(ILogger.EV_AUDIT,
+                            ILogger.S_TKS,
+                            ILogger.LL_INFO, "process DiversifyKey: Missing MasterKey in Slot");
+                }
+
                 CMS.getLogger().log(ILogger.EV_AUDIT,
                         ILogger.S_TKS,
-                        ILogger.LL_INFO, "process DiversifyKey: Missing MasterKey in Slot");
-            }
-
-            CMS.getLogger().log(ILogger.EV_AUDIT,
-                    ILogger.S_TKS,
-                  ILogger.LL_INFO,
-                  "process DiversifyKey for CUID=" +
-                     trim(pp.toHexString(xCUID)) +  // AC: KDF SPEC CHANGE:  Log both CUID and KDD
-                  ", KDD=" +
-                     trim(pp.toHexString(xKDD))
-                            + ";from oldMasterKeyName=" + oldSelectedToken + ":" + oldKeyNickName
-                            + ";to newMasterKeyName=" + newSelectedToken + ":" + newKeyNickName);
+                        ILogger.LL_INFO,
+                        "process DiversifyKey for CUID=" +
+                                trim(pp.toHexString(xCUID)) + // AC: KDF SPEC CHANGE:  Log both CUID and KDD
+                                ", KDD=" +
+                                trim(pp.toHexString(xKDD))
+                                + ";from oldMasterKeyName=" + oldSelectedToken + ":" + oldKeyNickName
+                                + ";to newMasterKeyName=" + newSelectedToken + ":" + newKeyNickName);
 
-            resp.setContentType("text/html");
+                resp.setContentType("text/html");
 
-          } // AC: KDF SPEC CHANGE - endif no error reading settings from settings file
+            } // AC: KDF SPEC CHANGE - endif no error reading settings from settings file
 
         } // ! missingParam
 
-        //CMS.debug("TokenServlet:processDiversifyKey " +outputString);
-        //String value="keySetData=%00" if the KeySetData=byte[0]=0;
 
         String value = "";
         String status = "0";
 
         if (KeySetData != null && KeySetData.length > 1) {
-            value = IRemoteRequest.RESPONSE_STATUS+"=0&" + IRemoteRequest.TKS_RESPONSE_KeySetData+"=" +
-                     com.netscape.cmsutil.util.Utils.SpecialEncode(KeySetData);
+            value = IRemoteRequest.RESPONSE_STATUS + "=0&" + IRemoteRequest.TKS_RESPONSE_KeySetData + "=" +
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(KeySetData);
             CMS.debug("TokenServlet:process DiversifyKey.encode " + value);
-        // AC: KDF SPEC CHANGE - check for settings file issue (flag)
-        } else if (missingSetting_exception != null){
+            // AC: KDF SPEC CHANGE - check for settings file issue (flag)
+        } else if (missingSetting_exception != null) {
             status = "6";
             errorMsg = "Problem reading required configuration value.";
             value = "status=" + status;
@@ -1266,11 +1834,11 @@ public class TokenServlet extends CMSServlet {
                 badParams = badParams.substring(0, badParams.length() - 1);
             }
             errorMsg = "Missing input parameters: " + badParams;
-            value = IRemoteRequest.RESPONSE_STATUS+"=" + status;
+            value = IRemoteRequest.RESPONSE_STATUS + "=" + status;
         } else {
             errorMsg = "Problem diversifying key data.";
             status = "1";
-            value = IRemoteRequest.RESPONSE_STATUS+"=" + status;
+            value = IRemoteRequest.RESPONSE_STATUS + "=" + status;
         }
 
         resp.setContentLength(value.length());
@@ -1287,48 +1855,48 @@ public class TokenServlet extends CMSServlet {
 
         if (status.equals("0")) {
 
-                // AC: KDF SPEC CHANGE - Log both CUID and KDD
-                //                       Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
-                //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
-                String[] logParams = {  log_string_from_specialDecoded_byte_array(xCUID),                               // CUID_decoded
-                                        log_string_from_specialDecoded_byte_array(xKDD),                                // KDD_decoded
-                                        ILogger.SUCCESS,                                                                // Outcome
-                                        status,                                                                         // status
-                                        agentId,                                                                        // AgentID
-
-                                        // AC: BUGFIX:  Record the actual parameters to DiversifyKey in the audit log.
-                                        oldKeyNickName,                                                                 // oldMasterKeyName
-                                        newKeyNickName,                                                                 // newMasterKeyName
-
-                                        keySet,                                                                         // TKSKeyset
-                                        log_string_from_keyInfo(xkeyInfo),                                              // OldKeyInfo_KeyVersion
-                                        log_string_from_keyInfo(xnewkeyInfo),                                           // NewKeyInfo_KeyVersion
-                                        "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF),     // NistSP800_108KdfOnKeyVersion
-                                        Boolean.toString(nistSP800_108KdfUseCuidAsKdd)                                  // NistSP800_108KdfUseCuidAsKdd
-                                     };
-                auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, logParams);
+            // AC: KDF SPEC CHANGE - Log both CUID and KDD
+            //                       Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
+            //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
+            String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.SUCCESS, // Outcome
+                    status, // status
+                    agentId, // AgentID
+
+                    // AC: BUGFIX:  Record the actual parameters to DiversifyKey in the audit log.
+                    oldKeyNickName, // oldMasterKeyName
+                    newKeyNickName, // newMasterKeyName
+
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(xkeyInfo), // OldKeyInfo_KeyVersion
+                    log_string_from_keyInfo(xnewkeyInfo), // NewKeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd) // NistSP800_108KdfUseCuidAsKdd
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, logParams);
         } else {
-                // AC: KDF SPEC CHANGE - Log both CUID and KDD
-                //                       Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
-                //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
-                String[] logParams = {  log_string_from_specialDecoded_byte_array(xCUID),                               // CUID_decoded
-                                        log_string_from_specialDecoded_byte_array(xKDD),                                // KDD_decoded
-                                        ILogger.FAILURE,                                                                // Outcome
-                                        status,                                                                         // status
-                                        agentId,                                                                        // AgentID
-
-                                        // AC: BUGFIX:  Record the actual parameters to DiversifyKey in the audit log.
-                                        oldKeyNickName,                                                                 // oldMasterKeyName
-                                        newKeyNickName,                                                                 // newMasterKeyName
-
-                                        keySet,                                                                         // TKSKeyset
-                                        log_string_from_keyInfo(xkeyInfo),                                              // OldKeyInfo_KeyVersion
-                                        log_string_from_keyInfo(xnewkeyInfo),                                           // NewKeyInfo_KeyVersion
-                                        "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF),     // NistSP800_108KdfOnKeyVersion
-                                        Boolean.toString(nistSP800_108KdfUseCuidAsKdd),                                 // NistSP800_108KdfUseCuidAsKdd
-                                        errorMsg                                                                        // Error
-                                     };
-                auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE, logParams);
+            // AC: KDF SPEC CHANGE - Log both CUID and KDD
+            //                       Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
+            //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
+            String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.FAILURE, // Outcome
+                    status, // status
+                    agentId, // AgentID
+
+                    // AC: BUGFIX:  Record the actual parameters to DiversifyKey in the audit log.
+                    oldKeyNickName, // oldMasterKeyName
+                    newKeyNickName, // newMasterKeyName
+
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(xkeyInfo), // OldKeyInfo_KeyVersion
+                    log_string_from_keyInfo(xnewkeyInfo), // NewKeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd), // NistSP800_108KdfUseCuidAsKdd
+                    errorMsg // Error
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE, logParams);
         }
 
         audit(auditMessage);
@@ -1336,10 +1904,10 @@ public class TokenServlet extends CMSServlet {
 
     private void processEncryptData(HttpServletRequest req,
             HttpServletResponse resp) throws EBaseException {
-        byte[] keyInfo, xCUID, encryptedData, xkeyInfo, xKDD;  // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD
+        byte[] keyInfo, xCUID, encryptedData, xkeyInfo, xKDD; // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD
 
         // AC: KDF SPEC CHANGE - new config file values (needed for symkey)
-        byte nistSP800_108KdfOnKeyVersion = (byte)0xff;
+        byte nistSP800_108KdfOnKeyVersion = (byte) 0xff;
         boolean nistSP800_108KdfUseCuidAsKdd = false;
 
         // AC: KDF SPEC CHANGE - flag for if there is an error reading our new setting
@@ -1391,13 +1959,12 @@ public class TokenServlet extends CMSServlet {
 
         // AC: KDF SPEC CHANGE:  Need to log both KDD and CUID
         String auditMessage = CMS.getLogMessage(
-                       LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST,
-                       rCUID,
-                       rKDD,            // AC: KDF SPEC CHANGE - Log both CUID and KDD.
-                       ILogger.SUCCESS,
-                       agentId,
-                       s_isRandom);
-
+                LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST,
+                rCUID,
+                rKDD, // AC: KDF SPEC CHANGE - Log both CUID and KDD.
+                ILogger.SUCCESS,
+                agentId,
+                s_isRandom);
         audit(auditMessage);
 
         if (isRandom) {
@@ -1441,9 +2008,9 @@ public class TokenServlet extends CMSServlet {
         }
 
         // AC: KDF SPEC CHANGE
-        xCUID = null;       // avoid errors about non-initialization
-        xKDD = null;        // avoid errors about non-initialization
-        xkeyInfo = null;    // avoid errors about non-initialization
+        xCUID = null; // avoid errors about non-initialization
+        xKDD = null; // avoid errors about non-initialization
+        xkeyInfo = null; // avoid errors about non-initialization
 
         if (!missingParam) {
             xCUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID);
@@ -1478,28 +2045,29 @@ public class TokenServlet extends CMSServlet {
         if (!missingParam) {
 
             // AC: KDF SPEC CHANGE - read new config file values (needed for symkey
-            try{
+            try {
                 nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet);
                 nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet);
 
                 // log settings read in to debug log along with xkeyInfo
                 CMS.debug("TokenServlet: processEncryptData():  xkeyInfo[0] = 0x"
-                          + Integer.toHexString((int)(xkeyInfo[0]) & 0x0000000FF)
-                          + ",  xkeyInfo[1] = 0x"
-                          + Integer.toHexString((int)(xkeyInfo[1]) & 0x0000000FF)
-                         );
+                        + Integer.toHexString((xkeyInfo[0]) & 0x0000000FF)
+                        + ",  xkeyInfo[1] = 0x"
+                        + Integer.toHexString((xkeyInfo[1]) & 0x0000000FF)
+                        );
                 CMS.debug("TokenServlet: processEncryptData():  Nist SP800-108 KDF will be used for key versions >= 0x"
-                          + Integer.toHexString((int)(nistSP800_108KdfOnKeyVersion) & 0x0000000FF)
-                         );
-                if (nistSP800_108KdfUseCuidAsKdd == true){
+                        + Integer.toHexString((nistSP800_108KdfOnKeyVersion) & 0x0000000FF)
+                        );
+                if (nistSP800_108KdfUseCuidAsKdd == true) {
                     CMS.debug("TokenServlet: processEncryptData():  Nist SP800-108 KDF (if used) will use CUID instead of KDD.");
-                }else{
+                } else {
                     CMS.debug("TokenServlet: processEncryptData():  Nist SP800-108 KDF (if used) will use KDD.");
                 }
-            // conform to the set-an-error-flag mentality
-            }catch(Exception e){
+                // conform to the set-an-error-flag mentality
+            } catch (Exception e) {
                 missingSetting_exception = e;
-                CMS.debug("TokenServlet: processEncryptData():  Exception reading Nist SP800-108 KDF config values: " + e.toString());
+                CMS.debug("TokenServlet: processEncryptData():  Exception reading Nist SP800-108 KDF config values: "
+                        + e.toString());
             }
 
             if (!isRandom)
@@ -1519,29 +2087,27 @@ public class TokenServlet extends CMSServlet {
 
             byte kekKeyArray[] =
                     com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key"));
-          // AC: KDF SPEC CHANGE - check for error reading settings
-          if (missingSetting_exception == null){
-
-            encryptedData = SessionKey.EncryptData(
-                       selectedToken,keyNickName,data,keyInfo,
-                       nistSP800_108KdfOnKeyVersion,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                       nistSP800_108KdfUseCuidAsKdd,    // AC: KDF SPEC CHANGE - pass in configuration file value
-                       xCUID,                           // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
-                       xKDD,                            // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
-                       kekKeyArray, useSoftToken_s, keySet);
+            // AC: KDF SPEC CHANGE - check for error reading settings
+            if (missingSetting_exception == null) {
 
-          // AC: KDF SPEC CHANGE - Log both CUID and KDD
+                encryptedData = SessionKey.EncryptData(
+                        selectedToken, keyNickName, data, keyInfo,
+                        nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value
+                        nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value
+                        xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID'
+                        xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
+                        kekKeyArray, useSoftToken_s, keySet);
 
+                // AC: KDF SPEC CHANGE - Log both CUID and KDD
 
-            CMS.getLogger().log(ILogger.EV_AUDIT,
-                     ILogger.S_TKS,
-                     ILogger.LL_INFO,"process EncryptData for CUID="+
-                       trim(pp.toHexString(xCUID)) +
-                       ", KDD=" +
-                       trim(pp.toHexString(xKDD)));
-
-          } // AC: KDF SPEC CHANGE - endif no error reading settings from settings file
+                CMS.getLogger().log(ILogger.EV_AUDIT,
+                        ILogger.S_TKS,
+                        ILogger.LL_INFO, "process EncryptData for CUID=" +
+                                trim(pp.toHexString(xCUID)) +
+                                ", KDD=" +
+                                trim(pp.toHexString(xKDD)));
 
+            } // AC: KDF SPEC CHANGE - endif no error reading settings from settings file
 
         } // !missingParam
 
@@ -1551,13 +2117,13 @@ public class TokenServlet extends CMSServlet {
         String status = "0";
         if (encryptedData != null && encryptedData.length > 0) {
             // sending both the pre-encrypted and encrypted data back
-            value = IRemoteRequest.RESPONSE_STATUS+"=0&"
-                    + IRemoteRequest.TOKEN_DATA+"=" +
-                         com.netscape.cmsutil.util.Utils.SpecialEncode(data) +
-                    "&"+IRemoteRequest.TKS_RESPONSE_EncryptedData+"=" +
-                         com.netscape.cmsutil.util.Utils.SpecialEncode(encryptedData);
-        // AC: KDF SPEC CHANGE - check for settings file issue (flag)
-        } else if (missingSetting_exception != null){
+            value = IRemoteRequest.RESPONSE_STATUS + "=0&"
+                    + IRemoteRequest.TOKEN_DATA + "=" +
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(data) +
+                    "&" + IRemoteRequest.TKS_RESPONSE_EncryptedData + "=" +
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(encryptedData);
+            // AC: KDF SPEC CHANGE - check for settings file issue (flag)
+        } else if (missingSetting_exception != null) {
             status = "6";
             errorMsg = "Problem reading required configuration value.";
             value = "status=" + status;
@@ -1567,11 +2133,11 @@ public class TokenServlet extends CMSServlet {
             }
             errorMsg = "Missing input parameters: " + badParams;
             status = "3";
-            value = IRemoteRequest.RESPONSE_STATUS+"=" + status;
+            value = IRemoteRequest.RESPONSE_STATUS + "=" + status;
         } else {
             errorMsg = "Problem encrypting data.";
             status = "1";
-            value = IRemoteRequest.RESPONSE_STATUS+"=" + status;
+            value = IRemoteRequest.RESPONSE_STATUS + "=" + status;
         }
 
         CMS.debug("TokenServlet:process EncryptData.encode " + value);
@@ -1589,42 +2155,42 @@ public class TokenServlet extends CMSServlet {
         }
 
         if (status.equals("0")) {
-                // AC: KDF SPEC CHANGE - Log both CUID and KDD
-                //                       Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
-                //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
-                String[] logParams = {  log_string_from_specialDecoded_byte_array(xCUID),                               // CUID_decoded
-                                        log_string_from_specialDecoded_byte_array(xKDD),                                // KDD_decoded
-                                        ILogger.SUCCESS,                                                                // Outcome
-                                        status,                                                                         // status
-                                        agentId,                                                                        // AgentID
-                                        s_isRandom,                                                                     // isRandom
-                                        selectedToken,                                                                  // SelectedToken
-                                        keyNickName,                                                                    // KeyNickName
-                                        keySet,                                                                         // TKSKeyset
-                                        log_string_from_keyInfo(xkeyInfo),                                              // KeyInfo_KeyVersion
-                                        "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF),     // NistSP800_108KdfOnKeyVersion
-                                        Boolean.toString(nistSP800_108KdfUseCuidAsKdd)                                  // NistSP800_108KdfUseCuidAsKdd
-                                     };
-                auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS, logParams);
+            // AC: KDF SPEC CHANGE - Log both CUID and KDD
+            //                       Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
+            //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
+            String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.SUCCESS, // Outcome
+                    status, // status
+                    agentId, // AgentID
+                    s_isRandom, // isRandom
+                    selectedToken, // SelectedToken
+                    keyNickName, // KeyNickName
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd) // NistSP800_108KdfUseCuidAsKdd
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS, logParams);
         } else {
-                // AC: KDF SPEC CHANGE - Log both CUID and KDD
-                //                       Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
-                //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
-                String[] logParams = {  log_string_from_specialDecoded_byte_array(xCUID),                               // CUID_decoded
-                                        log_string_from_specialDecoded_byte_array(xKDD),                                // KDD_decoded
-                                        ILogger.FAILURE,                                                                // Outcome
-                                        status,                                                                         // status
-                                        agentId,                                                                        // AgentID
-                                        s_isRandom,                                                                     // isRandom
-                                        selectedToken,                                                                  // SelectedToken
-                                        keyNickName,                                                                    // KeyNickName
-                                        keySet,                                                                         // TKSKeyset
-                                        log_string_from_keyInfo(xkeyInfo),                                              // KeyInfo_KeyVersion
-                                        "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF),     // NistSP800_108KdfOnKeyVersion
-                                        Boolean.toString(nistSP800_108KdfUseCuidAsKdd),                                 // NistSP800_108KdfUseCuidAsKdd
-                                        errorMsg                                                                        // Error
-                                     };
-                auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE, logParams);
+            // AC: KDF SPEC CHANGE - Log both CUID and KDD
+            //                       Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd
+            //                       Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available.
+            String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded
+                    log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded
+                    ILogger.FAILURE, // Outcome
+                    status, // status
+                    agentId, // AgentID
+                    s_isRandom, // isRandom
+                    selectedToken, // SelectedToken
+                    keyNickName, // KeyNickName
+                    keySet, // TKSKeyset
+                    log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion
+                    "0x" + Integer.toHexString(nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion
+                    Boolean.toString(nistSP800_108KdfUseCuidAsKdd), // NistSP800_108KdfUseCuidAsKdd
+                    errorMsg // Error
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE, logParams);
         }
 
         audit(auditMessage);
@@ -1687,9 +2253,9 @@ public class TokenServlet extends CMSServlet {
         CMS.debug("TokenServlet::processComputeRandomData data size requested: " + dataSize);
 
         String auditMessage = CMS.getLogMessage(
-                       LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST,
-                       ILogger.SUCCESS,
-                       agentId);
+                LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST,
+                ILogger.SUCCESS,
+                agentId);
 
         audit(auditMessage);
 
@@ -1727,9 +2293,9 @@ public class TokenServlet extends CMSServlet {
         resp.setContentType("text/html");
         String value = "";
 
-        value = IRemoteRequest.RESPONSE_STATUS+"=" + status;
+        value = IRemoteRequest.RESPONSE_STATUS + "=" + status;
         if (status.equals("0")) {
-            value = value + "&"+IRemoteRequest.TKS_RESPONSE_RandomData+"=" + randomDataOut;
+            value = value + "&" + IRemoteRequest.TKS_RESPONSE_RandomData + "=" + randomDataOut;
         }
 
         try {
@@ -1746,17 +2312,17 @@ public class TokenServlet extends CMSServlet {
 
         if (status.equals("0")) {
             auditMessage = CMS.getLogMessage(
-                        LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,
-                        ILogger.SUCCESS,
-                        status,
-                        agentId);
+                    LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,
+                    ILogger.SUCCESS,
+                    status,
+                    agentId);
         } else {
             auditMessage = CMS.getLogMessage(
-                        LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,
-                        ILogger.FAILURE,
-                        status,
-                        agentId,
-                        errorMsg);
+                    LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,
+                    ILogger.FAILURE,
+                    status,
+                    agentId,
+                    errorMsg);
         }
 
         audit(auditMessage);
@@ -1771,7 +2337,7 @@ public class TokenServlet extends CMSServlet {
 
         try {
             authzToken = authorize(mAclMethod, authToken,
-                        mAuthzResourceName, "execute");
+                    mAuthzResourceName, "execute");
         } catch (Exception e) {
         }
 
@@ -1796,6 +2362,10 @@ public class TokenServlet extends CMSServlet {
         }
 
         String temp = req.getParameter(IRemoteRequest.TOKEN_CARD_CHALLENGE);
+        String protocol = req.getParameter(IRemoteRequest.CHANNEL_PROTOCOL);
+        String derivationConstant = req.getParameter(IRemoteRequest.DERIVATION_CONSTANT);
+        CMS.debug("Protocol: " + protocol + " temp: " + temp);
+
         setDefaultSlotAndKeyName(req);
         if (temp != null) {
             processComputeSessionKey(req, resp);
@@ -1805,6 +2375,12 @@ public class TokenServlet extends CMSServlet {
             processDiversifyKey(req, resp);
         } else if (req.getParameter(IRemoteRequest.TOKEN_DATA_NUM_BYTES) != null) {
             processComputeRandomData(req, resp);
+        } else if (protocol != null && protocol.contains("2") && (derivationConstant != null)) {
+            //SCP02 compute one session key.
+            processComputeSessionKeySCP02(req, resp);
+        } else {
+            throw new EBaseException("Process: Can't decide upon function to call!");
+
         }
     }
 
@@ -1818,4 +2394,54 @@ public class TokenServlet extends CMSServlet {
             throws ServletException, IOException {
         super.service(req, resp);
     }
+
+    private PK11SymKey getSharedSecretKey() throws EBaseException, NotInitializedException {
+
+        IConfigStore configStore = CMS.getConfigStore();
+        String sharedSecretName = null;
+        try {
+
+            sharedSecretName = getSharedSecretName(configStore);
+
+        } catch (EBaseException e) {
+            throw new EBaseException("TokenServlet.getSharedSecetKey: Internal error finding config value: "
+                    + e);
+
+        }
+
+        CMS.debug("TokenServlet.getSharedSecretTransportKey: calculated key name: " + sharedSecretName);
+
+        String symmKeys = null;
+        boolean keyPresent = false;
+        try {
+            symmKeys = SessionKey.ListSymmetricKeys("internal");
+            CMS.debug("TokenServlet.getSharedSecretTransportKey: symmKeys List: " + symmKeys);
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            CMS.debug(e);
+        }
+
+        for (String keyName : symmKeys.split(",")) {
+            if (sharedSecretName.equals(keyName)) {
+                CMS.debug("TokenServlet.getSharedSecret: shared secret key found!");
+                keyPresent = true;
+                break;
+            }
+
+        }
+
+        if (!keyPresent) {
+            throw new EBaseException("TokenServlet.getSharedSecret: Can't find shared secret!");
+        }
+
+        // We know for now that shared secret is on this token
+        String tokenName = "Internal Key Storage Token";
+        PK11SymKey sharedSecret = SessionKey.GetSymKeyByName(tokenName, sharedSecretName);
+
+        CMS.debug("TokenServlet.getSharedSecret: SymKey returns: " + sharedSecret);
+
+        return sharedSecret;
+
+    }
+
 }
diff --git a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
index a383a4b..ebaf126 100644
--- a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
+++ b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
@@ -34,6 +34,9 @@ public interface IRemoteRequest {
     public static final String RESPONSE_STATUS_XML = "Status";
 
     // TKS request params
+    public static final String CHANNEL_PROTOCOL = "protocol";
+    public static final String SEQUENCE_COUNTER = "sequenceCounter";
+    public static final String DERIVATION_CONSTANT = "derivationConstant";
     public static final String SERVER_SIDE_KEYGEN = "serversideKeygen";
     public static final String TOKEN_CARD_CHALLENGE = "card_challenge";
     public static final String TOKEN_HOST_CHALLENGE = "host_challenge";
@@ -43,6 +46,7 @@ public interface IRemoteRequest {
     public static final String TOKEN_DATA_NUM_BYTES = "dataNumBytes";
     public static final String TOKEN_NEW_KEYINFO = "newKeyInfo";
     public static final String TOKEN_DATA = "data";
+    public static final String WRAPPED_DEK_SESSION_KEY = "wrappedDekKey";
 
     // TKS response params
     /* computeSessionKey responses */
diff --git a/base/symkey/src/com/netscape/symkey/Buffer.cpp b/base/symkey/src/com/netscape/symkey/Buffer.cpp
index 5c687c5..6241534 100644
--- a/base/symkey/src/com/netscape/symkey/Buffer.cpp
+++ b/base/symkey/src/com/netscape/symkey/Buffer.cpp
@@ -171,10 +171,10 @@ Buffer::dump() const
     unsigned int i;
 
     for( i=0; i < len; ++i ) {
-        printf("%02x ", buf[i]);
-        if( i % 16 == 15 )  printf("\n");
+        PR_fprintf(PR_STDOUT,"%02x ", buf[i]);
+        if( i % 16 == 15 )  PR_fprintf(PR_STDOUT,"\n");
     }
-    printf("\n");
+    PR_fprintf(PR_STDOUT,"\n");
 }
 
 static const char hextbl[] = {
diff --git a/base/symkey/src/com/netscape/symkey/EncryptData.cpp b/base/symkey/src/com/netscape/symkey/EncryptData.cpp
index 3963b50..cebc0bb 100644
--- a/base/symkey/src/com/netscape/symkey/EncryptData.cpp
+++ b/base/symkey/src/com/netscape/symkey/EncryptData.cpp
@@ -219,7 +219,6 @@ Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstr
             if (masterKey != NULL)
             {
 
-
                 // ---------------------------------
                 // AC KDF SPEC CHANGE: Determine which KDF to use.
                 //
@@ -279,11 +278,9 @@ Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstr
                     GetDiversificationData(kddValue,kekData,kek);
 
                     // AC: Derives the Kek key for the token.
-                    kekKey = ComputeCardKeyOnToken(masterKey,kekData);
+                    kekKey = ComputeCardKeyOnToken(masterKey,kekData,1);
 
                 } // endif use original KDF
-                // ---------------------------------
-
 
                 if (kekKey != NULL)
                 {
diff --git a/base/symkey/src/com/netscape/symkey/SessionKey.cpp b/base/symkey/src/com/netscape/symkey/SessionKey.cpp
index 2c14673..6109280 100644
--- a/base/symkey/src/com/netscape/symkey/SessionKey.cpp
+++ b/base/symkey/src/com/netscape/symkey/SessionKey.cpp
@@ -349,6 +349,101 @@ JSS_PK11_getSymKeyPtr(JNIEnv *env, jobject symKeyObject, PK11SymKey **ptr)
         SYM_KEY_PROXY_SIG, (void**)ptr);
 }
 #endif                                            //STEAL_JSS
+
+
+/* ToDo: fully support nistSP800 in next ticket
+*/
+PK11SymKey *DeriveKeySCP02(PK11SymKey *cardKey, const Buffer& sequenceCounter, const Buffer& derivationConstant)
+{
+
+    PK11SymKey *key = NULL;
+    PK11SymKey *master = NULL;
+    unsigned char message[KEYLENGTH] = {0};
+    unsigned char derivationData[DES3_LENGTH] = {0};
+
+    PRBool invalid_mechanism = PR_TRUE;
+    SECStatus s = SECFailure;
+    int len = 0;
+    int i = 0;
+
+    SECItem *secParam = NULL;
+
+    PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+    PK11Context *context = NULL;
+     SECItem param = { siBuffer, NULL, 0 };
+
+    unsigned char icv[EIGHT_BYTES] = { 0 };
+
+    if( sequenceCounter == NULL || derivationConstant == NULL || 
+        sequenceCounter.size() != 2 || derivationConstant.size() != 2 || 
+        cardKey == NULL) {
+         PR_fprintf(PR_STDERR,"In DeriveKeySCP02!  Error invalid input data!\n");
+         goto done;
+    }
+
+    PR_fprintf(PR_STDOUT,"In DeriveKeySCP02! \n");
+    PR_fprintf(PR_STDOUT,"In DeriveKeySCP02! seqCounter[0] : %d sequenceCounter [1] : %d \n", sequenceCounter[0], sequenceCounter[1]);
+    PR_fprintf(PR_STDOUT,"In DeriveKeySCP02! derivationConstant[0] : %x derivationConstant[1] : %x \n", derivationConstant[0], derivationConstant[1]);
+
+    master = cardKey;
+
+    message[0] = (unsigned char) derivationConstant[0];
+    message[1] = (unsigned char) derivationConstant[1];
+    message[2] = (unsigned char) sequenceCounter[0];
+    message[3] = (unsigned char) sequenceCounter[1];
+
+
+    //ToDo use the new NSS provided derive mechanisms for this operation
+    if(invalid_mechanism == PR_FALSE) {
+       // Use derive mechanisms
+    } else {
+
+        //Use encryption method
+        param.data = (unsigned char *) &icv;
+        param.len = 8;
+        secParam = PK11_ParamFromIV(CKM_DES3_CBC_PAD, &param);
+        context = PK11_CreateContextBySymKey(CKM_DES3_CBC_PAD, CKA_ENCRYPT, master, secParam);
+        if(context == NULL) {
+            goto done;
+        }
+         s = PK11_CipherOp(context,&derivationData[0] , &len, EIGHT_BYTES, &message[0], EIGHT_BYTES);
+
+         if (s != SECSuccess) { goto done; }
+        
+         s = PK11_CipherOp(context, &derivationData[EIGHT_BYTES], &len, EIGHT_BYTES, &message[EIGHT_BYTES], EIGHT_BYTES); 
+         if (s != SECSuccess) { goto done; } 
+
+         for(i = 0;i < EIGHT_BYTES ;i++)
+         {
+             derivationData[i+KEYLENGTH] = derivationData[i];
+         }
+
+         key = CreateUnWrappedSymKeyOnToken( slot,  master, &derivationData[0] , DES3_LENGTH, PR_FALSE );
+
+          PR_fprintf(PR_STDOUT,"In DeriveKeySCP02! calculated key: %p  \n", key);
+    }
+
+    done:
+
+    memset(derivationData, 0, sizeof derivationData);
+    if ( context != NULL) {
+        PK11_DestroyContext(context, PR_TRUE);
+        context = NULL;
+    }
+
+    if (slot) {
+        PK11_FreeSlot(slot);
+        slot = NULL;
+    }
+
+    if (secParam) {
+        SECITEM_FreeItem(secParam, PR_TRUE);
+        secParam = NULL;
+    }
+
+    return key;
+}
+
 // Function takes wither a symkey OR a keybuffer (for the default keyset case)
 // To derive a new key.
 PK11SymKey *DeriveKey(PK11SymKey *cardKey, const Buffer& hostChallenge, const Buffer& cardChallenge)
@@ -567,6 +662,369 @@ finish:
 }
 
 
+//ToDo: Fix this to conform the nistSP800
+//=================================================================================
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/*
+ * Class:     com_netscape_cms_servlet_tks_RASessionKey
+ * Method:    ComputeSessionKeySCP02
+ * Signature: ([B[B[B[B)[B
+ */
+    JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeSessionKeySCP02
+        (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jstring, jstring, jstring);
+#ifdef __cplusplus
+}
+#endif
+#define KEYLENGTH 16
+extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeSessionKeySCP02(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray keyInfo, jbyteArray CUID, jbyteArray devKeyArray, jbyteArray sequenceCounter, jbyteArray derivationConstant, jstring useSoftToken_s, jstring keySet, jstring sharedSecretKeyName)
+{
+    /* hardcode permanent dev key */
+    jbyte *dev_key = NULL;
+    if (devKeyArray != NULL) {
+       dev_key = (jbyte*)(env)->GetByteArrayElements(devKeyArray, NULL);
+    } else {
+        return NULL;
+    }
+
+    SECItem wrappedKeyItem = { siBuffer, NULL , 0};
+    SECItem noParams = { siBuffer, NULL, 0 };
+    SECStatus wrapStatus = SECFailure;
+
+
+    char *keyNameChars=NULL;
+    char *tokenNameChars=NULL;
+    PK11SlotInfo *slot = NULL;
+    PK11SlotInfo *internal = PK11_GetInternalKeySlot();
+
+    PK11SymKey *symkey = NULL;
+    PK11SymKey *transportKey = NULL;
+    PK11SymKey *masterKey = NULL;
+
+    PK11SymKey *devSymKey = NULL;
+    PK11SymKey *symkey16 = NULL;
+    PK11SymKey *devKey = NULL;
+
+
+    BYTE devData[KEYLENGTH];
+    char keyname[KEYNAMELENGTH];
+    
+    const char *devKeyName = NULL;
+
+    const char *macName = "mac";
+    const char *encName = "enc";
+    const char *kekName = "kek";
+
+    keyType kType = mac;
+
+    /* Derive vars */
+
+    CK_ULONG bitPosition = 0;
+    SECItem paramsItem = { siBuffer, NULL, 0 };
+
+    /* Java object return vars */
+
+    jbyteArray handleBA=NULL;
+    jbyte *handleBytes=NULL;
+
+    jbyte *    cuidValue = NULL;
+
+    jbyte *sc = NULL;
+    int sc_len = 0;
+
+    int dc_len = 0;
+    jbyte *dc = NULL;
+
+    jbyte *    keyVersion = NULL;
+    int keyVersion_len = 0;
+
+    Buffer devBuff( ( BYTE *) dev_key , KEYLENGTH );
+
+    char *keySetStringChars = NULL;
+    if( keySet != NULL ) {
+       keySetStringChars = (char *) (env)->GetStringUTFChars( keySet, NULL);
+    }
+
+    char *keySetString =  keySetStringChars;
+
+    if ( keySetString == NULL ) {
+        keySetString = (char *) DEFKEYSET_NAME;
+    }
+
+    char *sharedSecretKeyNameChars =  NULL;
+
+    if( sharedSecretKeyName != NULL ) {
+        sharedSecretKeyNameChars = (char *) (env)->GetStringUTFChars( sharedSecretKeyName, NULL);
+    }
+
+    char *sharedSecretKeyNameString = sharedSecretKeyNameChars;
+
+    if ( sharedSecretKeyNameString == NULL ) {
+        sharedSecretKeyNameString = (char *) TRANSPORT_KEY_NAME;
+    }
+
+    GetSharedSecretKeyName(sharedSecretKeyNameString);
+
+    if( sequenceCounter != NULL) {
+        sc = (jbyte*)(env)->GetByteArrayElements( sequenceCounter, NULL);
+        sc_len =  (env)->GetArrayLength(sequenceCounter);
+    } 
+
+    if( sc == NULL || sc_len != 2) {
+        goto done;
+    } 
+
+    if( derivationConstant != NULL) {
+        dc = (jbyte*)(env)->GetByteArrayElements( derivationConstant, NULL);
+        dc_len = (env)->GetArrayLength( derivationConstant);
+    }
+ 
+    if( dc == NULL || dc_len != 2) {
+        goto done;
+    }
+ 
+    if( keyInfo != NULL) { 
+      keyVersion = (jbyte*)(env)->GetByteArrayElements( keyInfo, NULL);
+
+      if( keyVersion) {
+          keyVersion_len =  (env)->GetArrayLength(keyInfo);
+      }
+    }
+
+    if( !keyVersion || (keyVersion_len < 2) ){
+        goto done;
+    }
+
+    if ( CUID != NULL ) {
+        cuidValue =  (jbyte*)(env)->GetByteArrayElements( CUID, NULL);
+    }
+
+    if( cuidValue == NULL) {
+        goto done;
+    }
+
+    if(tokenName)
+    {
+        tokenNameChars = (char *)(env)->GetStringUTFChars(tokenName, NULL);
+        slot = ReturnSlot(tokenNameChars);
+        (env)->ReleaseStringUTFChars(tokenName, (const char *)tokenNameChars);
+    }
+
+    if(keyName)
+    {
+        keyNameChars = (char *)(env)->GetStringUTFChars(keyName, NULL);
+        strncpy(keyname,keyNameChars,KEYNAMELENGTH);
+        (env)->ReleaseStringUTFChars(keyName, (const char *)keyNameChars);
+    }else
+        GetKeyName(keyVersion,keyname);
+
+    //Get  key type from derivation constant
+    switch((unsigned char) dc[1]) {
+       case 0x1 :
+           kType = mac;
+           devKeyName = macName;
+       break;
+
+       case 0x82:
+           kType = enc;
+           devKeyName = encName;
+       break;
+
+       case 0x81:
+           kType = kek;
+           devKeyName = kekName;
+       break;
+
+       default:
+          kType = mac;
+          devKeyName = macName;
+      break;
+    }
+
+    GetDiversificationData(cuidValue,devData,kType);
+
+    PR_fprintf(PR_STDOUT,"In SessionKey.ComputeSessionKeySCP02! keyName %s keyVersion[0] %d keyVersion[1] %d \n",keyname,(int) keyVersion[0],(int) keyVersion[1]);
+
+    if ( (keyVersion[0] == 0x1 && keyVersion[1]== 0x1 ) ||
+        (keyVersion[0] == -1 && keyVersion[1] == 0x1))
+     
+    {
+        /* default manufacturers key */
+
+        devSymKey = ReturnDeveloperSymKey(slot, (char *) devKeyName , keySetString, devBuff);
+
+        if( devSymKey == NULL ) {
+            goto done;
+        }
+
+        PR_fprintf(PR_STDOUT,"SessionKey.ComputeSessionKeySCP02! sc[0] : %d sc[1] : %d \n", sc[0], sc[1]);
+        symkey = DeriveKeySCP02(                       //Util::DeriveKey(
+            devSymKey, Buffer((BYTE*)sc, sc_len), Buffer((BYTE*)dc, dc_len));
+
+        if(symkey == NULL) 
+        {
+            goto done;
+        }
+
+        //In the enc key case create the auth as well, we may need it later.
+
+        if(kType == enc) {
+
+            PK11SymKey *authKey = NULL;
+
+            authKey = ReturnDeveloperSymKey(slot, (char *) "auth" , keySetString, devBuff);
+     
+            if(authKey == NULL)
+            {
+               goto done;
+            }
+
+            PK11_FreeSymKey(authKey);
+            authKey = NULL; 
+
+        }
+
+    }else
+    {
+        PR_fprintf(PR_STDOUT,"SessionKey.ComputeSessionKeySCP02! Attempting with master key. \n");
+        masterKey = ReturnSymKey( slot,keyname);
+        if(masterKey == NULL)
+        {
+            goto done;
+        }
+
+        devKey =ComputeCardKeyOnToken(masterKey,devData,2);
+        if(devKey == NULL)
+        {
+            goto done;
+        }
+         
+        symkey = DeriveKeySCP02(devKey, Buffer((BYTE*)sc, sc_len), Buffer((BYTE*)dc, dc_len));
+
+        if(symkey == NULL)
+        {
+            goto done;
+        }
+    }
+    //Now wrap the key for the trip back to TPS with shared secret transport key
+
+    symkey16 = NULL;
+     transportKey = ReturnSymKey( internal, GetSharedSecretKeyName(NULL));
+    if ( transportKey == NULL ) {
+        PR_fprintf(PR_STDERR, "Can't find shared secret transport key! \n");
+        goto done;
+    }
+
+    handleBA = (env)->NewByteArray( KEYLENGTH);
+    handleBytes = (env)->GetByteArrayElements(handleBA, NULL);
+
+    paramsItem.data = (CK_BYTE *) &bitPosition;
+    paramsItem.len = sizeof bitPosition;
+
+    symkey16 = PK11_Derive(symkey, CKM_EXTRACT_KEY_FROM_KEY, &paramsItem, CKA_ENCRYPT,
+                                                            CKA_DERIVE, 16);
+    if ( !symkey16 ) {
+        PR_fprintf(PR_STDERR,"Can't derive 16 byte key from 24 byte symkey! \n");
+        goto done;
+    }
+
+    wrappedKeyItem.data = (unsigned char *) handleBytes;
+    wrappedKeyItem.len  =  KEYLENGTH;
+    wrapStatus = PK11_WrapSymKey(CKM_DES3_ECB,&noParams, transportKey, symkey16, &wrappedKeyItem);
+
+    if(wrapStatus == SECFailure )
+    {
+        PR_fprintf(PR_STDERR, "Can't wrap session key! Error: %d \n", PR_GetError());
+    }
+
+done:
+
+    if( slot) {
+        PK11_FreeSlot(slot);
+        slot = NULL;
+    }
+
+    if( internal ) {
+        PK11_FreeSlot(internal);
+        internal = NULL;
+    }
+
+    if ( symkey ) {
+        PK11_FreeSymKey( symkey);
+        symkey = NULL;
+    }
+
+    if ( transportKey )  {
+        PK11_FreeSymKey( transportKey );
+        transportKey = NULL;
+    }
+
+    if ( symkey16 ) {
+        PK11_FreeSymKey( symkey16 );
+        symkey16 = NULL;
+    }
+
+    if( masterKey ) {
+        PK11_FreeSymKey( masterKey);
+        masterKey = NULL;
+    }
+           
+    if( devKey ) {
+        PK11_FreeSymKey( devKey);
+        devKey = NULL;
+    }
+
+    if( devSymKey ) {
+        PK11_FreeSymKey( devSymKey );
+        devSymKey = NULL;
+    }
+
+    if( keySetStringChars ) {
+        (env)->ReleaseStringUTFChars(keySet, (const char *)keySetStringChars);
+        keySetStringChars = NULL;
+    }
+
+    if( sharedSecretKeyNameChars ) {
+        (env)->ReleaseStringUTFChars(sharedSecretKeyName, (const char *)sharedSecretKeyNameChars);
+        sharedSecretKeyNameChars = NULL;
+    }
+
+    if ( handleBytes != NULL) {
+        (env)->ReleaseByteArrayElements( handleBA, handleBytes, 0);
+    }
+
+    if ( sc != NULL) {
+        (env)->ReleaseByteArrayElements(sequenceCounter, sc, JNI_ABORT);
+    }
+
+    if ( dc != NULL) {
+        (env)->ReleaseByteArrayElements(derivationConstant, dc, JNI_ABORT);
+    }
+
+    if( keyVersion != NULL) {
+        (env)->ReleaseByteArrayElements(keyInfo, keyVersion, JNI_ABORT);
+    }
+
+    if ( cuidValue != NULL) {
+        (env)->ReleaseByteArrayElements(CUID, cuidValue, JNI_ABORT);
+    }
+
+    if( dev_key != NULL) {
+        (env)->ReleaseByteArrayElements(devKeyArray, dev_key, JNI_ABORT);
+    }
+
+    if (wrapStatus != SECFailure ){
+        return handleBA;
+    }else{
+        return NULL;
+    }
+
+}
+
+
+
 //=================================================================================
 #ifdef __cplusplus
 extern "C"
@@ -595,7 +1053,7 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
         return NULL;
     }
 
-    char input[KEYLENGTH];
+    unsigned char input[KEYLENGTH] = {0};
     int i = 0;
 
     SECItem wrappedKeyItem = { siBuffer, NULL , 0};
@@ -762,7 +1220,7 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
     }else
     GetKeyName(keyVersion,keyname);
 
-    PR_fprintf(PR_STDOUT,"In SessionKey.ComputeSessionKey! \n");
+    PR_fprintf(PR_STDOUT,"In SessionKey.ComputeSessionKey! keyName %s keyVersion[0] %d keyVersion[1] %d \n",keyname,(int) keyVersion[0],(int) keyVersion[1]);
 
     if ( (keyVersion[0] == 0x1 && keyVersion[1]== 0x1 && strcmp( keyname, "#01#01") == 0) ||
         (keyVersion[0] == -1 && strstr(keyname, "#FF")))
@@ -781,13 +1239,13 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
 
     }else
     {
+        PR_fprintf(PR_STDOUT,"In SessionKey.ComputeSessionKey! Upgraded keyset mode. \n");
         masterKey = ReturnSymKey( slot,keyname);
         if(masterKey == NULL)
         {
             goto done;
         }
 
-
         // ---------------------------------
         // AC KDF SPEC CHANGE: Determine which KDF to use.
         //
@@ -847,17 +1305,12 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
             GetDiversificationData(kddValue,macData,mac);//keytype is mac
 
             // AC: Derives the mac key for the token.
-            macKey =ComputeCardKeyOnToken(masterKey,macData);
+            macKey =ComputeCardKeyOnToken(masterKey,macData,1);
 
         } // endif use original KDF
         // ---------------------------------
 
 
-        if(macKey == NULL)
-        {
-            goto done;
-        }
-
         // AC: This computes the GP session key using the card-specific MAC key we previously derived.
         symkey = DeriveKey(macKey, Buffer((BYTE*)hc, hc_len), Buffer((BYTE*)cc, cc_len));
 
@@ -1030,7 +1483,7 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
         return NULL;
     }
 
-    char input[KEYLENGTH];
+    unsigned char input[KEYLENGTH] = {0};
     int i = 0;
 
     SECItem wrappedKeyItem = { siBuffer, NULL , 0};
@@ -1205,7 +1658,6 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
             goto done;
         }
 
-
         // ---------------------------------
         // AC KDF SPEC CHANGE: Determine which KDF to use.
         //
@@ -1265,12 +1717,11 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
             GetDiversificationData(kddValue,encData,enc);
 
             // AC: Derives the enc key for the token.
-            encKey =ComputeCardKeyOnToken(masterKey,encData);
+            encKey =ComputeCardKeyOnToken(masterKey,encData,1);
 
         } // endif use original KDF
         // ---------------------------------
 
-
         if(encKey == NULL) {
             goto done;
         }
@@ -1446,7 +1897,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_Compute
         keySetString = (char *) DEFKEYSET_NAME;
     }
 
-    char input[KEYLENGTH];
+    unsigned char input[KEYLENGTH] = {0};
     int i;
     jobject keyObj = NULL;
 
@@ -1575,7 +2026,6 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_Compute
             goto done;
         }
 
-
         // ---------------------------------
         // AC KDF SPEC CHANGE: Determine which KDF to use.
         //
@@ -1635,12 +2085,11 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_Compute
             GetDiversificationData(kddValue,kekData,kek);//keytype is kek
 
             // AC: Derives the mac key for the token.
-            kekKey =ComputeCardKeyOnToken(masterKey,kekData);
+            kekKey =ComputeCardKeyOnToken(masterKey,kekData,1);
 
         } // endif use original KDF
         // ---------------------------------
 
-
     }
 
     if(kekKey == NULL) {
@@ -2049,7 +2498,6 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
             goto done;
         }
 
-
         // ---------------------------------
         // AC KDF SPEC CHANGE: Determine which KDF to use.
         //
@@ -2109,12 +2557,11 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp
             GetDiversificationData(kddValue,authData,enc);
 
             // AC: Derives the mac key for the token.
-            authKey = ComputeCardKeyOnToken(masterKey,authData);
+            authKey = ComputeCardKeyOnToken(masterKey,authData,1);
 
         } // endif use original KDF
         // ---------------------------------
 
-
         if (authKey == NULL)
         {
             goto done;
@@ -2297,14 +2744,24 @@ Java_com_netscape_symkey_SessionKey_ECBencrypt
         goto finish;
     }
 
+    PR_fprintf(PR_STDOUT,"In SessionKey: ECBencrypt! 16 byte key derived....  \n");
+
     //Wrap the new 16 bit key with the input symkey.
 
     wrappedKeyItem.data = (unsigned char *) handleBytes;
     wrappedKeyItem.len  = dlen;
+
+    PR_fprintf(PR_STDOUT,"In SessionKey: ECBencrypt! About to wrap des key with sym key.\n");
     wrapStatus = PK11_WrapSymKey(CKM_DES3_ECB,&noParams, symkey, newdeskey, &wrappedKeyItem);
 
     if( wrapStatus == SECSuccess) {
        PR_fprintf(PR_STDERR, "ECBencrypt wrapStatus %d wrappedKeySize %d \n", wrapStatus, wrappedKeyItem.len); 
+
+       PR_fprintf(PR_STDOUT," ECBencrypt wrapped data: \n");
+         Buffer wrappedDataBuf(wrappedKeyItem.data,wrappedKeyItem.len);
+         wrappedDataBuf.dump();
+
+
     } else {
        PR_fprintf(PR_STDERR, "ECBecrypt wrap failed! Error %d \n", PR_GetError());
     }
@@ -2328,6 +2785,105 @@ extern "C"
 {
 #endif
 /*
+ * Class:     com_netscape_cms_servlet_tks_DeriveDESKeyFrom3DesKey
+ * Method:    DeriveDESKeyFrom3DesKey 
+ * Signature: ([B[B[B[B)[B
+ */
+    JNIEXPORT jobject JNICALL
+        Java_com_netscape_symkey_SessionKey_DeriveDESKeyFrom3DesKey
+        (JNIEnv*, jclass,jstring ,jobject,jlong);
+#ifdef __cplusplus
+}
+#endif
+extern "C" JNIEXPORT jobject JNICALL 
+Java_com_netscape_symkey_SessionKey_DeriveDESKeyFrom3DesKey
+(JNIEnv* env, jclass this2, jstring tokenName, jobject des3Key,jlong alg)
+{
+    PK11SymKey * des3 = NULL;
+    PK11SymKey * des  = NULL;
+    PK11SymKey * desFinal = NULL;
+    PRStatus  r = PR_FAILURE;
+    CK_ULONG bitPosition = 0;
+    SECItem paramsItem = { siBuffer, NULL, 0 };
+    jobject keyObj = NULL;
+    char *tokenNameChars = NULL;
+    PK11SlotInfo *slot = NULL;
+
+    if( des3Key == NULL) {
+        goto loser;
+    }
+
+    if(alg != CKM_DES_CBC && alg != CKM_DES_ECB) {
+        PR_fprintf(PR_STDOUT,"SessionKey.DeriveDESKeyFrom3DesKey invalid alg!.. \n");
+        goto loser;
+    }
+
+    if (tokenName)
+    {
+        tokenNameChars = (char *)(env)->GetStringUTFChars(tokenName, NULL);
+        if ( tokenNameChars && !strcmp(tokenNameChars, "internal")) {
+            slot = PK11_GetInternalSlot();
+        } else {
+            slot = ReturnSlot(tokenNameChars);
+        }
+
+        (env)->ReleaseStringUTFChars(tokenName, (const char *)tokenNameChars);
+    } else {
+        slot = PK11_GetInternalKeySlot();
+    }
+
+    if(slot == NULL) {
+        goto loser;
+    }
+
+    r = JSS_PK11_getSymKeyPtr(env, des3Key, &des3);
+
+    if (r != PR_SUCCESS) {
+        PR_fprintf(PR_STDOUT,"SessionKey: DeriveDESKeyFrom3DesKey Unable to get input session 3des sym key! \n");
+        goto loser;
+    }
+
+    /* Now create a DES key with the first 8 bytes of the input 3des key */
+
+    // Extract first eight bytes from generated key into another key.
+     bitPosition = 0;
+     paramsItem.data = (CK_BYTE *) &bitPosition;
+     paramsItem.len = sizeof bitPosition;
+
+
+     des = PK11_Derive(des3, CKM_EXTRACT_KEY_FROM_KEY, &paramsItem,  alg , CKA_DERIVE, 8);
+     if (des  == NULL ) {
+         goto loser;
+     }
+
+     //Make sure we move this to the orig token, in case it got moved by NSS
+     //during the derive phase.
+
+     desFinal =  PK11_MoveSymKey ( slot, CKA_ENCRYPT, 0, PR_FALSE, des);
+
+
+         /* wrap the sesssion in java object. */
+     keyObj = JSS_PK11_wrapSymKey(env, &desFinal, NULL);
+
+loser:
+
+    if ( slot != NULL ) {
+       PK11_FreeSlot( slot);
+       slot = NULL;
+    }
+
+    if ( des != NULL) {
+        PK11_FreeSymKey(des);
+        des = NULL;
+    }
+    return keyObj;
+}
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/*
  * Class:     com_netscape_cms_servlet_tks_UnwrapSessionKeyWithSharedSecret
  * Method:    UnwrapSessionKeyWithSharedSecret 
  * Signature: ([B[B[B[B)[B
@@ -2396,6 +2952,7 @@ Java_com_netscape_symkey_SessionKey_UnwrapSessionKeyWithSharedSecret
     wrappedItem.data = (unsigned char *) sessionKeyBytes;
     wrappedItem.len =  sessionKeyLen;
 
+
     sessionKey = PK11_UnwrapSymKey(sharedSecret,
                           CKM_DES3_ECB,SecParam, &wrappedItem,
                           CKM_DES3_ECB,
@@ -2437,6 +2994,12 @@ loser:
         sessionKey = NULL; 
     }
 
+    if (SecParam) {
+        SECITEM_FreeItem(SecParam, PR_TRUE);
+        SecParam = NULL;
+    }
+
+
     // Don't free finalKey ptr because wrapping routine takes that out of our hands.
 
     return keyObj;
@@ -2467,7 +3030,6 @@ Java_com_netscape_symkey_SessionKey_GetSymKeyByName
     char *tokenNameChars = NULL;
     char *keyNameChars = NULL;
     PK11SlotInfo *slot = NULL;
-    CK_OBJECT_HANDLE keyhandle = 0;
 
     PR_fprintf(PR_STDOUT,"In SessionKey GetSymKeyByName!\n");
 
diff --git a/base/symkey/src/com/netscape/symkey/SessionKey.java b/base/symkey/src/com/netscape/symkey/SessionKey.java
index d44cc54..7a32de6 100644
--- a/base/symkey/src/com/netscape/symkey/SessionKey.java
+++ b/base/symkey/src/com/netscape/symkey/SessionKey.java
@@ -74,6 +74,19 @@ public class SessionKey {
     // external calls from RA
     public static native byte[] ComputeKeyCheck(PK11SymKey desKey); /* byte data[] ); */
 
+   //SCP02/GP211 methods
+
+    public static native byte[] ComputeSessionKeySCP02(String tokenName,
+            String keyName,
+            byte[] keyInfo,
+            byte[] CUID,
+            byte[] devKeyArray,
+            byte[] sequenceCounter,
+            byte[] derivationConstant,
+            String useSoftToken,
+            String keySet,
+            String sharedSecretKeyName);
+
     public static native byte[] ComputeSessionKey(String tokenName,
             String keyName,
             byte[] card_challenge,
@@ -173,7 +186,7 @@ public class SessionKey {
             byte[] CUIDValue,
             byte[] KDD,                           // AC: KDF SPEC CHANGE
             byte[] kekKeyArray,
-            String useSoftToken, String keySet);
+            String useSoftToken, String keySet,byte protocol);
 
     // internal calls from config TKS keys tab
     public static native String GenMasterKey(String token,
@@ -197,4 +210,6 @@ public class SessionKey {
     // with functionality only available now in NSS. This is all to preserve exact functional parity with the current TKS.
     public static native PK11SymKey UnwrapSessionKeyWithSharedSecret(String tokenName, PK11SymKey sharedSecret,
             byte[] sessionKeyArray);
+
+    public static native PK11SymKey DeriveDESKeyFrom3DesKey(String tokenName, PK11SymKey key3Des,long alg);
 }
diff --git a/base/symkey/src/com/netscape/symkey/SymKey.cpp b/base/symkey/src/com/netscape/symkey/SymKey.cpp
index 6e206db..44567f9 100644
--- a/base/symkey/src/com/netscape/symkey/SymKey.cpp
+++ b/base/symkey/src/com/netscape/symkey/SymKey.cpp
@@ -91,6 +91,8 @@ char masterKeyNickName[KEYNAMELENGTH];
 char masterNewKeyNickName[KEYNAMELENGTH];
 char sharedSecretSymKeyName[KEYNAMELENGTH] = { 0 };
 
+static int checkForDeveloperKeySet(char * keyInfo);
+
 //=================================================================================
 #ifdef __cplusplus
 extern "C"
@@ -509,10 +511,10 @@ static secuPWData pwdata = { secuPWData::PW_NONE, 0 };
  * Internal token is required when we are doing key diversification
  * where raw key material needs to be accessed
  */
-PK11SymKey *ComputeCardKeyOnSoftToken(PK11SymKey *masterKey, unsigned char *data)
+PK11SymKey *ComputeCardKeyOnSoftToken(PK11SymKey *masterKey, unsigned char *data, int protocol)
 {
     PK11SlotInfo *slot = PK11_GetInternalKeySlot();
-    PK11SymKey *key = ComputeCardKey(masterKey, data, slot);
+    PK11SymKey *key = ComputeCardKey(masterKey, data, slot,protocol);
     if( slot != NULL) {
         PK11_FreeSlot(slot);
         slot = NULL;
@@ -521,7 +523,7 @@ PK11SymKey *ComputeCardKeyOnSoftToken(PK11SymKey *masterKey, unsigned char *data
     return key;
 }
 
-PK11SymKey *ComputeCardKey(PK11SymKey *masterKey, unsigned char *data, PK11SlotInfo *slot)
+PK11SymKey *ComputeCardKey(PK11SymKey *masterKey, unsigned char *data, PK11SlotInfo *slot,int protocol)
 {
     PK11SymKey *key = NULL;
     PK11Context *context = NULL;
@@ -533,9 +535,12 @@ PK11SymKey *ComputeCardKey(PK11SymKey *masterKey, unsigned char *data, PK11SlotI
     static SECItem noParams = { siBuffer, NULL, 0 };
     unsigned char *in = data;
     PK11SymKey *tmpkey = NULL;
+    unsigned char icv[EIGHT_BYTES] = { 0 };
     unsigned char wrappedkey[DES3_LENGTH];
     SECItem wrappeditem = { siBuffer, NULL, 0 };
 
+    PR_fprintf(PR_STDOUT,"ComputeCardKey: protocol %d.\n",protocol);
+
     keyData = (unsigned char*)malloc(keysize);
 
     for (i = 0;i < keysize; i++)
@@ -550,7 +555,7 @@ PK11SymKey *ComputeCardKey(PK11SymKey *masterKey, unsigned char *data, PK11SlotI
 
     context = PK11_CreateContextBySymKey(CKM_DES3_ECB, CKA_ENCRYPT,
         masterKey,
-        &noParams);
+        &noParams); 
 
     if (context == NULL) {
         PR_fprintf(PR_STDERR,"ComputeCardKey: failed to create context.\n");
@@ -591,7 +596,7 @@ PK11SymKey *ComputeCardKey(PK11SymKey *masterKey, unsigned char *data, PK11SlotI
         PR_fprintf(PR_STDERR,"ComputeCardKey: failed to keygen. \n");
         goto done;
     }
-  
+
     context = PK11_CreateContextBySymKey(CKM_DES3_ECB, CKA_ENCRYPT,
         tmpkey,
         &noParams);
@@ -635,10 +640,10 @@ done:
     return key;
 }
 
-PK11SymKey * ComputeCardKeyOnToken(PK11SymKey *masterKey, BYTE* data)
+PK11SymKey * ComputeCardKeyOnToken(PK11SymKey *masterKey, BYTE* data, int protocol)
 {
     PK11SlotInfo *slot = PK11_GetSlotFromKey(masterKey);
-    PK11SymKey *key = ComputeCardKey(masterKey, data, slot);
+    PK11SymKey *key = ComputeCardKey(masterKey, data, slot,protocol);
 
     if( slot) {
         PK11_FreeSlot(slot);
@@ -787,13 +792,14 @@ PRStatus ComputeKeyCheckWithSymKey(PK11SymKey * newKey, Buffer& output)
 }
 
 // Create key set data with the help of either a provided old_keyk_ke2_sym key or key buffer (for the Default keyset case).
-PRStatus CreateKeySetDataWithSymKeys( Buffer &newMasterVer,const Buffer &old_kek_key2, PK11SymKey *old_kek_key2_sym, PK11SymKey *new_auth_key, PK11SymKey *new_mac_key, PK11SymKey *new_kek_key, Buffer &output)
+PRStatus CreateKeySetDataWithSymKeys( Buffer &newMasterVer,const Buffer &old_kek_key2, PK11SymKey *old_kek_key2_sym, PK11SymKey *new_auth_key, PK11SymKey *new_mac_key, PK11SymKey *new_kek_key,int protocol, Buffer &output)
 {
     PRStatus rv = PR_FAILURE;
     static SECItem noParams = { siBuffer, NULL, 0 };
     PK11SymKey *transportKey = NULL;
     PK11SymKey *wrappingKey = NULL;
     BYTE masterKeyData[DES3_LENGTH];
+    BYTE alg = 0x81;
 
     /* Wrapping vars */
     SECItem wrappedKeyItem   = { siBuffer, NULL , 0 };
@@ -819,34 +825,69 @@ PRStatus CreateKeySetDataWithSymKeys( Buffer &newMasterVer,const Buffer &old_kek
     Buffer kc_kek_key(3);
     Buffer result;
 
-    PR_fprintf(PR_STDOUT,"In CreateKeySetDataWithSymKeys!\n");
+    //Buffer *dumpBuffer = NULL;
+    //int showDerivedKeys = 0;
+
+    PR_fprintf(PR_STDOUT,"In CreateKeySetDataWithSymKeys! Protocol: %d \n",protocol);
 
     if ( new_auth_key == NULL || new_mac_key == NULL || new_kek_key == NULL) {
         return rv;
     }
 
     slot = PK11_GetSlotFromKey(new_auth_key);
-    if ( old_kek_key2_sym == NULL ) { /* perm key mode */
-        /* Find transport key, shared secret */
-        transportKey = ReturnSymKey( slot, GetSharedSecretKeyName(NULL));
-        if ( transportKey == NULL ) {
-            goto done;
+
+    if(protocol == 1) {
+        if ( old_kek_key2_sym == NULL ) { /* perm key mode */
+            /* Find transport key, shared secret */
+            transportKey = ReturnSymKey( slot, GetSharedSecretKeyName(NULL));
+            if ( transportKey == NULL ) {
+                goto done;
+            }
+
+            /* convert 16-byte to 24-byte triple-DES key */
+            memcpy(masterKeyData, old_kek_key2, KEYLENGTH);
+            memcpy(masterKeyData+16, old_kek_key2, EIGHT_BYTES);
+
+            wrappingKey = CreateUnWrappedSymKeyOnToken( slot, transportKey,  masterKeyData, sizeof(masterKeyData), PR_FALSE);
+
+        } else { /* card key mode */
+            PR_fprintf(PR_STDOUT,"In CreateKeySetDataWithSymKeys: SCP01 card key mode.\n");
+            wrappingKey = old_kek_key2_sym;
         }
 
-        /* convert 16-byte to 24-byte triple-DES key */
-        memcpy(masterKeyData, old_kek_key2, KEYLENGTH);
-        memcpy(masterKeyData+16, old_kek_key2, EIGHT_BYTES);
+   } else if(protocol == 2) {
 
-        wrappingKey = CreateUnWrappedSymKeyOnToken( slot, transportKey,  masterKeyData, sizeof(masterKeyData), PR_FALSE);
+       PR_fprintf(PR_STDOUT,"In CreateKeySetDataWithSymKeys: Using dekKey from SCP02 for wrapping key.\n");
 
-    } else { /* card key mode */
-        wrappingKey = old_kek_key2_sym;
-    }
+
+       // Use the unwapped SCP02 DEK sym key pointer. 
+       wrappingKey = old_kek_key2_sym;
+
+   } else {
+       PR_fprintf(PR_STDERR,"Invalid protocol %d . \n",protocol);
+            goto done;
+
+   }
 
         //Now derive 16 byte versions of the provided symkeys
         authKey16 = PK11_Derive(new_auth_key, CKM_EXTRACT_KEY_FROM_KEY, &paramsItem, CKA_ENCRYPT,
                                                             CKA_DERIVE, 16);
 
+        /*
+
+        if(showDerivedKeys == 1) {
+            SECItem *keyData = NULL;
+            PK11_ExtractKeyValue( authKey16 ); 
+            keyData = PK11_GetKeyData(authKey16);
+            dumpBuffer = new Buffer(keyData->data,keyData->len  );
+            PR_fprintf(PR_STDERR,"Debug authKey16 data: \n");
+            dumpBuffer->dump();
+            delete dumpBuffer;
+            dumpBuffer = NULL;
+        }
+
+        */
+
         if ( authKey16 == NULL ) {
             PR_fprintf(PR_STDERR,"Error deriving authKey16. Error %d \n", PR_GetError());
             goto done;
@@ -860,13 +901,28 @@ PRStatus CreateKeySetDataWithSymKeys( Buffer &newMasterVer,const Buffer &old_kek
             goto done;
         }
 
-         macKey16 = PK11_Derive(new_mac_key, CKM_EXTRACT_KEY_FROM_KEY, &paramsItem, CKA_ENCRYPT, CKA_DERIVE, 16);
+        macKey16 = PK11_Derive(new_mac_key, CKM_EXTRACT_KEY_FROM_KEY, &paramsItem, CKA_ENCRYPT, CKA_DERIVE, 16);
 
         if ( macKey16 == NULL ) {
             PR_fprintf(PR_STDERR,"Error deriving macKey16. Error %d \n", PR_GetError());
             goto done;
         }
 
+        /*
+
+        if(showDerivedKeys == 1) {
+            SECItem *keyData = NULL;
+            PK11_ExtractKeyValue( macKey16 );
+            keyData = PK11_GetKeyData(macKey16);
+            dumpBuffer = new Buffer(keyData->data,keyData->len  );
+            PR_fprintf(PR_STDERR,"Debug macKey16 data: \n");
+            dumpBuffer->dump();
+            delete dumpBuffer;
+            dumpBuffer = NULL;
+        }
+
+        */
+
         wrappedKeyItem.data = (unsigned char *) encrypted_mac_key;
         wrappedKeyItem.len  = encrypted_mac_key.size();
         wrapStatus = PK11_WrapSymKey(CKM_DES3_ECB,&noParams, wrappingKey, macKey16, &wrappedKeyItem);
@@ -883,6 +939,21 @@ PRStatus CreateKeySetDataWithSymKeys( Buffer &newMasterVer,const Buffer &old_kek
             PR_fprintf(PR_STDERR,"Error deriving kekKey16. Error %d \n", PR_GetError());
         }
 
+        /*
+
+        if(showDerivedKeys == 1) {
+            SECItem *keyData = NULL;
+            PK11_ExtractKeyValue( kekKey16 );
+            keyData = PK11_GetKeyData(kekKey16);
+            dumpBuffer = new Buffer(keyData->data,keyData->len  );
+            PR_fprintf(PR_STDERR,"Debug kekKey16 data: \n");
+            dumpBuffer->dump();
+            delete dumpBuffer;
+            dumpBuffer = NULL;
+        }
+   
+        */
+
         wrappedKeyItem.data = (unsigned char *) encrypted_kek_key;
         wrappedKeyItem.len  = encrypted_mac_key.size();
         wrapStatus = PK11_WrapSymKey(CKM_DES3_ECB,&noParams, wrappingKey, kekKey16, &wrappedKeyItem);
@@ -897,18 +968,22 @@ PRStatus CreateKeySetDataWithSymKeys( Buffer &newMasterVer,const Buffer &old_kek
 
         ComputeKeyCheckWithSymKey(new_kek_key, kc_kek_key);
 
+        if(protocol == 2) {
+            alg = 0x80;
+        } //default at top is 0x81
+
         result = newMasterVer +
-            Buffer(1, (BYTE)0x81) +
+            Buffer(1, (BYTE)alg) +
             Buffer(1, (BYTE)0x10) +
             encrypted_auth_key +
             Buffer(1, (BYTE)0x03) +
             kc_auth_key +
-            Buffer(1, (BYTE)0x81) +
+            Buffer(1, (BYTE)alg) +
             Buffer(1, (BYTE)0x10) +
             encrypted_mac_key +
             Buffer(1, (BYTE)0x03) +
             kc_mac_key +
-            Buffer(1, (BYTE)0x81) +
+            Buffer(1, (BYTE)alg) +
             Buffer(1, (BYTE)0x10) +
             encrypted_kek_key +
             Buffer(1, (BYTE)0x03) +
@@ -1042,13 +1117,13 @@ void getFullName(char * fullMasterKeyName, char * masterKeyNameChars )
 // AC: KDF SPEC CHANGE: function signature change - added jstring oldKeyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD
 // AC: BUGFIX for key versions higher than 09:  We need to specialDecode keyInfo parameters before sending them into symkey!  This means the parameters must be jbyteArray's
 //     -- Changed parameter "jstring keyInfo" to "jbyteArray newKeyInfo"
-extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_DiversifyKey
-(JNIEnv *, jclass, jstring, jstring, jstring, jstring, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, jbyteArray, jstring, jstring);
+//extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_DiversifyKey
+//(JNIEnv *, jclass, jstring, jstring, jstring, jstring, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, jbyteArray, jstring, jstring);
 
 // AC: KDF SPEC CHANGE: function signature change - added jstring oldKeyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD
 // AC: BUGFIX for key versions higher than 09:  We need to specialDecode keyInfo parameters before sending them into symkey!  This means the parameters must be jbyteArray's
 //     -- Changed parameter "jstring keyInfo" to "jbyteArray newKeyInfo"
-extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_DiversifyKey( JNIEnv * env, jclass this2, jstring tokenName,jstring newTokenName, jstring oldMasterKeyName, jstring newMasterKeyName, jbyteArray oldKeyInfo, jbyteArray newKeyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUIDValue, jbyteArray KDD, jbyteArray kekKeyArray, jstring useSoftToken_s, jstring keySet)
+extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_DiversifyKey( JNIEnv * env, jclass this2, jstring tokenName,jstring newTokenName, jstring oldMasterKeyName, jstring newMasterKeyName, jbyteArray oldKeyInfo, jbyteArray newKeyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUIDValue, jbyteArray KDD, jbyteArray kekKeyArray, jstring useSoftToken_s, jstring keySet,jbyte protocol)
 {
     PK11SymKey *encKey = NULL;
     PK11SymKey *macKey = NULL;
@@ -1069,6 +1144,7 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
     PK11SymKey* old_mac_sym_key = NULL;
     PK11SymKey* old_enc_sym_key = NULL;
     PK11SymKey* old_kek_sym_key = NULL;
+    BYTE scp02DekKeyData[DES3_LENGTH];
 
     char *keySetStringChars =  NULL;
     if ( keySet != NULL ) {
@@ -1106,10 +1182,10 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
     Buffer output;
     PK11SlotInfo *newSlot =NULL;
     char * newTokenNameChars = NULL;
-    char *keyInfoChars = NULL;
+    //char *keyInfoChars = NULL;
 
     // AC: KDF SPEC CHANGE:  Need to retrieve old key info from JNI.
-    char* oldKeyInfoChars = NULL;
+    //char* oldKeyInfoChars = NULL;
 
     // AC: KDF SPEC CHANGE:  Convert new setting value to BYTE (unsigned).
     BYTE nistSP800_108KdfOnKeyVersion_byte = static_cast<BYTE>(nistSP800_108KdfOnKeyVersion);
@@ -1125,6 +1201,7 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
 
     PK11SymKey * masterKey = NULL;
     PK11SymKey * oldMasterKey = NULL;
+    PK11SymKey * transportKey = NULL;
 
     BYTE KDCenc[KEYLENGTH];
     BYTE KDCmac[KEYLENGTH];
@@ -1170,7 +1247,7 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
         goto done;
     }
 
-    PR_fprintf(PR_STDOUT,"In SessionKey.DiversifyKey! \n");
+    PR_fprintf(PR_STDOUT,"In SessionKey.DiversifyKey! Protocol: %d \n", protocol);
 
     // AC: KDF SPEC CHANGE:
     // Changed from "cuidValue" to "kddValue".
@@ -1274,106 +1351,103 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
     if(newSlot == NULL) {
         newSlot = slot;
     }
-    if(strcmp( oldMasterKeyNameChars, "#01#01") == 0 || strcmp( oldMasterKeyNameChars, "#FF#01") == 0)
-    {
-        old_kek_key_buff    =   Buffer((BYTE*)old_kek_key, KEYLENGTH);
 
+    if(protocol == 1) {
+        if( checkForDeveloperKeySet(oldMasterKeyNameChars ))
+        {
+            old_kek_key_buff    =   Buffer((BYTE*)old_kek_key, KEYLENGTH);
+        }
+        else
+        {
+            oldMasterKey =     ReturnSymKey(slot,fullMasterKeyName);
 
-    // AC: BUGFIX: Remove garbage code.
-    //             I believe that this code is a no-op as long as the system is working correctly
-    //             (with the developer keyset data populated in the config file & copied to HSM).
-    //
-    //             Notes:
-    //               "old_kek_key_buff" appears to only be used if unable to read/load the developer keys into HSM.
-    //               "old_kek_key_buff" is populated with incorrect data (not appropriate key-length)
-    //               "output" is overwritten when "CreateKeySetDataWithSymKeys" is called
-    //
-    //             As a result, only when there is some failure (i.e. we execute a "goto" and skip assignment
-    //               to "output") do we return a keyset data that is 16 bytes in length (the default KEK).
-    //               This is unlikely to work and even if it does, is a horrible idea as the caller has no way
-    //               of knowing that we've now essentially inserted a "backdoor" on the token.  So, instead of
-    //               this, we treat #00#00 just like any other "normal" case.
-    //
-    //}else if(strcmp( oldMasterKeyNameChars, "#00#00") == 0)
-    //{
-    //    /* print Debug message - do not create real keysetdata */
-    //    old_kek_key_buff    =       Buffer((BYTE*)"#00#00", 6);
-    //    output              =       Buffer((BYTE*)old_kek_key, KEYLENGTH);
 
+            // AC: BUGFIX: Check for nonexistent master key instead of (potentially) crashing.
+            if (oldMasterKey == NULL){
+                goto done;
+            }
 
-    }
-    else
-    {
-        oldMasterKey =     ReturnSymKey(slot,fullMasterKeyName);
 
+            // ---------------------------------
+            // AC KDF SPEC CHANGE: Determine which KDF to use.
+            //
+            // if old key version meets setting value, use NIST SP800-108 KDF for deriving old keys
+            if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, oldKeyVersion) == true){
 
-        // AC: BUGFIX: Check for nonexistent master key instead of (potentially) crashing.
-        if (oldMasterKey == NULL){
-            goto done;
-        }
+                PR_fprintf(PR_STDOUT,"DiversifyKey old key NistSP800_108KDF code: Using NIST SP800-108 KDF for old keyset.\n");
 
+                // react to "UseCUIDAsKDD" setting value
+                jbyte* context_jbyte = NULL;
+                jsize context_len_jsize = 0;
+                if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){
+                    context_jbyte = cuidValue;
+                    context_len_jsize = cuidValue_len;
+                }else{
+                    context_jbyte = kddValue;
+                    context_len_jsize = kddValue_len;
+                }
 
-        // ---------------------------------
-        // AC KDF SPEC CHANGE: Determine which KDF to use.
-        //
-        // if old key version meets setting value, use NIST SP800-108 KDF for deriving old keys
-        if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, oldKeyVersion) == true){
+                // Converting this way is safe since jbyte is guaranteed to be 8 bits
+                // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical),
+                //            but it looks like this assumption is also made in GetDiversificationData
+                const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte);
 
-            PR_fprintf(PR_STDOUT,"DiversifyKey old key NistSP800_108KDF code: Using NIST SP800-108 KDF for old keyset.\n");
+                // Convert jsize to size_t
+                const size_t context_len = static_cast<size_t>(context_len_jsize);
+                if (context_len > 0x000000FF){  // sanity check (CUID should never be larger than 255 bytes)
+                    PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n");
+                    goto done;
+                }
 
-            // react to "UseCUIDAsKDD" setting value
-            jbyte* context_jbyte = NULL;
-            jsize context_len_jsize = 0;
-            if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){
-                context_jbyte = cuidValue;
-                context_len_jsize = cuidValue_len;
+                // call NIST SP800-108 KDF routine
+                try{
+                    NistSP800_108KDF::ComputeCardKeys(oldMasterKey, context, context_len, &old_enc_sym_key, &old_mac_sym_key, &old_kek_sym_key);
+                }catch(std::runtime_error& ex){
+                    PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: ");
+                    PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what());
+                    goto done;
+                }catch(...){
+                    PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n");
+                    goto done;
+                }
+
+            // if not a key version where we use the NIST SP800-108 KDF, use the original KDF
             }else{
-                context_jbyte = kddValue;
-                context_len_jsize = kddValue_len;
-            }
 
-            // Converting this way is safe since jbyte is guaranteed to be 8 bits
-            // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical),
-            //            but it looks like this assumption is also made in GetDiversificationData
-            const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte);
+                PR_fprintf(PR_STDOUT,"DiversifyKey old key NistSP800_108KDF code: Using original KDF for old keyset.\n");
 
-            // Convert jsize to size_t
-            const size_t context_len = static_cast<size_t>(context_len_jsize);
-            if (context_len > 0x000000FF){  // sanity check (CUID should never be larger than 255 bytes)
-                PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n");
-                goto done;
-            }
+                // AC: Derives the kek key for the token.
+                old_kek_sym_key = ComputeCardKeyOnToken(oldMasterKey,KDCkek,1);
 
-            // call NIST SP800-108 KDF routine
-            try{
-                NistSP800_108KDF::ComputeCardKeys(oldMasterKey, context, context_len, &old_enc_sym_key, &old_mac_sym_key, &old_kek_sym_key);
-            }catch(std::runtime_error& ex){
-                PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: ");
-                PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what());
-                goto done;
-            }catch(...){
-                PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n");
-                goto done;
-            }
+            } // endif use original KDF
+        }
 
-        // if not a key version where we use the NIST SP800-108 KDF, use the original KDF
-        }else{
+    }  else if(protocol == 2) {
 
-            PR_fprintf(PR_STDOUT,"DiversifyKey old key NistSP800_108KDF code: Using original KDF for old keyset.\n");
+            Buffer dek_key_buf    =   Buffer((BYTE*)old_kek_key, 16);
 
-            // AC: Derives the kek key for the token.
-            old_kek_sym_key = ComputeCardKeyOnToken(oldMasterKey,KDCkek);
+            PR_fprintf(PR_STDOUT,"DiversifyKey: protocol is 2 import wrapped dek key. \n");
+            dek_key_buf.dump();
+            transportKey = ReturnSymKey( slot, GetSharedSecretKeyName(NULL));
 
-        } // endif use original KDF
-        // ---------------------------------
+            if(transportKey == NULL)
+                goto done;
+
+            /* convert 16-byte to 24-byte triple-DES key */
+            memcpy(scp02DekKeyData, old_kek_key, 16);
 
+            old_kek_sym_key = UnwrapWrappedSymKeyOnToken( slot, transportKey,DES2_LENGTH, scp02DekKeyData,PR_FALSE);
 
+        } else {
+            PR_fprintf(PR_STDERR,"DiversifyKey: invalid protocol! \n");
+           goto done;
+
+        }
         // AC KDF SPEC CHANGE: Moved this code down so we don't skip it during "goto done".
         //if (oldMasterKey) {
         //    PK11_FreeSymKey( oldMasterKey );
         //    oldMasterKey = NULL;
         //}
-    }
 
     // AC KDF SPEC CHANGE: Moved this code down so we don't skip it during "goto done".
     //if(oldMasterKeyNameChars) {
@@ -1406,7 +1480,6 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
     } else {
         PR_fprintf(PR_STDOUT,"DiversifyKey: Compute card key on token case ! \n");
 
-
         // ---------------------------------
         // AC KDF SPEC CHANGE: Determine which KDF to use.
         //
@@ -1457,14 +1530,13 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
 
             // AC: Derives the kek key for the token.
             /* compute card key */
-            encKey = ComputeCardKeyOnSoftToken(masterKey, KDCenc);
-            macKey = ComputeCardKeyOnSoftToken(masterKey, KDCmac);
-            kekKey = ComputeCardKeyOnSoftToken(masterKey, KDCkek);
+            encKey = ComputeCardKeyOnSoftToken(masterKey, KDCenc,protocol);
+            macKey = ComputeCardKeyOnSoftToken(masterKey, KDCmac,protocol);
+            kekKey = ComputeCardKeyOnSoftToken(masterKey, KDCkek,protocol);
 
         } // endif use original KDF
         // ---------------------------------
 
-
         /* Fixes Bugscape Bug #55855: TKS crashes if specified key
          * is not present -- for each portion of the key, check if
          * the PK11SymKey is NULL before sending it to PK11_GetKeyData()!
@@ -1487,19 +1559,24 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive
     }
 
     if (old_kek_sym_key != NULL) {
+        transportKey = ReturnSymKey( slot, GetSharedSecretKeyName(NULL));
+
         CreateKeySetDataWithSymKeys(newMasterKeyBuffer, Buffer(),
             old_kek_sym_key,
             encKey,
             macKey,
             kekKey,
+            protocol,
             output); }
     else {
         old_kek_sym_key =  ReturnDeveloperSymKey(slot, (char *) "kek", keySetString, old_kek_key_buff);
+
         CreateKeySetDataWithSymKeys(newMasterKeyBuffer, Buffer(),
             old_kek_sym_key,
             encKey,
             macKey,
             kekKey,
+            protocol,
             output);
     }
 
@@ -1628,6 +1705,101 @@ done:
     }
 }
 
+// Unwrap previously wrapped sym key. Assume the final output is 24 bytes DES3
+
+PK11SymKey *UnwrapWrappedSymKeyOnToken( PK11SlotInfo *slot, PK11SymKey * unWrappingKey,int sizeOfWrappedKey, unsigned char * wrappedKeyData, PRBool isPerm) {
+
+    PK11SymKey * unWrappedSymKey = NULL;
+    int final_len = DES3_LENGTH;
+    SECItem unWrappedKeyItem = { siBuffer, NULL, 0};
+    PK11SymKey *unwrapper = NULL;
+    PK11SymKey *unwrapped24Key = NULL;
+
+
+    PR_fprintf( PR_STDOUT,
+        "Creating UnWrappedSymKey on  token. UnwrapWrappedSymKeyOnToken.  \n");
+
+    if ( (slot == NULL) || (unWrappingKey == NULL) || (wrappedKeyData == NULL)) 
+    {
+        return NULL;
+    }
+
+    if(sizeOfWrappedKey == DES2_LENGTH) {
+       PR_fprintf( PR_STDOUT,
+        "UnwrapWrappedSymKeyOnToken . Given 16 byte encrypted key will have to derive a 24 byte on later. \n");
+   }
+
+   PK11SlotInfo *unwrapKeySlot = PK11_GetSlotFromKey( unWrappingKey );
+
+   if ( unwrapKeySlot != slot ) {
+       unwrapper =  PK11_MoveSymKey ( slot, CKA_ENCRYPT, 0, PR_FALSE, unWrappingKey);
+   }
+
+   SECItem *SecParam = PK11_ParamFromIV(CKM_DES3_ECB, NULL);
+   if ( SecParam == NULL) {
+       goto done;
+   }
+
+   if ( final_len != DES3_LENGTH ) {
+       goto done;
+   }
+
+   unWrappedKeyItem.data = wrappedKeyData;
+   unWrappedKeyItem.len  = sizeOfWrappedKey;
+
+   /* Now try to unwrap our key into the token */
+   unWrappedSymKey = PK11_UnwrapSymKeyWithFlagsPerm(unwrapper ? unwrapper : unWrappingKey,
+                          CKM_DES3_ECB,SecParam, &unWrappedKeyItem,
+                          CKM_DES3_ECB,
+                          CKA_UNWRAP,
+                          sizeOfWrappedKey, 0, isPerm );
+
+  if(unWrappedSymKey == NULL) {
+       PR_fprintf( PR_STDERR,
+        "UnwrapWrappedSymKeyOnToken . Unable to unwrap key onto token!. \n");
+       goto done;
+   }
+
+   if(sizeOfWrappedKey == DES2_LENGTH) {
+       unwrapped24Key = CreateDesKey24Byte(slot, unWrappedSymKey);
+       if(unwrapped24Key == NULL) {
+           PR_fprintf( PR_STDERR,
+        "UnwrapWrappedSymKeyOnToken . Unable to unwrap 24 byte key onto token!. \n");
+
+       }
+   }
+
+done:
+
+    if( SecParam != NULL ) {
+        SECITEM_FreeItem(SecParam, PR_TRUE);
+        SecParam = NULL;
+    }
+
+    if( unwrapper != NULL ) {
+        PK11_FreeSymKey( unwrapper );
+        unwrapper = NULL;
+    }
+
+    if( unwrapKeySlot != NULL) {
+        PK11_FreeSlot( unwrapKeySlot);
+        unwrapKeySlot = NULL;
+    }
+
+    if( unwrapped24Key != NULL) {
+        PK11_FreeSymKey( unWrappedSymKey);
+        unWrappedSymKey = unwrapped24Key;
+    }
+
+    PR_fprintf( PR_STDOUT,
+        "UnwrapWrappedSymKey on token result: %p \n",unWrappedSymKey);
+
+
+    return unWrappedSymKey;
+
+}
+
+
 PK11SymKey *CreateUnWrappedSymKeyOnToken( PK11SlotInfo *slot, PK11SymKey * unWrappingKey, BYTE *keyToBeUnWrapped, int sizeOfKeyToBeUnWrapped, PRBool isPerm)
 {
     PK11SymKey * unWrappedSymKey = NULL;
@@ -1675,6 +1847,10 @@ PK11SymKey *CreateUnWrappedSymKeyOnToken( PK11SlotInfo *slot, PK11SymKey * unWra
         goto done;
     }
 
+     PR_fprintf( PR_STDOUT,
+        "Creating UnWrappedSymKey on  token. final len %d \n", final_len);
+
+
     if ( final_len != DES3_LENGTH ) {
         goto done;
     }
@@ -1735,6 +1911,8 @@ PK11SymKey *ReturnDeveloperSymKey(PK11SlotInfo *slot, char *keyType, char *keySe
 
     snprintf(devKeyName,maxKeyNameSize,"%s-%sKey", keySet, keyType);
 
+    PR_fprintf(PR_STDOUT,"ReturnDeveloperSymKey! trying to find key %s. \n",devKeyName);
+
     devSymKey = ReturnSymKey( slot, devKeyName );
  
     // Try to create the key once and leave it there. 
@@ -1767,7 +1945,10 @@ PK11SymKey *ReturnDeveloperSymKey(PK11SlotInfo *slot, char *keyType, char *keySe
                     PR_fprintf(PR_STDERR, "Can't set the nickname of just written devKey! \n");
                 }
             }
+        } else {
+             PR_fprintf(PR_STDOUT,"ReturnDeveloperSymKey! input key size %d. \n",inputKey.size());
         }
+
     }
 
 done:
@@ -1806,3 +1987,20 @@ JNICALL Java_com_netscape_symkey_SessionKey_SetDefaultPrefix(JNIEnv * env, jclas
 
     return;
 }
+
+int checkForDeveloperKeySet(char * keyInfo) 
+{
+
+    if(keyInfo == NULL)
+        return 1;   
+
+    //SCP01 or SCP02
+    if(strcmp( keyInfo, "#01#01") == 0 || strcmp( keyInfo, "#FF#01") == 0)
+        return 1; 
+           
+    //SCP02 
+    if(strcmp( keyInfo, "#01#02") == 0 || strcmp( keyInfo, "#FF#02") == 0) 
+        return 1; 
+
+    return  0;
+}
diff --git a/base/symkey/src/com/netscape/symkey/SymKey.h b/base/symkey/src/com/netscape/symkey/SymKey.h
index efe1870..4f1bbe7 100644
--- a/base/symkey/src/com/netscape/symkey/SymKey.h
+++ b/base/symkey/src/com/netscape/symkey/SymKey.h
@@ -41,13 +41,14 @@ extern char sharedSecretSymKeyName[KEYNAMELENGTH];
 void GetDiversificationData(jbyte *cuidValue,BYTE *KDC,keyType keytype);
 PK11SymKey * ReturnSymKey( PK11SlotInfo *slot, char *keyname);
 void GetKeyName(jbyte *keyVersion,char *keyname);
-PK11SymKey * ComputeCardKeyOnToken(PK11SymKey *masterKey, BYTE* data);
+PK11SymKey * ComputeCardKeyOnToken(PK11SymKey *masterKey, BYTE* data,int protocol);
 PRStatus EncryptData(const Buffer &kek_key, PK11SymKey *card_key, Buffer &input, Buffer &output);
 PK11SlotInfo *ReturnSlot(char *tokenNameChars);
-PK11SymKey *ComputeCardKey(PK11SymKey *masterKey, unsigned char *data, PK11SlotInfo *slot);
+PK11SymKey *ComputeCardKey(PK11SymKey *masterKey, unsigned char *data, PK11SlotInfo *slot,int protocol);
 PK11SymKey *CreateUnWrappedSymKeyOnToken( PK11SlotInfo *slot, PK11SymKey * unWrappingKey, BYTE *keyToBeUnWrapped, int sizeOfKeyToBeUnWrapped, PRBool isPerm);
 PK11SymKey *ReturnDeveloperSymKey(PK11SlotInfo *slot, char *keyType, char *keySet, Buffer &inputKey);
 PK11SymKey *CreateDesKey24Byte(PK11SlotInfo *slot, PK11SymKey *origKey);
+PK11SymKey *UnwrapWrappedSymKeyOnToken( PK11SlotInfo *slot, PK11SymKey * unWrappingKey,int sizeOfWrappedKey, unsigned char * wrappedKeyData, PRBool isPerm);
 
 char *GetSharedSecretKeyName(char *newKeyName);
 
diff --git a/base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java b/base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java
new file mode 100644
index 0000000..e5f38e1
--- /dev/null
+++ b/base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java
@@ -0,0 +1,108 @@
+package org.dogtagpki.server.tps.channel;
+
+import org.dogtagpki.tps.main.TPSBuffer;
+
+public class PlatformAndSecChannelProtoInfo {
+
+    private String platform ;
+    private byte protocol;
+    private TPSBuffer oidCardRecognitionData;
+    private TPSBuffer oidCardManagementTypeAndVer;
+    private TPSBuffer oidCardIdentificationScheme;
+    private TPSBuffer keysetInfoData;
+
+    private byte implementation;
+    public PlatformAndSecChannelProtoInfo(String platform, byte protocol, byte implementation) {
+        // TODO Auto-generated constructor stub
+        this.platform = platform;
+        this.protocol = protocol;
+        this.implementation = implementation;
+    }
+    public PlatformAndSecChannelProtoInfo() {
+        setPlatform(SecureChannel.GP201);
+        setProtocol(SecureChannel.SECURE_PROTO_01);
+        setImplementation((byte)0);
+    }
+    public String getPlatform() {
+        return platform;
+    }
+    public void setPlatform(String platform) {
+        this.platform = platform;
+    }
+    public byte getProtocol() {
+        return protocol;
+    }
+
+    public boolean isGP201() {
+        if(SecureChannel.GP201.equals(platform)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean isGP211() {
+        if(SecureChannel.GP211.equals(platform)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public boolean isSCP01() {
+        if(protocol == SecureChannel.SECURE_PROTO_01) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isSCP02() {
+        if(protocol == SecureChannel.SECURE_PROTO_02) {
+            return true;
+        }
+        return false;
+    }
+    public void setProtocol(byte protocol) {
+        this.protocol = protocol;
+    }
+    public void setOidCardRecognitionData(TPSBuffer oidCardRecognitionData) {
+        // TODO Auto-generated method stub
+        this.oidCardRecognitionData = oidCardRecognitionData;
+
+    }
+
+    public TPSBuffer getOidCardRecognitionData() {
+        return oidCardRecognitionData;
+    }
+    public void setOidCardManagementTypeAndVer(TPSBuffer oidCardManagementTypeAndVer) {
+        // TODO Auto-generated method stub
+        this.oidCardManagementTypeAndVer = oidCardManagementTypeAndVer;
+    }
+
+    public TPSBuffer getOidCardManagementTypeAndVer() {
+        return oidCardManagementTypeAndVer;
+    }
+    public void setOidCardIdentificationScheme(TPSBuffer oidCardIdentificationScheme) {
+        this.oidCardIdentificationScheme = oidCardIdentificationScheme;
+
+    }
+
+    public TPSBuffer getOidCardIdentificationScheme() {
+        return oidCardIdentificationScheme;
+    }
+
+    public void setImplementation(byte implementation) {
+        this.implementation = implementation;
+    }
+
+    public byte getImplementation() {
+        return implementation;
+    }
+    public TPSBuffer getKeysetInfoData() {
+        return keysetInfoData;
+    }
+    public void setKeysetInfoData(TPSBuffer keysetInfoData) {
+        this.keysetInfoData = keysetInfoData;
+    }
+
+}
diff --git a/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java b/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java
index 7c3a01b..eb8b514 100644
--- a/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java
+++ b/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java
@@ -19,21 +19,27 @@ package org.dogtagpki.server.tps.channel;
 
 import java.io.IOException;
 
+import org.dogtagpki.server.tps.engine.TPSEngine;
 import org.dogtagpki.server.tps.processor.TPSProcessor;
 import org.dogtagpki.tps.apdu.APDU;
 import org.dogtagpki.tps.apdu.APDUResponse;
 import org.dogtagpki.tps.apdu.CreateObjectAPDU;
 import org.dogtagpki.tps.apdu.CreatePinAPDU;
 import org.dogtagpki.tps.apdu.DeleteFileAPDU;
+import org.dogtagpki.tps.apdu.DeleteFileGP211APDU;
 import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDU;
 import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDU.SecurityLevel;
+import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDUGP211;
 import org.dogtagpki.tps.apdu.GenerateKeyAPDU;
 import org.dogtagpki.tps.apdu.GenerateKeyECCAPDU;
 import org.dogtagpki.tps.apdu.ImportKeyEncAPDU;
 import org.dogtagpki.tps.apdu.InstallAppletAPDU;
+import org.dogtagpki.tps.apdu.InstallAppletAPDUGP211;
 import org.dogtagpki.tps.apdu.InstallLoadAPDU;
+import org.dogtagpki.tps.apdu.InstallLoadGP211APDU;
 import org.dogtagpki.tps.apdu.LifecycleAPDU;
 import org.dogtagpki.tps.apdu.LoadFileAPDU;
+import org.dogtagpki.tps.apdu.LoadFileAPDUGP211;
 import org.dogtagpki.tps.apdu.PutKeyAPDU;
 import org.dogtagpki.tps.apdu.ReadObjectAPDU;
 import org.dogtagpki.tps.apdu.SetIssuerInfoAPDU;
@@ -55,10 +61,23 @@ public class SecureChannel {
 
     // Have not written all code to use all of these as of yet.
 
-    private TPSProcessor processor;
+    public TPSProcessor processor;
     private PK11SymKey sessionKey;
+
+    //SCP01 or SCP02 key
     private PK11SymKey encSessionKey;
+
+    //SCP02 session keys
+    private PK11SymKey cmacSessionKey;
+    //Used for security level we do not yet suport.
+    private PK11SymKey rmacSessionKey;
+    private PK11SymKey dekSessionKey;
+
+    private TPSBuffer dekSessionKeyWrapped;
+
     private TPSBuffer drmDesKey;
+
+    //SCP01 kek key
     private TPSBuffer kekDesKey;
     private TPSBuffer keyCheck;
     private TPSBuffer keyDiversificationData;
@@ -68,7 +87,10 @@ public class SecureChannel {
     private TPSBuffer hostCryptogram;
     private TPSBuffer icv;
     private TPSBuffer keyInfoData;
-    private SecurityLevel secLevel;
+    private TPSBuffer sequenceCounter;
+    private ExternalAuthenticateAPDU.SecurityLevel secLevel;
+    private PlatformAndSecChannelProtoInfo platProtInfo;
+    private ExternalAuthenticateAPDUGP211.SecurityLevel secLevelGP211;
 
     public enum TokenKeyType {
         KEY_TYPE_ENCRYPTION,
@@ -76,9 +98,29 @@ public class SecureChannel {
         KEY_TYPE_SIGNING_AND_ENCRYPTION
     }
 
+    public final static byte GP211_SCP02_IMPL_15 = 0x15;
+    public final static String GP201 = "2.0.1";
+    public final static String GP211 = "2.1.1";
+
+    public final static byte SECURE_PROTO_01 = 1;
+    public final static byte SECURE_PROTO_02 = 2;
+    public final static byte SECURE_PROTO_03 = 3;
+
+    public final static byte[] GP211_GET_DATA_CARD_DATA = { 0x00, (byte) 0x66 };
+    public final static byte[] GP211_GET_DATA_KEY_INFO = { 0x00, (byte) 0xe0 };
+    public final static byte[] GP201_GET_DATA_CPLC_WHOLE_CPLC = { (byte) 0x9F, (byte) 0x7F };
+    public final static byte[] GP211_GET_DATA_CPLC_WHOLE_CPLC = { (byte) 0x9F, (byte) 0x7F };
+
+    public final static byte[] C_MACDerivationConstant = { 0x01, 0x01 };
+    public final static byte[] ENCDerivationConstant = { (byte) 0x01, (byte) 0x82 };
+    public final static byte[] DEKDerivationConstant = { 0x01, (byte) 0x81 };
+    public final static byte[] R_MACDerivationConstant = { 0x01, 0x02 };
+
+    //For SCP01
     public SecureChannel(TPSProcessor processor, PK11SymKey sessionKey, PK11SymKey encSessionKey, TPSBuffer drmDesKey,
             TPSBuffer kekDesKey, TPSBuffer keyCheck, TPSBuffer keyDiversificationData, TPSBuffer cardChallenge,
-            TPSBuffer cardCryptogram, TPSBuffer hostChallenge, TPSBuffer hostCryptogram, TPSBuffer keyInfoData)
+            TPSBuffer cardCryptogram, TPSBuffer hostChallenge, TPSBuffer hostCryptogram, TPSBuffer keyInfoData,
+            PlatformAndSecChannelProtoInfo platformInfo)
             throws TPSException {
 
         if (processor == null || sessionKey == null | encSessionKey == null || keyDiversificationData == null
@@ -88,6 +130,9 @@ public class SecureChannel {
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
 
+        CMS.debug("SecureChannel.SecureChannel: For SCP01. ");
+
+        this.platProtInfo = platformInfo;
         this.processor = processor;
         this.sessionKey = sessionKey;
         this.encSessionKey = encSessionKey;
@@ -103,11 +148,105 @@ public class SecureChannel {
         this.keyInfoData = keyInfoData;
 
         this.secLevel = SecurityLevel.SECURE_MSG_MAC_ENC;
-        //ToDo: Write method that reads this from the config
 
     }
 
-    public static void main(String[] args) {
+    //For SCP02
+    public SecureChannel(TPSProcessor processor, PK11SymKey encSessionKey, PK11SymKey cmacSessionKey,
+            PK11SymKey rmacSessionKey, PK11SymKey dekSessionKey, TPSBuffer drmDesKey,
+            TPSBuffer kekDesKey, TPSBuffer keyCheck,
+            TPSBuffer keyDiversificationData,
+            TPSBuffer keyInfoData, TPSBuffer sequenceCounter, TPSBuffer hostChallenge, TPSBuffer cardChallenge,
+            TPSBuffer cardCryptogram, PlatformAndSecChannelProtoInfo platformInfo)
+            throws TPSException {
+
+        if (processor == null || encSessionKey == null | cmacSessionKey == null || rmacSessionKey == null
+                || dekSessionKey == null || keyDiversificationData == null || hostChallenge == null
+                || cardChallenge == null || cardCryptogram == null
+                || keyInfoData == null || platformInfo == null) {
+            throw new TPSException("SecureChannel.SecureChannel: Invalid data in constructor!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        this.sequenceCounter = sequenceCounter;
+        this.platProtInfo = platformInfo;
+        this.processor = processor;
+
+        this.encSessionKey = encSessionKey;
+        this.cmacSessionKey = cmacSessionKey;
+        this.setRmacSessionKey(rmacSessionKey);
+
+        this.keyDiversificationData = keyDiversificationData;
+
+        this.icv = new TPSBuffer(8);
+        this.keyInfoData = keyInfoData;
+        this.cardChallenge = cardChallenge;
+        this.cardCryptogram = cardCryptogram;
+        this.hostChallenge = hostChallenge;
+        this.drmDesKey = drmDesKey;
+        this.kekDesKey = kekDesKey;
+
+        //SCP02
+        this.secLevelGP211 = ExternalAuthenticateAPDUGP211.SecurityLevel.CDEC_CMAC;
+        this.keyCheck = keyCheck;
+
+        byte finalKeyIndex = gp211CalculateLatestKeySet(platformInfo.getKeysetInfoData());
+
+        CMS.debug("SecureChannel.SecureChannel: For SCP02: calculated latest key index: " + finalKeyIndex);
+
+    }
+
+    private byte gp211CalculateLatestKeySet(TPSBuffer keysetInfoData) throws TPSException {
+
+        if (keysetInfoData == null) {
+
+            throw new TPSException("SecureChannel.gp211calculateKeyInfoData invalid input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+
+        }
+
+        CMS.debug("SecureChannel.gp211calculateKeyInfoData: input keysetInfoData: " + keysetInfoData.toHexString());
+
+        int pos = 0;
+        byte next = keysetInfoData.at(pos++);
+
+        if (next != (byte) 0xE0) {
+            throw new TPSException("SecureChannel.gp211calculateKeyInfoData: malformed input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        next = keysetInfoData.at(pos++);
+
+        int numKeys = (next) / 6;
+
+        int remainder = (next) % 6;
+
+        if (remainder != 0) {
+            throw new TPSException("SecureChannel.gp211calculateKeyInfoData: malformed input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("SecureChannel.gp211calculateKeyInfoData: number of keys: " + numKeys);
+
+        int numKeySets = numKeys / 3; //Three keys per set
+
+        CMS.debug("SecureChannel.gp211calculateKeyInfoData: number of keysets: " + numKeySets);
+
+        int offset = (numKeySets - 1) * 6 * 3 + 3;
+
+        CMS.debug("SecureChannel.gp211calculateKeyInfoData: offset " + offset);
+
+        offset += pos;
+
+        if (offset > keysetInfoData.size()) {
+            throw new TPSException("SecureChannel.gp211calculateKeyInfoData: malformed input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        byte finalKeyIndex = keysetInfoData.at(offset);
+
+        return finalKeyIndex;
+
     }
 
     public void appendPKCS11Attribute(TPSBuffer buffer, long type, TPSBuffer attribute) {
@@ -204,29 +343,77 @@ public class SecureChannel {
 
     public void externalAuthenticate() throws TPSException, IOException {
 
-        CMS.debug("SecureChannel.externalAuthenticate: entering.");
+        CMS.debug("SecureChannel.externalAuthenticate: entering. &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
 
-        ExternalAuthenticateAPDU externalAuth = new ExternalAuthenticateAPDU(hostCryptogram,
-                secLevel);
+        if (platProtInfo.isGP211() && platProtInfo.isSCP02()) {
+            CMS.debug("SecureChannel.externalAuthenticate: Attempting an External Authenticate for SCP02!");
 
-        computeAPDUMac(externalAuth);
+            TPSBuffer calculatedCardCryptogram = computeCardCryptogramSCP02(encSessionKey);
 
-        APDUResponse response = processor.handleAPDURequest(externalAuth);
+            if (false == cardCryptogram.equals(calculatedCardCryptogram)) {
+
+                CMS.debug("SecureChannel.eternalAuthenticate. Failed to match calculated to returned card cryptogram!. cardCryptogram: "
+                        + cardCryptogram.toHexString()
+                        + " calculatedCardCrytpogram: "
+                        + calculatedCardCryptogram.toHexString());
+                throw new TPSException(
+                        "SecureChannel.eternalAuthenticate. Failed to match calculated to returned card cryptogram!.",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+
+            }
+
+            TPSBuffer calculatedHostCryptogram = computeHostCryptogramSCP02(encSessionKey);
+            this.hostCryptogram = calculatedHostCryptogram;
+
+            ExternalAuthenticateAPDUGP211 externalAuth = new ExternalAuthenticateAPDUGP211(hostCryptogram,
+                    /* secLevel */secLevelGP211);
+
+            CMS.debug("SecureChannel.externalAuthenticate: about to call computeAPDUMacSCP02. &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
+            computeAPDUMacSCP02(externalAuth);
+
+            APDUResponse response = processor.handleAPDURequest(externalAuth);
+
+            if (!response.checkResult()) {
+                throw new TPSException(
+                        "SecureChannel.eternalAuthenticate SCP02. Failed to external authenticate to token.",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+            CMS.debug("SecureChannel.externalAuthenticate: SCP02 external authenticate returns Success!!!");
+
+        } else { //SCP01
+
+            ExternalAuthenticateAPDU externalAuth = new ExternalAuthenticateAPDU(hostCryptogram,
+                    /* secLevel */ExternalAuthenticateAPDU.SecurityLevel.SECURE_MSG_MAC_ENC);
+
+            CMS.debug("SecureChannel.externalAuthenticate: about to call computeAPDUMac. &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
+            computeAPDUMac(externalAuth);
+
+            APDUResponse response = processor.handleAPDURequest(externalAuth);
+
+            if (!response.checkResult()) {
+                throw new TPSException(
+                        "SecureChannel.eternalAuthenticate SCP01. Failed to external authenticate to token.",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
 
-        if (!response.checkResult()) {
-            throw new TPSException("SecureChannel.eternalAuthenticate. Failed to external authenticate to token.",
-                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
 
         CMS.debug("SecureChannel.externalAuthenticate: Successfully completed, exiting ...");
-
     }
 
     //This method computes the mac AND encryption if needed.
+    // Handle SCP02 if required.
     private void computeAPDU(APDU apdu) throws TPSException {
 
         CMS.debug("SecureChannel.computeAPDU: entering..");
 
+        if (isSCP02()) {
+            computeAPDU_SCP02(apdu);
+            return;
+
+        }
+
         if (apdu == null) {
             throw new TPSException("SecureChannel.computeAPDU: bad input apdu!",
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
@@ -236,9 +423,9 @@ public class SecureChannel {
 
         if (secLevel == SecurityLevel.SECURE_MSG_MAC_ENC) {
             try {
-                // CMS.debug("SecureChannel.computeAPDU: Before encryption data value: " + apdu.getData().toHexString());
+                CMS.debug("SecureChannel.computeAPDU: Before encryption data value: " + apdu.getData().toHexString());
                 apdu.secureMessage(encSessionKey);
-                // CMS.debug("SecureChannel.computeAPDU: After encryption data value: " + apdu.getData().toHexString());
+                CMS.debug("SecureChannel.computeAPDU: After encryption data value: " + apdu.getData().toHexString());
             } catch (EBaseException e) {
                 throw new TPSException("SecureChannel.computeAPDU: Can't encrypt outgoing data! " + e);
             }
@@ -247,6 +434,79 @@ public class SecureChannel {
         }
     }
 
+    private void computeAPDU_SCP02(APDU apdu) throws TPSException {
+        CMS.debug("SecureChannel.computeAPDU_SCP02: entering..");
+
+        if (apdu == null) {
+            throw new TPSException("SecureChannel.computeAPDU_SCP02: bad input apdu!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        computeAPDUMacSCP02(apdu);
+
+        if (secLevelGP211 == ExternalAuthenticateAPDUGP211.SecurityLevel.CDEC_CMAC) {
+            try {
+                CMS.debug("SecureChannel.computeAPDU_SCP02: Before encryption data value: "
+                        + apdu.getData().toHexString());
+                apdu.secureMessageSCP02(encSessionKey);
+                CMS.debug("SecureChannel.computeAPDU_SCP02: After encryption data value: "
+                        + apdu.getData().toHexString());
+            } catch (EBaseException e) {
+                throw new TPSException("SecureChannel.computeAPDU_SCP02: Can't encrypt outgoing data! " + e);
+            }
+
+            CMS.debug("SecureChannel.computeAPDU_SCP02: Successfully encrypted apdu data.");
+        }
+
+    }
+
+    private void computeAPDUMacSCP02(APDU apdu) throws TPSException {
+
+        TPSBuffer newMac = null;
+        TPSBuffer data = null;
+        TPSBuffer singleDes = null;
+
+        if (apdu == null) {
+            throw new TPSException("SecureChannel.computeAPDUMacSCP02: bad input apdu!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        data = apdu.getDataToMAC();
+
+        CMS.debug("SecureChannel.computeAPDUMacSCP02: data To MAC: " + data.toHexString() + " incoming icv: "
+                + icv.toHexString());
+
+        try {
+
+            if (apdu.getType() != APDU.Type.APDU_EXTERNAL_AUTHENTICATE
+                    && (secLevelGP211 == ExternalAuthenticateAPDUGP211.SecurityLevel.CMAC || secLevelGP211 == ExternalAuthenticateAPDUGP211.SecurityLevel.CDEC_CMAC)) {
+                CMS.debug("SecureChannel.computeAPDUMacSCP02: data To MAC, calcuating single des encyption before mac.");
+
+                singleDes = Util.computeEncEcbDes(cmacSessionKey, icv);
+                CMS.debug("SecureChannel.computeAPDUMacSCP02: data To MAC, calcuating single des encyption before mac. result: "
+                        + singleDes.toHexString());
+
+                newMac = Util.computeMACdes3des(cmacSessionKey, data, singleDes);
+            } else {
+                CMS.debug("SecureChannel.computeAPDUMacSCP02: No ecnrypton of ICV.");
+                newMac = Util.computeMACdes3des(cmacSessionKey, data, icv);
+
+            }
+
+        } catch (EBaseException e) {
+            CMS.debug("SecureChannel.computeAPDUMacSCP02: Can't compute mac. " + e);
+            throw new TPSException("SecureChannel.compuatAPDUMacSCP02: Can't compute mac.",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("SecureChannel.computeAPDUMacSCP02: computed MAC: " + newMac.toHexString());
+
+        apdu.setMAC(newMac);
+
+        icv.set(newMac);
+
+    }
+
     // This method computes MAC only.
     private void computeAPDUMac(APDU apdu) throws TPSException {
         TPSBuffer newMac = null;
@@ -282,25 +542,47 @@ public class SecureChannel {
             throw new TPSException("SecureChannel.deleteFileX: no input aid!");
         }
 
-        DeleteFileAPDU deleteFile = new DeleteFileAPDU(aid);
+        if (isGP211()) {
+
+            CMS.debug("SecureChannel.deleteFileX: attempting gp211...");
+            DeleteFileGP211APDU deleteFile = new DeleteFileGP211APDU(aid);
+
+            computeAPDU(deleteFile);
+
+            processor.handleAPDURequest(deleteFile);
+        } else {
+
+            CMS.debug("SecureChannel.deleteFileX: attempting gp201...");
+            DeleteFileAPDU deleteFile = new DeleteFileAPDU(aid);
+
+            computeAPDU(deleteFile);
 
-        computeAPDU(deleteFile);
+            processor.handleAPDURequest(deleteFile);
 
-        processor.handleAPDURequest(deleteFile);
+        }
 
     }
 
     // Begin process of loading applet onto token.
     public void installLoad(TPSBuffer packageAID, TPSBuffer sdAID, int fileLength) throws TPSException, IOException {
 
-        CMS.debug("SecureChannel.installLoad: entering ...");
+        CMS.debug("SecureChannel.installLoad: entering ... packageAID: " + packageAID.toHexString() + " sdAID: "
+                + sdAID.toHexString());
 
         if (packageAID == null || sdAID == null || fileLength <= 0) {
             throw new TPSException("SecureChannel.insallLoad bad input parameters!",
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
 
-        InstallLoadAPDU install = new InstallLoadAPDU(packageAID, sdAID, fileLength);
+        TPSBuffer emptySDAID = new TPSBuffer();
+
+        if (isGP211()) {
+            TPSBuffer cardMgrGP211AIDBuff = new TPSBuffer(TPSEngine.CFG_DEF_CARDMGR_211_INSTANCE_AID);
+            installLoadGP211(packageAID, cardMgrGP211AIDBuff, fileLength);
+            return;
+        }
+
+        InstallLoadAPDU install = new InstallLoadAPDU(packageAID, emptySDAID, fileLength);
 
         CMS.debug("SecureChannel.installLoad: Pre computed apdu: " + install.getEncoding().toHexString());
 
@@ -315,11 +597,34 @@ public class SecureChannel {
 
     }
 
+    public void installLoadGP211(TPSBuffer packageAID, TPSBuffer sdAID, int fileLength) throws TPSException,
+            IOException {
+
+        CMS.debug("SecureChannel.installLoadGP211: entering ...");
+
+        if (packageAID == null || sdAID == null || fileLength <= 0) {
+            throw new TPSException("SecureChannel.insallLoadGP211 bad input parameters!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        InstallLoadGP211APDU install = new InstallLoadGP211APDU(packageAID, sdAID,
+                fileLength);
+
+        computeAPDU(install);
+
+        APDUResponse response = processor.handleAPDURequest(install);
+
+        if (!response.checkResult()) {
+            throw new TPSException("SecureChannel.installLoadGP211. Failed to perform installLoadGP211 operation.",
+                    TPSStatus.STATUS_ERROR_UPGRADE_APPLET);
+        }
+    }
+
     //Actually load applet file onto the token.
 
     public void loadFile(TPSBuffer programFile, int blockSize, int startProgress, int endProgress) throws TPSException,
             IOException {
-        CMS.debug("SecureChannel.loadFile entering...");
+        CMS.debug("SecureChannel.loadFile entering... blockSize: " + blockSize);
 
         if (programFile == null || blockSize <= 0) {
             throw new TPSException("ScureChannel.loadFile. Bad input data.", TPSStatus.STATUS_ERROR_UPGRADE_APPLET);
@@ -407,11 +712,25 @@ public class SecureChannel {
                     TPSStatus.STATUS_ERROR_UPGRADE_APPLET);
         }
 
-        LoadFileAPDU loadFile = new LoadFileAPDU(refControl, (byte) count, piece);
+        CMS.debug("SecureChannel.loadFileSegment: count: " + count + " piece: " + piece.toHexString());
+
+        APDUResponse response = null;
+
+        if (isGP211()) {
+            LoadFileAPDUGP211 loadFile = new LoadFileAPDUGP211(refControl, (byte) count, piece);
 
-        computeAPDU(loadFile);
+            computeAPDU(loadFile);
 
-        APDUResponse response = processor.handleAPDURequest(loadFile);
+            response = processor.handleAPDURequest(loadFile);
+        } else {
+            CMS.debug("SecureChannel.loadFileSegment: gp211.");
+            LoadFileAPDU loadFile = new LoadFileAPDU(refControl, (byte) count, piece);
+
+            computeAPDU(loadFile);
+
+            response = processor.handleAPDURequest(loadFile);
+
+        }
 
         if (!response.checkResult()) {
             throw new TPSException(
@@ -437,12 +756,25 @@ public class SecureChannel {
 
         }
 
-        InstallAppletAPDU install = new InstallAppletAPDU(netkeyPAIDBuff, netkeyAIDBuff, appPrivileges,
-                channelInstanceSize, channelAppletMemSize);
+        APDUResponse response = null;
 
-        computeAPDU(install);
+        if (isGP211()) {
+            InstallAppletAPDUGP211 install = new InstallAppletAPDUGP211(netkeyPAIDBuff, netkeyAIDBuff, appPrivileges,
+                    channelInstanceSize, channelAppletMemSize);
 
-        APDUResponse response = processor.handleAPDURequest(install);
+            computeAPDU(install);
+
+            response = processor.handleAPDURequest(install);
+        } else {
+
+            InstallAppletAPDU install = new InstallAppletAPDU(netkeyPAIDBuff, netkeyAIDBuff, appPrivileges,
+                    channelInstanceSize, channelAppletMemSize);
+
+            computeAPDU(install);
+
+            response = processor.handleAPDURequest(install);
+
+        }
 
         if (!response.checkResult()) {
             throw new TPSException("SecureChannel.installApplett. Failed installApplet operation.",
@@ -972,7 +1304,8 @@ public class SecureChannel {
 
     public void putKeys(byte curVersion, byte curIndex, TPSBuffer keySetData) throws TPSException, IOException {
 
-        CMS.debug("SecureChannel.putKeys: entering..");
+        CMS.debug("SecureChannel.putKeys: entering.. curVersion: " + curVersion + " curIndex:  " + curIndex
+                + " keySetData: " + keySetData);
 
         if (keySetData == null) {
             throw new TPSException("SecureChannel.putKeys: Invalid input data!", TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER);
@@ -980,14 +1313,27 @@ public class SecureChannel {
 
         byte keyVersion = curVersion;
 
-        if (curVersion == 0xff) {
-            keyVersion = 0x0;
+        if (curVersion == (byte) 0xff) {
+            CMS.debug("Setting keyVersion to 1");
+            keyVersion = 0x1;
         }
 
-        PutKeyAPDU putKey = new PutKeyAPDU(keyVersion, (byte) (0x80 | curIndex), keySetData);
+        CMS.debug("keyVersion now set to: " + keyVersion);
+
+        PutKeyAPDU putKey = new PutKeyAPDU(keyVersion, (byte) 0x81, keySetData);
 
+        if (isSCP02()) {
+            TPSBuffer trailer = new TPSBuffer(1);
+            putKey.setTrailer(trailer);
+
+        }
         computeAPDU(putKey);
 
+       int kill = 0;
+        if (kill == 1) {
+            throw new TPSException("putKeys end of progress.");
+        }
+
         APDUResponse response = processor.handleAPDURequest(putKey);
 
         if (!response.checkResult()) {
@@ -1017,11 +1363,12 @@ public class SecureChannel {
 
         CMS.debug("SecureChannel.importKeyEnc entering...");
 
-        if(data == null) {
-            throw new TPSException("SecureChannel.importKeyEnc: Invalid input data!",TPSStatus.STATUS_ERROR_MAC_CERT_PDU);
+        if (data == null) {
+            throw new TPSException("SecureChannel.importKeyEnc: Invalid input data!",
+                    TPSStatus.STATUS_ERROR_MAC_CERT_PDU);
         }
 
-        ImportKeyEncAPDU importKeyEnc = new ImportKeyEncAPDU((byte)pe1,(byte) pe2, data);
+        ImportKeyEncAPDU importKeyEnc = new ImportKeyEncAPDU((byte) pe1, (byte) pe2, data);
 
         computeAPDU(importKeyEnc);
 
@@ -1042,4 +1389,119 @@ public class SecureChannel {
         this.kekDesKey = kekDesKey;
     }
 
+    public TPSBuffer getSequenceCounter() {
+        return sequenceCounter;
+    }
+
+    public PlatformAndSecChannelProtoInfo getChannelPlatformAndProtocolInfo() {
+        return platProtInfo;
+    }
+
+    protected TPSBuffer computeCardCryptogramSCP02(PK11SymKey encSessionKey)
+            throws TPSException {
+
+        if (encSessionKey == null) {
+            throw new TPSException("TPSProcessor.computeCardCryptogramSCP02: invalide input data",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        TPSBuffer data = new TPSBuffer(hostChallenge);
+        data.add(sequenceCounter);
+        data.add(cardChallenge);
+
+        if (data.size() != 16) {
+            throw new TPSException("calculateCardCryptogramSCP02: card cyrptogram source data incorrect size.",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        TPSBuffer cardCryptogram = null;
+        try {
+            cardCryptogram = Util.computeMAC(encSessionKey, data, icv);
+        } catch (EBaseException e) {
+            throw new TPSException("calculateCardCryptogramSCP02: card cyrptogram: Error calculating the MAC value",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("TPSProcessor.calculateCardCrytogramSCP02: returning calculated card cryptogram; "
+                + cardCryptogram.toHexString());
+
+        return cardCryptogram;
+
+    }
+
+    protected TPSBuffer computeHostCryptogramSCP02(PK11SymKey encSessionKey)
+            throws TPSException {
+
+        if (encSessionKey == null) {
+            throw new TPSException("TPSProcessor.computeHostCryptogramSCP02: invalide input data",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        TPSBuffer hostCryptogramSCP02 = null;
+
+        TPSBuffer data = new TPSBuffer(sequenceCounter);
+        data.add(cardChallenge);
+        data.add(hostChallenge);
+
+        if (data.size() != 16) {
+            throw new TPSException("calculateHostCryptogramSCP02: host cyrptogram source data incorrect size.",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        try {
+            hostCryptogramSCP02 = Util.computeMAC(encSessionKey, data, icv);
+        } catch (EBaseException e) {
+            throw new TPSException("calculateHostCryptogramSCP02: host cyrptogram: Error calculating the MAC value",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("TPSProcessor.calculateHostCrytogramSCP02: returning calculated host cryptogram; "
+                + hostCryptogramSCP02.toHexString());
+
+        return hostCryptogramSCP02;
+
+    }
+
+    public boolean isSCP02() {
+        if (platProtInfo.isGP211() && platProtInfo.isSCP02()) {
+
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean isGP211() {
+
+        if (platProtInfo.isGP211()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public TPSBuffer getDekSessionKeyWrapped() {
+        return dekSessionKeyWrapped;
+    }
+
+    public void setDekSessionKeyWrapped(TPSBuffer dekSessionKeyWrapped) {
+        this.dekSessionKeyWrapped = dekSessionKeyWrapped;
+    }
+
+    public PK11SymKey getDekSessionKey() {
+        return dekSessionKey;
+    }
+
+    public void setDekSessionKey(PK11SymKey dekSessionKey) {
+        this.dekSessionKey = dekSessionKey;
+    }
+
+    public PK11SymKey getRmacSessionKey() {
+        return rmacSessionKey;
+    }
+
+    public void setRmacSessionKey(PK11SymKey rmacSessionKey) {
+        this.rmacSessionKey = rmacSessionKey;
+    }
+
 }
diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
index 3c371a4..c6537e4 100644
--- a/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
+++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
@@ -22,6 +22,7 @@ import java.util.Hashtable;
 
 import org.dogtagpki.server.connector.IRemoteRequest;
 import org.dogtagpki.server.tps.TPSSubsystem;
+import org.dogtagpki.server.tps.channel.SecureChannel;
 import org.dogtagpki.tps.main.TPSBuffer;
 import org.dogtagpki.tps.main.Util;
 
@@ -93,7 +94,7 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
 
         boolean serverKeygen =
                 conf.getBoolean("op.enroll." +
-                tokenType + ".keyGen.encryption.serverKeygen.enable",
+                        tokenType + ".keyGen.encryption.serverKeygen.enable",
                         false);
         String keySet =
                 conf.getString("connector." + connid + "keySet", "defKeySet");
@@ -205,6 +206,143 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
     }
 
     /*
+     * computeSessionKey
+     *
+     * Usage Example:
+     *   TKSRemoteRequestHandler tksReq = new TKSRemoteRequestHandler("tks1");
+     *   TKSComputeSessionKeyResponse responseObj =
+     *     tksReq.computeSessionKey(
+     *      cuid,
+     *      keyInfo,
+     *      card_challenge,
+     *      card_cryptogram,
+     *      host_challenge);
+     *   - on success return, one can say
+     *    TPSBuffer value = responseObj.getSessionKey();
+     *      to get response param value session key
+     *
+     * @param cuid token cuid
+     * @param keyInfo keyInfo
+     * @param card_challenge card challenge
+     * @param card_cryptogram card cryptogram
+     * @param host_challenge host challenge
+     * @return response TKSComputeSessionKeyResponse class object
+     */
+    public TKSComputeSessionKeyResponse computeSessionKeySCP02(
+            TPSBuffer cuid,
+            TPSBuffer keyInfo,
+            TPSBuffer sequenceCounter,
+            TPSBuffer derivationConstant,
+            String tokenType)
+            throws EBaseException {
+
+        CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): begins.");
+        if (cuid == null || keyInfo == null ||
+                sequenceCounter == null
+                || derivationConstant == null) {
+            throw new EBaseException("TKSRemoteRequestHandler: computeSessionKeySCP02(): input parameter null.");
+        }
+
+        IConfigStore conf = CMS.getConfigStore();
+
+        boolean serverKeygen =
+                conf.getBoolean("op.enroll." +
+                        tokenType + ".keyGen.encryption.serverKeygen.enable",
+                        false);
+        String keySet =
+                conf.getString("connector." + connid + "keySet", "defKeySet");
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+
+        String requestString = IRemoteRequest.SERVER_SIDE_KEYGEN + "=" + serverKeygen +
+                "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) +
+                "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(keyInfo) +
+                "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet +
+                "&" + IRemoteRequest.CHANNEL_PROTOCOL + "=" + SecureChannel.SECURE_PROTO_02 +
+                "&" + IRemoteRequest.SEQUENCE_COUNTER + "=" + Util.specialURLEncode(sequenceCounter) +
+                "&" + IRemoteRequest.DERIVATION_CONSTANT + "=" + Util.specialURLEncode(derivationConstant);
+
+        HttpResponse resp =
+                conn.send("computeSessionKey",
+                        requestString
+                        );
+
+        String content = resp.getContent();
+
+        if (content != null && !content.equals("")) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+
+            /*
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             * Note: serverKeygen and !serverKeygen returns different set of
+             *     response values so "missing" might not be bad
+             */
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            String value = (String) response.get(IRemoteRequest.RESPONSE_STATUS);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): status not found.");
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): got content = " + content);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): got status = " + value);
+                ist = Integer.parseInt(value);
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_SessionKey);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_SessionKey);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): got IRemoteRequest.TKS_RESPONSE_SessionKey = ");
+                response.put(IRemoteRequest.TKS_RESPONSE_SessionKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): got IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey = ");
+                response.put(IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_KEK_DesKey);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_KEK_DesKey);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): got IRemoteRequest.TKS_RESPONSE_KEK_DesKey = ");
+                response.put(IRemoteRequest.TKS_RESPONSE_KEK_DesKey, Util.specialDecode(value));
+
+            }
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_KeyCheck);
+
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_KeyCheck);
+
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): got IRemoteRequest.TKS_RESPONSE_KeyCheck = ");
+                response.put(IRemoteRequest.TKS_RESPONSE_KeyCheck, Util.specialDecode(value));
+            }
+
+            CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): ends.");
+
+            return new TKSComputeSessionKeyResponse(response);
+
+        } else {
+            CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): no response content.");
+            throw new EBaseException("TKSRemoteRequestHandler: computeSessionKeySCP02(): no response content.");
+        }
+    }
+
+    /*
      * createKeySetData
      *
      * Usage Example:
@@ -226,7 +364,7 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
     public TKSCreateKeySetDataResponse createKeySetData(
             TPSBuffer NewMasterVer,
             TPSBuffer version,
-            TPSBuffer cuid)
+            TPSBuffer cuid, int protocol, TPSBuffer wrappedDekSessionKey)
             throws EBaseException {
         CMS.debug("TKSRemoteRequestHandler: createKeySetData(): begins.");
         if (cuid == null || NewMasterVer == null || version == null) {
@@ -242,12 +380,21 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
         HttpConnector conn =
                 (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
         CMS.debug("TKSRemoteRequestHandler: createKeySetData(): sending request to tks.");
+
+        String command = IRemoteRequest.TOKEN_NEW_KEYINFO + "=" + Util.specialURLEncode(NewMasterVer) +
+                "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) +
+                "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(version) +
+                "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet +
+                "&" + IRemoteRequest.CHANNEL_PROTOCOL + "=" + protocol;
+
+        if(wrappedDekSessionKey != null) { // We have secure channel protocol 02 trying to upgrade the key set.
+            command += "&" + IRemoteRequest.WRAPPED_DEK_SESSION_KEY + "=" + Util.specialURLEncode(wrappedDekSessionKey);
+        }
+
+
         HttpResponse resp =
                 conn.send("createKeySetData",
-                        IRemoteRequest.TOKEN_NEW_KEYINFO + "=" + Util.specialURLEncode(NewMasterVer) +
-                                "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) +
-                                "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(version) +
-                                "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet);
+                        command);
 
         String content = resp.getContent();
 
diff --git a/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java b/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java
index 9221d1b..e7efcc0 100644
--- a/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java
+++ b/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java
@@ -106,11 +106,13 @@ public class TPSEngine {
     public static final String CFG_APPLET_DIRECTORY = "update.applet.directory";
     public static final String CFG_APPLET_EXTENSION = "general.applet_ext";
 
-    public static final String CFG_CHANNEL_BLOCK_SIZE = "channel.blockSize";
+    public static final String CFG_CHANNEL_BLOCK_SIZE = "channel.blocksize";
     public static final String CFG_CHANNEL_INSTANCE_SIZE = "channel.instanceSize";
     public static final String CFG_CHANNEL_DEFKEY_VERSION = "channel.defKeyVersion";
     public static final String CFG_CHANNEL_APPLET_MEMORY_SIZE = "channel.appletMemorySize";
     public static final String CFG_CHANNEL_DEFKEY_INDEX = "channel.defKeyIndex";
+    public static final String CFG_CHANNEL_DEF_PLATFORM = "channel.defPlatform";
+    public static final String CFG_CHANNEL_DEF_SECURE_PROTO = "channel.defSecureProtocol";
     public static final String CFG_ISSUER_INFO_ENABLE = "issuerinfo.enable";
     public static final String CFG_ISSUER_INFO_VALUE = "issuerinfo.value";
     public static final String CFG_UPDATE_APPLET_ENCRYPTION = "update.applet.encryption";
@@ -119,6 +121,7 @@ public class TPSEngine {
 
     /* default values */
     public static final String CFG_DEF_CARDMGR_INSTANCE_AID = "A0000000030000";
+    public static final String CFG_DEF_CARDMGR_211_INSTANCE_AID = "A000000003000000";
     public static final String CFG_DEF_NETKEY_INSTANCE_AID = "627601FF000000";
     public static final String CFG_DEF_NETKEY_FILE_AID = "627601FF0000";
     public static final String CFG_DEF_NETKEY_OLD_INSTANCE_AID = "A00000000101";
@@ -204,6 +207,45 @@ public class TPSEngine {
         return rc;
     }
 
+    public TKSComputeSessionKeyResponse computeSessionKeySCP02(
+            TPSBuffer cuid,
+            TPSBuffer keyInfo,
+            TPSBuffer sequenceCounter,
+            TPSBuffer derivationConstant,
+            String connId,
+            String tokenType)
+            throws TPSException {
+
+        if (cuid == null || keyInfo == null || sequenceCounter == null || derivationConstant == null
+                || tokenType == null) {
+            throw new TPSException("TPSEngine.computeSessionKeySCP02: Invalid input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("TPSEngine.computeSessionKeySCP02");
+
+        TKSRemoteRequestHandler tks = null;
+
+        TKSComputeSessionKeyResponse resp = null;
+        try {
+            tks = new TKSRemoteRequestHandler(connId);
+            resp = tks.computeSessionKeySCP02(cuid, keyInfo, sequenceCounter, derivationConstant, tokenType);
+        } catch (EBaseException e) {
+            throw new TPSException("TPSEngine.computeSessionKeySCP02: Error computing session key!" + e,
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        int status = resp.getStatus();
+        if (status != 0) {
+            CMS.debug("TPSEngine.computeSessionKeySCP02: Non zero status result: " + status);
+            throw new TPSException("TPSEngine.computeSessionKeySCP02: invalid returned status: " + status);
+
+        }
+
+        return resp;
+
+    }
+
     public TKSComputeSessionKeyResponse computeSessionKey(TPSBuffer cuid,
             TPSBuffer keyInfo,
             TPSBuffer card_challenge,
@@ -330,7 +372,7 @@ public class TPSEngine {
 
     }
 
-    public TPSBuffer createKeySetData(TPSBuffer newMasterVersion, TPSBuffer oldVersion, TPSBuffer cuid, String connId)
+    public TPSBuffer createKeySetData(TPSBuffer newMasterVersion, TPSBuffer oldVersion, int protocol, TPSBuffer cuid, TPSBuffer wrappedDekSessionKey, String connId)
             throws TPSException {
         CMS.debug("TPSEngine.createKeySetData. entering...");
 
@@ -345,7 +387,7 @@ public class TPSEngine {
 
         try {
             tks = new TKSRemoteRequestHandler(connId);
-            resp = tks.createKeySetData(newMasterVersion, oldVersion, cuid);
+            resp = tks.createKeySetData(newMasterVersion, oldVersion, cuid, protocol,wrappedDekSessionKey);
         } catch (EBaseException e) {
 
             throw new TPSException("TPSEngine.createKeySetData, failure to get key set data from TKS",
@@ -541,27 +583,27 @@ public class TPSEngine {
 
     }
 
-  //Check to see if special operations transition is allowed
+    //Check to see if special operations transition is allowed
 
     public boolean isOperationTransitionAllowed(TokenStatus oldState, TokenStatus newState) throws TPSException {
         boolean allowed = true;
 
-        if(transitionList == null) {
+        if (transitionList == null) {
 
             IConfigStore configStore = CMS.getConfigStore();
 
-            String transConfig  = CFG_OPERATIONS_TRANSITIONS;
+            String transConfig = CFG_OPERATIONS_TRANSITIONS;
 
             CMS.debug("TPSEngine.isOperationTransistionAllowed: getting config: " + transConfig);
             try {
-                transitionList = configStore.getString(transConfig,null);
+                transitionList = configStore.getString(transConfig, null);
             } catch (EBaseException e) {
                 throw new TPSException(
                         "TPSProcessor.isOperationTransitionAllowed: Internal error getting config value for operations transition list!",
                         TPSStatus.STATUS_ERROR_MISCONFIGURATION);
             }
 
-            if(transitionList == null) {
+            if (transitionList == null) {
                 throw new TPSException(
                         "TPSProcessor.isOperationTransitionAllowed: Can't find non null config value for operations transition list!",
                         TPSStatus.STATUS_ERROR_MISCONFIGURATION);
@@ -569,21 +611,21 @@ public class TPSEngine {
 
             CMS.debug("TPSEngine.isOperationTransistionAllowed: transitionList is: " + transitionList);
 
-
         }
 
         String transition = oldState.toInt() + ":" + newState.toInt();
 
         CMS.debug("TPSEngine.isOperationTransistionAllowed: checking for transition: " + transition);
 
-        if(transitionList.indexOf(transition) == -1) {
+        if (transitionList.indexOf(transition) == -1) {
             CMS.debug("TPSEngine.isOperationTransistionAllowed: checking for transition: " + transition);
             allowed = false;
         }
 
-        CMS.debug("TPSEngine.isOperationTransistionAllowed: checking for transition: " + transition + " allowed: " + allowed);
+        CMS.debug("TPSEngine.isOperationTransistionAllowed: checking for transition: " + transition + " allowed: "
+                + allowed);
 
-        return  allowed;
+        return allowed;
 
     }
 
diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
index 482236b..5f739f3 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
@@ -301,12 +301,10 @@ public class TPSEnrollProcessor extends TPSProcessor {
         CMS.debug("TPSEnrollProcessor.enroll: Finished updating applet if needed.");
 
         //Check and upgrade keys if called for
-
         SecureChannel channel = checkAndUpgradeSymKeys();
         channel.externalAuthenticate();
 
         //Reset the token's pin, create one if we don't have one already
-
         checkAndHandlePinReset(channel);
         tokenRecord.setKeyInfo(channel.getKeyInfoData().toHexStringPlain());
         String tksConnId = getTKSConnectorID();
@@ -2360,6 +2358,10 @@ public class TPSEnrollProcessor extends TPSProcessor {
 
         TPSBuffer keyCheck = channel.getKeyCheck();
 
+        if (keyCheck == null) {
+            keyCheck = new TPSBuffer();
+        }
+
         CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8 : keyCheck: " + keyCheck.toHexString());
 
         // String ivParams = ssKeyGenResponse.getIVParam();
@@ -2380,7 +2382,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
             CMS.debug("TPSEnrollProcessor.iportPrivateKeyPKC8: null kekWrappedDesKey!");
 
         byte alg = (byte) 0x80;
-        if (kekWrappedDesKey == null || kekWrappedDesKey.size() > 0) {
+        if (kekWrappedDesKey != null && kekWrappedDesKey.size() > 0) {
             alg = (byte) 0x81;
         }
 
@@ -2391,9 +2393,13 @@ public class TPSEnrollProcessor extends TPSProcessor {
         data.add((byte) kekWrappedDesKey.size());
         data.add(kekWrappedDesKey);
         data.add((byte) keyCheck.size());
-        data.add(keyCheck);
+        if (keyCheck.size() > 0) {
+            data.add(keyCheck);
+        }
         data.add((byte) ivParamsBuff.size());
         data.add(ivParamsBuff);
+        CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8: key data outgoing: " + data.toHexString());
+
 
         int pe1 = (cEnrollInfo.getKeyUser() << 4) + cEnrollInfo.getPrivateKeyNumber();
         int pe2 = (cEnrollInfo.getKeyUsage() << 4) + cEnrollInfo.getPublicKeyNumber();
diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
index 1a87f3e..5335b2a 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
@@ -39,6 +39,7 @@ import org.dogtagpki.server.tps.TPSSession;
 import org.dogtagpki.server.tps.TPSSubsystem;
 import org.dogtagpki.server.tps.authentication.AuthUIParameter;
 import org.dogtagpki.server.tps.authentication.TPSAuthenticator;
+import org.dogtagpki.server.tps.channel.PlatformAndSecChannelProtoInfo;
 import org.dogtagpki.server.tps.channel.SecureChannel;
 import org.dogtagpki.server.tps.cms.CARemoteRequestHandler;
 import org.dogtagpki.server.tps.cms.CARevokeCertResponse;
@@ -57,7 +58,6 @@ import org.dogtagpki.server.tps.profile.BaseTokenProfileResolver;
 import org.dogtagpki.server.tps.profile.TokenProfileParams;
 import org.dogtagpki.tps.apdu.APDU;
 import org.dogtagpki.tps.apdu.APDUResponse;
-import org.dogtagpki.tps.apdu.ExternalAuthenticateAPDU.SecurityLevel;
 import org.dogtagpki.tps.apdu.GetDataAPDU;
 import org.dogtagpki.tps.apdu.GetStatusAPDU;
 import org.dogtagpki.tps.apdu.GetVersionAPDU;
@@ -79,6 +79,7 @@ import org.dogtagpki.tps.msg.TPSMessage;
 import org.dogtagpki.tps.msg.TokenPDURequestMsg;
 import org.dogtagpki.tps.msg.TokenPDUResponseMsg;
 import org.mozilla.jss.CryptoManager.NotInitializedException;
+import org.mozilla.jss.crypto.SymmetricKey.NotExtractableException;
 import org.mozilla.jss.pkcs11.PK11SymKey;
 
 import com.netscape.certsrv.apps.CMS;
@@ -104,7 +105,11 @@ public class TPSProcessor {
     public static final int DIVERSIFICATION_DATA_SIZE = 10;
     public static final int CARD_CRYPTOGRAM_OFFSET = 20;
     public static final int CARD_CRYPTOGRAM_SIZE = 8;
+    public static final int CARD_CHALLENGE_SIZE_GP211_SC02 = 6;
+    public static final int SEQUENCE_COUNTER_OFFSET_GP211_SC02 = 12;
+    public static final int SEQUENCE_COUNTER_SIZE_GP211_SC02 = 2;
     public static final int CARD_CHALLENGE_OFFSET = 12;
+    public static final int CARD_CHALLENGE_OFFSET_GP211_SC02 = 14;
     public static final int CARD_CHALLENGE_SIZE = 8;
 
     protected boolean isExternalReg;
@@ -119,6 +124,7 @@ public class TPSProcessor {
     protected String currentTokenOperation;
 
     protected BeginOpMsg beginMsg;
+    private PlatformAndSecChannelProtoInfo platProtInfo;
 
     public TPSProcessor(TPSSession session) {
         setSession(session);
@@ -286,7 +292,7 @@ public class TPSProcessor {
     }
 
     protected TPSBuffer getCplcData() throws IOException, TPSException {
-        CMS.debug("In TPS_Processor.GetData");
+        CMS.debug("In TPS_Processor.");
 
         GetDataAPDU get_data_apdu = new GetDataAPDU();
 
@@ -304,6 +310,24 @@ public class TPSProcessor {
         return respApdu.getData();
     }
 
+    public TPSBuffer getData(byte[] identifier) throws TPSException, IOException {
+        CMS.debug("In TPSProcessor.getData: identifier: " + identifier.toString());
+
+        if (identifier == null || identifier.length != 2) {
+            throw new TPSException("TPSProcessor.getData: Can't get data, invalid input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+        GetDataAPDU get_data_apdu = new GetDataAPDU(identifier);
+
+        APDUResponse respApdu = handleAPDURequest(get_data_apdu);
+
+        if (!respApdu.checkResult()) {
+            throw new TPSException("TPSProcessor.getData: Can't get data!", TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        return respApdu.getData();
+    }
+
     protected TPSBuffer getAppletVersion() throws IOException, TPSException {
         //We return null if no applet present
         // This is not an error, the token can be blank.
@@ -389,6 +413,10 @@ public class TPSProcessor {
         CMS.debug("In TPS_Processor.initializeUpdate.");
         InitializeUpdateAPDU initUpdate = new InitializeUpdateAPDU(keyVersion, keyIndex, randomData);
 
+        int done = 0;
+        if (done == 1)
+            throw new TPSException("TPSProcessor.initializeUpdate. debugging exit...");
+
         APDUResponse resp = handleAPDURequest(initUpdate);
 
         if (!resp.checkResult()) {
@@ -418,7 +446,7 @@ public class TPSProcessor {
         int defKeyVersion = getChannelDefKeyVersion();
         int defKeyIndex = getChannelDefKeyIndex();
 
-        channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, SecurityLevel.SECURE_MSG_MAC_ENC,
+        channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex,
                 getTKSConnectorID());
 
         channel.externalAuthenticate();
@@ -426,15 +454,20 @@ public class TPSProcessor {
         return channel;
     }
 
-    protected SecureChannel setupSecureChannel(byte keyVersion, byte keyIndex, SecurityLevel securityLevel,
+    protected SecureChannel setupSecureChannel(byte keyVersion, byte keyIndex,
             String connId)
             throws IOException, TPSException {
 
         //Assume generating host challenge on TKS, we no longer support not involving the TKS.
 
+        CMS.debug("TPSProcessor.setupSecureChannel: keyVersion: " + keyVersion + " keyIndex: " + keyIndex
+                );
+
         TPSBuffer randomData = computeRandomData(8, connId);
         CMS.debug("TPSProcessor.setupSecureChannel: obtained randomData: " + randomData.toHexString());
 
+        acquireChannelPlatformAndProtocolInfo();
+
         TPSBuffer initUpdateResp = initializeUpdate(keyVersion, keyIndex, randomData);
 
         TPSBuffer key_diversification_data = initUpdateResp.substr(0, DIVERSIFICATION_DATA_SIZE);
@@ -446,10 +479,43 @@ public class TPSProcessor {
         TokenRecord tokenRecord = getTokenRecord();
         tokenRecord.setKeyInfo(key_info_data.toHexStringPlain());
 
-        TPSBuffer card_cryptogram = initUpdateResp.substr(CARD_CRYPTOGRAM_OFFSET, CARD_CRYPTOGRAM_SIZE);
+        TPSBuffer card_cryptogram = null;
+        TPSBuffer sequenceCounter = null;
+
+        boolean isGp211scp02 = false;
+
+        if (platProtInfo.getPlatform().equals(SecureChannel.GP211)) {
+            isGp211scp02 = true;
+        }
+
+        card_cryptogram = initUpdateResp.substr(CARD_CRYPTOGRAM_OFFSET, CARD_CRYPTOGRAM_SIZE);
         CMS.debug("TPSProcessor.setupSecureChannel: card cryptogram: " + card_cryptogram.toHexString());
 
-        TPSBuffer card_challenge = initUpdateResp.substr(CARD_CHALLENGE_OFFSET, CARD_CHALLENGE_SIZE);
+        TPSBuffer card_challenge = null;
+
+        if (isGp211scp02) {
+            sequenceCounter = initUpdateResp.substr(SEQUENCE_COUNTER_OFFSET_GP211_SC02, 2);
+
+            {
+                card_challenge = initUpdateResp
+                        .substr(CARD_CHALLENGE_OFFSET_GP211_SC02, CARD_CHALLENGE_SIZE_GP211_SC02);
+                card_cryptogram = initUpdateResp.substr(CARD_CRYPTOGRAM_OFFSET, CARD_CRYPTOGRAM_SIZE); //new TPSBuffer(canned_card_challenge);
+
+                CMS.debug("TPSProcessor.setupSecureChannel 02: card cryptogram: " + card_cryptogram.toHexString());
+                CMS.debug("TPSProcessor.setupSecureChannel 02: card challenge: " + card_challenge.toHexString());
+                CMS.debug("TPSProcessor.setupSecureChannel 02: host challenge: " + randomData.toHexString());
+
+            }
+
+            //Set the second byte of the keyInfo data to 0x1, this only gives us the secure protocol version 0x2 here.
+            //This will allow symkey to not get confused with that 0x02.
+            CMS.debug("TPSProcessor.setupSecureChannel 02: key Info , before massage: " + key_info_data.toHexString());
+            key_info_data.setAt(1, (byte) 0x1);
+            CMS.debug("TPSProcessor.setupSecureChannel 02: key Info , after massage: " + key_info_data.toHexString());
+
+        } else {
+            card_challenge = initUpdateResp.substr(CARD_CHALLENGE_OFFSET, CARD_CHALLENGE_SIZE);
+        }
         CMS.debug("TPSProcessor.setupSecureChannel: card challenge: " + card_challenge.toHexString());
 
         SecureChannel channel = null;
@@ -457,7 +523,7 @@ public class TPSProcessor {
         try {
             channel = generateSecureChannel(connId, key_diversification_data, key_info_data, card_challenge,
                     card_cryptogram,
-                    randomData);
+                    randomData, sequenceCounter);
         } catch (EBaseException e) {
             throw new TPSException("TPSProcessor.setupSecureChannel: Can't set up secure channel: " + e,
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
@@ -468,27 +534,35 @@ public class TPSProcessor {
     }
 
     protected SecureChannel generateSecureChannel(String connId, TPSBuffer keyDiversificationData,
-            TPSBuffer keyInfoData, TPSBuffer cardChallenge, TPSBuffer cardCryptogram, TPSBuffer hostChallenge)
-            throws EBaseException, TPSException {
+            TPSBuffer keyInfoData, TPSBuffer cardChallenge, TPSBuffer cardCryptogram, TPSBuffer hostChallenge,
+            TPSBuffer sequenceCounter)
+            throws EBaseException, TPSException, IOException {
 
-        CMS.debug("TPSProcessor.generateSecureChannel: entering..");
+        if (connId == null || keyDiversificationData == null || keyInfoData == null || cardChallenge == null
+                || cardCryptogram == null || hostChallenge == null || sequenceCounter == null) {
+            throw new TPSException("TPSProcessor.generateSecureChannel: Invalid input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("TPSProcessor.generateSecureChannel: entering.. keyInfoData: " + keyInfoData.toHexString());
+        CMS.debug("TPSProcessor.generateSecureChannel: isSCP02: " + platProtInfo.isSCP02());
 
         TPSEngine engine = getTPSEngine();
 
         SecureChannel channel = null;
         TPSBuffer hostCryptogram = null;
+        PK11SymKey sessionKey = null;
+        PK11SymKey encSessionKey = null;
+        TKSComputeSessionKeyResponse resp = null;
+        TKSComputeSessionKeyResponse respEnc02 = null;
+        TKSComputeSessionKeyResponse respDek02 = null;
+        TKSComputeSessionKeyResponse respCMac02 = null;
+        TKSComputeSessionKeyResponse respRMac02 = null;
 
-        TKSComputeSessionKeyResponse resp = engine.computeSessionKey(keyDiversificationData, keyInfoData,
-                cardChallenge, hostChallenge, cardCryptogram,
-                connId, getSelectedTokenType());
-
-        hostCryptogram = resp.getHostCryptogram();
-
-        if (hostCryptogram == null) {
-            new TPSException("TPSProcessor.generateSecureChannel: No host cryptogram returned from token!",
-                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
-
-        }
+        PK11SymKey encSessionKeySCP02 = null;
+        PK11SymKey dekSessionKeySCP02 = null;
+        PK11SymKey cmacSessionKeySCP02 = null;
+        PK11SymKey rmacSessionKeySCP02 = null;
 
         PK11SymKey sharedSecret = null;
 
@@ -500,59 +574,207 @@ public class TPSProcessor {
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
 
-        PK11SymKey sessionKey = null;
-        PK11SymKey encSessionKey = null;
         String tokenName = "Internal Key Storage Token";
 
-        try {
-            TPSBuffer sessionKeyWrapped = resp.getSessionKey();
-            TPSBuffer encSessionKeyWrapped = resp.getEncSessionKey();
+        if (platProtInfo.isGP201() || platProtInfo.isSCP01()) {
+
+            resp = engine.computeSessionKey(keyDiversificationData, keyInfoData,
+                    cardChallenge, hostChallenge, cardCryptogram,
+                    connId, getSelectedTokenType());
 
-            sessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
-                    sessionKeyWrapped.toBytesArray());
+            hostCryptogram = resp.getHostCryptogram();
 
-            if (sessionKey == null) {
-                CMS.debug("TPSProcessor.generateSecureChannel: Can't extract session key!");
-                throw new TPSException("TPSProcessor.generateSecureChannel: Can't extract session key!",
+            if (hostCryptogram == null) {
+                throw new TPSException("TPSProcessor.generateSecureChannel: No host cryptogram returned from token!",
                         TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+
             }
-            CMS.debug("TPSProcessor.generateSecureChannel: retrieved session key: " + sessionKey);
 
-            encSessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
-                    encSessionKeyWrapped.toBytesArray());
+            try {
+                TPSBuffer sessionKeyWrapped = resp.getSessionKey();
+                TPSBuffer encSessionKeyWrapped = resp.getEncSessionKey();
+
+                sessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
+                        sessionKeyWrapped.toBytesArray());
+
+                if (sessionKey == null) {
+                    CMS.debug("TPSProcessor.generateSecureChannel: Can't extract session key!");
+                    throw new TPSException("TPSProcessor.generateSecureChannel: Can't extract session key!",
+                            TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+                }
+                CMS.debug("TPSProcessor.generateSecureChannel: retrieved session key: " + sessionKey);
+
+                encSessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
+                        encSessionKeyWrapped.toBytesArray());
+
+                if (encSessionKey == null) {
+                    CMS.debug("TPSProcessor.generateSecureChannel: Can't extract enc session key!");
+                    throw new TPSException("TPSProcessor.generateSecureChannel: Can't extract enc session key!",
+                            TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+                }
+
+                CMS.debug("TPSProcessor.generateSecureChannel: retrieved enc session key: " + encSessionKey);
 
-            if (encSessionKey == null) {
-                CMS.debug("TPSProcessor.generateSecureChannel: Can't extract enc session key!");
-                throw new TPSException("TPSProcessor.generateSecureChannel: Can't extract enc session key!",
+                TPSBuffer drmDesKey = null;
+                TPSBuffer kekDesKey = null;
+                TPSBuffer keyCheck = null;
+
+                drmDesKey = resp.getDRM_Trans_DesKey();
+                keyCheck = resp.getKeyCheck();
+                kekDesKey = resp.getKekWrappedDesKey();
+
+                if (checkServerSideKeyGen(connId)) {
+
+                    CMS.debug("TPSProcessor.generateSecureChannel: drmDesKey: " + drmDesKey + " kekDesKey : "
+                            + kekDesKey
+                            + " keyCheck: " + keyCheck);
+                    //ToDo handle server side keygen.
+
+                }
+                channel = new SecureChannel(this, sessionKey, encSessionKey, drmDesKey,
+                        kekDesKey, keyCheck, keyDiversificationData, cardChallenge,
+                        cardCryptogram, hostChallenge, hostCryptogram, keyInfoData, platProtInfo);
+
+            } catch (Exception e) {
+                CMS.debug(e);
+                e.printStackTrace();
+                throw new TPSException("TPSProcessor.generateSecureChannel: Problem extracting session keys! " + e,
                         TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
             }
 
-            CMS.debug("TPSProcessor.generateSecureChannel: retrieved enc session key: " + encSessionKey);
-        } catch (Exception e) {
-            CMS.debug(e);
-            e.printStackTrace();
-            throw new TPSException("TPSProcessor.generateSecureChannel: Problem extracting session keys! " + e,
-                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
 
-        TPSBuffer drmDesKey = null;
-        TPSBuffer kekDesKey = null;
-        TPSBuffer keyCheck = null;
+        if (platProtInfo.isGP211() && platProtInfo.isSCP02()) {
+            //Generate the 4 keys we need for SCP02, Impl 15
+
+            CMS.debug("TPSProcessor.generateSecureChannel Trying secure channel protocol 02");
+            respEnc02 = engine.computeSessionKeySCP02(keyDiversificationData, keyInfoData,
+                    sequenceCounter, new TPSBuffer(SecureChannel.ENCDerivationConstant),
+                    connId, getSelectedTokenType());
 
-        drmDesKey = resp.getDRM_Trans_DesKey();
-        keyCheck = resp.getKeyCheck();
-        kekDesKey = resp.getKekWrappedDesKey();
+            TPSBuffer encSessionKeyWrappedSCP02 = respEnc02.getSessionKey();
+            encSessionKeySCP02 = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
+                    encSessionKeyWrappedSCP02.toBytesArray());
+
+            if (encSessionKeySCP02 == null) {
+                CMS.debug("TPSProcessor.generateSecureChannel: Can't extract the SCP02 enc session key!");
+                throw new TPSException("TPSProcessor.generateSecureChannel: Can't the emc SCP02 session keys!",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+            TPSBuffer debugEncKeyData = null;
+            try {
+                debugEncKeyData = new TPSBuffer(encSessionKeySCP02.getKeyData());
+            } catch (NotExtractableException e) {
+                debugEncKeyData = new TPSBuffer();
+            }
+
+            CMS.debug("Debug only encSessionKeyDAta: " + debugEncKeyData.toHexString());
+
+            respCMac02 = engine.computeSessionKeySCP02(keyDiversificationData, keyInfoData,
+                    sequenceCounter, new TPSBuffer(SecureChannel.C_MACDerivationConstant),
+                    connId, getSelectedTokenType());
+
+            TPSBuffer cmacSessionKeyWrappedSCP02 = respCMac02.getSessionKey();
+
+            cmacSessionKeySCP02 = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
+                    cmacSessionKeyWrappedSCP02.toBytesArray());
+
+            if (cmacSessionKeySCP02 == null) {
+                CMS.debug("TPSProcessor.generateSecureChannel: Can't extract the SCP02 cmac session key!");
+                throw new TPSException("TPSProcessor.generateSecureChannel: Can't the s,ac SCP02 session keys!",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+            TPSBuffer debugcmacKeyData = null;
+            try {
+                debugcmacKeyData = new TPSBuffer(cmacSessionKeySCP02.getKeyData());
+            } catch (NotExtractableException e) {
+                debugcmacKeyData = new TPSBuffer();
+            }
 
-        if (checkServerSideKeyGen(connId)) {
+            CMS.debug("Debug only cmacSessionKeyDAta: " + debugcmacKeyData.toHexString());
+
+            respRMac02 = engine.computeSessionKeySCP02(keyDiversificationData, keyInfoData,
+                    sequenceCounter, new TPSBuffer(SecureChannel.R_MACDerivationConstant),
+                    connId, getSelectedTokenType());
+
+            TPSBuffer rmacSessionKeyWrappedSCP02 = respRMac02.getSessionKey();
+
+            rmacSessionKeySCP02 = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
+                    rmacSessionKeyWrappedSCP02.toBytesArray());
+
+            if (rmacSessionKeySCP02 == null) {
+                CMS.debug("TPSProcessor.generateSecureChannel: Can't extract the SCP02 cmac session key!");
+                throw new TPSException("TPSProcessor.generateSecureChannel: Can't the cmac SCP02 session keys!",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+            TPSBuffer debugrmacKeyData = null;
+            try {
+                debugrmacKeyData = new TPSBuffer(rmacSessionKeySCP02.getKeyData());
+            } catch (NotExtractableException e) {
+                debugrmacKeyData = new TPSBuffer();
+            }
+
+            CMS.debug("Debug only rmacSessionKeyDAta: " + debugrmacKeyData.toHexString());
+
+            respDek02 = engine.computeSessionKeySCP02(keyDiversificationData, keyInfoData,
+                    sequenceCounter, new TPSBuffer(SecureChannel.DEKDerivationConstant),
+                    connId, getSelectedTokenType());
+
+            CMS.debug("Past engine.computeSessionKeyData: After dek key request.");
+
+            TPSBuffer dekSessionKeyWrappedSCP02 = respDek02.getSessionKey();
+
+            dekSessionKeySCP02 = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, sharedSecret,
+                    dekSessionKeyWrappedSCP02.toBytesArray());
+
+            if (dekSessionKeySCP02 == null) {
+                CMS.debug("TPSProcessor.generateSecureChannel: Can't extract the SCP02 dek session key!");
+                throw new TPSException("TPSProcessor.generateSecureChannel: Can't the dek SCP02 session keys!",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+            TPSBuffer drmDesKey = null;
+            TPSBuffer kekDesKey = null;
+            TPSBuffer keyCheck = null;
+
+            drmDesKey = respDek02.getDRM_Trans_DesKey();
+            kekDesKey = respDek02.getKekWrappedDesKey();
+            keyCheck = respDek02.getKeyCheck();
+
+            if (drmDesKey == null || kekDesKey == null) {
+                CMS.debug("TPSProcessor.generateSecureChannel: Can't get drmDesKey or kekDesKey from TKS when processing the DEK session key!");
+                throw new TPSException(
+                        "TPSProcessor.generateSecureChannel: Can't get drmDesKey or kekDesKey from TKS when processing the DEK session key!",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+            TPSBuffer debugdekKeyData = null;
+            try {
+                debugdekKeyData = new TPSBuffer(dekSessionKeySCP02.getKeyData());
+            } catch (NotExtractableException e) {
+                debugdekKeyData = new TPSBuffer();
+            }
 
-            CMS.debug("TPSProcessor.generateSecureChannel: drmDesKey: " + drmDesKey + " kekDesKey : " + kekDesKey
-                    + " keyCheck: " + keyCheck);
-            //ToDo handle server side keygen.
+            CMS.debug("Debug only dekSessionKeyDAta: " + debugdekKeyData.toHexString());
+            CMS.debug("Debug only dekSessionKeyWrapped: " + dekSessionKeyWrappedSCP02.toHexString());
 
+            channel = new SecureChannel(this, encSessionKeySCP02, cmacSessionKeySCP02,
+                    rmacSessionKeySCP02, dekSessionKeySCP02, drmDesKey, kekDesKey, keyCheck,
+                    keyDiversificationData,
+                    keyInfoData, sequenceCounter, hostChallenge, cardChallenge, cardCryptogram, platProtInfo);
+
+            channel.setDekSessionKeyWrapped(dekSessionKeyWrappedSCP02);
+
+        }
+
+        if (channel == null) {
+            throw new TPSException(
+                    "TPSProcessor.generateSecureChannel: Can't create Secure Channel, possibly invalid secure channel protocol requested.",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
-        channel = new SecureChannel(this, sessionKey, encSessionKey, drmDesKey,
-                kekDesKey, keyCheck, keyDiversificationData, cardChallenge,
-                cardCryptogram, hostChallenge, hostCryptogram, keyInfoData);
 
         return channel;
     }
@@ -584,18 +806,12 @@ public class TPSProcessor {
     }
 
     protected void checkAndUpgradeApplet(AppletInfo appletInfo) throws TPSException, IOException {
-        // TODO Auto-generated method stub
 
         CMS.debug("checkAndUpgradeApplet: entering..");
 
-        SecurityLevel securityLevel = SecurityLevel.SECURE_MSG_MAC;
-
-        boolean useEncryption = checkUpdateAppletEncryption();
-
         String tksConnId = getTKSConnectorID();
-        if (useEncryption)
-            securityLevel = SecurityLevel.SECURE_MSG_MAC_ENC;
 
+        int upgraded = 0;
         if (checkForAppletUpdateEnabled()) {
 
             String targetAppletVersion = checkForAppletUpgrade("op." + currentTokenOperation);
@@ -608,18 +824,26 @@ public class TPSProcessor {
 
             if (targetAppletVersion.compareTo(currentAppletVersion) != 0) {
 
+                upgraded = 1;
                 CMS.debug("TPSProcessor.checkAndUpgradeApplet: Upgrading applet to : " + targetAppletVersion);
-                upgradeApplet("op." + currentTokenOperation, targetAppletVersion, securityLevel, getBeginMessage()
+                upgradeApplet("op." + currentTokenOperation, targetAppletVersion, getBeginMessage()
                         .getExtensions(),
                         tksConnId, 5, 12);
-            } else {
-                CMS.debug("TPSProcessor.checkAndUpgradeApplet: applet already at correct version.");
             }
         }
 
+        if (upgraded == 0) {
+            CMS.debug("TPSProcessor.checkAndUpgradeApplet: applet already at correct version or upgrade disabled.");
+
+            // We didn't need to upgrade the applet but create new channel for now.
+            selectCardManager();
+            setupSecureChannel();
+
+        }
+
     }
 
-    protected void upgradeApplet(String operation, String new_version, SecurityLevel securityLevel,
+    protected void upgradeApplet(String operation, String new_version,
             Map<String, String> extensions, String connId, int startProgress, int endProgress) throws IOException,
             TPSException {
 
@@ -659,17 +883,16 @@ public class TPSProcessor {
             throw new TPSException("TPSProcessor.upgradeApplet: Can't selelect the card manager!");
         }
 
-        SecureChannel channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, securityLevel, connId);
+        SecureChannel channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, connId);
 
         channel.externalAuthenticate();
+
         channel.deleteFileX(netkeyAIDBuff);
         channel.deleteFileX(netkeyPAIDBuff);
 
         // Next step will be to load the applet file to token.
 
-        TPSBuffer empty = new TPSBuffer();
-
-        channel.installLoad(netkeyPAIDBuff, empty, appletData.length);
+        channel.installLoad(netkeyPAIDBuff, cardMgrAIDBuff, appletData.length);
 
         TPSBuffer appletDataBuff = new TPSBuffer(appletData);
 
@@ -691,6 +914,7 @@ public class TPSProcessor {
 
     public void selectCoolKeyApplet() throws TPSException, IOException {
 
+        CMS.debug("In selectCoolKeyApplet!");
         TPSBuffer netkeyAIDBuff = getNetkeyAID();
 
         APDUResponse select = selectApplet((byte) 0x04, (byte) 0x00, netkeyAIDBuff);
@@ -1394,13 +1618,13 @@ public class TPSProcessor {
         if (vals != null) {
             for (String val : vals) {
                 CMS.debug(method + ": retrieved certsToDelete:" + val);
-                
+
                 //  Each cert is represented as
                 //     (serial#, caID, revokeOnDelete)
                 //  e.g.
                 //     (234, ca1, true)
                 //     note: number above is in decimal
-                 
+
                 String[] items = val.split(",");
                 ExternalRegCertToDelete erCert =
                         new ExternalRegCertToDelete();
@@ -1674,11 +1898,9 @@ public class TPSProcessor {
         CMS.debug("TPSProcessor.format: appletVersion found: " + appletVersion + " requiredVersion: "
                 + appletRequiredVersion);
 
-        SecurityLevel secLevel = SecurityLevel.SECURE_MSG_MAC_ENC;
-
         String tksConnId = getTKSConnectorID();
 
-        upgradeApplet(TPSEngine.OP_FORMAT_PREFIX, appletRequiredVersion, secLevel,
+        upgradeApplet(TPSEngine.OP_FORMAT_PREFIX, appletRequiredVersion,
                 beginMsg.getExtensions(), tksConnId,
                 10, 90);
         CMS.debug("TPSProcessor.format: Completed applet upgrade.");
@@ -1738,8 +1960,6 @@ public class TPSProcessor {
             UnsupportedEncodingException {
         if (checkIssuerInfoEnabled()) {
 
-            SecurityLevel secLevel = SecurityLevel.SECURE_MSG_MAC_ENC;
-
             String tksConnId = getTKSConnectorID();
 
             int defKeyIndex = getChannelDefKeyIndex();
@@ -1751,7 +1971,7 @@ public class TPSProcessor {
                 channel = origChannel;
             } else {
 
-                channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, secLevel, tksConnId);
+                channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, tksConnId);
                 channel.externalAuthenticate();
 
             }
@@ -1896,6 +2116,8 @@ public class TPSProcessor {
         String profileConfig = "config.Profiles." + selectedTokenType + ".state";
         String profileState = null;
 
+        CMS.debug("TPSProcessor.checkProfileStateOK: config value to check: " + profileConfig);
+
         try {
             profileState = configStore.getString(profileConfig, TPSEngine.CFG_ENABLED);
         } catch (EBaseException e) {
@@ -2121,7 +2343,7 @@ public class TPSProcessor {
         IConfigStore configStore = CMS.getConfigStore();
         try {
 
-            cardMgrAID = configStore.getString(TPSEngine.CFG_DEF_CARDMGR_INSTANCE_AID,
+            cardMgrAID = configStore.getString(TPSEngine.CFG_APPLET_CARDMGR_INSTANCE_AID,
                     TPSEngine.CFG_DEF_CARDMGR_INSTANCE_AID);
 
         } catch (EBaseException e1) {
@@ -2520,7 +2742,6 @@ public class TPSProcessor {
             try {
 
                 channel = setupSecureChannel((byte) requiredVersion, (byte) defKeyIndex,
-                        SecurityLevel.SECURE_MSG_MAC_ENC,
                         getTKSConnectorID());
 
             } catch (TPSException e) {
@@ -2553,15 +2774,46 @@ public class TPSProcessor {
                 TPSBuffer curKeyInfo = channel.getKeyInfoData();
                 TPSEngine engine = getTPSEngine();
 
-                TPSBuffer keySetData = engine.createKeySetData(newVersion, curKeyInfo,
-                        channel.getKeyDiversificationData(), connId);
+                int protocol = 1;
+                if (channel.isSCP02()) {
+                    protocol = 2;
+                }
+
+                TPSBuffer keySetData = engine.createKeySetData(newVersion, curKeyInfo, protocol,
+                        channel.getKeyDiversificationData(), channel.getDekSessionKeyWrapped(), connId);
 
                 CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: new keySetData from TKS: " + keySetData.toHexString());
 
                 byte curVersion = curKeyInfo.at(0);
                 byte curIndex = curKeyInfo.at(1);
 
-                channel.putKeys(curVersion, curIndex, keySetData);
+                int done = 0;
+                if (done == 1)
+                    throw new TPSException("TPSProcessor.checkAndUpgradeSymKeys: end of progress.");
+
+                try {
+                    channel.putKeys(curVersion, curIndex, keySetData);
+                } catch (TPSException e) {
+
+                    CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: failed to put key, checking to see if this a SCP02 with 0xFF default key set.");
+
+                    if (protocol == 2 && curVersion == (byte) 0xff) {
+                        CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: failed to put key, but we have SCP02 and the 0xFF dev key, try again.");
+
+                        byte[] nv_dev = { (byte) 0x1, (byte) 0x1 };
+                        TPSBuffer devKeySetData = engine.createKeySetData(new TPSBuffer(nv_dev), curKeyInfo, protocol,
+                                channel.getKeyDiversificationData(), channel.getDekSessionKeyWrapped(), connId);
+
+                        CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: about to get rid of keyset 0xFF and replace it with keyset 0x1 with developer key set");
+                        channel.putKeys((byte) 0x0, (byte) 0x1, devKeySetData);
+
+                        CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: We've only upgraded to the dev key set on key set #01, will have to try again to upgrade to #02");
+
+                    } else {
+                        throw e;
+                    }
+
+                }
 
                 String curVersionStr = curKeyInfo.toHexString();
                 String newVersionStr = newVersion.toHexString();
@@ -2571,11 +2823,9 @@ public class TPSProcessor {
 
                 CMS.debug("TPSProcessor.checkAndUpgradeSymKeys: curVersionStr: " + curVersionStr + " newVersionStr: "
                         + newVersionStr);
-
                 selectCoolKeyApplet();
 
                 channel = setupSecureChannel((byte) requiredVersion, (byte) defKeyIndex,
-                        SecurityLevel.SECURE_MSG_MAC_ENC,
                         getTKSConnectorID());
 
             } else {
@@ -2588,7 +2838,7 @@ public class TPSProcessor {
 
             defKeyVersion = getChannelDefKeyVersion();
 
-            channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex, SecurityLevel.SECURE_MSG_MAC_ENC,
+            channel = setupSecureChannel((byte) defKeyVersion, (byte) defKeyIndex,
                     getTKSConnectorID());
 
         }
@@ -2878,6 +3128,144 @@ public class TPSProcessor {
         }
     }
 
+    public void acquireChannelPlatformAndProtocolInfo() throws TPSException, IOException {
+
+        if (platProtInfo == null) {
+            platProtInfo = new PlatformAndSecChannelProtoInfo();
+        } else { // We don't need this any more
+            return;
+        }
+
+        try {
+            gp211GetSecureChannelProtocolDetails();
+        } catch (TPSException e) {
+            CMS.debug("TPSProcessor.acquireChannelPlatformProtocolInfo: Error getting gp211 protocol data, assume scp01 "
+                    + e);
+            platProtInfo.setPlatform(SecureChannel.GP201);
+            platProtInfo.setProtocol(SecureChannel.SECURE_PROTO_01);
+
+        }
+
+        if(platProtInfo.isGP211() && platProtInfo.isSCP02()) {
+             // We only support impl 15, the most common, at this point.
+
+            if(platProtInfo.getImplementation() != SecureChannel.GP211_SCP02_IMPL_15) {
+                throw new TPSException("SecureChannel.acquireChannelPlatformAndProtocolInfo card returning a non supported implementation for SCP02 " + platProtInfo.getImplementation());
+            }
+        }
+
+    }
+
+    public void gp211GetSecureChannelProtocolDetails() throws TPSException, IOException {
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: Query card for secure channel protocol details for gp211.");
+
+        TPSBuffer data = null;
+        TPSBuffer keyData = null;
+
+        selectCardManager();
+        try {
+
+            data = getData(SecureChannel.GP211_GET_DATA_CARD_DATA);
+            keyData = getData(SecureChannel.GP211_GET_DATA_KEY_INFO);
+
+        } catch (TPSException e) {
+            CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: Card can't understand GP211! " + e);
+
+            throw e;
+
+        }
+
+        if (data.size() < 5) {
+            throw new TPSException("TPSProcessor.gp211GetSecureChannelProtocolDetails: invalide return data.",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: returned data: " + data.toHexString());
+
+        // Now process the GP211 data returned by the card.
+
+        int offset = 0;
+        int totalLength = 0;
+        int length = 0;
+
+        if (data.at(offset) == (byte) 0x66) {
+            offset++;
+
+            totalLength = data.getIntFrom1Byte(offset++);
+            offset++;
+
+        } else {
+            offset++;
+            totalLength = data.getIntFrom1Byte(offset++);
+
+        }
+
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: totalLength: " + totalLength);
+
+        if (totalLength == 0 || totalLength >= data.size()) {
+            throw new TPSException("TPSProcessor.gp211GetSecureChannelProtocolDetails: Invalid return data.",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        offset++;
+
+        length = data.getIntFrom1Byte(offset++);
+
+        TPSBuffer oidCardRecognitionData = data.substr(offset, length);
+
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: oidCardRecognitionData: "
+                + oidCardRecognitionData.toHexString());
+
+        platProtInfo.setOidCardRecognitionData(oidCardRecognitionData);
+
+        offset += length + 2 + 1;
+
+        length = data.getIntFrom1Byte(offset++);
+
+        TPSBuffer oidCardManagementTypeAndVer = data.substr(offset, length);
+
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: oidCardManagementTypeAndVer: "
+                + oidCardManagementTypeAndVer.toHexString());
+
+        platProtInfo.setOidCardManagementTypeAndVer(oidCardManagementTypeAndVer);
+
+        offset += length + 2 + 1;
+
+        length = data.getIntFrom1Byte(offset++);
+
+        TPSBuffer oidCardIdentificationScheme = data.substr(offset, length);
+
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: oidCardIdentificationScheme: "
+                + oidCardIdentificationScheme.toHexString());
+
+        platProtInfo.setOidCardIdentificationScheme(oidCardIdentificationScheme);
+
+        offset += length + 2 + 1;
+
+        length = data.getIntFrom1Byte(offset++);
+
+        TPSBuffer oidSecureChannelProtocol = data.substr(offset, length);
+
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: oidSecureChannelProtocol: "
+                + oidSecureChannelProtocol.toHexString());
+
+        byte protocol = oidSecureChannelProtocol.at(length - 2);
+        byte implementation = oidSecureChannelProtocol.at(length - 1);
+
+        platProtInfo.setProtocol(protocol);
+        platProtInfo.setImplementation(implementation);
+        platProtInfo.setKeysetInfoData(keyData);
+        platProtInfo.setPlatform(SecureChannel.GP211);
+
+        CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: protocol: " + protocol + " implementation: "
+                + implementation + " keyInfoData: " + keyData.toHexString());
+
+    }
+
+    public PlatformAndSecChannelProtoInfo getChannelPlatformAndProtocolInfo() {
+        return platProtInfo;
+    }
+
     public static void main(String[] args) {
     }
 
-- 
1.8.3.1

