From c893fe67fc56b9f81c286e8028037eed41522fe8 Mon Sep 17 00:00:00 2001
From: Jack Magne <jmagne@dhcp-16-206.sjc.redhat.com>
Date: Wed, 1 Jun 2016 10:23:33 -0700
Subject: [PATCH] First cut of scp03 support. Supports the g&d smartcafe out of
 the box.

Developer keyset token operations and key change over supported.

Caveats.

-The diversification step going from master key to card key uses DES3 as required for the token.
-After that point, everything is scp03 to the spec with minor excpetions so far.

Supports 128 bit AES for now. Will resolve this.

Minor config tweaks:

TPS

Symmetric Key Changeover

Use this applet for scp03:

RSA/KeyRecovery/GP211/SCP02/SCP03 applet : 1.5.558cdcff.ijc

TKS:

Symmetric Key Changeover

tks.mk_mappings.#02#03=internal:new_master

tks.defKeySet.mk_mappings.#02#03=internal:new_master

Use the uncommented one because scp03 returns a different key set data string.

ToDo:

-Support the rest of the AES sizes other than 128.
-Support optional RMAC apdu.
-Test and adjust the config capability for other tokens.
-Support AES master key. Right now the standard key ends up creating AES card and session keys.
---
 .../src/org/dogtagpki/tps/TPSConnection.java       |   8 +-
 base/common/src/org/dogtagpki/tps/apdu/APDU.java   |  68 ++
 base/common/src/org/dogtagpki/tps/main/Util.java   | 570 ++++++++++++++-
 .../src/org/dogtagpki/tps/msg/TPSMessage.java      |   6 +
 .../src/com/netscape/cms/servlet/tks/GPParams.java | 108 +++
 .../cms/src/com/netscape/cms/servlet/tks/KDF.java  |  59 +-
 .../netscape/cms/servlet/tks/NistSP800_108KDF.java | 426 ++++++++++-
 .../cms/servlet/tks/SecureChannelProtocol.java     | 779 ++++++++++++++++++--
 .../com/netscape/cms/servlet/tks/StandardKDF.java  |  41 +-
 .../com/netscape/cms/servlet/tks/TokenServlet.java | 800 ++++++++++++++++++++-
 .../dogtagpki/server/connector/IRemoteRequest.java |   2 +
 .../channel/PlatformAndSecChannelProtoInfo.java    |   8 +
 .../server/tps/channel/SecureChannel.java          | 207 +++++-
 .../tps/cms/TKSComputeSessionKeyResponse.java      |   8 +
 .../server/tps/cms/TKSRemoteRequestHandler.java    | 157 +++-
 .../org/dogtagpki/server/tps/engine/TPSEngine.java |  58 +-
 .../server/tps/processor/TPSEnrollProcessor.java   |  10 +-
 .../server/tps/processor/TPSProcessor.java         | 162 ++++-
 18 files changed, 3314 insertions(+), 163 deletions(-)
 create mode 100644 base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java

diff --git a/base/common/src/org/dogtagpki/tps/TPSConnection.java b/base/common/src/org/dogtagpki/tps/TPSConnection.java
index c5a971e..dd9538b 100644
--- a/base/common/src/org/dogtagpki/tps/TPSConnection.java
+++ b/base/common/src/org/dogtagpki/tps/TPSConnection.java
@@ -93,9 +93,11 @@ public class TPSConnection {
         String s = message.encode();
 
         // don't print the pdu_data
-        int idx = s.lastIndexOf("pdu_data=");
-        String toDebug = null; 
-        if (idx == -1)
+        int idx =  s.lastIndexOf("pdu_data=");
+
+        int debug = 0;
+        String toDebug = null;
+        if (idx == -1 || debug == 1)
             CMS.debug("TPSConnection.write: Writing: " + s);
         else {
             toDebug = s.substring(0, idx-1);
diff --git a/base/common/src/org/dogtagpki/tps/apdu/APDU.java b/base/common/src/org/dogtagpki/tps/apdu/APDU.java
index 009c470..545c842 100644
--- a/base/common/src/org/dogtagpki/tps/apdu/APDU.java
+++ b/base/common/src/org/dogtagpki/tps/apdu/APDU.java
@@ -219,6 +219,74 @@ public abstract class APDU {
         data.set(dataEncrypted);
     }
 
+    //Used for scp03, provide a padding buffer of the requested size, first byte set to 0x80
+    public void padBuffer80(TPSBuffer buffer, int blockSize) {
+        int length = buffer.size();
+
+        int padSize = 0;
+
+        if( buffer == null || blockSize <= 0)
+            return;
+
+        int rem = length % blockSize ;
+
+        padSize = blockSize - rem;
+
+        TPSBuffer padding = new TPSBuffer( padSize);
+        padding.setAt(0, (byte) 0x80);
+
+        buffer.add(padding);
+
+    }
+
+    //Assume the whole buffer is to be incremented
+    //Used for SCP03 encrypted apdu messages
+    public void incrementBuffer(TPSBuffer buffer) {
+
+        if(buffer == null)
+            return;
+
+        int len = buffer.size();
+
+        if (len < 1)
+            return;
+        int offset = 0;
+        for (short i = (short) (offset + len - 1); i >= offset; i--) {
+            byte cur = buffer.at(i);
+            if (cur != (byte) 0xFF) {
+                    cur++;
+                    buffer.setAt(i, cur);
+                    break;
+            } else
+                    buffer.setAt(i,(byte) 0x00);
+        }
+
+        System.out.println("enc buffer: " + buffer.toHexString());
+    }
+
+    //Implement SCP03 encrypted apdu scheme.
+    public void secureMessageSCP03(PK11SymKey encKey, TPSBuffer encryptionCounter) throws EBaseException {
+
+        TPSBuffer data = this.getData();
+
+        //increment encryption count and encrypt it for new iv
+
+        incrementBuffer(encryptionCounter);
+
+        if (data != null && data.size() > 0) {
+
+            padBuffer80(data, 16);
+
+            TPSBuffer encryptedCounter = Util.encryptDataAES(encryptionCounter, encKey, null);
+
+            TPSBuffer encryptedData = Util.encryptDataAES(data, encKey, encryptedCounter);
+
+            data.set(encryptedData);
+
+        }
+
+    }
+
     public void secureMessageSCP02(PK11SymKey encKey) throws EBaseException {
 
         if (encKey == null) {
diff --git a/base/common/src/org/dogtagpki/tps/main/Util.java b/base/common/src/org/dogtagpki/tps/main/Util.java
index b212478..b640d0a 100644
--- a/base/common/src/org/dogtagpki/tps/main/Util.java
+++ b/base/common/src/org/dogtagpki/tps/main/Util.java
@@ -24,6 +24,9 @@ import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.Calendar;
 
@@ -34,10 +37,14 @@ import netscape.security.x509.SubjectKeyIdentifierExtension;
 import netscape.security.x509.X509CertImpl;
 
 import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.crypto.BadPaddingException;
 import org.mozilla.jss.crypto.Cipher;
 import org.mozilla.jss.crypto.CryptoToken;
 import org.mozilla.jss.crypto.EncryptionAlgorithm;
 import org.mozilla.jss.crypto.IVParameterSpec;
+import org.mozilla.jss.crypto.IllegalBlockSizeException;
+import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.TokenException;
 import org.mozilla.jss.pkcs11.PK11SymKey;
 
 import com.netscape.certsrv.apps.CMS;
@@ -47,6 +54,13 @@ import com.netscape.symkey.SessionKey;
 
 public class Util {
 
+    //SCP03 AES-CMAC related constants
+    private static final byte AES_CMAC_CONSTANT = (byte) 0x87;
+    private static final int AES_CMAC_BLOCK_SIZE = 16;
+
+    public static final byte CARD_CRYPTO_KDF_CONSTANT_SCP03 = 0x0;
+    public static final byte HOST_CRYPTO_KDF_CONSTANT_SCP03 = 0x1;
+
     public Util() {
     }
 
@@ -84,11 +98,6 @@ public class Util {
         return Integer.toHexString(val);
     }
 
-    public static void main(String[] args) {
-        // TODO Auto-generated method stub
-
-    }
-
     public static String uriDecode(String encoded) throws UnsupportedEncodingException {
 
         return URLDecoder.decode(encoded, "UTF-8");
@@ -332,6 +341,196 @@ public class Util {
         return output;
     }
 
+    //Use AES-CMAC (SCP03, counter method) to calculate cryptogram, constant determines whether it is a card or host cryptogram
+    public static TPSBuffer compute_AES_CMAC_Cryptogram(SymmetricKey symKey, TPSBuffer context, byte kdfConstant,
+            int outputSizeBytes) throws EBaseException {
+
+        String method = "Util compute_AES_Crypto:";
+        // 11 bytes label
+        byte[] label = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+        // sanity checking
+
+        if (symKey == null || context == null || outputSizeBytes <= 0) {
+            throw new EBaseException(method + " Invalid input!");
+        }
+
+        TPSBuffer data = new TPSBuffer();
+        int outputBits = outputSizeBytes * 8;
+
+        //output size of cmac PRF
+        final int h = 128;
+
+        int remainder = outputBits % h;
+
+        //calculate counter size
+        int n = 0;
+        if (remainder == 0) {
+            n = outputBits / h;
+        } else {
+            n = outputBits / h + 1;
+        }
+
+        byte b1 = (byte) ((outputBits >> 8) & 0xFF);
+        byte b2 = (byte) (outputBits & 0xFF);
+
+        TPSBuffer outputBitsBinary = new TPSBuffer(2);
+        outputBitsBinary.setAt(0, b1);
+        outputBitsBinary.setAt(1, b2);
+
+        data.addBytes(label);
+        data.add(kdfConstant);
+        data.add((byte) 0x0);
+        data.add(outputBitsBinary);
+
+        TPSBuffer output = new TPSBuffer();
+        TPSBuffer input = new TPSBuffer();
+
+        TPSBuffer kI = null;
+
+        for (int i = 1; i <= n; i++) {
+            input.add(data);
+            input.add((byte) i);
+            input.add(context);
+
+            kI = Util.computeAES_CMAC(symKey, input);
+
+            output.add(kI);
+        }
+
+        return output.substr(0,8);
+    }
+
+    // Implements agorithm http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38b.pdf
+    // Input an aes key of 128, 192, or 256 bits
+
+    public static TPSBuffer computeAES_CMAC(SymmetricKey aesKey, TPSBuffer input) throws EBaseException {
+
+        String method = "Util.computeAES_CMAC:";
+        byte iv[] = null;
+
+        if (aesKey == null || input == null) {
+            throw new EBaseException(method + " invalid input data!");
+        }
+
+        TPSBuffer data = new TPSBuffer(input);
+
+        String alg = aesKey.getAlgorithm();
+        System.out.println(" AES ALG: " + alg);
+
+        EncryptionAlgorithm eAlg = EncryptionAlgorithm.AES_128_CBC;
+        int ivLength = eAlg.getIVLength();
+
+        if (ivLength > 0) {
+            iv = new byte[ivLength];
+        }
+
+        if (!("AES".equals(alg))) {
+            throw new EBaseException(method + " invalid in put key type , must be AES!");
+        }
+
+        byte[] k0 = new byte[AES_CMAC_BLOCK_SIZE];
+
+        //Encrypt the zero array
+        CryptoToken token = aesKey.getOwningToken();
+        Cipher encryptor = null;
+
+        try {
+            encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC);
+            encryptor.initEncrypt(aesKey, new IVParameterSpec(iv));
+            k0 = encryptor.doFinal(k0);
+
+        } catch (NoSuchAlgorithmException | TokenException | IllegalStateException | IllegalBlockSizeException
+                | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
+            throw new EBaseException(e);
+        }
+
+        byte[] k1 = getAES_CMAC_SubKey(k0);
+        byte[] k2 = getAES_CMAC_SubKey(k1);
+
+        int numBlocks = 0;
+        int messageSize = data.size();
+        boolean perfectBlocks = false;
+
+        if (((messageSize % AES_CMAC_BLOCK_SIZE) == 0) && (messageSize != 0)) {
+            numBlocks = messageSize / AES_CMAC_BLOCK_SIZE;
+            perfectBlocks = true;
+        }
+        else {
+            numBlocks = messageSize / AES_CMAC_BLOCK_SIZE + 1;
+            perfectBlocks = false;
+        }
+
+        int index = 0;
+        byte inb = 0;
+        if (perfectBlocks == true)
+        {
+            // If the size of the message is an integer multiple of the block  block size (namely, 128 bits) (16 bytes)
+            // the last block shall be exclusive-OR'ed with the first subKey k1
+
+            for (int j = 0; j < k1.length; j++) {
+                index = messageSize - AES_CMAC_BLOCK_SIZE + j;
+                inb = data.at(index);
+                data.setAt(index, (byte) (inb ^ k1[j]));
+            }
+        }
+        else
+        {
+            // Otherwise, the last block shall be padded with 10^i
+            TPSBuffer padding = new TPSBuffer(AES_CMAC_BLOCK_SIZE - messageSize % AES_CMAC_BLOCK_SIZE);
+            padding.setAt(0, (byte) 0x80);
+
+            data.add(padding);
+            //Get new data size , it's changed
+            messageSize = data.size();
+
+            // and exclusive-OR'ed with K2
+            for (int j = 0; j < k2.length; j++) {
+                index = messageSize - AES_CMAC_BLOCK_SIZE + j;
+                inb = data.at(index);
+                data.setAt(index, (byte) (inb ^ k2[j]));
+            }
+        }
+
+        // Initialization vector starts as zeroes but changes inside the loop's
+        // subsequent iterations, it becomes the last encryption output
+        byte[] encData = new byte[AES_CMAC_BLOCK_SIZE];
+        TPSBuffer currentBlock = null;
+
+        for (int i = 0; i < numBlocks; i++) {
+            try {
+                encryptor.initEncrypt(aesKey, new IVParameterSpec(encData));
+                currentBlock = data.substr(i * AES_CMAC_BLOCK_SIZE, AES_CMAC_BLOCK_SIZE);
+                encData = encryptor.doFinal(currentBlock.toBytesArray());
+            } catch (TokenException | IllegalStateException | IllegalBlockSizeException
+                    | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
+                throw new EBaseException(e);
+            }
+        }
+
+        TPSBuffer aesMacData = new TPSBuffer(encData);
+        return aesMacData;
+
+    }
+
+    //Support method for AES-CMAC alg (SCP03).
+    private static byte[] getAES_CMAC_SubKey(byte[] input) {
+
+        byte[] output = new byte[input.length];
+
+        boolean msbSet = ((input[0]&0x80) != 0);
+        for (int i=0; i<input.length; i++) {
+            output[i] = (byte) (input[i] << 1);
+            if (i+1 < input.length && ((input[i+1]&0x80) != 0)) {
+                output[i] |= 0x01;
+            }
+        }
+        if (msbSet) {
+            output[output.length-1] ^= AES_CMAC_CONSTANT;
+        }
+        return output;
+    }
+
     public static TPSBuffer computeMAC(PK11SymKey symKey, TPSBuffer input, TPSBuffer icv) throws EBaseException {
         TPSBuffer output = null;
         TPSBuffer result = null;
@@ -425,6 +624,43 @@ public class Util {
         return tbuf;
     }
 
+    //Encrypt data with aes. Supports 128 for now.
+    public static TPSBuffer encryptDataAES(TPSBuffer dataToEnc, PK11SymKey encKey,TPSBuffer iv) throws EBaseException {
+
+        TPSBuffer encrypted = null;
+        if (encKey == null || dataToEnc == null) {
+            throw new EBaseException("Util.encryptDataAES: called with no sym key or no data!");
+        }
+
+        CryptoToken token = null;
+
+
+        try {
+            token = CryptoManager.getInstance().getInternalKeyStorageToken();
+            Cipher cipher = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC);
+            AlgorithmParameterSpec algSpec = null;
+            int len = EncryptionAlgorithm.AES_128_CBC.getIVLength();
+
+            byte[] ivEnc = null;
+            if(iv == null) { //create one
+                ivEnc = new byte[len];
+            } else {
+                ivEnc = iv.toBytesArray();
+             }
+
+            algSpec = new IVParameterSpec(ivEnc);
+            cipher.initEncrypt(encKey, algSpec);
+            byte[] encryptedBytes = cipher.doFinal(dataToEnc.toBytesArray());
+            encrypted = new TPSBuffer(encryptedBytes);
+
+        } catch (Exception e) {
+            throw new EBaseException("Util.encryptDataAES: problem encrypting data: " + e.toString());
+        }
+
+        return encrypted;
+
+    }
+
     public static TPSBuffer encryptData(TPSBuffer dataToEnc, PK11SymKey encKey) throws EBaseException {
 
         TPSBuffer encrypted = null;
@@ -533,4 +769,328 @@ public class Util {
         return timeString;
     }
 
+    //AES CMAC test samples
+    public static void main(String[] args) {
+
+   /*     Options options = new Options();
+
+        options.addOption("d", true, "Directory for tokendb");
+
+        String db_dir = null;
+        CryptoManager cm = null;
+
+        // 128 bit aes test key
+        byte devKey[] = { (byte) 0x2b, (byte) 0x7e, (byte) 0x15, (byte) 0x16, (byte) 0x28, (byte) 0xae, (byte) 0xd2,
+                (byte) 0xa6, (byte) 0xab, (byte) 0xf7, (byte) 0x15, (byte) 0x88, (byte) 0x09, (byte) 0xcf, (byte) 0x4f,
+                (byte) 0x3c };
+
+        // 192 bit aes test key
+        byte devKey192[] = { (byte) 0x8e, (byte) 0x73, (byte) 0xb0, (byte) 0xf7, (byte) 0xda, (byte) 0x0e, (byte) 0x64,
+                (byte) 0x52,
+                (byte) 0xc8, (byte) 0x10, (byte) 0xf3, (byte) 0x2b, (byte) 0x80, (byte) 0x90, (byte) 0x79, (byte) 0xe5,
+                (byte) 0x62, (byte) 0xf8, (byte) 0xea, (byte) 0xd2, (byte) 0x52, (byte) 0x2c, (byte) 0x6b, (byte) 0x7b
+        };
+
+        byte devKey256[] = { (byte) 0x60, (byte) 0x3d, (byte) 0xeb, (byte) 0x10, (byte) 0x15, (byte) 0xca, (byte) 0x71,
+                (byte) 0xbe,
+                (byte) 0x2b, (byte) 0x73, (byte) 0xae, (byte) 0xf0, (byte) 0x85, (byte) 0x7d, (byte) 0x77, (byte) 0x81,
+                (byte) 0x1f, (byte) 0x35, (byte) 0x2c, (byte) 0x07, (byte) 0x3b, (byte) 0x61, (byte) 0x08, (byte) 0xd7,
+                (byte) 0x2d,
+                (byte) 0x98, (byte) 0x10, (byte) 0xa3, (byte) 0x09, (byte) 0x14, (byte) 0xdf, (byte) 0xf4
+
+        };
+
+        byte message[] = { (byte) 0x6b, (byte) 0xc1, (byte) 0xbe, (byte) 0xe2, (byte) 0x2e, (byte) 0x40, (byte) 0x9f,
+                (byte) 0x96, (byte) 0xe9, (byte) 0x3d, (byte) 0x7e, (byte) 0x11,
+                (byte) 0x73, (byte) 0x93, (byte) 0x17, (byte) 0x2a };
+
+        byte message320[] = { (byte) 0x6b, (byte) 0xc1, (byte) 0xbe, (byte) 0xe2, (byte) 0x2e, (byte) 0x40,
+                (byte) 0x9f, (byte) 0x96, (byte) 0xe9,
+                (byte) 0x3d, (byte) 0x7e, (byte) 0x11, (byte) 0x73, (byte) 0x93, (byte) 0x17, (byte) 0x2a,
+                (byte) 0xae, (byte) 0x2d, (byte) 0x8a, (byte) 0x57, (byte) 0x1e, (byte) 0x03, (byte) 0xac, (byte) 0x9c,
+                (byte) 0x9e, (byte) 0xb7,
+                (byte) 0x6f, (byte) 0xac, (byte) 0x45, (byte) 0xaf, (byte) 0x8e, (byte) 0x51,
+                (byte) 0x30, (byte) 0xc8, (byte) 0x1c, (byte) 0x46, (byte) 0xa3, (byte) 0x5c, (byte) 0xe4, (byte) 0x11 };
+
+        byte message512[] = { (byte) 0x6b, (byte) 0xc1, (byte) 0xbe, (byte) 0xe2, (byte) 0x2e, (byte) 0x40,
+                (byte) 0x9f, (byte) 0x96, (byte) 0xe9, (byte) 0x3d,
+                (byte) 0x7e, (byte) 0x11, (byte) 0x73, (byte) 0x93, (byte) 0x17, (byte) 0x2a,
+                (byte) 0xae, (byte) 0x2d, (byte) 0x8a, (byte) 0x57, (byte) 0x1e, (byte) 0x03, (byte) 0xac, (byte) 0x9c,
+                (byte) 0x9e, (byte) 0xb7, (byte) 0x6f,
+                (byte) 0xac, (byte) 0x45, (byte) 0xaf, (byte) 0x8e, (byte) 0x51,
+                (byte) 0x30, (byte) 0xc8, (byte) 0x1c, (byte) 0x46, (byte) 0xa3, (byte) 0x5c, (byte) 0xe4, (byte) 0x11,
+                (byte) 0xe5, (byte) 0xfb, (byte) 0xc1,
+                (byte) 0x19, (byte) 0x1a, (byte) 0x0a, (byte) 0x52, (byte) 0xef,
+                (byte) 0xf6, (byte) 0x9f, (byte) 0x24, (byte) 0x45, (byte) 0xdf, (byte) 0x4f, (byte) 0x9b, (byte) 0x17,
+                (byte) 0xad, (byte) 0x2b, (byte) 0x41,
+                (byte) 0x7b, (byte) 0xe6, (byte) 0x6c, (byte) 0x37, (byte) 0x10
+
+        };
+
+
+        byte message_test1[] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x04,0x0,0x0,(byte) 0x80,0x01,
+                (byte)0xd0,(byte)0x61,(byte) 0xff,(byte)0xf4,(byte)0xd8,(byte)0x2f,(byte)0xdf,
+                (byte)0x87,(byte)0x5a,(byte)0x5c,(byte)0x90,(byte)0x99,(byte)0x98,(byte)0x3b,(byte)0x24,(byte)0xdc };
+
+        byte devKey_test1[] = {(byte)0x88,(byte)0xc6,(byte)0x46,(byte)0x2e,(byte)0x55,(byte)0x58,(byte)0x6c,
+                (byte)0x47,(byte)0xf9,(byte)0xff,0x00,(byte)0x92,(byte)0x39,(byte)0xce,(byte)0xb6,(byte)0xea};
+
+        //Test keys and messages found here: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38b.pdf
+        //Results computed in this test program can be compared against those in the preceding document.
+
+        try {
+            CommandLineParser parser = new DefaultParser();
+            CommandLine cmd = parser.parse(options, args);
+
+            if (cmd.hasOption("d")) {
+                db_dir = cmd.getOptionValue("d");
+            }
+
+        } catch (ParseException e) {
+            System.err.println("Error in parsing command line options: " + e.getMessage());
+
+        }
+
+        SymmetricKey aes128 = null;
+        SymmetricKey aes192 = null;
+        SymmetricKey aes256 = null;
+
+        SymmetricKey tempKey = null;
+
+        // Initialize token
+        try {
+            CryptoManager.initialize(db_dir);
+            cm = CryptoManager.getInstance();
+
+            CryptoToken token = cm.getInternalKeyStorageToken();
+
+            // Generate temp key with only function is to
+            // unwrap the various test keys onto the token
+
+            KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.AES);
+
+            SymmetricKey.Usage usages[] = new SymmetricKey.Usage[4];
+            usages[0] = SymmetricKey.Usage.WRAP;
+            usages[1] = SymmetricKey.Usage.UNWRAP;
+            usages[2] = SymmetricKey.Usage.ENCRYPT;
+            usages[3] = SymmetricKey.Usage.DECRYPT;
+
+            kg.setKeyUsages(usages);
+            kg.temporaryKeys(true);
+            kg.initialize(128);
+            tempKey = kg.generate();
+
+            //unwrap the test aes keys onto the token
+
+            Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC);
+
+            int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength();
+            byte[] iv = null;
+
+            if (ivLength > 0) {
+                iv = new byte[ivLength]; // all zeroes
+            }
+
+            encryptor.initEncrypt(tempKey, new IVParameterSpec(iv));
+            byte[] wrappedKey = encryptor.doFinal(devKey);
+
+            encryptor.initEncrypt(tempKey, new IVParameterSpec(iv));
+            byte[]wrappedKey_test1 = encryptor.doFinal(devKey_test1);
+
+            // 192 bit key
+
+            TPSBuffer aesKey192Buf = new TPSBuffer(devKey192);
+            TPSBuffer aesKey192Pad = new TPSBuffer(8);
+            aesKey192Pad.setAt(0, (byte) 0x80);
+            aesKey192Buf.add(aesKey192Pad);
+
+            encryptor.initEncrypt(tempKey, new IVParameterSpec(iv));
+            byte[] wrappedKey192 = encryptor.doFinal(aesKey192Buf.toBytesArray());
+
+            // 128 bit key
+
+            KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyWrap.initUnwrap(tempKey, new IVParameterSpec(iv));
+            aes128 = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.AES, 16);
+
+
+            KeyWrapper keyWrap1 = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyWrap1.initUnwrap(tempKey,new IVParameterSpec(iv));
+            SymmetricKey aes128_test = keyWrap1.unwrapSymmetric(wrappedKey_test1,SymmetricKey.AES,16);
+
+            System.out.println(new TPSBuffer(message_test1).toHexString());
+            System.out.println(new TPSBuffer(devKey_test1).toHexString());
+            System.out.println(new TPSBuffer(aes128_test.getKeyData()).toHexString());
+            TPSBuffer input1 = new TPSBuffer(message_test1);
+            TPSBuffer output1 = Util.computeAES_CMAC(aes128_test, input1);
+            System.out.println("blub: " + output1.toHexString());
+
+            // 192 bit key
+
+            KeyWrapper keyWrap192 = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyWrap192.initUnwrap(tempKey, new IVParameterSpec(iv));
+            aes192 = keyWrap.unwrapSymmetric(wrappedKey192, SymmetricKey.AES, 24);
+
+            // 256 bit key
+
+            TPSBuffer aesKey256Buf = new TPSBuffer(devKey256);
+            encryptor.initEncrypt(tempKey, new IVParameterSpec(iv));
+            byte[] wrappedKey256 = encryptor.doFinal(aesKey256Buf.toBytesArray());
+
+            KeyWrapper keyWrap256 = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyWrap256.initUnwrap(tempKey, new IVParameterSpec(iv));
+            aes256 = keyWrap.unwrapSymmetric(wrappedKey256, SymmetricKey.AES, 32);
+
+            System.out.println("");
+            System.out.println("Now use 128 bit AES key:");
+            System.out.println("");
+
+            //Attempt 0 bytes
+
+            System.out.println("");
+            System.out.println("Use message of 0 bytes:");
+            System.out.println("");
+
+            TPSBuffer input0 = new TPSBuffer(0);
+            TPSBuffer output0 = Util.computeAES_CMAC(aes128, input0);
+
+            System.out.println("Message:" + input0.toHexString());
+            System.out.println("AES-CMAC output: " + output0.toHexString());
+            System.out.println("");
+
+            System.out.println("");
+            System.out.println("Use message of 16 bytes:");
+            System.out.println("");
+
+            //Attempt 16 bytes
+
+            TPSBuffer input = new TPSBuffer(message);
+            TPSBuffer output = Util.computeAES_CMAC(aes128, input);
+
+            System.out.println("Message:" + input.toHexString());
+            System.out.println("AES-CMAC output: " + output.toHexString());
+            System.out.println("");
+
+            System.out.println("");
+            System.out.println("Use message of 40 bytes:");
+            System.out.println("");
+
+            //Attempt 40 bytes
+
+            TPSBuffer input320 = new TPSBuffer(message320);
+            TPSBuffer output320 = Util.computeAES_CMAC(aes128, input320);
+
+            System.out.println("Message:" + input320.toHexString());
+            System.out.println("AES-CMAC output: " + output320.toHexString());
+            System.out.println("");
+
+            System.out.println("");
+            System.out.println("Use message of 64 bytes:");
+            System.out.println("");
+
+            //Attempt 64 bytes
+
+            TPSBuffer input512 = new TPSBuffer(message512);
+            TPSBuffer output512 = Util.computeAES_CMAC(aes128, input512);
+            System.out.println("Message:" + input512.toHexString());
+            System.out.println("AES-CMAC output: " + output512.toHexString());
+
+            // Now used the AES 192 key
+
+            System.out.println("");
+            System.out.println("Now use 192 bit AES key:");
+            System.out.println("");
+
+            System.out.println("");
+            System.out.println("Use message of 0 bytes:");
+            System.out.println("");
+
+            // Attempt 0 bytes
+
+            TPSBuffer input192_0 = new TPSBuffer(0);
+            TPSBuffer output192_0 = Util.computeAES_CMAC(aes192, input192_0);
+            System.out.println("Message:" + input192_0.toHexString());
+            System.out.println("AES-CMAC output: " + output192_0.toHexString());
+            System.out.println("");
+
+            System.out.println("");
+            System.out.println("Use message of 16 bytes:");
+            System.out.println("");
+
+            //Attempt 16 bytes
+
+            TPSBuffer input192_128 = new TPSBuffer(message);
+            TPSBuffer output192_128 = Util.computeAES_CMAC(aes192, input);
+            System.out.println("Message:" + input192_128.toHexString());
+            System.out.println("AES-CMAC output: " + output192_128.toHexString());
+            System.out.println("");
+
+            System.out.println("");
+            System.out.println("Use message of 40 bytes:");
+            System.out.println("");
+
+            //Attempt 40 bytes
+
+            TPSBuffer input192_320 = new TPSBuffer(message320);
+            TPSBuffer output192_320 = Util.computeAES_CMAC(aes192, input192_320);
+            System.out.println("Message:" + input192_320.toHexString());
+            System.out.println("AES-CMAC output: " + output192_320.toHexString());
+            System.out.println("");
+
+            System.out.println("");
+            System.out.println("Use message of 64 bytes:");
+            System.out.println("");
+
+            //Attempt 64 bytes
+
+            TPSBuffer input192_512 = new TPSBuffer(message512);
+            TPSBuffer output192_512 = Util.computeAES_CMAC(aes192, input512);
+            System.out.println("Message:" + input192_512.toHexString());
+            System.out.println("AES-CMAC output: " + output192_512.toHexString());
+
+            System.out.println("");
+            System.out.println("Now use 256 bit AES key:");
+            System.out.println("");
+
+            // Attempt 0 bytes
+
+            TPSBuffer input256_0 = new TPSBuffer(0);
+            TPSBuffer output256_0 = Util.computeAES_CMAC(aes256, input256_0);
+            System.out.println("Message:" + input256_0.toHexString());
+            System.out.println("AES-CMAC output: " + output256_0.toHexString());
+            System.out.println("");
+
+            //Attempt 16 bytes
+
+            TPSBuffer input256_128 = new TPSBuffer(message);
+            TPSBuffer output256_128 = Util.computeAES_CMAC(aes256, input256_128);
+            System.out.println("Message:" + input256_128.toHexString());
+            System.out.println("AES-CMAC output: " + output256_128.toHexString());
+            System.out.println("");
+
+            //Attempt 40 bytes
+
+            TPSBuffer input256_320 = new TPSBuffer(message320);
+            TPSBuffer output256_320 = Util.computeAES_CMAC(aes256, input256_320);
+            System.out.println("Message:" + input256_320.toHexString());
+            System.out.println("AES-CMAC output: " + output256_320.toHexString());
+            System.out.println("");
+
+            //Attempt 64 bytes
+
+            TPSBuffer input256_512 = new TPSBuffer(message512);
+            TPSBuffer output256_512 = Util.computeAES_CMAC(aes256, input256_512);
+            System.out.println("Message:" + input256_512.toHexString());
+            System.out.println("AES-CMAC output: " + output256_512.toHexString());
+
+        } catch (AlreadyInitializedException e) {
+            // it is ok if it is already initialized
+        } catch (Exception e) {
+            System.err.println("JSS error!" + e);
+            System.exit(1);
+        }
+*/
+    }
+
+
 }
diff --git a/base/common/src/org/dogtagpki/tps/msg/TPSMessage.java b/base/common/src/org/dogtagpki/tps/msg/TPSMessage.java
index c7ad7f7..aee4302 100644
--- a/base/common/src/org/dogtagpki/tps/msg/TPSMessage.java
+++ b/base/common/src/org/dogtagpki/tps/msg/TPSMessage.java
@@ -527,6 +527,12 @@ public class TPSMessage {
             }
         }
 
+        int debug = 1;
+
+        if (debug == 1) {
+            CMS.debug("TPSMessage.createMessage: message: " + message);
+        }
+
         TPSMessage new_msg = new TPSMessage(message);
 
         return new_msg.createMessage();
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java b/base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java
new file mode 100644
index 0000000..f16481b
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java
@@ -0,0 +1,108 @@
+// --- 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 com.netscape.cms.servlet.tks;
+
+
+//Simple class used to hold scp03 related settings in TKS keyset config block
+// Ex: tks.defKeySet.prot3.divers=emv
+//     tks.defKeySet.prot3.diversVer1Keys=emv
+
+// Will probably be extended to allow params for future tokens
+
+public class GPParams {
+
+    public static String DIVER_EMV = "emv";
+    public static String DIVER_NONE = "none";
+    public static String DIVER_VISA2 = "visa2";
+    public static String NIST_SP800 = "nistsp_800";
+
+    public GPParams() {
+    }
+
+    // Diversification scheme for all keysets after 1
+    private String diversificationScheme;
+    //Diversification scheme for just version one or developer keys
+    private String version1DiversificationScheme;
+
+    public boolean isDiversEmv() {
+        if (DIVER_EMV.equalsIgnoreCase(diversificationScheme))
+            return true;
+        else
+            return false;
+    }
+
+    public boolean isDiversVisa2() {
+        if (DIVER_VISA2.equalsIgnoreCase(diversificationScheme))
+            return true;
+        else
+            return false;
+    }
+
+    public boolean isDiversNone() {
+        if (DIVER_NONE.equalsIgnoreCase(diversificationScheme))
+            return true;
+        else
+            return false;
+    }
+
+    public boolean isVer1DiversEmv() {
+        if (DIVER_EMV.equalsIgnoreCase(version1DiversificationScheme))
+            return true;
+        else
+            return false;
+    }
+
+    public boolean isVer1DiversVisa2() {
+        if (DIVER_VISA2.equalsIgnoreCase(version1DiversificationScheme))
+            return true;
+        else
+            return false;
+
+    }
+
+    public boolean isVer1DiversNone() {
+        if (DIVER_NONE.equalsIgnoreCase(version1DiversificationScheme))
+            return true;
+        else
+            return false;
+    }
+
+    public void setDiversificationScheme(String scheme) {
+        diversificationScheme = scheme;
+    }
+
+    public String getDiversificationScheme() {
+        return diversificationScheme;
+    }
+
+    public String getVersion1DiversificationScheme() {
+        return version1DiversificationScheme;
+    }
+
+    public void setVersion1DiversificationScheme(String version1DiversificationScheme) {
+        this.version1DiversificationScheme = version1DiversificationScheme;
+    }
+
+    public String toString() {
+        String output = " Version1 Diversification Scheme: " + version1DiversificationScheme + " All other versions : "
+                + diversificationScheme;
+
+        return output;
+    }
+
+}
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java b/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java
index 0407e29..e7b183b 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java
@@ -41,9 +41,64 @@ public class KDF {
             /* Even...0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe */
             /* E */0xf1, 0xf2, 0xf4, 0xf7, 0xf8, 0xfb, 0xfd, 0xfe, };
 
-    public static byte[] getDiversificationData(byte[] context, String type) throws EBaseException {
+    //Add the emv diversification method, used in SCP03 g&d card.
+    public static byte[] getDiversificationData_EMV(byte[] context, String type) throws EBaseException {
 
-        String method = "KDF.getDiversificationData:";
+        String method = "KDF.getDiversificationData_EMV:";
+
+        CMS.debug(method + " entering ...");
+
+        if (context == null || type == null) {
+            throw new EBaseException(method + "Invalid input parameters!");
+        }
+
+        byte[] KDC = new byte[SecureChannelProtocol.DES2_LENGTH];
+
+        KDC[0] = context[4 + 0];
+        KDC[1] = context[4 + 1];
+        KDC[2] = context[4 + 2];
+        KDC[3] = context[4 + 3];
+        KDC[4] = context[4 + 4];
+        KDC[5] = context[4 + 5];
+        KDC[6] = (byte) 0xF0;
+
+        KDC[7] = 0x1;
+
+        KDC[8] = context[4 + 0];
+        KDC[9] = context[4 + 1];
+        KDC[10] = context[4 + 2];
+        KDC[11] = context[4 +3];
+        KDC[12] = context[4 + 4];
+        KDC[13] = context[4 + 5];
+        KDC[14] = (byte) 0x0f;
+
+        KDC[15] = 0x1;
+
+        if (type.equals(SecureChannelProtocol.encType))
+            return KDC;
+
+        KDC[7] = 0x02;
+        KDC[15] = 0x02;
+        if (type.equals(SecureChannelProtocol.macType))
+            return KDC;
+
+        KDC[7] = 0x03;
+        KDC[15] = 0x03;
+        if (type.equals(SecureChannelProtocol.kekType))
+            return KDC;
+
+        KDC[7] = 0x04;
+        KDC[15] = 0x04;
+        if (type.equals(SecureChannelProtocol.rmacType))
+            return KDC;
+        return KDC;
+
+    }
+
+    //Standard visa2 diversification method
+    public static byte[] getDiversificationData_VISA2(byte[] context, String type) throws EBaseException {
+
+        String method = "KDF.getDiversificationData_VISA2:";
 
         CMS.debug(method + " entering ...");
 
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java b/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java
index e392ce1..fc794dc 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java
@@ -1,13 +1,24 @@
 package com.netscape.cms.servlet.tks;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.mozilla.jss.crypto.BadPaddingException;
+import org.mozilla.jss.crypto.Cipher;
 import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.EncryptionAlgorithm;
 import org.mozilla.jss.crypto.HMACAlgorithm;
+import org.mozilla.jss.crypto.IVParameterSpec;
+import org.mozilla.jss.crypto.IllegalBlockSizeException;
 import org.mozilla.jss.crypto.JSSMessageDigest;
 import org.mozilla.jss.crypto.SymmetricKey;
+import org.mozilla.jss.crypto.TokenException;
 
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
@@ -22,7 +33,18 @@ public class NistSP800_108KDF extends KDF {
 
     static final byte KDF_LABEL = 0x04; // arbitra
 
-    static final int SHA256_LENGTH = 32;
+    //SCP03, AES related constants
+
+    public static final int SHA256_LENGTH = 32;
+    private static final int AES_CMAC_BLOCK_SIZE = 16;
+    private static final byte AES_CMAC_CONSTANT = (byte) 0x87;
+    public static final byte ENC_KDF_CONSTANT = (byte) 0x04;
+    public static final byte MAC_KDF_CONSTANT = (byte) 0x06;
+    public static final byte RMAC_KDF_CONSTANT = (byte) 0x07;
+    public static final byte CARD_CRYPTO_KDF_CONSTANT = 0x0;
+    public static final byte HOST_CRYPTO_KDF_CONSTANT = 0x1;
+
+
 
     SecureChannelProtocol protocol = null;
 
@@ -57,7 +79,7 @@ public class NistSP800_108KDF extends KDF {
         String method = "NistSP800_108KDF.computeCardKeys:";
 
         if (masterKey == null || context == null || token == null) {
-            throw new EBaseException(method + " Invlalid input parameters!");
+            throw new EBaseException(method + " Invalid input parameters!");
         }
 
         Map<String, SymmetricKey> keys = new HashMap<String, SymmetricKey>();
@@ -101,9 +123,9 @@ public class NistSP800_108KDF extends KDF {
         Arrays.fill(kek, (byte) 0);
         Arrays.fill(kdf_output, (byte) 0);
 
-        SymmetricKey macKey = protocol.unwrapSymKeyOnToken(token, null, macFinal, false);
-        SymmetricKey encKey = protocol.unwrapSymKeyOnToken(token, null, encFinal, false);
-        SymmetricKey kekKey = protocol.unwrapSymKeyOnToken(token, null, kekFinal, false);
+        SymmetricKey macKey = protocol.unwrapSymKeyOnToken(token, null, macFinal, false,SymmetricKey.DES3);
+        SymmetricKey encKey = protocol.unwrapSymKeyOnToken(token, null, encFinal, false,SymmetricKey.DES3);
+        SymmetricKey kekKey = protocol.unwrapSymKeyOnToken(token, null, kekFinal, false,SymmetricKey.DES3);
 
         Arrays.fill(encFinal, (byte) 0);
         Arrays.fill(macFinal, (byte) 0);
@@ -117,6 +139,79 @@ public class NistSP800_108KDF extends KDF {
 
     }
 
+    //Compute the AES based CMAC operation. Used to derive session keys and cryptograms
+    public byte[] kdf_AES_CMAC_SCP03(SymmetricKey masterKey, byte[] context, byte kdfConstant,
+            int kdfOutputSizeBytes) throws EBaseException {
+
+        String method = "NistSP800_108KDF.kdb_AES_CMAC_SCP0:";
+        // 11 bytes label
+        byte[] label = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+        // sanity checking
+
+        if (masterKey == null || context == null || kdfOutputSizeBytes <= 0) {
+            throw new EBaseException(method + " Invalid input!");
+        }
+
+        ByteArrayOutputStream data = new ByteArrayOutputStream();
+
+        int outputBits = kdfOutputSizeBytes * 8;
+
+        //output size of cmac PRF
+        final int h = 128;
+
+        int remainder = outputBits % h;
+
+        //calculate counter size
+        int n = 0;
+        if (remainder == 0) {
+            n = outputBits / h;
+        } else {
+            n = outputBits / h + 1;
+        }
+
+        byte b1 = (byte) ((outputBits >> 8) & 0xFF);
+        byte b2 = (byte) (outputBits & 0xFF);
+
+        byte[] outputBitsBinary = new byte[2];
+        outputBitsBinary[0] = b1;
+        outputBitsBinary[1] = b2;
+
+        try {
+            data.write(label);
+            data.write(kdfConstant);
+            data.write(0x0);
+            data.write(outputBitsBinary);
+        } catch (IOException e) {
+            throw new EBaseException(method + "Unable to calculate kdf!");
+        }
+
+        byte[] headerBytes = data.toByteArray();
+
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        ByteArrayOutputStream input = new ByteArrayOutputStream();
+
+        byte[] kI = null;
+        for (int i = 1; i <= n; i++) {
+
+            try {
+                input.write(headerBytes);
+                input.write((byte) i);
+                input.write(context);
+
+                kI = computeAES_CMAC(masterKey, input.toByteArray());
+
+                output.write(kI);
+
+            } catch (IOException e) {
+                throw new EBaseException(method + "Unable to calculate kdf!");
+            }
+
+        }
+
+        return output.toByteArray();
+    }
+
     /*******************************************************************************
      Key Derivation Function in Counter Mode using PRF = SHA256HMAC (NIST SP 800-108)
        Calculates 384 bits of diversified output from the provided master key (K_I)
@@ -132,7 +227,6 @@ public class NistSP800_108KDF extends KDF {
         int L_BYTE_array_length = 2; // 384 = 0x0180 hex; 2 byte long representation
 
         if (context == null) {
-            throw new EBaseException(method + " Input value context  must not be null.");
         }
         // sanity check that output buffer is large enough to contain 384 bits
         if (kdfOutputSizeBytes < KDF_OUTPUT_SIZE_BYTES) {
@@ -215,4 +309,324 @@ public class NistSP800_108KDF extends KDF {
         return digestBytes;
     }
 
+    // Implements agorithm http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38b.pdf
+    // Input an aes key of 128, 192, or 256 bits
+    // For now calling code only using 128
+    // Will move later to common class used by both tks and tps
+
+    public static byte[] computeAES_CMAC(SymmetricKey aesKey, byte[] input) throws EBaseException {
+
+        String method = "NistSP800_108KDF.computeAES_CMAC:";
+        byte iv[] = null;
+
+        if (aesKey == null || input == null) {
+            throw new EBaseException(method + " invalid input data!");
+        }
+
+        byte[] data = new byte[input.length];
+        System.arraycopy(input, 0, data, 0, input.length);
+
+        String alg = aesKey.getAlgorithm();
+        System.out.println(" AES ALG: " + alg);
+
+        EncryptionAlgorithm eAlg = EncryptionAlgorithm.AES_128_CBC;
+        int ivLength = eAlg.getIVLength();
+
+        if (ivLength > 0) {
+            iv = new byte[ivLength];
+        }
+
+        if (!("AES".equals(alg))) {
+            throw new EBaseException(method + " invalid in put key type , must be AES!");
+        }
+
+        byte[] k0 = new byte[AES_CMAC_BLOCK_SIZE];
+
+        //Encrypt the zero array
+        CryptoToken token = aesKey.getOwningToken();
+        Cipher encryptor = null;
+
+        try {
+            encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC);
+            encryptor.initEncrypt(aesKey, new IVParameterSpec(iv));
+            k0 = encryptor.doFinal(k0);
+
+        } catch (NoSuchAlgorithmException | TokenException | IllegalStateException | IllegalBlockSizeException
+                | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
+            throw new EBaseException(e);
+        }
+
+        byte[] k1 = getAES_CMAC_SubKey(k0);
+        byte[] k2 = getAES_CMAC_SubKey(k1);
+
+        int numBlocks = 0;
+        int messageSize = data.length;;
+        boolean perfectBlocks = false;
+
+        if (((messageSize % AES_CMAC_BLOCK_SIZE) == 0) && (messageSize != 0)) {
+            numBlocks = messageSize / AES_CMAC_BLOCK_SIZE;
+            perfectBlocks = true;
+        }
+        else {
+            numBlocks = messageSize / AES_CMAC_BLOCK_SIZE + 1;
+            perfectBlocks = false;
+        }
+
+        int index = 0;
+        byte inb = 0;
+
+        byte[] finalData = null;
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
+
+        if (perfectBlocks == true)
+        {
+            // If the size of the message is an integer multiple of the block  block size (namely, 128 bits) (16 bytes)
+            // the last block shall be exclusive-OR'ed with the first subKey k1
+
+            for (int j = 0; j < k1.length; j++) {
+                index = messageSize - AES_CMAC_BLOCK_SIZE + j;
+                inb = data[index];
+                data[index] = (byte) (inb ^ k1[j]);
+            }
+            try {
+                outputStream.write(data);
+            } catch (IOException e) {
+                throw new EBaseException(method + " internal buffer erro!");
+            }
+            finalData = outputStream.toByteArray();
+        }
+        else
+        {
+            // Otherwise, the last block shall be padded with 10^i
+            byte[] padding = new byte[AES_CMAC_BLOCK_SIZE - messageSize % AES_CMAC_BLOCK_SIZE];
+            padding[0] = (byte) 0x80;
+
+            try {
+                outputStream.write(data);
+                outputStream.write(padding);
+            } catch (IOException e) {
+                throw new EBaseException(method + " internal buffer error!");
+            }
+
+            finalData = outputStream.toByteArray();
+
+            //Get new data size , it's changed
+            messageSize = finalData.length;
+
+            // and exclusive-OR'ed with K2
+            for (int j = 0; j < k2.length; j++) {
+                index = messageSize - AES_CMAC_BLOCK_SIZE + j;
+                inb = finalData[index];
+                finalData[index] = (byte) (inb ^ k2[j]);
+            }
+        }
+
+        // Initialization vector starts as zeroes but changes inside the loop's
+        // subsequent iterations, it becomes the last encryption output
+        byte[] encData = new byte[AES_CMAC_BLOCK_SIZE];
+        byte[] currentBlock = new byte[AES_CMAC_BLOCK_SIZE];
+        for (int i = 0; i < numBlocks; i++) {
+            try {
+                encryptor.initEncrypt(aesKey, new IVParameterSpec(encData));
+                System.arraycopy(finalData, i * AES_CMAC_BLOCK_SIZE, currentBlock, 0, AES_CMAC_BLOCK_SIZE);
+                encData = encryptor.doFinal(currentBlock);
+            } catch (TokenException | IllegalStateException | IllegalBlockSizeException
+                    | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
+                throw new EBaseException(e);
+            }
+        }
+
+        return encData;
+
+    }
+
+    // SCP03 AES-CMAC support function
+    private static byte[] getAES_CMAC_SubKey(byte[] input) {
+
+        byte[] output = new byte[input.length];
+
+        boolean msbSet = ((input[0]&0x80) != 0);
+        for (int i=0; i<input.length; i++) {
+            output[i] = (byte) (input[i] << 1);
+            if (i+1 < input.length && ((input[i+1]&0x80) != 0)) {
+                output[i] |= 0x01;
+            }
+        }
+        if (msbSet) {
+            output[output.length-1] ^= AES_CMAC_CONSTANT;
+        }
+        return output;
+    }
+
+    // Collection of informal invocations of api used to create various session keys
+    // Done with test data.
+    public static void main(String[] args) {
+/*
+      Options options = new Options();
+
+        options.addOption("d", true, "Directory for tokendb");
+
+        String db_dir = null;
+        CryptoManager cm = null;
+
+        byte devKey[] = { (byte) 0x40, (byte) 0x41, (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46,
+                (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x4a, (byte) 0x4b, (byte) 0x4c, (byte) 0x4d, (byte) 0x4e,
+                (byte) 0x4f };
+
+        byte test_cuid[] = { (byte) 0x47,(byte) 0x90,(byte)0x50,(byte)0x37,(byte)0x72,(byte)0x71,(byte)0x97,(byte)0x00,(byte)0x74,(byte)0xA9 };
+        byte test_kdd[] = { (byte)0x00, (byte)0x00, (byte)0x50, (byte)0x24,(byte) 0x97,(byte) 0x00,(byte) 0x74, (byte) 0xA9, (byte)0x72,(byte)0x71 };
+
+
+        byte test_host_challenge[]  = { 0x06 ,(byte)0xA4 ,0x46 ,0x57 ,(byte) 0x8B ,0x65 ,0x48 ,0x51 };
+        byte test_card_challenge[]  = { (byte) 0xAD ,(byte) 0x2E ,(byte)0xD0 ,0x1E ,0x7C ,0x2D ,0x0C ,0x6F};
+
+        byte test_key_info[] = { (byte) 0x02,(byte) 03,(byte) 00 };
+        byte test_old_key_info[] = {0x01,0x03,0x00};
+
+        try {
+            CommandLineParser parser = new DefaultParser();
+            CommandLine cmd = parser.parse(options, args);
+
+            if (cmd.hasOption("d")) {
+                db_dir = cmd.getOptionValue("d");
+            }
+
+        } catch (ParseException e) {
+            System.err.println("Error in parsing command line options: " + e.getMessage());
+
+        }
+
+        SymmetricKey encKey = null;
+        SymmetricKey macKey = null;
+        SymmetricKey kekKey = null;
+
+        SymmetricKey putEncKey = null;
+        SymmetricKey putMacKey = null;
+        SymmetricKey putKekKey = null;
+
+        SymmetricKey tempKey = null;
+
+        try {
+            CryptoManager.initialize(db_dir);
+            cm = CryptoManager.getInstance();
+
+            CryptoToken token = cm.getInternalKeyStorageToken();
+
+           KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.AES);
+
+            SymmetricKey.Usage usages[] = new SymmetricKey.Usage[4];
+            usages[0] = SymmetricKey.Usage.WRAP;
+            usages[1] = SymmetricKey.Usage.UNWRAP;
+            usages[2] = SymmetricKey.Usage.ENCRYPT;
+            usages[3] = SymmetricKey.Usage.DECRYPT;
+
+            kg.setKeyUsages(usages);
+            kg.temporaryKeys(true);
+            kg.initialize(128);
+            tempKey = kg.generate();
+
+
+            Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC);
+
+            int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength();
+            byte[] iv = null;
+
+            if (ivLength > 0) {
+                iv = new byte[ivLength]; // all zeroes
+            }
+
+            encryptor.initEncrypt(tempKey, new IVParameterSpec(iv));
+            byte[] wrappedKey = encryptor.doFinal(devKey);
+
+            KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyWrap.initUnwrap(tempKey, new IVParameterSpec(iv));
+
+            encKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 16);
+            macKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 16);
+            kekKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 16);
+
+            String transportName = "TPS-dhcp-16-206.sjc.redhat.com-8443 sharedSecret";
+            SecureChannelProtocol prot = new SecureChannelProtocol(SecureChannelProtocol.PROTOCOL_THREE);
+
+            SymmetricKey masterKey =  SecureChannelProtocol.getSymKeyByName(token,"new_master");
+
+            GPParams params = new GPParams();
+            params.setVersion1DiversificationScheme("visa2");
+            params.setDiversificationScheme("visa2");
+
+            putEncKey = prot.computeSessionKey_SCP03("internal", "new_master",test_old_key_info,
+                    SecureChannelProtocol.encType, devKey, "defKeySet", test_cuid, test_kdd, null, null,
+                    transportName,params);
+
+            putMacKey = prot.computeSessionKey_SCP03("internal", "new_master",test_old_key_info,
+                    SecureChannelProtocol.macType, devKey, "defKeySet", test_cuid, test_kdd, null, null,
+                    transportName,params);
+
+            putKekKey = prot.computeSessionKey_SCP03("internal", "new_master",test_old_key_info,
+                    SecureChannelProtocol.kekType, devKey, "defKeySet", test_cuid, test_kdd, null, null,
+                    transportName,params);
+
+            //create test session keys
+            encKey = prot.computeSessionKey_SCP03("internal", "new_master",test_key_info,
+                    SecureChannelProtocol.encType, devKey, "defKeySet", test_cuid, test_kdd, test_host_challenge, test_card_challenge,
+                    transportName,params);
+
+            macKey = prot.computeSessionKey_SCP03("internal", "new_master",test_key_info,
+                    SecureChannelProtocol.macType,devKey,"defKeySet", test_cuid, test_kdd, test_host_challenge, test_card_challenge,
+                    transportName,params);
+
+            kekKey = prot.computeSessionKey_SCP03("internal", "new_master",test_key_info,
+                    SecureChannelProtocol.kekType, devKey, "defKeySet", test_cuid, test_kdd, test_host_challenge, test_card_challenge,
+                    transportName,params);
+
+            System.out.println("masterKey: " + masterKey);
+
+            System.out.println("\n");
+
+            SecureChannelProtocol.debugByteArray(putEncKey.getKeyData(), " derived putEnc session key data: ");
+            SecureChannelProtocol.debugByteArray(putMacKey.getKeyData(), " derived putMac session key data: ");
+            SecureChannelProtocol.debugByteArray(putKekKey.getKeyData(), " derived putKek session key data: ");
+
+            System.out.println("\n");
+
+            SecureChannelProtocol.debugByteArray(encKey.getKeyData(), " derived enc session key data: ");
+            SecureChannelProtocol.debugByteArray(macKey.getKeyData(), " derived mac session key data: ");
+            SecureChannelProtocol.debugByteArray(kekKey.getKeyData(), " derived kek session key data: ");
+
+            ByteArrayOutputStream contextStream = new ByteArrayOutputStream();
+            try {
+                contextStream.write(test_host_challenge);
+                contextStream.write(test_card_challenge);
+            } catch (IOException e) {
+            }
+
+            StandardKDF standard = new StandardKDF(prot);
+
+            ByteArrayOutputStream testContext = new ByteArrayOutputStream();
+
+            testContext.write(test_host_challenge);
+            testContext.write(test_card_challenge);
+
+            NistSP800_108KDF  nistKdf = new NistSP800_108KDF(prot);
+
+            byte[] finalEncBytes = nistKdf.kdf_AES_CMAC_SCP03(encKey, testContext.toByteArray(), (byte) 0x04, 16);
+            byte[] finalMacBytes = nistKdf.kdf_AES_CMAC_SCP03(macKey, testContext.toByteArray(), (byte) 0x06, 16);
+
+            SymmetricKey sEnc  = prot.unwrapAESSymKeyOnToken(token, finalEncBytes, false);
+            SymmetricKey sMac  = macKey = prot.unwrapAESSymKeyOnToken(token, finalMacBytes, false);
+
+            byte[] cardCryptoVerify = nistKdf.kdf_AES_CMAC_SCP03(sMac, testContext.toByteArray(), CARD_CRYPTO_KDF_CONSTANT, 8);
+            SecureChannelProtocol.debugByteArray(cardCryptoVerify, " calculated card cryptogram");
+
+            byte[] hostCrypto = nistKdf.kdf_AES_CMAC_SCP03(sMac, testContext.toByteArray(), HOST_CRYPTO_KDF_CONSTANT, 8);
+            SecureChannelProtocol.debugByteArray(hostCrypto, " calculated host cryptogram");
+
+        } catch (AlreadyInitializedException e) {
+            // it is ok if it is already initialized
+        } catch (Exception e) {
+            System.err.println("JSS error!" + e);
+            System.exit(1);
+        }
+*/
+    }
 }
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java b/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java
index 1766f04..414613d 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java
@@ -15,6 +15,7 @@ import org.mozilla.jss.NoSuchTokenException;
 import org.mozilla.jss.crypto.Cipher;
 import org.mozilla.jss.crypto.CryptoToken;
 import org.mozilla.jss.crypto.EncryptionAlgorithm;
+import org.mozilla.jss.crypto.IVParameterSpec;
 import org.mozilla.jss.crypto.KeyGenAlgorithm;
 import org.mozilla.jss.crypto.KeyGenerator;
 import org.mozilla.jss.crypto.KeyWrapAlgorithm;
@@ -24,12 +25,12 @@ import org.mozilla.jss.crypto.SymmetricKey.NotExtractableException;
 import org.mozilla.jss.crypto.SymmetricKeyDeriver;
 import org.mozilla.jss.crypto.TokenException;
 
+import sun.security.pkcs11.wrapper.PKCS11Constants;
+
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.cmsutil.crypto.CryptoUtil;
 
-import sun.security.pkcs11.wrapper.PKCS11Constants;
-
 public class SecureChannelProtocol {
 
     static String sharedSecretKeyName = null;
@@ -43,12 +44,14 @@ public class SecureChannelProtocol {
     static final int KEYNAMELENGTH = PREFIXLENGHT + 7;
     static final String TRANSPORT_KEY_NAME = "sharedSecret";
     static final String DEFKEYSET_NAME = "defKeySet";
+    static int protocol = 1;
 
     static final String encType = "enc";
     static final String macType = "mac";
     static final String kekType = "kek";
     static final String authType = "auth";
     static final String dekType = "dek";
+    static final String rmacType = "rmac";
     static final int PROTOCOL_ONE = 1;
     static final int PROTOCOL_TWO = 2;
     static final int PROTOCOL_THREE = 3;
@@ -57,12 +60,26 @@ public class SecureChannelProtocol {
     //Size of long type in bytes, since java7 has no define for this
     static final int LONG_SIZE = 8;
 
+    //  constants
+
+    static final int AES_128_BYTES = 16;
+    static final int AES_192_BYTES = 24;
+    static final int AES_256_BYTES = 32;
+
+    static final int AES_128_BITS = 128;
+    static final int AES_192_BITS = 192;
+    static final int AES_256_BITS = 256;
+
     private SymmetricKey transportKey = null;
     CryptoManager cryptoManager = null;
 
     public SecureChannelProtocol() {
     }
 
+    public SecureChannelProtocol(int theProtocol) {
+        protocol = theProtocol;
+    }
+
     public byte[] computeCryptogram_SCP01(
             String selectedToken, String keyNickName, byte[] card_challenge,
             byte[] host_challenge, byte[] keyInfo,
@@ -144,6 +161,218 @@ public class SecureChannelProtocol {
         throw new EBaseException(method + " Not yet implemented!");
     }
 
+    public int getProtocol() {
+        return protocol;
+    }
+
+    // Either calculate a full session key, with the KDF applied or
+    // Merely calculate the card key. Card key mode is when host_challenge and
+    // card_challenge are passed in as null. Card keys are calculated
+    // when creating a new keyset to send to the token
+    public SymmetricKey computeSessionKey_SCP03(String selectedToken,
+            String keyNickName, byte[] keyInfo, String keyType,
+            byte[] devKeyArray, String keySet, byte[] xCUID, byte[] xKDD,
+            byte[] host_challenge, byte[] card_challenge, String transportKeyName, GPParams params)
+            throws EBaseException {
+
+        final byte mac_constant = 0x06;
+        final byte enc_constant = 0x04;
+        final byte rmac_constant = 0x07;
+
+        boolean noDerive = false;
+
+        byte constant = 0;
+
+        String method = "SecureChannelProtocol.computeSessionKey_SCP03:";
+
+        if (keyType == null || devKeyArray == null
+                || transportKeyName == null) {
+            throw new EBaseException(method + " invalid input data");
+        }
+
+        if (xCUID == null || xCUID.length <= 0) {
+            throw new EBaseException(method + "CUID invalid size!");
+        }
+
+        if (xKDD == null || xKDD.length != NistSP800_108KDF.KDD_SIZE_BYTES) {
+            throw new EBaseException(method + "KDD invalid size!");
+        }
+
+
+        //Detect card key mode or full derivation mode
+        if (card_challenge == null && host_challenge == null) {
+            noDerive = true;
+        } else {
+            if (card_challenge == null || host_challenge == null) {
+                throw new EBaseException(method + " Invalid challenge data!");
+            }
+        }
+
+        CMS.debug(method + " entering. nickname: " + keyNickName + " selectedToken: " + selectedToken);
+
+        CryptoManager cm = null;
+        CryptoToken token = null;
+        CryptoToken internalToken = null;
+        try {
+            cm = CryptoManager.getInstance();
+            token = returnTokenByName(selectedToken, cm);
+            internalToken = returnTokenByName("internal", cm);
+        } catch (NotInitializedException e) {
+            CMS.debug(method + " " + e);
+            throw new EBaseException(e);
+
+        } catch (NoSuchTokenException e) {
+            CMS.debug(method + " " + e);
+            throw new EBaseException(e);
+        }
+
+        sharedSecretKeyName = SecureChannelProtocol.getSharedSecretKeyName(transportKeyName);
+        transportKey = getSharedSecretKey(internalToken);
+
+        //concat host and card challenge:
+
+        byte[] context = null;
+
+        ByteArrayOutputStream contextStream = new ByteArrayOutputStream();
+
+        // Full derivation mode create context used in derivation
+        // host_challenge + card_challenge concatenated
+        if (noDerive == false) {
+            try {
+                contextStream.write(host_challenge);
+                contextStream.write(card_challenge);
+            } catch (IOException e) {
+                throw new EBaseException(method + " Error calculating derivation data!");
+            }
+
+            context = contextStream.toByteArray();
+        }
+
+        //Calculate the constant based on what type of key we want.
+        // Note the kek key never goes through final derivation in scp03
+
+        if (keyType.equalsIgnoreCase(SecureChannelProtocol.encType)) {
+            constant = enc_constant;
+        }
+
+        if (keyType.equalsIgnoreCase(SecureChannelProtocol.macType)) {
+            constant = mac_constant;
+        }
+
+        if (keyType.equalsIgnoreCase(SecureChannelProtocol.rmacType)) {
+            constant = rmac_constant;
+        }
+
+        if (keyType.equalsIgnoreCase(SecureChannelProtocol.kekType)) {
+            constant = 0;
+        }
+
+        String keyNameStr = null;
+
+        SymmetricKey sessionKey = null;
+        SymmetricKey masterKey = null;
+
+        if (keyNickName == null) {
+            keyNameStr = this.getKeyName(keyInfo);
+        } else {
+            keyNameStr = keyNickName;
+        }
+
+        boolean noDivers = false;
+
+        CMS.debug(method + " keyNameStr: " + keyNameStr);
+
+        //Starting with version 1 or factory keyset.
+        if ((keyInfo[0] == 0x1 && keyNameStr.contains("#01#")) ||
+                (keyInfo[0] == -1 && keyNameStr.indexOf("#FF") != -1))
+
+        {
+            String finalKeyType = keyType;
+            SymmetricKey devSymKey = returnDeveloperSymKey(token, finalKeyType, keySet, devKeyArray);
+
+            StandardKDF standard = new StandardKDF(this);
+            SymmetricKey divKey = null;
+
+            byte[] keyDiversified = null;
+
+            //Consult the config to determine with diversification method to use.
+            if (params.isVer1DiversNone()) {
+                noDivers = true;
+            } else if (params.isVer1DiversEmv()) {
+                keyDiversified = KDF.getDiversificationData_EMV(xKDD, keyType);
+            } else if (params.isVer1DiversVisa2()) {
+                keyDiversified = KDF.getDiversificationData_VISA2(xKDD, keyType);
+            } else {
+                throw new EBaseException(method + " Invalid diversification method!");
+            }
+
+            //Obtain the card key,it may just be the raw developer key
+            if (noDivers == true) {
+                divKey = unwrapAESSymKeyOnToken(token, devKeyArray, false);
+            } else {
+
+                // The g&d calls for computing the aes card key with DES, it will then be treated as aes
+                divKey = standard.computeCardKey_SCP03_WithDES3(devSymKey, keyDiversified, token);
+            }
+
+            NistSP800_108KDF nistKdf = new NistSP800_108KDF(this);
+
+            //IN scp03, the kek key IS the card key
+            if (constant == 0 /* kek key */) {
+                sessionKey = divKey;
+            } else { // session keys will become AES
+                if (noDerive) {
+                    sessionKey = divKey;
+                }
+                else {
+                    byte[] finalKeyBytes = nistKdf.kdf_AES_CMAC_SCP03(divKey, context, constant, 16);
+                    sessionKey = unwrapAESSymKeyOnToken(token, finalKeyBytes, false);
+
+                    //The final session key is AES.
+                }
+            }
+        } else { // Creating a session key for the case where we have already upgraded the keys on the token, using the master key
+            CMS.debug(method + "In master key mode.");
+
+            masterKey = getSymKeyByName(token, keyNameStr);
+
+            StandardKDF standard = new StandardKDF(this);
+
+            byte[] keyDiversified = null;
+
+            if (params.isDiversNone()) {
+                throw new EBaseException(method + " No diversification requested in master key mode. Aborting...");
+            } //Allow choice of emv or standard diversification
+            else if (params.isDiversEmv()) {
+                keyDiversified = KDF.getDiversificationData_EMV(xKDD, keyType);
+            } else if (params.isDiversVisa2()) {
+                keyDiversified = KDF.getDiversificationData_VISA2(xKDD, keyType);
+            }
+
+            SymmetricKey divKey = null;
+
+            divKey = standard.computeCardKey_SCP03_WithDES3(masterKey, keyDiversified, token);
+
+            NistSP800_108KDF nistKdf = new NistSP800_108KDF(this);
+            // The kek session key does not call for derivation
+            if (constant == 0 /* kek key */) {
+                sessionKey = divKey;
+            } else {
+                if (noDerive) {
+                    sessionKey = divKey;
+                }
+                else {
+                    byte[] finalKeyBytes = nistKdf.kdf_AES_CMAC_SCP03(divKey, context, constant, 16);
+                    sessionKey = unwrapAESSymKeyOnToken(token, finalKeyBytes, false);
+                }
+            }
+        }
+
+        //SecureChannelProtocol.debugByteArray(sessionKey.getEncoded(), keyType + " : session key");
+
+        return sessionKey;
+    }
+
     public SymmetricKey computeKEKKey_SCP01(
             String selectedToken, String keyNickName,
             byte[] keyInfo,
@@ -153,7 +382,6 @@ public class SecureChannelProtocol {
             byte[] xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use
             byte[] devKeyArray, String useSoftToken_s, String keySet, String transportKeyName) throws EBaseException {
 
-
         String method = "SecureChannelProtocol.computeKEKKey_SCP01:";
 
         CMS.debug(method + " entering... ");
@@ -294,7 +522,7 @@ public class SecureChannelProtocol {
             } else {
                 StandardKDF standardKDF = new StandardKDF(this);
                 CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using original KDF.");
-                byte[] data = KDF.getDiversificationData(context, keyType);
+                byte[] data = KDF.getDiversificationData_VISA2(context, keyType);
                 devKey = standardKDF.computeCardKey(masterKey, data, token, PROTOCOL_ONE);
             }
 
@@ -349,7 +577,7 @@ public class SecureChannelProtocol {
                 byte[] parityEncrypted = KDF.getDesParity(encrypted);
                 CMS.debug(method + "encryption completed");
 
-                derivedKey = this.unwrapSymKeyOnToken(token, null, parityEncrypted, false);
+                derivedKey = this.unwrapSymKeyOnToken(token, null, parityEncrypted, false, SymmetricKey.DES3);
             }
 
         } catch (TokenException | InvalidKeyException | EBaseException e) {
@@ -440,7 +668,7 @@ public class SecureChannelProtocol {
     From that point it is a simple matter of retrieving  the desired key from the token.
     No security advantage is implied or desired here.
     */
-    private SymmetricKey returnDeveloperSymKey(CryptoToken token, String keyType, String keySet, byte[] inputKeyArray)
+    public SymmetricKey returnDeveloperSymKey(CryptoToken token, String keyType, String keySet, byte[] inputKeyArray)
             throws EBaseException {
 
         SymmetricKey devKey = null;
@@ -497,10 +725,155 @@ public class SecureChannelProtocol {
         return devKey;
     }
 
-    public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray,
+    //Takes raw des key 16 bytes, such as developer key and returns an AES key of the same size
+    //Supports 128 bits for now
+    public SymmetricKey unwrapAESSymKeyOnToken(CryptoToken token, byte[] inputKeyArray,
             boolean isPerm)
             throws EBaseException {
 
+        String method = "SecureChannelProtocol.unwrapAESSymKeyOnToken:";
+        CMS.debug(method + "Entering...");
+
+        if(token == null || inputKeyArray == null) {
+            throw new EBaseException(method + " Invalid input data!");
+        }
+
+        if(inputKeyArray.length < 16) {
+            throw new EBaseException(method + " Invalid key size!");
+        }
+
+        byte[] finalInputKeyArray = inputKeyArray;
+        if(inputKeyArray.length > 16) {
+            finalInputKeyArray = new byte[16];
+            System.arraycopy(inputKeyArray, 0, finalInputKeyArray, 0, 16);;
+
+        }
+
+        KeyGenerator kg;
+        SymmetricKey finalAESKey;
+        try {
+            kg = token.getKeyGenerator(KeyGenAlgorithm.AES);
+
+            SymmetricKey.Usage usages[] = new SymmetricKey.Usage[4];
+            usages[0] = SymmetricKey.Usage.WRAP;
+            usages[1] = SymmetricKey.Usage.UNWRAP;
+            usages[2] = SymmetricKey.Usage.ENCRYPT;
+            usages[3] = SymmetricKey.Usage.DECRYPT;
+
+            kg.setKeyUsages(usages);
+            kg.temporaryKeys(true);
+            kg.initialize(128);
+            SymmetricKey tempKey = kg.generate();
+
+            //unwrap the test aes keys onto the token
+
+            Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC);
+
+            int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength();
+            byte[] iv = null;
+
+            if (ivLength > 0) {
+                iv = new byte[ivLength]; // all zeroes
+            }
+
+            encryptor.initEncrypt(tempKey, new IVParameterSpec(iv));
+            byte[] wrappedKey = encryptor.doFinal(finalInputKeyArray);
+
+            KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyWrap.initUnwrap(tempKey, new IVParameterSpec(iv));
+            finalAESKey = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.AES, 16);
+
+        } catch (Exception e) {
+            throw new EBaseException(method + " Can't unwrap key onto token!");
+        }
+
+        return finalAESKey;
+    }
+
+    //Supports 128 bits for now
+    //Used to convert a des key (on token) to aes
+    //Not used as of now, future if needed
+    public SymmetricKey unwrapAESSymKeyOnToken(CryptoToken token, SymmetricKey keyToUnwrap,
+            boolean isPerm)
+            throws EBaseException {
+
+        String method = "SecureChannelProtocol.unwrapAESSymKeyOnToken:";
+        CMS.debug(method + "Entering...");
+
+        if(token == null || keyToUnwrap == null) {
+            throw new EBaseException(method + " Invalid input data!");
+        }
+
+        if(keyToUnwrap.getLength()< 16) {
+            throw new EBaseException(method + " Invalid key size!");
+        }
+
+        KeyGenerator kg;
+        SymmetricKey finalAESKey;
+        try {
+            kg = token.getKeyGenerator(KeyGenAlgorithm.AES);
+
+            SymmetricKey.Usage usages[] = new SymmetricKey.Usage[4];
+            usages[0] = SymmetricKey.Usage.WRAP;
+            usages[1] = SymmetricKey.Usage.UNWRAP;
+            usages[2] = SymmetricKey.Usage.ENCRYPT;
+            usages[3] = SymmetricKey.Usage.DECRYPT;
+
+            kg.setKeyUsages(usages);
+            kg.temporaryKeys(true);
+            kg.initialize(128);
+            SymmetricKey tempKey = kg.generate();
+
+
+            int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength();
+            byte[] iv = null;
+
+            if (ivLength > 0) {
+                iv = new byte[ivLength]; // all zeroes
+            }
+
+            //Wrap the arbitrary key first
+
+            int len = keyToUnwrap.getLength();
+
+            SymmetricKey finalKeyToWrap = null;
+            SymmetricKey key16 = null;
+            if(len > 16) {
+                key16 = extractDes2FromDes3(keyToUnwrap, token.getName());
+                if(key16 != null)
+                len = key16.getLength();
+                finalKeyToWrap = key16;
+            } else {
+                finalKeyToWrap = keyToUnwrap;
+            }
+
+            KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyWrap.initWrap(tempKey, new IVParameterSpec(iv));
+            byte[] wrappedKey = keyWrap.wrap(finalKeyToWrap);
+
+            //Now unwrap to an AES key
+
+            KeyWrapper keyUnWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+            keyUnWrap.initUnwrap(tempKey, new IVParameterSpec(iv));
+            finalAESKey = keyUnWrap.unwrapSymmetric(wrappedKey, SymmetricKey.AES, 16);
+
+            byte[] finalKeyBytes = finalAESKey.getKeyData();
+
+            displayByteArray(finalKeyBytes, false);
+
+        } catch (Exception e) {
+            throw new EBaseException(method + " Can't unwrap key onto token!");
+        }
+
+        return finalAESKey;
+
+    }
+
+    //Final param allows us to request the final type, DES or AES
+    public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray,
+            boolean isPerm, SymmetricKey.Type finalKeyType)
+            throws EBaseException {
+
         String method = "SecureChannelProtocol.unwrapSymKeyOnToken:";
         CMS.debug(method + "Entering...");
         SymmetricKey unwrapped = null;
@@ -535,7 +908,7 @@ public class SecureChannelProtocol {
 
         byte[] finalKeyArray = null;
 
-        if (inputKeyArray.length == DES2_LENGTH) {
+        if (inputKeyArray.length == DES2_LENGTH && finalKeyType == SymmetricKey.DES3) {
             finalKeyArray = SecureChannelProtocol.makeDes3FromDes2(inputKeyArray);
         }
 
@@ -570,9 +943,9 @@ public class SecureChannelProtocol {
 
             if (isPerm == true) {
                 unwrapped = keyWrap.unwrapSymmetricPerm(wrappedKey,
-                        SymmetricKey.DES3, 0);
+                        finalKeyType, 0);
             } else {
-                unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 0);
+                unwrapped = keyWrap.unwrapSymmetric(wrappedKey, finalKeyType, 0);
             }
 
         } catch (Exception e) {
@@ -590,8 +963,9 @@ public class SecureChannelProtocol {
         return unwrapped;
     }
 
+    //Final param allows us to request the final type, DES or AES
     public SymmetricKey unwrapWrappedSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray,
-            boolean isPerm)
+            boolean isPerm, SymmetricKey.Type keyType)
             throws EBaseException {
 
         String method = "SecureChannelProtocol.unwrapWrappedSymKeyOnToken:";
@@ -613,13 +987,15 @@ public class SecureChannelProtocol {
 
             if (isPerm) {
                 unwrapped = keyWrap.unwrapSymmetricPerm(inputKeyArray,
-                        SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, inputKeyArray.length);
+                        keyType, SymmetricKey.Usage.UNWRAP, inputKeyArray.length);
             } else {
-                unwrapped = keyWrap.unwrapSymmetric(inputKeyArray, SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP,
+                unwrapped = keyWrap.unwrapSymmetric(inputKeyArray, keyType, SymmetricKey.Usage.UNWRAP,
                         inputKeyArray.length);
             }
 
-            finalUnwrapped = makeDes3KeyDerivedFromDes2(unwrapped, token.getName());
+            if (keyType == SymmetricKey.DES3) {
+                finalUnwrapped = makeDes3KeyDerivedFromDes2(unwrapped, token.getName());
+            }
 
         } catch (Exception e) {
             CMS.debug(method + " " + e);
@@ -629,7 +1005,10 @@ public class SecureChannelProtocol {
         //CMS.debug(method + "Returning symkey: " + unwrapped);
         CMS.debug(method + "Returning symkey...");
 
-        return finalUnwrapped;
+        if (finalUnwrapped != null)
+            return finalUnwrapped;
+        else
+            return unwrapped;
     }
 
     public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, byte[] inputKeyArray, boolean isPerm)
@@ -648,7 +1027,7 @@ public class SecureChannelProtocol {
         }
 
         SymmetricKey transport = getSharedSecretKey(token);
-        unwrapped = this.unwrapSymKeyOnToken(token, transport, inputKeyArray, isPerm);
+        unwrapped = this.unwrapSymKeyOnToken(token, transport, inputKeyArray, isPerm, SymmetricKey.DES3);
 
         CMS.debug(method + "Returning symkey: " + unwrapped);
 
@@ -710,16 +1089,36 @@ public class SecureChannelProtocol {
     public static void debugByteArray(byte[] array, String message) {
 
         CMS.debug("About to dump array: " + message);
+        System.out.println("About to dump array: " + message);
 
         if (array == null) {
             CMS.debug("Array to dump is empty!");
             return;
         }
 
+        System.out.println("################### ");
         CMS.debug("################### ");
 
         String result = getHexString(array);
         CMS.debug(result);
+        System.out.println(result);
+    }
+
+    public static void
+            displayByteArray(byte[] ba, boolean has_check_sum) {
+        char mask = 0xff;
+
+        if (has_check_sum == true)
+            mask = 0xfe;
+
+        for (int i = 0; i < ba.length; i++) {
+
+            System.out.print(Integer.toHexString(ba[i] & mask) + " ");
+            if ((i % 26) == 25) {
+                System.out.println("");
+            }
+        }
+        System.out.println("");
     }
 
     final protected static char[] hex = "0123456789abcdef".toCharArray();
@@ -815,6 +1214,8 @@ public class SecureChannelProtocol {
         }
         SymmetricKey des2 = this.extractDes2FromDes3(symKey, devKeyToken);
 
+        //SecureChannelProtocol.debugByteArray(des2.getEncoded(), method + " raw des2 key, to be wrapped.");
+
         result = this.wrapSessionKey(selectedToken, des2, devKey);
 
         // SecureChannelProtocol.debugByteArray(result, " Wrapped des2 key");
@@ -929,25 +1330,93 @@ public class SecureChannelProtocol {
             wrapper = wrappingKey;
         }
 
-        try {
-            CryptoManager cm = this.getCryptoManger();
-            CryptoToken token = returnTokenByName(tokenName, cm);
+        CMS.debug(method + " wrapper key type: " + wrapper.getType());
 
-            keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB);
-            keyWrap.initWrap(wrapper, null);
-            wrappedSessKeyData = keyWrap.wrap(sessionKey);
-        } catch (Exception e) {
-            CMS.debug(method + " " + e);
-            throw new EBaseException(e);
+        if (wrapper.getType() != SymmetricKey.AES) {
+            CMS.debug(method + "Trying to wrap a key with an DES key!");
+
+            try {
+                CryptoManager cm = this.getCryptoManger();
+                CryptoToken token = returnTokenByName(tokenName, cm);
+
+                keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB);
+                keyWrap.initWrap(wrapper, null);
+                wrappedSessKeyData = keyWrap.wrap(sessionKey);
+
+            } catch (
+                    Exception e) {
+                CMS.debug(method + " " + e);
+                throw new EBaseException(e);
+            }
+
+        } else if (wrapper.getType() == SymmetricKey.AES) {
+            CMS.debug(method + "Trying to wrap a key with an AES key!");
+            try {
+                CryptoManager cm = this.getCryptoManger();
+                CryptoToken token = returnTokenByName(tokenName, cm);
+
+                int ivLength = EncryptionAlgorithm.AES_128_CBC.getIVLength();
+                byte[] iv = null;
+
+                if (ivLength > 0) {
+                    iv = new byte[ivLength]; // all zeroes
+                }
+
+                keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_CBC);
+                keyWrap.initWrap(wrapper, new IVParameterSpec(iv));
+                wrappedSessKeyData = keyWrap.wrap(sessionKey);
+
+
+            } catch (Exception e) {
+                CMS.debug(method + " " + e);
+                throw new EBaseException(e);
+            }
         }
 
-        //CMS.debug(method + " About to return session key: " + wrappedSessKeyData);
+
+        //SecureChannelProtocol.debugByteArray(wrappedSessKeyData, "wrappedSessKeyData");
         CMS.debug(method + " returning session key");
 
         return wrappedSessKeyData;
 
     }
 
+    //128 for now.
+    public byte[] computeAES_CBCEncryption(SymmetricKey symKey, String selectedToken, byte[] input, byte[] iv)
+            throws EBaseException
+    {
+        String method = "SecureChannelProtocol.computeAES_CBCEncryption";
+        byte[] output = null;
+        byte[] finalIv = null;
+
+        if (symKey == null || selectedToken == null) {
+            throw new EBaseException(method + " Invalid input data.");
+        }
+
+        if (iv == null) {
+            finalIv = new byte[16];
+
+        } else {
+            finalIv = iv;
+        }
+
+        try {
+            CryptoManager cm = this.getCryptoManger();
+            CryptoToken token = returnTokenByName(selectedToken, cm);
+            Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.AES_128_CBC);
+            encryptor.initEncrypt(symKey, new IVParameterSpec(finalIv));
+            output = encryptor.doFinal(input);
+
+            SecureChannelProtocol.debugByteArray(output, "Encrypted data:");
+        } catch (Exception e) {
+
+            CMS.debug(method + e);
+            throw new EBaseException(method + e);
+        }
+
+        return output;
+    }
+
     public byte[] computeDes3EcbEncryption(SymmetricKey desKey, String selectedToken, byte[] input)
             throws EBaseException {
 
@@ -983,6 +1452,61 @@ public class SecureChannelProtocol {
         return output;
     }
 
+    //SCP03 uses aes
+    public byte[] computeKeyCheck_SCP03(SymmetricKey symKey, String selectedToken) throws EBaseException {
+
+        String method = "SecureChannelProtocol.computeKeyCheck_SCP03:";
+
+        if (symKey == null || selectedToken == null) {
+            throw new EBaseException(method + " invalid input data!");
+        }
+
+        byte[] key_check_message = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
+        //zero iv vector
+        byte[] key_check_iv = new byte[16];
+
+        byte[] output = null;
+        byte[] finalOutput = new byte[3];
+
+        try {
+            output = computeAES_CBCEncryption(symKey, selectedToken, key_check_message, key_check_iv);
+        } catch (EBaseException e) {
+            CMS.debug(method + e);
+            throw e;
+
+        }
+
+        //Get the 3 bytes needed
+        System.arraycopy(output, 0, finalOutput, 0, 3);
+
+        //SecureChannelProtocol.debugByteArray(finalOutput, method + " output: ");
+
+        return finalOutput;
+    }
+
+    //AES, uses AES_CMAC alg to do the work.
+    public byte[] computeCryptogram_SCP03(SymmetricKey symKey, String selectedToken, byte[] context, byte cryptoType)
+            throws EBaseException {
+        String method = "SecureChannelProtocol.computeCryptogram_";
+
+        CMS.debug(method + " entering ..");
+
+        if (symKey == null || selectedToken == null || (cryptoType != NistSP800_108KDF.CARD_CRYPTO_KDF_CONSTANT)
+                && cryptoType != NistSP800_108KDF.HOST_CRYPTO_KDF_CONSTANT) {
+            throw new EBaseException(method + " Invalid input data.");
+        }
+
+        NistSP800_108KDF nistKdf = new NistSP800_108KDF(this);
+        byte[] crypto = nistKdf.kdf_AES_CMAC_SCP03(symKey, context, cryptoType, 8);
+        //SecureChannelProtocol.debugByteArray(crypto, " calculated cryptogram");
+
+        byte[] finalCrypto = new byte[8];
+
+        System.arraycopy(crypto, 0, finalCrypto, 0, 8);
+
+        return finalCrypto;
+    }
+
     public byte[] computeKeyCheck(SymmetricKey desKey, String selectedToken) throws EBaseException {
 
         String method = "SecureChannelProtocol.computeKeyCheck:";
@@ -1115,6 +1639,9 @@ public class SecureChannelProtocol {
         return output;
     }
 
+    //Calculates the 3 new card keys to be written to the token for
+    //Symmetric key changeover. Supports SCP03 now.
+    //Provide all the static developer key arrays should we need them
     public byte[] diversifyKey(String tokenName,
             String newTokenName,
             String oldMasterKeyName,
@@ -1125,12 +1652,18 @@ public class SecureChannelProtocol {
             boolean nistSP800_108KdfUseCuidAsKdd,
             byte[] CUIDValue,
             byte[] KDD,
-            byte[] kekKeyArray,
-            String useSoftToken, String keySet, byte protocol) throws EBaseException {
+            byte[] kekKeyArray, byte[] encKeyArray, byte[] macKeyArray,
+            String useSoftToken, String keySet, byte protocol, GPParams params) throws EBaseException {
 
         String method = "SecureChannelProtocol.diversifyKey:";
 
-        CMS.debug(method + " Entering ... newTokenName: " + newTokenName);
+        CMS.debug(method + " Entering ... newTokenName: " + newTokenName + " protocol: " + protocol);
+        CMS.debug(method + " oldMasterKeyName: " + oldMasterKeyName);
+        CMS.debug(method + " newMasterKeyName: " + newMasterKeyName);
+
+        SecureChannelProtocol.debugByteArray(encKeyArray, " Developer enc key array: ");
+        SecureChannelProtocol.debugByteArray(macKeyArray, " Developer mac key array: ");
+        SecureChannelProtocol.debugByteArray(kekKeyArray, " Developer kek key array: ");
 
         SymmetricKey masterKey = null;
         SymmetricKey oldMasterKey = null;
@@ -1151,7 +1684,7 @@ public class SecureChannelProtocol {
         byte[] output = null;
 
         if (oldMasterKeyName == null || oldKeyInfo == null || newKeyInfo == null
-                || keySet == null) {
+                || keySet == null || params == null) {
             throw new EBaseException(method + "Invalid input!");
         }
 
@@ -1162,6 +1695,9 @@ public class SecureChannelProtocol {
         String fullNewMasterKeyName = getFullMasterKeyName(newMasterKeyName);
         String fullOldMasterKeyName = getFullMasterKeyName(oldMasterKeyName);
 
+        CMS.debug(method + " fullOldMasterKeyName: " + fullOldMasterKeyName);
+        CMS.debug(method + " fullNewMasterKeyName: " + fullNewMasterKeyName);
+
         CryptoManager cm = null;
         CryptoToken token = null;
         CryptoToken newToken = null;
@@ -1217,16 +1753,27 @@ public class SecureChannelProtocol {
         StandardKDF standardKDF = new StandardKDF(this);
         NistSP800_108KDF nistKDF = new NistSP800_108KDF(this);
 
-        KDCenc = KDF.getDiversificationData(KDD, SecureChannelProtocol.encType);
-        KDCmac = KDF.getDiversificationData(KDD, SecureChannelProtocol.macType);
-        KDCkek = KDF.getDiversificationData(KDD, SecureChannelProtocol.kekType);
+        KDCenc = KDF.getDiversificationData_VISA2(KDD, SecureChannelProtocol.encType);
+        KDCmac = KDF.getDiversificationData_VISA2(KDD, SecureChannelProtocol.macType);
+        KDCkek = KDF.getDiversificationData_VISA2(KDD, SecureChannelProtocol.kekType);
 
-        if (protocol == PROTOCOL_ONE) {
-            if (checkForDeveloperKeySet(oldMasterKeyName) == true) {
-                CMS.debug(method + " Developer key set case: ");
-            } else {
-                CMS.debug(method + " Not Developer key set case: ");
+        //This routine does not even support protocol 2, bail if asked to do so.
+        if (protocol == PROTOCOL_TWO) {
+            throw new EBaseException(method + " SCP 02 not yet supported here.");
+        }
 
+        String transportKeyName = SecureChannelProtocol.getSharedSecretKeyName(null);
+
+        if (checkForDeveloperKeySet(oldMasterKeyName) == true) {
+            //Starting with the deve key set, do nothing in this clause
+            CMS.debug(method + " Developer key set case:  protocol: " + protocol);
+        } else {
+            //Case where down grading back to the deve key set, or to another master key set
+            // This clause does nothing but calculate the kek key of the
+            // Current keyset, which will be used to wrap the new keys, to be calculated
+            CMS.debug(method + " Not Developer key set case: ");
+
+            if (protocol == PROTOCOL_ONE ) {
                 if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, oldKeyVersion)) {
                     CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF.");
 
@@ -1252,19 +1799,44 @@ public class SecureChannelProtocol {
                     old_kek_sym_key = standardKDF.computeCardKey(oldMasterKey, KDCkek, token, PROTOCOL_ONE);
                 }
 
-            }
+            } else { // Protocol 3
 
-            /* special case #01#01 */
-            if (fullNewMasterKeyName != null && fullNewMasterKeyName.equals("#01#01"))
-            {
-                CMS.debug(method + " Special case dev key set for DiversifyKey!");
+                old_kek_sym_key = this.computeSessionKey_SCP03(tokenName, oldMasterKeyName,
+                        oldKeyInfo, SecureChannelProtocol.kekType, kekKeyArray, keySet,
+                        CUIDValue, KDD, null, null, transportKeyName, params);
 
+                CMS.debug(method + " Moving back to the developer key set case, protocol 3");
+            }
+        }
+
+        // Now compute the new keys to be written to the token.
+        /* special case #01#01 */
+        if (fullNewMasterKeyName != null
+                && (fullNewMasterKeyName.equals("#01#01") || fullNewMasterKeyName.contains("#01#03")))
+        {
+            //This is the case where we revert to the original developer key set or key set 1
+            if (protocol == PROTOCOL_ONE) {
+                CMS.debug(method + " Special case returning to the dev key set (1) for DiversifyKey, protocol 1!");
                 encKey = returnDeveloperSymKey(newToken, SecureChannelProtocol.encType, keySet, null);
                 macKey = returnDeveloperSymKey(newToken, SecureChannelProtocol.macType, keySet, null);
                 kekKey = returnDeveloperSymKey(newToken, SecureChannelProtocol.kekType, keySet, null);
-            } else {
-                CMS.debug(method + " Compute card key on token case ! For new key version");
+            } else if (protocol == PROTOCOL_THREE) {
+                CMS.debug(method + " Special case or returning to the dev key set (or ver 1) for DiversifyKey, protocol 3!");
+                encKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, newKeyInfo,
+                        SecureChannelProtocol.encType, kekKeyArray,
+                        keySet, CUIDValue, KDD, null, null, transportKeyName, params);
+                macKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, newKeyInfo,
+                        SecureChannelProtocol.macType, kekKeyArray,
+                        keySet, CUIDValue, KDD, null, null, transportKeyName, params);
+                kekKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, newKeyInfo,
+                        SecureChannelProtocol.kekType, kekKeyArray,
+                        keySet, CUIDValue, KDD, null, null, transportKeyName, params);
+            }
+        } else {
+            //Compute new card keys for upgraded key set
+            CMS.debug(method + " Compute card key on token case ! For new key version.");
 
+            if (protocol == PROTOCOL_ONE) {
                 if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, newKeyVersion)) {
                     CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF. For new key version.");
 
@@ -1289,17 +1861,31 @@ public class SecureChannelProtocol {
                     kekKey = standardKDF.computeCardKeyOnToken(masterKey, KDCkek, protocol);
                 }
 
-                if (encKey == null || macKey == null || kekKey == null) {
-                    throw new EBaseException(method
-                            + " Can't derive session keys with selected KDF. For new key version.");
-                }
+            } else { // protocol 3
 
+                CMS.debug(method + " Generating new card keys to upgrade to, protocol 3.");
+                encKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, oldKeyInfo,
+                        SecureChannelProtocol.encType, kekKeyArray,
+                        keySet, CUIDValue, KDD, null, null, transportKeyName, params);
+                macKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, oldKeyInfo,
+                        SecureChannelProtocol.macType, kekKeyArray,
+                        keySet, CUIDValue, KDD, null, null, transportKeyName, params);
+                kekKey = this.computeSessionKey_SCP03(tokenName, newMasterKeyName, oldKeyInfo,
+                        SecureChannelProtocol.kekType, kekKeyArray,
+                        keySet, CUIDValue, KDD, null, null, transportKeyName, params);
+
+                // Generate an old kek key to do the encrypting of the new static keys
+
+                old_kek_sym_key = this.computeSessionKey_SCP03(tokenName, oldMasterKeyName, oldKeyInfo,
+                        SecureChannelProtocol.kekType, kekKeyArray,
+                        keySet, CUIDValue, KDD, null, null, transportKeyName, params);
+            }
+
+            if (encKey == null || macKey == null || kekKey == null) {
+                throw new EBaseException(method
+                        + " Can't derive session keys with selected KDF. For new key version.");
             }
 
-        } else if (protocol == PROTOCOL_TWO) {
-            throw new EBaseException(method + " SCP 02 not yet supported here.");
-        } else {
-            throw new EBaseException(method + " Unsupported protocol verison.");
         }
 
         boolean showKeysForDebug = false;
@@ -1327,7 +1913,9 @@ public class SecureChannelProtocol {
         } else {
 
             CMS.debug(method + " old kek sym key is null");
+
             old_kek_sym_key = returnDeveloperSymKey(token, SecureChannelProtocol.kekType, keySet, kekKeyArray);
+
             output = createKeySetDataWithSymKeys(newKeyVersion, (byte[]) null,
                     old_kek_sym_key,
                     encKey,
@@ -1340,6 +1928,8 @@ public class SecureChannelProtocol {
         return output;
     }
 
+    //Create the actual blob of new keys to be written to the token
+    // Suports prot1 and prot3
     private byte[] createKeySetDataWithSymKeys(byte newKeyVersion, byte[] old_kek_key_array,
             SymmetricKey old_kek_sym_key,
             SymmetricKey encKey, SymmetricKey macKey, SymmetricKey kekKey, byte protocol, String tokenName)
@@ -1392,6 +1982,8 @@ public class SecureChannelProtocol {
                 wrappingKey = old_kek_sym_key;
             }
 
+            CMS.debug(method + "Wrapping key: length: " + wrappingKey.getLength());
+
             alg = (byte) 0x81;
             encKey16 = extractDes2FromDes3(encKey, tokenName);
             macKey16 = extractDes2FromDes3(macKey, tokenName);
@@ -1404,23 +1996,48 @@ public class SecureChannelProtocol {
             keycheck_enc_key = this.computeKeyCheck(encKey, tokenName);
             keycheck_mac_key = this.computeKeyCheck(macKey, tokenName);
             keycheck_kek_key = this.computeKeyCheck(kekKey, tokenName);
-            /*
-            debugByteArray(keycheck_enc_key, " Keycheck enc key: ");
-            debugByteArray(keycheck_mac_key, " Keycheck mac key: ");
-            debugByteArray(keycheck_kek_key, " KeyCheck kek key: ");
-            */
 
         } else if (protocol == PROTOCOL_TWO) {
-            alg = (byte) 0x80;
             throw new EBaseException(method + " SCP 02 not yet implemented!");
+        } else if (protocol == PROTOCOL_THREE) {
+            CMS.debug(method + " Attempting SCP03");
+
+            if (old_kek_sym_key == null) {
+                CMS.debug(method + " SCP03: Using old kek key array.");
+                wrappingKey = unwrapAESSymKeyOnToken(token, old_kek_key_array, false);
+            } else {
+                CMS.debug(method + "SCP03: Using input old key key sym key.");
+                wrappingKey = old_kek_sym_key;
+            }
+
+            alg = (byte) 0x88;
+
+            encrypted_enc_key = this.wrapSessionKey(tokenName, encKey, wrappingKey);
+            encrypted_mac_key = this.wrapSessionKey(tokenName, macKey, wrappingKey);
+            encrypted_kek_key = this.wrapSessionKey(tokenName, kekKey, wrappingKey);
+
+            keycheck_enc_key = this.computeKeyCheck_SCP03(encKey, tokenName);
+            keycheck_mac_key = this.computeKeyCheck_SCP03(macKey, tokenName);
+            keycheck_kek_key = this.computeKeyCheck_SCP03(kekKey, tokenName);
+
         } else {
             throw new EBaseException(method + " Invalid SCP version requested!");
         }
 
         // Compose the final key set data byte array
 
-        byte[] b1 = new byte[] { alg, 0x10 };
-        byte[] b2 = new byte[] { 0x3 };
+        byte[] b1 = null;
+        byte[] b2 = null;
+
+        if (protocol == PROTOCOL_THREE) {
+            //Will be different if the key is bigger than AES 128
+            // Support 128 for now
+            b1 = new byte[] { alg, 0x11, (byte) encrypted_enc_key.length };
+        } else {
+            b1 = new byte[] { alg, 0x10 };
+        }
+
+        b2 = new byte[] { 0x3 };
 
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 
@@ -1484,6 +2101,10 @@ public class SecureChannelProtocol {
         if (keyInfo.equals("#01#02") || keyInfo.equals("#FF#02"))
             return true;
 
+        //SCP03
+        if (keyInfo.contains("#01#03") || keyInfo.contains("#FF#03"))
+            return true;
+
         return false;
     }
 
@@ -1493,6 +2114,42 @@ public class SecureChannelProtocol {
         }
     }
 
+
+    //SCP03 wrap a session key with AES kek key.
+    public byte[] encryptData_SCP03(String selectedToken, String keyNickName, byte[] data, byte[] keyInfo,
+            byte nistSP800_108KdfOnKeyVersion, boolean nistSP800_108KdfUseCuidAsKdd, byte[] xCUID, byte[] xKDD,
+            byte[] kekKeyArray, String useSoftToken_s, String keySet, GPParams params) throws EBaseException {
+
+        String method = "SecureChannelProtocol.encryptData_SCP03:";
+
+        CMS.debug(method + " Entering ....");
+
+        String transportKeyName = SecureChannelProtocol.getSharedSecretKeyName(null);
+
+        if (keyInfo == null || keySet == null || (keyInfo == null || keyInfo.length < 2) || params == null) {
+            throw new EBaseException(method + "Invalid input!");
+        }
+
+        if (xCUID == null || xCUID.length <= 0) {
+            throw new EBaseException(method + "CUID invalid size!");
+        }
+
+        if (xKDD == null || xKDD.length != NistSP800_108KDF.KDD_SIZE_BYTES) {
+            throw new EBaseException(method + "KDD invalid size!");
+        }
+
+        SymmetricKey kekKey = computeSessionKey_SCP03(selectedToken, keyNickName,
+                keyInfo, kekType, kekKeyArray, keySet, xCUID, xKDD,
+                null, null, transportKeyName, params);
+
+        byte[] output = null;
+        output = computeAES_CBCEncryption(kekKey, selectedToken, data, null);
+
+        debugByteArray(output, method + " encryptData: Output: ");
+
+        return output;
+    }
+
     public byte[] encryptData(String selectedToken, String keyNickName, byte[] data, byte[] keyInfo,
             byte nistSP800_108KdfOnKeyVersion, boolean nistSP800_108KdfUseCuidAsKdd, byte[] xCUID, byte[] xKDD,
             byte[] kekKeyArray, String useSoftToken_s, String keySet) throws EBaseException {
diff --git a/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java b/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java
index 1d61a46..8c228a1 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java
@@ -19,6 +19,39 @@ public class StandardKDF extends KDF {
         this.protocol = protocol;
     }
 
+    // For the scp03 g&d smart cafe, the dev keys must start out as DES3 keys
+    // but then this routine must return the AES version of the key
+
+    public SymmetricKey computeCardKey_SCP03_WithDES3(SymmetricKey masterKey, byte[] derivationData, CryptoToken token)
+            throws EBaseException {
+
+        String method = "StandardKDF.computeCardKey_SCP03_WithDES3:";
+
+        CMS.debug(method + " entering ...");
+
+        if (masterKey == null || token == null
+                || derivationData == null || ((derivationData.length != SecureChannelProtocol.AES_128_BYTES) &&
+                        (derivationData.length != SecureChannelProtocol.AES_192_BYTES) &&
+                (derivationData.length != SecureChannelProtocol.AES_256_BYTES))) {
+
+            CMS.debug(method + " Invalid input parameters!");
+
+            throw new EBaseException(method + " Invalid input parameters!");
+        }
+
+        //Now the new key data is the derivation data encrypted with DESS3
+
+        byte[] encrypted;
+        try {
+            encrypted = this.protocol.computeDes3EcbEncryption(masterKey, token.getName(), derivationData);
+        } catch (TokenException e) {
+           throw new EBaseException(method + "Can't derive key data!");
+        }
+        //SecureChannelProtocol.debugByteArray(encrypted, "calculated key: ");
+
+        return this.protocol.unwrapAESSymKeyOnToken(token, encrypted, false);
+
+    }
     public SymmetricKey computeCardKey(SymmetricKey masterKey, byte[] derivationData, CryptoToken token, int protocol)
             throws EBaseException {
 
@@ -54,12 +87,14 @@ public class StandardKDF extends KDF {
                 CMS.debug(method + "Now try this the old fashioned way");
 
                 byte[] encrypted = this.protocol.computeDes3EcbEncryption(masterKey, token.getName(), derivationData);
-                byte[] parityEncrypted = KDF.getDesParity(encrypted);
+                SecureChannelProtocol.debugByteArray(encrypted, "calculated key: ");
+              //  byte[] parityEncrypted = KDF.getDesParity(encrypted);
                 CMS.debug(method + "done computeDes3EcbEncryptiong");
+
                 derivedKey = this.protocol.unwrapSymKeyOnToken(token, null,
-                        parityEncrypted, false);
+                        encrypted, false,SymmetricKey.DES3);
 
-                // The key this way is aleady des3, return
+                // The key this way is already final, return
 
                 return derivedKey;
             }
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 a282cd2..6a17466 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
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.util.ArrayList;
 import java.util.StringTokenizer;
 
 import javax.servlet.ServletConfig;
@@ -1570,8 +1571,11 @@ public class TokenServlet extends CMSServlet {
         return cs.getString("tks.tksSharedSymKeyName", TRANSPORT_KEY_NAME);
     }
 
+    //Accepts protocol param and supports scp03.
     private void processDiversifyKey(HttpServletRequest req,
             HttpServletResponse resp) throws EBaseException {
+
+        String method = "TokenServlet.processDiversifyKey: ";
         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.
@@ -1609,6 +1613,9 @@ public class TokenServlet extends CMSServlet {
 
         String rProtocol = req.getParameter(IRemoteRequest.CHANNEL_PROTOCOL);
         String rWrappedDekKey = req.getParameter(IRemoteRequest.WRAPPED_DEK_SESSION_KEY);
+
+        CMS.debug(method + "rWrappedDekKey: " + rWrappedDekKey);
+
         int protocol = 1;
         String auditMessage = "";
 
@@ -1670,13 +1677,13 @@ public class TokenServlet extends CMSServlet {
 
         if (!missingParam) {
             xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(oldMasterKeyName);
-            if (xkeyInfo == null || xkeyInfo.length != 2) {
+            if (xkeyInfo == null || (xkeyInfo.length != 2 && xkeyInfo.length != 3)) {
                 badParams += " KeyInfo length,";
                 CMS.debug("TokenServlet: Invalid key info length");
                 missingParam = true;
             }
             xnewkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(newMasterKeyName);
-            if (xnewkeyInfo == null || xnewkeyInfo.length != 2) {
+            if (xnewkeyInfo == null || (xnewkeyInfo.length != 2 && xnewkeyInfo.length != 3)) {
                 badParams += " NewKeyInfo length,";
                 CMS.debug("TokenServlet: Invalid new key info length");
                 missingParam = true;
@@ -1692,7 +1699,6 @@ public class TokenServlet extends CMSServlet {
             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.");
@@ -1766,7 +1772,12 @@ public class TokenServlet extends CMSServlet {
             if (mNewKeyNickName != null)
                 newMasterKeyName = mNewKeyNickName;
 
-            String oldKeyInfoMap = "tks." + keySet + ".mk_mappings." + req.getParameter(IRemoteRequest.TOKEN_KEYINFO); //#xx#xx
+            String tokKeyInfo =  req.getParameter(IRemoteRequest.TOKEN_KEYINFO);
+
+            // Get the first 6 characters, since scp03 gives us extra characters.
+            tokKeyInfo = tokKeyInfo.substring(0,6);
+            String oldKeyInfoMap = "tks." + keySet + ".mk_mappings." + tokKeyInfo; //#xx#xx
+            CMS.debug(method + " oldKeyInfoMap: " + oldKeyInfoMap);
             String oldMappingValue = CMS.getConfigStore().getString(oldKeyInfoMap, null);
             String oldSelectedToken = null;
             if (oldMappingValue == null) {
@@ -1778,7 +1789,9 @@ public class TokenServlet extends CMSServlet {
                 oldKeyNickName = st.nextToken();
             }
 
-            String newKeyInfoMap = "tks.mk_mappings." + rnewKeyInfo; //#xx#xx
+
+            String newKeyInfoMap = "tks.mk_mappings." + rnewKeyInfo.substring(0,6); //#xx#xx
+            CMS.debug(method + " newKeyInfoMap: " + newKeyInfoMap);
             String newMappingValue = CMS.getConfigStore().getString(newKeyInfoMap, null);
             String newSelectedToken = null;
             if (newMappingValue == null) {
@@ -1795,14 +1808,22 @@ public class TokenServlet extends CMSServlet {
                     " oldKeyNickName=" + oldKeyNickName + " newKeyNickName=" +
                     newKeyNickName);
 
-            byte kekKeyArray[] =
-                    com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key"));
+            byte kekKeyArray[] = getDeveKeyArray("kek_key", sconfig, keySet);
+            byte macKeyArray[] = getDeveKeyArray("auth_key", sconfig, keySet);
+            byte encKeyArray[] = getDeveKeyArray("mac_key", sconfig, keySet);
+
+            //        com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key"));
+
+            //GPParams for scp03 right now, reads some scp03 specific values from the config of a given keyset
+            // passed down to the SecureChannelProtocol functions that deal with SCP03
+
+            GPParams gp3Params = readGPSettings(keySet);
 
-            SecureChannelProtocol secProtocol = new SecureChannelProtocol();
+            SecureChannelProtocol secProtocol = new SecureChannelProtocol(protocol);
             // AC: KDF SPEC CHANGE - check for error reading settings
             if (missingSetting_exception == null) {
-                if (protocol == 1) {
-                    KeySetData = secProtocol.diversifyKey(oldSelectedToken,
+                if (protocol == 1 || protocol == 3) {
+                   KeySetData = secProtocol.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
@@ -1811,7 +1832,7 @@ public class TokenServlet extends CMSServlet {
                             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);
+                            kekKeyArray,encKeyArray,macKeyArray, useSoftToken_s, keySet, (byte) protocol,gp3Params);
 
                 } else if (protocol == 2) {
                     KeySetData = SessionKey.DiversifyKey(oldSelectedToken, newSelectedToken, oldKeyNickName,
@@ -1954,6 +1975,8 @@ public class TokenServlet extends CMSServlet {
         String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO);
         String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID);
 
+        String protocolValue = req.getParameter(IRemoteRequest.CHANNEL_PROTOCOL);
+
         // AC: KDF SPEC CHANGE - read new KDD parameter from TPS
         String rKDD = req.getParameter("KDD");
         if ((rKDD == null) || (rKDD.length() == 0)) {
@@ -1996,6 +2019,8 @@ public class TokenServlet extends CMSServlet {
                 s_isRandom);
         audit(auditMessage);
 
+        GPParams gp3Params = readGPSettings(keySet);
+
         if (isRandom) {
             if ((rdata == null) || (rdata.equals(""))) {
                 CMS.debug("TokenServlet: processEncryptData(): no data in request.  Generating random number as data");
@@ -2058,7 +2083,7 @@ public class TokenServlet extends CMSServlet {
             }
 
             xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo);
-            if (xkeyInfo == null || xkeyInfo.length != 2) {
+            if (xkeyInfo == null || (xkeyInfo.length != 2 && xkeyInfo.length != 3)) {
                 badParams += " KeyInfo length,";
                 CMS.debug("TokenServlet: Invalid key info length");
                 missingParam = true;
@@ -2103,7 +2128,7 @@ public class TokenServlet extends CMSServlet {
                 data = com.netscape.cmsutil.util.Utils.SpecialDecode(rdata);
             keyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo);
 
-            String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo;
+            String keyInfoMap = "tks." + keySet + ".mk_mappings." +  rKeyInfo.substring(0,6);
             String mappingValue = CMS.getConfigStore().getString(keyInfoMap, null);
             if (mappingValue == null) {
                 selectedToken = CMS.getConfigStore().getString("tks.defaultSlot", CryptoUtil.INTERNAL_TOKEN_NAME);
@@ -2114,20 +2139,53 @@ public class TokenServlet extends CMSServlet {
                 keyNickName = st.nextToken();
             }
 
+
+            //calculate the protocol
+
+            int protocolInt = SecureChannelProtocol.PROTOCOL_ONE;
+            try
+            {
+                 protocolInt = Integer.parseInt(protocolValue);
+            }
+            catch (NumberFormatException nfe)
+            {
+                protocolInt = SecureChannelProtocol.PROTOCOL_ONE;
+            }
+
+            CMS.debug( "TokenServerlet.encryptData: protocol input: " + protocolInt);
+
+            //Check for reasonable sanity, leave room for future versions
+            if(protocolInt <= 0 || protocolInt > 20) {
+                CMS.debug( "TokenServerlet.encryptData: unfamliar protocl, assume default of 1.");
+                protocolInt = 1;
+
+            }
+
             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) {
 
-                SecureChannelProtocol protocol = new SecureChannelProtocol();
 
-                encryptedData = protocol.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);
+                SecureChannelProtocol protocol = new SecureChannelProtocol(protocolInt);
+
+                if (protocolInt != SecureChannelProtocol.PROTOCOL_THREE) {
+
+                    encryptedData = protocol.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);
+
+                } else {
+
+                    encryptedData = protocol.encryptData_SCP03(selectedToken, keyNickName, data, xkeyInfo,
+                            nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, kekKeyArray,
+                            useSoftToken_s, keySet,gp3Params);
+
+                }
 
                 SecureChannelProtocol.debugByteArray(encryptedData, "New Encrypt Data: ");
 
@@ -2402,7 +2460,7 @@ public class TokenServlet extends CMSServlet {
         //CMS.debug("Protocol: " + protocol + " temp: " + temp);
 
         setDefaultSlotAndKeyName(req);
-        if (temp != null) {
+        if (temp != null && protocol == null) {
             processComputeSessionKey(req, resp);
         } else if (req.getParameter(IRemoteRequest.TOKEN_DATA) != null) {
             processEncryptData(req, resp);
@@ -2413,10 +2471,515 @@ public class TokenServlet extends CMSServlet {
         } else if (protocol != null && protocol.contains("2") && (derivationConstant != null)) {
             //SCP02 compute one session key.
             processComputeSessionKeySCP02(req, resp);
+
+        }  else if (protocol != null && protocol.contains("3") ) {
+            processComputeSessionKeysSCP03(req,resp);
         } else {
             throw new EBaseException("Process: Can't decide upon function to call!");
+        }
+    }
+
+    //Create all the session keys for scp03 at once and return.
+    //ToDo: calcualte the optional rmac key
+    private void processComputeSessionKeysSCP03(HttpServletRequest req, HttpServletResponse resp) throws EBaseException {
+        String method = "processComputeSessionKeysSCP03:";
+        CMS.debug(method + " entering ...");
+
+        byte[] card_challenge, host_challenge, xCUID, xKDD;
+        byte[] card_crypto, host_cryptogram, input_card_crypto;
+        byte[] xcard_challenge, xhost_challenge;
+        byte[] enc_session_key, xkeyInfo,mac_session_key, kek_session_key;
+        String auditMessage = null;
+        String errorMsg = "";
+        String badParams = "";
+        String transportKeyName = "";
+        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 keySet = req.getParameter(IRemoteRequest.TOKEN_KEYSET);
+        if (keySet == null || keySet.equals("")) {
+            keySet = "defKeySet";
+        }
+        CMS.debug("keySet selected: " + keySet);
+
+        GPParams gp3Params = readGPSettings(keySet);
+
+        boolean serversideKeygen = false;
+
+        IConfigStore sconfig = CMS.getConfigStore();
+        boolean isCryptoValidate = true;
+        boolean missingParam = false;
+
+        Exception missingSetting_exception = null;
+
+        mac_session_key = null;
+        kek_session_key = null;
+        card_crypto = null;
+        host_cryptogram = null;
+        enc_session_key = null;
+
+        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,
+                ILogger.SUCCESS,
+                agentId);
+
+        audit(auditMessage);
+
+        String kek_wrapped_desKeyString = null;
+        String keycheck_s = null;
+
+        String useSoftToken_s = CMS.getConfigStore().getString("tks.useSoftToken", "true");
+        if (!useSoftToken_s.equalsIgnoreCase("true"))
+            useSoftToken_s = "false";
+
+        CMS.debug(method + " useSoftToken: " + useSoftToken_s);
+
+        String rServersideKeygen = req.getParameter(IRemoteRequest.SERVER_SIDE_KEYGEN);
+        if (rServersideKeygen.equals("true")) {
+
+            serversideKeygen = true;
+        }
+
+        CMS.debug(method + " serversideKeygen: " + serversideKeygen);
+
+        try {
+            isCryptoValidate = sconfig.getBoolean("cardcryptogram.validate.enable", true);
+        } catch (EBaseException eee) {
+        }
+
+        CMS.debug(method + " Do crypto validation: " + isCryptoValidate);
+
+        transportKeyName = getSharedSecretName(sconfig);
+
+        String rcard_challenge = req.getParameter(IRemoteRequest.TOKEN_CARD_CHALLENGE);
+        String rhost_challenge = req.getParameter(IRemoteRequest.TOKEN_HOST_CHALLENGE);
+        String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO);
+        String rcard_cryptogram = req.getParameter(IRemoteRequest.TOKEN_CARD_CRYPTOGRAM);
+
+        if ((rCUID == null) || (rCUID.equals(""))) {
+            CMS.debug(method + " missing request parameter: CUID");
+            badParams += " CUID,";
+            missingParam = true;
+        }
+
+        if ((rKDD == null) || (rKDD.length() == 0)) {
+            CMS.debug(method + " missing request parameter: KDD");
+            badParams += " KDD,";
+            missingParam = true;
+        }
+
+        if ((rcard_challenge == null) || (rcard_challenge.equals(""))) {
+            badParams += " card_challenge,";
+            CMS.debug(method + " missing request parameter: card challenge");
+            missingParam = true;
+        }
+
+        if ((rhost_challenge == null) || (rhost_challenge.equals(""))) {
+            badParams += " host_challenge,";
+            CMS.debug(method + " missing request parameter: host challenge");
+            missingParam = true;
+        }
+
+        if ((rcard_cryptogram == null) || (rcard_cryptogram.equals(""))) {
+            badParams += " card_cryptogram,";
+            CMS.debug(method + " missing request parameter: card_cryptogram");
+            missingParam = true;
+        }
+
+        if ((rKeyInfo == null) || (rKeyInfo.equals(""))) {
+            badParams += " KeyInfo,";
+            CMS.debug(method + "missing request parameter: key info");
+            missingParam = true;
+        }
+
+        String selectedToken = null;
+        String keyNickName = null;
+        boolean sameCardCrypto = true;
+
+        xCUID = null;
+        xKDD = null;
+        xkeyInfo = null;
+        xcard_challenge = null;
+        xhost_challenge = null;
+
+        if (!missingParam) {
+            xCUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID);
+            if (xCUID == null || xCUID.length != 10) {
+                badParams += " CUID length,";
+                CMS.debug("TokenServlet: 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;
+            }
+
+            xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo);
+            if (xkeyInfo == null || xkeyInfo.length != 3) {
+                badParams += " KeyInfo length,";
+                CMS.debug("TokenServlet: Invalid key info length.");
+                missingParam = true;
+            }
+            xcard_challenge =
+                    com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_challenge);
+            if (xcard_challenge == null || xcard_challenge.length != 8) {
+                badParams += " card_challenge length,";
+                CMS.debug("TokenServlet: Invalid card challenge length.");
+                missingParam = true;
+            }
+
+            xhost_challenge = com.netscape.cmsutil.util.Utils.SpecialDecode(rhost_challenge);
+            if (xhost_challenge == null || xhost_challenge.length != 8) {
+                badParams += " host_challenge length,";
+                CMS.debug("TokenServlet: Invalid host challenge length");
+                missingParam = true;
+            }
+        }
+
+        ArrayList<String> serverSideValues = null;
+
+        if (!missingParam) {
+            card_challenge =
+                    com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_challenge);
+
+            host_challenge = com.netscape.cmsutil.util.Utils.SpecialDecode(rhost_challenge);
+
+            String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo.substring(0,6); //#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();
+            }
+
+            CMS.debug(method + " selectedToken: " + selectedToken + " keyNickName: " + keyNickName );
+
+            SymmetricKey macSessionKey = null;
+            SymmetricKey encSessionKey = null;
+            SymmetricKey kekSessionKey = null;
+
+            if (selectedToken != null && keyNickName != null
+                    && missingSetting_exception == null) {
+
+                try {
+
+                    byte macKeyArray[] =
+                            com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+                                    + keySet + ".mac_key"));
+                    CMS.debug("TokenServlet about to try ComputeSessionKey selectedToken="
+                            + selectedToken + " keyNickName=" + keyNickName);
+
+                    SecureChannelProtocol protocol = new SecureChannelProtocol(SecureChannelProtocol.PROTOCOL_THREE);
+
+                    macSessionKey = protocol.computeSessionKey_SCP03(selectedToken, keyNickName,xkeyInfo,
+                            SecureChannelProtocol.macType, macKeyArray, keySet,xCUID, xKDD, xhost_challenge, xcard_challenge,
+                            transportKeyName,gp3Params);
+
+                    mac_session_key = protocol.wrapSessionKey(selectedToken, macSessionKey, null);
+
+                    if (mac_session_key == null) {
+                        CMS.debug(method + " Can't get mac session key bytes");
+                        throw new Exception(method + " Can't get mac session key bytes");
+
+                    }
+
+                    byte encKeyArray[] =
+                            com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+                                    + keySet + ".auth_key"));
+
+                    encSessionKey = protocol.computeSessionKey_SCP03(selectedToken, keyNickName,xkeyInfo,
+                            SecureChannelProtocol.encType, encKeyArray, keySet, xCUID, xKDD, xhost_challenge, xcard_challenge,
+                            transportKeyName,gp3Params);
+
+                    enc_session_key = protocol.wrapSessionKey(selectedToken, encSessionKey, null);
+
+                    if (enc_session_key == null) {
+                        CMS.debug("TokenServlet:Tried ComputeEncSessionKey, got NULL ");
+                        throw new Exception("Can't compute enc session key!");
+
+                    }
+
+                    byte kekKeyArray[] =
+                            com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+                                    + keySet + ".kek_key"));
+
+                    kekSessionKey = protocol.computeSessionKey_SCP03(selectedToken, keyNickName, xkeyInfo,
+                            SecureChannelProtocol.kekType, kekKeyArray, keySet, xCUID, xKDD, xhost_challenge,
+                            xcard_challenge,
+                            transportKeyName,gp3Params);
+
+                    kek_session_key = protocol.wrapSessionKey(selectedToken, kekSessionKey, null);
+
+
+                    //Offload some of the tedious params gathering to another method
+                    //ToDo, create a method that reads all this stuff at once for all major methods
+                    if (serversideKeygen) {
+                        try {
+                            serverSideValues = calculateServerSideKeygenValues(useSoftToken_s, selectedToken,
+                                    kekSessionKey, protocol);
+                        } catch (EBaseException e) {
+
+                            CMS.debug(method + " Can't calcualte server side keygen required values...");
+
+                        }
+                    }
+
+                    try {
+                        isCryptoValidate = sconfig.getBoolean("cardcryptogram.validate.enable", true);
+                    } catch (EBaseException eee) {
+                    }
+
+                    ByteArrayOutputStream contextStream = new ByteArrayOutputStream();
+                    try {
+                        contextStream.write(host_challenge);
+                        contextStream.write(card_challenge);
+                    } catch (IOException e) {
+                        throw new EBaseException(method + " Error calculating derivation data!");
+                    }
+
+                    host_cryptogram = protocol.computeCryptogram_SCP03(macSessionKey, selectedToken, contextStream.toByteArray(),NistSP800_108KDF.HOST_CRYPTO_KDF_CONSTANT);
+                    SecureChannelProtocol.debugByteArray(host_cryptogram, method + " calculated host crypto: " + host_cryptogram.length);
+
+
+                   if( isCryptoValidate) {
+                       if (rcard_cryptogram == null) {
+                           CMS.debug(method + " missing card cryptogram");
+                           throw new Exception(method + "Missing card cryptogram");
+                       }
+                       input_card_crypto =
+                               com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_cryptogram);
+                       card_crypto = protocol.computeCryptogram_SCP03(macSessionKey, selectedToken, contextStream.toByteArray(),NistSP800_108KDF.CARD_CRYPTO_KDF_CONSTANT);
+                       SecureChannelProtocol.debugByteArray(card_crypto, method + " calculated card crypto: ");
+                       SecureChannelProtocol.debugByteArray(input_card_crypto, method + " original card crypto: ");
+
+                       if(!cryptoGramsAreEqual(input_card_crypto, card_crypto)) {
+                           throw new Exception(method + "Card cryptogram mismatch!");
+                       }
+
+                   }
+                } catch (Exception e) {
+                    CMS.debug(e);
+                    CMS.debug("TokenServlet Computing Session Key: " + e.toString());
+                    if (isCryptoValidate)
+                        sameCardCrypto = false;
+                }
+            }
+        } // ! missingParam
+
+        String value = "";
+
+        resp.setContentType("text/html");
+
+        String encSessionKeyString = "";
+        String macSessionKeyString = "";
+        String kekSessionKeyString = "";
+
+        String drm_trans_wrapped_desKeyString = "";
+        String cryptogram = "";
+        String status = "0";
+
+        if (enc_session_key != null && enc_session_key.length > 0) {
+            encSessionKeyString =
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(enc_session_key);
+        } else {
+            status = "1";
+        }
+
+        if (mac_session_key != null && mac_session_key.length > 0) {
+            macSessionKeyString =
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(mac_session_key);
+        } else {
+            status = "1";
+        }
+
+        if (kek_session_key != null && kek_session_key.length > 0) {
+            kekSessionKeyString =
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(kek_session_key);
+        } else {
+            status = "1";
+        }
+
+        if (serversideKeygen == true) {
+            if (serverSideValues.size() == 3) {
+                drm_trans_wrapped_desKeyString = serverSideValues.get(2);
+                kek_wrapped_desKeyString = serverSideValues.get(0);
+                keycheck_s = serverSideValues.get(1);
+            }
+            else {
+                status = "1";
+            }
+        }
 
+        if (host_cryptogram != null && host_cryptogram.length > 0) {
+            cryptogram =
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(host_cryptogram);
+        } else {
+            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) {
+                status = "4";
+            }
         }
+
+        if (!sameCardCrypto) {
+            if (status.equals("0") == true) {
+                status = "5";
+            }
+        }
+
+        if (missingSetting_exception != null) {
+            status = "6";
+        }
+
+        if (missingParam) {
+            status = "3";
+        }
+
+        if (!status.equals("0")) {
+
+            if (status.equals("1")) {
+                errorMsg = "Problem generating session key info.";
+            }
+
+            if (status.equals("2")) {
+                errorMsg = "Problem creating host_cryptogram.";
+            }
+
+            if (status.equals("5")) {
+                errorMsg = "Card cryptogram mismatch. Token likely has incorrect keys.";
+            }
+
+            if (status.equals("4")) {
+                errorMsg = "Problem obtaining token information.";
+            }
+
+            if (status.equals("6")) {
+                errorMsg = "Problem reading required configuration value.";
+            }
+
+            if (status.equals("3")) {
+                if (badParams.endsWith(",")) {
+                    badParams = badParams.substring(0, badParams.length() - 1);
+                }
+                errorMsg = "Missing input parameters :" + badParams;
+            }
+
+            value = IRemoteRequest.RESPONSE_STATUS + "=" + status;
+        } else {
+            if (serversideKeygen == true) {
+                StringBuffer sb = new StringBuffer();
+                sb.append(IRemoteRequest.RESPONSE_STATUS + "=0&");
+                sb.append(IRemoteRequest.TKS_RESPONSE_MacSessionKey + "=");
+                sb.append(macSessionKeyString);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_HostCryptogram + "=");
+                sb.append(cryptogram);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_EncSessionKey + "=");
+                sb.append(encSessionKeyString);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_KekSessionKey + "=");
+                sb.append(kekSessionKeyString);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_KEK_DesKey + "=");
+                sb.append(kek_wrapped_desKeyString);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_KeyCheck + "=");
+                sb.append(keycheck_s);
+                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_MacSessionKey + "=");
+                sb.append(macSessionKeyString);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_HostCryptogram + "=");
+                sb.append(cryptogram);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_EncSessionKey + "=");
+                sb.append(encSessionKeyString);
+                sb.append("&" + IRemoteRequest.TKS_RESPONSE_KekSessionKey + "=");
+                value = sb.toString();
+            }
+
+        }
+        //CMS.debug(method + "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(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
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS,
+                    logParams);
+
+        } else {
+            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
+                    errorMsg // Error
+            };
+            auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,
+                    logParams);
+
+        }
+
+        audit(auditMessage);
+
     }
 
     /**
@@ -2479,4 +3042,197 @@ public class TokenServlet extends CMSServlet {
 
     }
 
+    //returns ArrayList of following values
+    // 0 : Kek wrapped des key
+    // 1 : keycheck value
+    // 2 : trans wrapped des key
+    private ArrayList<String> calculateServerSideKeygenValues(String useSoftToken, String selectedToken,
+            SymmetricKey kekSessionKey, SecureChannelProtocol protocol) throws EBaseException {
+
+        SymmetricKey desKey = null;
+        String method = "TokenServlet.calculateSErverSideKeygenValues: ";
+        ArrayList<String> values = new ArrayList<String>();
+
+        /**
+         * 0. generate des key
+         * 1. encrypt des key with kek 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
+         **/
+        CMS.debug(method + " entering...");
+
+        // (1) generate DES key
+        /* applet does not support DES3
+        org.mozilla.jss.crypto.KeyGenerator kg =
+            internalToken.getKeyGenerator(KeyGenAlgorithm.DES3);
+            desKey = kg.generate();*/
+
+        /*
+         * GenerateSymkey firt generates a 16 byte DES2 key.
+         * It then pads it into a 24 byte key with last
+         * 8 bytes copied from the 1st 8 bytes.  Effectively
+         * making it a 24 byte DES2 key.  We need this for
+         * wrapping private keys on DRM.
+         */
+        /*generate it on whichever token the master key is at*/
+
+        if (useSoftToken.equals("true")) {
+            CMS.debug(method + " key encryption key generated on internal");
+            desKey = protocol.generateSymKey("internal");
+            //cfu audit here? sym key gen done
+        } else {
+            CMS.debug("TokenServlet: key encryption key generated on " + selectedToken);
+            desKey = protocol.generateSymKey(selectedToken);
+        }
+        if (desKey == null) {
+            throw new EBaseException(method + "can't generate key encryption key");
+        }
+
+        /*
+         * 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
+         */
+
+
+       // protocol.wrapSessionKey(tokenName, sessionKey, wrappingKey)
+
+        byte[] encDesKey = protocol.ecbEncrypt(kekSessionKey, desKey, selectedToken);
+
+        String kek_wrapped_desKeyString =
+                com.netscape.cmsutil.util.Utils.SpecialEncode(encDesKey);
+
+        CMS.debug(method + "kek_wrapped_desKeyString: " + kek_wrapped_desKeyString);
+
+        values.add(kek_wrapped_desKeyString);
+
+        // get keycheck
+
+        byte[] keycheck = null;
+
+        keycheck = protocol.computeKeyCheck(desKey, selectedToken);
+
+        String keycheck_s =
+                com.netscape.cmsutil.util.Utils.SpecialEncode(keycheck);
+
+        CMS.debug(method + "keycheck_s " + keycheck_s);
+
+        values.add(keycheck_s);
+
+        //use DRM transport cert to wrap desKey
+        String drmTransNickname = CMS.getConfigStore().getString("tks.drm_transport_cert_nickname", "");
+
+        if ((drmTransNickname == null) || (drmTransNickname == "")) {
+            CMS.debug(method + " did not find DRM transport certificate nickname");
+            throw new EBaseException(method + "can't find DRM transport certificate nickname");
+        } else {
+            CMS.debug(method + " drmtransport_cert_nickname=" + drmTransNickname);
+        }
+
+        X509Certificate drmTransCert = null;
+        try {
+
+            drmTransCert = CryptoManager.getInstance().findCertByNickname(drmTransNickname);
+            // wrap kek session key with DRM transport public key
+            CryptoToken token = null;
+            if (useSoftToken.equals("true")) {
+                //token = CryptoManager.getInstance().getTokenByName(selectedToken);
+                token = CryptoManager.getInstance().getInternalCryptoToken();
+            } else {
+                token = CryptoManager.getInstance().getTokenByName(selectedToken);
+            }
+            PublicKey pubKey = drmTransCert.getPublicKey();
+            String pubKeyAlgo = pubKey.getAlgorithm();
+            CMS.debug("Transport Cert Key Algorithm: " + pubKeyAlgo);
+            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);
+            }
+            CMS.debug("desKey token " + desKey.getOwningToken().getName() + " token: " + token.getName());
+            byte[] drm_trans_wrapped_desKey = keyWrapper.wrap(desKey);
+
+            String drmWrappedDesStr =
+                    com.netscape.cmsutil.util.Utils.SpecialEncode(drm_trans_wrapped_desKey);
+
+            CMS.debug(method + " drmWrappedDesStr: " + drmWrappedDesStr);
+            values.add(drmWrappedDesStr);
+
+        } catch (Exception e) {
+            throw new EBaseException(e);
+        }
+
+        return values;
+    }
+
+    private boolean cryptoGramsAreEqual(byte[] original_cryptogram, byte[] calculated_cryptogram) {
+        boolean sameCardCrypto = true;
+
+        if (original_cryptogram == null || calculated_cryptogram == null) {
+            return false;
+        }
+        if (original_cryptogram.length == calculated_cryptogram.length) {
+            for (int i = 0; i < original_cryptogram.length; i++) {
+                if (original_cryptogram[i] != calculated_cryptogram[i]) {
+                    sameCardCrypto = false;
+                    break;
+                }
+            }
+        } else {
+            // different length; must be different
+            sameCardCrypto = false;
+        }
+
+        return sameCardCrypto;
+    }
+
+  //For now only used for scp03
+
+    static GPParams readGPSettings(String keySet) {
+        GPParams params = new GPParams();
+
+        String method = "TokenServlet.readGPSettings: ";
+        String gp3Settings = "tks." + keySet + ".prot3";
+
+        String divers = "emv";
+        try {
+            divers = CMS.getConfigStore().getString(gp3Settings + ".divers", "emv");
+        } catch (EBaseException e) {
+        }
+
+        params.setDiversificationScheme(divers);
+
+        CMS.debug(method + " Divers: " + divers);
+
+        String diversVer1Keys = "emv";
+
+        try {
+            diversVer1Keys = CMS.getConfigStore().getString(gp3Settings + ".diversVer1Keys","emv");
+        } catch (EBaseException e) {
+        }
+
+        params.setVersion1DiversificationScheme(diversVer1Keys);
+        CMS.debug(method + " Version 1 keys Divers: " + divers);
+
+        return params;
+    }
+
+    private byte[] getDeveKeyArray(String keyType,IConfigStore sconfig,String keySet) throws EBaseException {
+        byte devKeyArray[] = null;
+        try {
+            devKeyArray = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks."
+                    + keySet + "." + keyType));
+        } catch (Exception e) {
+            throw new EBaseException("Can't read static developer key array: " + keySet + ": " + keyType);
+        }
+
+        return devKeyArray;
+    }
+
+
 }
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 2331249..82482d0 100644
--- a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
+++ b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
@@ -53,6 +53,8 @@ public interface IRemoteRequest {
     /* computeSessionKey responses */
     public static final String TKS_RESPONSE_SessionKey = "sessionKey";
     public static final String TKS_RESPONSE_EncSessionKey = "encSessionKey";
+    public static final String TKS_RESPONSE_MacSessionKey = "macSessionKey";
+    public static final String TKS_RESPONSE_KekSessionKey = "kekSessionKey";
     public static final String TKS_RESPONSE_KEK_DesKey = "kek_wrapped_desKey";
     public static final String TKS_RESPONSE_DRM_Trans_DesKey = "drm_trans_desKey";
     public static final String TKS_RESPONSE_KeyCheck = "keycheck";
diff --git a/base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java b/base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java
index e5f38e1..e1de8f2 100644
--- a/base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java
+++ b/base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java
@@ -62,6 +62,14 @@ public class PlatformAndSecChannelProtoInfo {
         }
         return false;
     }
+
+    public boolean isSCP03() {
+        if(protocol == SecureChannel.SECURE_PROTO_03) {
+            return true;
+        }
+        return false;
+    }
+
     public void setProtocol(byte protocol) {
         this.protocol = protocol;
     }
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 f2e3236..9fc7ee0 100644
--- a/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java
+++ b/base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java
@@ -65,15 +65,22 @@ public class SecureChannel {
     public TPSProcessor processor;
     private PK11SymKey sessionKey;
 
-    //SCP01 or SCP02 key
+    //SCP01 or SCP02  or SCP03 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;
 
+    //SCP03
+    private PK11SymKey macSessionKey;
+
     private TPSBuffer dekSessionKeyWrapped;
 
     private TPSBuffer drmDesKey;
@@ -112,11 +119,65 @@ public class SecureChannel {
     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 };
 
+    // SCP02
     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 };
 
+    //SCP03 encryption counter
+
+    private TPSBuffer encryptionCounter;
+
+
+    //For SCP03
+
+    public SecureChannel(TPSProcessor processor,  PK11SymKey encSessionKey, PK11SymKey macSessionKey, PK11SymKey dekSessionKey,
+            TPSBuffer drmDesKey,TPSBuffer kekDesKey,
+             TPSBuffer keyCheck, TPSBuffer keyDiversificationData, TPSBuffer cardChallenge,
+            TPSBuffer cardCryptogram, TPSBuffer hostChallenge, TPSBuffer hostCryptogram, TPSBuffer keyInfoData,
+            PlatformAndSecChannelProtoInfo platformInfo)
+            throws TPSException {
+
+        if (processor == null ||  encSessionKey == null || keyDiversificationData == null
+                || cardChallenge == null || cardCryptogram == null || hostChallenge == null || hostCryptogram == null
+                || keyInfoData == null) {
+            throw new TPSException("SecureChannel.SecureChannel: Invalid data in constructor!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("SecureChannel.SecureChannel: For SCP03. :  ");
+
+        CMS.debug("kekDesKey: " + kekDesKey.toHexString());
+        CMS.debug("keyCheck: " + keyCheck.toHexString());
+
+        this.platProtInfo = platformInfo;
+        this.processor = processor;
+        this.encSessionKey = encSessionKey;
+        this.macSessionKey = macSessionKey;
+        this.dekSessionKey = dekSessionKey;
+
+        this.drmDesKey = drmDesKey;
+        this.setKekDesKey(kekDesKey);
+
+        this.keyCheck = keyCheck;
+        this.keyDiversificationData = keyDiversificationData;
+        this.cardChallenge = cardChallenge;
+        this.cardCryptogram = cardCryptogram;
+        this.hostChallenge = hostChallenge;
+        this.hostCryptogram = hostCryptogram;
+
+        //16 bytes of chaining value
+        this.icv = new TPSBuffer(16);
+
+        this.keyInfoData = keyInfoData;
+
+        this.secLevel = SecurityLevel.SECURE_MSG_NONE;
+        this.secLevelGP211 = ExternalAuthenticateAPDUGP211.SecurityLevel.CDEC_CMAC;
+        encryptionCounter = new TPSBuffer(16);
+
+    }
+
     //For SCP01
     public SecureChannel(TPSProcessor processor, PK11SymKey sessionKey, PK11SymKey encSessionKey, TPSBuffer drmDesKey,
             TPSBuffer kekDesKey, TPSBuffer keyCheck, TPSBuffer keyDiversificationData, TPSBuffer cardChallenge,
@@ -345,12 +406,46 @@ public class SecureChannel {
 
     public void externalAuthenticate() throws TPSException, IOException {
 
-        CMS.debug("SecureChannel.externalAuthenticate: entering. &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
+        String method = "SecureChannel.externalAuthenticate.";
+        CMS.debug(method + ": entering. &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
 
-        if (platProtInfo.isGP211() && platProtInfo.isSCP02()) {
+        TPSBuffer calculatedCardCryptogram = null;
+        if(platProtInfo.isSCP03()) {
+            CMS.debug("SecureChannel.externalAuthenticate: Attempting an External Authenticate for SCP03!");
+
+            TPSBuffer context = new TPSBuffer(hostChallenge);
+            context.add(cardChallenge);
+            try {
+                calculatedCardCryptogram = Util.compute_AES_CMAC_Cryptogram(macSessionKey, context, Util.CARD_CRYPTO_KDF_CONSTANT_SCP03, 8);
+            } catch (EBaseException e) {
+                throw new TPSException(method + "Failed to calculate card cryptogram!", TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+            CMS.debug(method + " dumped macSessionKey: " + new TPSBuffer(macSessionKey.getEncoded()).toHexString() );
+
+            CMS.debug(method + " actual card cryptogram " + cardCryptogram.toHexString());
+            CMS.debug(method + " calculated card cryptogram " + calculatedCardCryptogram.toHexString());
+
+            ExternalAuthenticateAPDUGP211 externalAuth = new ExternalAuthenticateAPDUGP211(hostCryptogram,
+                    /* secLevel */secLevelGP211);
+
+            computeAPDUMacSCP03(externalAuth);
+
+            APDUResponse response = processor.handleAPDURequest(externalAuth);
+
+            if (!response.checkResult()) {
+                throw new TPSException(
+                        "SecureChannel.eternalAuthenticate SCP03. Failed to external authenticate to token.",
+                        TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+            }
+
+        }
+
+
+        if (platProtInfo.isSCP02()) {
             CMS.debug("SecureChannel.externalAuthenticate: Attempting an External Authenticate for SCP02!");
 
-            TPSBuffer calculatedCardCryptogram = computeCardCryptogramSCP02(encSessionKey);
+            calculatedCardCryptogram = computeCardCryptogramSCP02(encSessionKey);
 
             if (false == cardCryptogram.equals(calculatedCardCryptogram)) {
 
@@ -383,7 +478,7 @@ public class SecureChannel {
 
             CMS.debug("SecureChannel.externalAuthenticate: SCP02 external authenticate returns Success!!!");
 
-        } else { //SCP01
+        } else  if(platProtInfo.isSCP01()){ //SCP01
 
             ExternalAuthenticateAPDU externalAuth = new ExternalAuthenticateAPDU(hostCryptogram,
                     /* secLevel */ExternalAuthenticateAPDU.SecurityLevel.SECURE_MSG_MAC_ENC);
@@ -410,15 +505,20 @@ public class SecureChannel {
 
         CMS.debug("SecureChannel.computeAPDU: entering..");
 
+        if (apdu == null) {
+            throw new TPSException("SecureChannel.computeAPDU: bad input apdu!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
         if (isSCP02()) {
             computeAPDU_SCP02(apdu);
             return;
 
         }
 
-        if (apdu == null) {
-            throw new TPSException("SecureChannel.computeAPDU: bad input apdu!",
-                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        if (isSCP03() ) {
+            computeAPDU_SCP03(apdu);
+            return;
         }
 
         computeAPDUMac(apdu);
@@ -436,6 +536,33 @@ public class SecureChannel {
         }
     }
 
+    private void computeAPDU_SCP03(APDU apdu) throws TPSException {
+        String method = "SecureChannel.computeAPDU_SCP03:";
+
+        CMS.debug(method + "entering..");
+        if (apdu == null) {
+            throw new TPSException(method + " bad input apdu!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        if (secLevelGP211 == ExternalAuthenticateAPDUGP211.SecurityLevel.CDEC_CMAC) {
+            try {
+                //CMS.debug("SecureChannel.computeAPDU_SCP03: Before encryption data value: "
+                //        + apdu.getData().toHexString());
+                apdu.secureMessageSCP03(encSessionKey, encryptionCounter);
+                ;
+                //CMS.debug("SecureChannel.computeAPDU_SCP03: After encryption data value: "
+                //        + apdu.getData().toHexString());
+            } catch (EBaseException e) {
+                throw new TPSException("SecureChannel.computeAPDU_SCP03: Can't encrypt outgoing data! " + e);
+            }
+
+            CMS.debug("SecureChannel.computeAPDU_SCP03: Successfully encrypted apdu data.");
+        }
+
+        computeAPDUMacSCP03(apdu);
+    }
+
     private void computeAPDU_SCP02(APDU apdu) throws TPSException {
         CMS.debug("SecureChannel.computeAPDU_SCP02: entering..");
 
@@ -448,11 +575,11 @@ public class SecureChannel {
 
         if (secLevelGP211 == ExternalAuthenticateAPDUGP211.SecurityLevel.CDEC_CMAC) {
             try {
-                CMS.debug("SecureChannel.computeAPDU_SCP02: Before encryption data value: "
-                        + apdu.getData().toHexString());
+                //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());
+                //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);
             }
@@ -462,6 +589,48 @@ public class SecureChannel {
 
     }
 
+    private void computeAPDUMacSCP03(APDU apdu) throws TPSException {
+        TPSBuffer newMac = null;
+        TPSBuffer data = null;
+
+        if (apdu == null) {
+            throw new TPSException("SecureChannel.computeAPDUMacSCP03: bad input apdu!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        data = apdu.getDataToMAC();
+
+        //CMS.debug("SecureChannel.computeAPDUMacSCP03: data To MAC: " + data.toHexString() + " incoming icv: "
+        //        + icv.toHexString());
+
+        try {
+
+                CMS.debug("SecureChannel.computeAPDUMacSCP03: No ecnrypton of ICV.");
+
+                TPSBuffer dataToMac = new TPSBuffer(icv);
+                /// Prepend the chaining value to the data to be maced.
+                dataToMac.add(data);
+
+                //CMS.debug("SecureChannel.computeAPDUMacSCP03: final data To MAC: " + dataToMac.toHexString() + " incoming icv: "
+                //        + icv.toHexString());
+
+                newMac = Util.computeAES_CMAC(macSessionKey, dataToMac);
+
+
+        } catch (EBaseException e) {
+            CMS.debug("SecureChannel.computeAPDUMacSCP03: Can't compute mac. " + e);
+            throw new TPSException("SecureChannel.compuatAPDUMacSCP03: Can't compute mac.",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("SecureChannel.computeAPDUMacSCP03: computed MAC: " /* + newMac.toHexString() */);
+
+        apdu.setMAC(newMac.substr(0,8));
+
+        icv.set(newMac);
+
+    }
+
     private void computeAPDUMacSCP02(APDU apdu) throws TPSException {
 
         TPSBuffer newMac = null;
@@ -571,7 +740,7 @@ public class SecureChannel {
     public void installLoad(TPSBuffer packageAID, TPSBuffer sdAID, int fileLength) throws TPSException, IOException {
 
         CMS.debug("SecureChannel.installLoad: entering ... packageAID: " + packageAID.toHexString() + " sdAID: "
-                + sdAID.toHexString());
+                + sdAID.toHexString() + " fileLength: " + fileLength);
 
         if (packageAID == null || sdAID == null || fileLength <= 0) {
             throw new TPSException("SecureChannel.insallLoad bad input parameters!",
@@ -1363,14 +1532,15 @@ public class SecureChannel {
 
         PutKeyAPDU putKey = new PutKeyAPDU(keyVersion, (byte) 0x81, keySetData);
 
-        if (isSCP02()) {
+        if (isSCP02() || isSCP03()) {
+            CMS.debug("SecureChannel.putKeys: adding trailing 0 byte");
             TPSBuffer trailer = new TPSBuffer(1);
             putKey.setTrailer(trailer);
 
         }
         computeAPDU(putKey);
 
-       int kill = 0;
+        int kill = 0;
         if (kill == 1) {
             throw new TPSException("putKeys end of progress.");
         }
@@ -1503,6 +1673,13 @@ public class SecureChannel {
 
     }
 
+    public boolean isSCP03() {
+        if (platProtInfo.isSCP03())
+            return true;
+        else
+            return false;
+    }
+
     public boolean isSCP02() {
         if (platProtInfo.isGP211() && platProtInfo.isSCP02()) {
 
diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java
index 2fe3825..910294e 100644
--- a/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java
+++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java
@@ -62,4 +62,12 @@ public class TKSComputeSessionKeyResponse extends RemoteResponse
     public TPSBuffer getKekWrappedDesKey() {
         return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_KEK_DesKey);
     }
+
+    public TPSBuffer getKekSessionKey() {
+        return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_KekSessionKey);
+    }
+
+    public TPSBuffer getMacSessionKey() {
+        return (TPSBuffer) nameValTable.get(IRemoteRequest.TKS_RESPONSE_MacSessionKey);
+    }
 }
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 f38d7de..65d0ed0 100644
--- a/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
+++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
@@ -226,6 +226,157 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
         }
     }
 
+    public TKSComputeSessionKeyResponse computeSessionKeysSCP03(
+            TPSBuffer kdd
+            ,TPSBuffer cuid,
+            TPSBuffer keyInfo,
+            TPSBuffer card_challenge,
+            TPSBuffer card_cryptogram,
+            TPSBuffer  host_challenge,
+            String tokenType) throws EBaseException {
+
+        String method = "TKSRemoteRequestHandler: computeSessionKeysSCP03()";
+
+        CMS.debug(method + " Entering: ");
+
+        if (cuid == null || kdd == null || keyInfo == null || card_challenge == null ||
+                card_cryptogram == null || host_challenge == null || tokenType == null
+
+               ) {
+            throw new EBaseException(method +  " invalid input!");
+        }
+
+        IConfigStore conf = CMS.getConfigStore();
+
+        boolean serverKeygen =
+                conf.getBoolean("op.enroll." +
+                        tokenType + ".keyGen.encryption.serverKeygen.enable",
+                        false);
+        if (keySet == null)
+            keySet = conf.getString("tps.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_KDD + "=" + Util.specialURLEncode(kdd) +
+                "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) +
+                "&" + IRemoteRequest.TOKEN_CARD_CHALLENGE + "=" + Util.specialURLEncode(card_challenge) +
+                "&" + IRemoteRequest.TOKEN_HOST_CHALLENGE + "=" + Util.specialURLEncode(host_challenge) +
+                "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(keyInfo) +
+                "&" + IRemoteRequest.CHANNEL_PROTOCOL + "=" + SecureChannel.SECURE_PROTO_03 +
+                "&" + IRemoteRequest.TOKEN_CARD_CRYPTOGRAM + "="
+                + Util.specialURLEncode(card_cryptogram.toBytesArray()) +
+                "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet;
+
+        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(method + " status not found.");
+                //CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): got content = " + content);
+            } else {
+                CMS.debug(method + " got status = " + value);
+                ist = Integer.parseInt(value);
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_EncSessionKey);
+            if (value == null) {
+                CMS.debug(method + " response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_EncSessionKey);
+            } else {
+                CMS.debug(method+ "got IRemoteRequest.TKS_RESPONSE_EncSessionKey");
+                response.put(IRemoteRequest.TKS_RESPONSE_EncSessionKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey);
+            if (value == null) {
+                CMS.debug(method + " response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_DRM_Trans_DesKey);
+            } else {
+                CMS.debug(method + "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_MacSessionKey);
+            if (value == null) {
+                CMS.debug(method + "response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_MacSessionKey);
+            } else {
+                CMS.debug(method + " got IRemoteRequest.TKS_RESPONSE_MacSessionKey");
+                response.put(IRemoteRequest.TKS_RESPONSE_MacSessionKey, Util.specialDecode(value));
+
+            }
+
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_KekSessionKey);
+            if (value == null) {
+                CMS.debug(method + "response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_KekSessionKey);
+            } else {
+                CMS.debug(method + " got IRemoteRequest.TKS_RESPONSE_KekSessionKey");
+                response.put(IRemoteRequest.TKS_RESPONSE_KekSessionKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_KEK_DesKey);
+            if (value == null) {
+                CMS.debug(method + "response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_KEK_DesKey);
+            } else {
+                CMS.debug(method + " 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(method + "response missing name-value pair for: " +
+                        IRemoteRequest.TKS_RESPONSE_KeyCheck);
+
+            } else {
+                CMS.debug(method + " got IRemoteRequest.TKS_RESPONSE_KeyCheck");
+                response.put(IRemoteRequest.TKS_RESPONSE_KeyCheck, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(IRemoteRequest.TKS_RESPONSE_HostCryptogram);
+            if ( value == null ) {
+                CMS.debug(method + " response missing name-value pair for: " + IRemoteRequest.TKS_RESPONSE_HostCryptogram);
+            } else {
+                CMS.debug(method + " got " + IRemoteRequest.TKS_RESPONSE_HostCryptogram);
+                response.put(IRemoteRequest.TKS_RESPONSE_HostCryptogram, Util.specialDecode(value));
+            }
+
+            CMS.debug(method + " ends.");
+
+            return new TKSComputeSessionKeyResponse(response);
+
+        } else {
+            CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): no response content.");
+            throw new EBaseException("TKSRemoteRequestHandler: computeSessionKeySCP02(): no response content.");
+        }
+
+    }
+
     /*
      * computeSessionKey
      *
@@ -559,7 +710,7 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
             TPSBuffer kdd,
             TPSBuffer cuid,
             TPSBuffer version,
-            TPSBuffer inData)
+            TPSBuffer inData,int protocol)
             throws EBaseException {
         CMS.debug("TKSRemoteRequestHandler: encryptData(): begins.");
         if (cuid == null || kdd == null || version == null || inData == null) {
@@ -582,7 +733,9 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
                                 "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialURLEncode(cuid) +
                                 "&" + IRemoteRequest.TOKEN_KDD + "=" + Util.specialURLEncode(kdd) +
                                 "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialURLEncode(version) +
-                                "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet);
+                                "&" + IRemoteRequest.TOKEN_KEYSET + "=" +  keySet +
+                                "&" + IRemoteRequest.CHANNEL_PROTOCOL + "=" + protocol
+                                );
 
         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 319ff67..540da40 100644
--- a/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java
+++ b/base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java
@@ -250,6 +250,48 @@ public class TPSEngine {
 
     }
 
+    // Compute 3 session keys enc, mac, and kek / dek in one shot and return the results
+    public TKSComputeSessionKeyResponse computeSessionKeysSCP03(TPSBuffer kdd, TPSBuffer cuid,
+            TPSBuffer keyInfo,
+            TPSBuffer card_challenge,
+            TPSBuffer host_challenge,
+            TPSBuffer card_cryptogram,
+            String connId,
+            String tokenType, String inKeySet) throws TPSException {
+
+        if (cuid == null || kdd == null || keyInfo == null || card_challenge == null || host_challenge == null
+                || card_cryptogram == null || connId == null || tokenType == null) {
+
+            throw new TPSException("TPSEngine.computeSessionKeySCP03: Invalid input data!",
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        CMS.debug("TPSEngine.computeSessionKeysSCP03");
+
+        TKSRemoteRequestHandler tks = null;
+
+        TKSComputeSessionKeyResponse resp = null;
+        try {
+            tks = new TKSRemoteRequestHandler(connId, inKeySet);
+            resp = tks.computeSessionKeysSCP03(kdd, cuid, keyInfo, card_challenge, card_cryptogram, host_challenge,
+                    tokenType);
+        } catch (EBaseException e) {
+            throw new TPSException("TPSEngine.computeSessionKeysSCP03: Error computing session keys!" + e,
+                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
+        }
+
+        int status = resp.getStatus();
+        if (status != 0) {
+            CMS.debug("TPSEngine.computeSessionKeysSCP03: Non zero status result: " + status);
+            throw new TPSException("TPSEngine.computeSessionKeysSCP03: invalid returned status: " + status);
+
+        }
+
+        return resp;
+
+    }
+
+
     public TKSComputeSessionKeyResponse computeSessionKey(TPSBuffer kdd, TPSBuffer cuid,
             TPSBuffer keyInfo,
             TPSBuffer card_challenge,
@@ -378,10 +420,12 @@ public class TPSEngine {
 
     public TPSBuffer createKeySetData(TPSBuffer newMasterVersion, TPSBuffer oldVersion, int protocol, TPSBuffer cuid, TPSBuffer kdd, TPSBuffer wrappedDekSessionKey, String connId, String inKeyset)
             throws TPSException {
-        CMS.debug("TPSEngine.createKeySetData. entering...");
+
+        String method = "TPSEngine.createKeySetData:";
+        CMS.debug(method + " entering...");
 
         if (newMasterVersion == null || oldVersion == null || cuid == null || kdd == null || connId == null) {
-            throw new TPSException("TPSEngine.createKeySetData: Invalid input data",
+            throw new TPSException(method + " Invalid input data",
                     TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER);
         }
 
@@ -394,14 +438,14 @@ public class TPSEngine {
             resp = tks.createKeySetData(newMasterVersion, oldVersion, cuid, kdd, protocol,wrappedDekSessionKey);
         } catch (EBaseException e) {
 
-            throw new TPSException("TPSEngine.createKeySetData, failure to get key set data from TKS",
+            throw new TPSException(method + " failure to get key set data from TKS",
                     TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER);
         }
 
         int status = resp.getStatus();
         if (status != 0) {
-            CMS.debug("TPSEngine.createKeySetData: Non zero status result: " + status);
-            throw new TPSException("TPSEngine.computeSessionKey: invalid returned status: " + status,
+            CMS.debug(method + " Non zero status result: " + status);
+            throw new TPSException(method + " invalid returned status: " + status,
                     TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER);
 
         }
@@ -409,8 +453,8 @@ public class TPSEngine {
         TPSBuffer keySetData = resp.getKeySetData();
 
         if (keySetData == null) {
-            CMS.debug("TPSEngine.createKeySetData: No valid key set data returned.");
-            throw new TPSException("TPSEngine.createKeySetData: No valid key set data returned.",
+            CMS.debug(method + " No valid key set data returned.");
+            throw new TPSException(method + " No valid key set data returned.",
                     TPSStatus.STATUS_ERROR_KEY_CHANGE_OVER);
 
         }
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 8b63703..672f53d 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
@@ -378,8 +378,10 @@ public class TPSEnrollProcessor extends TPSProcessor {
         String tksConnId = getTKSConnectorID();
         TPSBuffer plaintextChallenge = computeRandomData(16, tksConnId);
 
+        CMS.debug(method + " plaintextChallenge: " + plaintextChallenge.toHexString());
+
         //These will be used shortly
-        TPSBuffer wrappedChallenge = encryptData(appletInfo, channel.getKeyInfoData(), plaintextChallenge, tksConnId);
+        TPSBuffer wrappedChallenge = encryptData(appletInfo, channel.getKeyInfoData(), plaintextChallenge, tksConnId,this.getProtocol());
         PKCS11Obj pkcs11objx = null;
 
         try {
@@ -3006,7 +3008,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
         //CMS.debug("TPSEnrollProcessor.importPrivateKeyPKCS8 : keyCheck: " + keyCheck.toHexString());
         CMS.debug("TPSEnrollProcessor.importPrivateKeyPKCS8 : got keyCheck");
 
-        // String ivParams = ssKeyGenResponse.getIVParam();
+        //String ivParams = ssKeyGenResponse.getIVParam();
         //CMS.debug("TPSEnrollProcessor.importPrivateKeyPKCS8: ivParams: " + ivParams);
         TPSBuffer ivParamsBuff = new TPSBuffer(Util.uriDecodeFromHex(ivParams));
 
@@ -3019,7 +3021,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
         TPSBuffer kekWrappedDesKey = channel.getKekDesKey();
 
         if (kekWrappedDesKey != null) {
-            //CMS.debug("TPSEnrollProcessor.importPrivateKeyPKCS8: keyWrappedDesKey: " + kekWrappedDesKey.toHexString());
+            CMS.debug("TPSEnrollProcessor.importPrivateKeyPKCS8: keyWrappedDesKey: " + kekWrappedDesKey.toHexString());
             CMS.debug("TPSEnrollProcessor.importPrivateKeyPKCS8: got keyWrappedDesKey");
         } else
             CMS.debug("TPSEnrollProcessor.iportPrivateKeyPKC8: null kekWrappedDesKey!");
@@ -3041,7 +3043,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
         }
         data.add((byte) ivParamsBuff.size());
         data.add(ivParamsBuff);
-        //CMS.debug("TPSEnrollProcessor.importprivateKeyPKCS8: key data outgoing: " + data.toHexString());
+        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 825df3f..7d17f36 100644
--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
+++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
@@ -33,6 +33,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import netscape.security.x509.RevocationReason;
+
 import org.dogtagpki.server.tps.TPSSession;
 import org.dogtagpki.server.tps.TPSSubsystem;
 import org.dogtagpki.server.tps.authentication.AuthUIParameter;
@@ -99,8 +101,6 @@ import com.netscape.cms.servlet.tks.SecureChannelProtocol;
 import com.netscape.cmsutil.crypto.CryptoUtil;
 import com.netscape.symkey.SessionKey;
 
-import netscape.security.x509.RevocationReason;
-
 public class TPSProcessor {
 
     public static final int RESULT_NO_ERROR = 0;
@@ -111,10 +111,14 @@ public class TPSProcessor {
     public static final int CPLC_MSN_SIZE = 4;
 
     public static final int INIT_UPDATE_DATA_SIZE = 28;
+    public static final int INIT_UPDATE_DATA_SIZE_02 = 29;
+    public static final int INIT_UPDATE_DATA_SIZE_03 = 32;
     public static final int DIVERSIFICATION_DATA_SIZE = 10;
     public static final int CARD_CRYPTOGRAM_OFFSET = 20;
+    public static final int CARD_CRYPTOGRAM_OFFSET_GP211_SC03 = 21;
     public static final int CARD_CRYPTOGRAM_SIZE = 8;
     public static final int CARD_CHALLENGE_SIZE_GP211_SC02 = 6;
+    public static final int CARD_CHALLENGE_OFFSET_GP211_SC03 =  13  ;
     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;
@@ -431,7 +435,7 @@ public class TPSProcessor {
 
 
     protected TPSBuffer encryptData(AppletInfo appletInfo, TPSBuffer keyInfo, TPSBuffer plaintextChallenge,
-            String connId) throws TPSException {
+            String connId,int protocol) throws TPSException {
 
         TKSRemoteRequestHandler tks = null;
 
@@ -439,7 +443,7 @@ public class TPSProcessor {
 
         try {
             tks = new TKSRemoteRequestHandler(connId, getSelectedKeySet());
-            data = tks.encryptData(appletInfo.getKDD(),appletInfo.getCUID(), keyInfo, plaintextChallenge);
+            data = tks.encryptData(appletInfo.getKDD(),appletInfo.getCUID(), keyInfo, plaintextChallenge,protocol);
         } catch (EBaseException e) {
             throw new TPSException("TPSProcessor.encryptData: Erorr getting wrapped data from TKS!",
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
@@ -482,7 +486,9 @@ public class TPSProcessor {
     protected TPSBuffer initializeUpdate(byte keyVersion, byte keyIndex, TPSBuffer randomData) throws IOException,
             TPSException {
 
-        CMS.debug("In TPS_Processor.initializeUpdate.");
+        String method = "TPSProcessor.initializeUpdate:";
+
+        CMS.debug(method + " Entering...");
         InitializeUpdateAPDU initUpdate = new InitializeUpdateAPDU(keyVersion, keyIndex, randomData);
 
         int done = 0;
@@ -500,7 +506,10 @@ public class TPSProcessor {
 
         TPSBuffer data = resp.getResultDataNoCode();
 
-        if (data.size() != INIT_UPDATE_DATA_SIZE) {
+        CMS.debug(method + " data.size() " + data.size());
+
+        if ((data.size() != INIT_UPDATE_DATA_SIZE) && (data.size() != INIT_UPDATE_DATA_SIZE_02)
+                && (data.size() != INIT_UPDATE_DATA_SIZE_03)) {
             throw new TPSException("TPSBuffer.initializeUpdate: Invalid response from token!",
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
@@ -550,12 +559,22 @@ public class TPSProcessor {
 
         TPSBuffer initUpdateResp = initializeUpdate(keyVersion, keyIndex, randomData);
 
+        CMS.debug("TPSProcessor.setupSecureChanne: initUpdateResponse: " + initUpdateResp.toHexString());
+
         TPSBuffer key_diversification_data = initUpdateResp.substr(0, DIVERSIFICATION_DATA_SIZE);
         appletInfo.setKDD(key_diversification_data);
 
         CMS.debug("TPSProcessor.setupSecureChannel: diversification data: " + key_diversification_data.toHexString());
 
-        TPSBuffer key_info_data = initUpdateResp.substr(DIVERSIFICATION_DATA_SIZE, 2);
+        TPSBuffer key_info_data =  null;
+
+        if (platProtInfo.isSCP03()) {
+            key_info_data = initUpdateResp.substr(DIVERSIFICATION_DATA_SIZE, 3);
+        } else {
+            key_info_data = initUpdateResp.substr(DIVERSIFICATION_DATA_SIZE, 2);
+        }
+
+
         CMS.debug("TPSProcessor.setupSecureChannel: key info data: " + key_info_data.toHexString());
 
         TokenRecord tokenRecord = getTokenRecord();
@@ -564,19 +583,13 @@ public class TPSProcessor {
         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());
         CMS.debug("TPSProcessor.setupSecureChannel: card cryptogram: extracted");
 
         TPSBuffer card_challenge = null;
 
-        if (isGp211scp02) {
+        if (platProtInfo.isSCP02()) {
             sequenceCounter = initUpdateResp.substr(SEQUENCE_COUNTER_OFFSET_GP211_SC02, 2);
 
             {
@@ -602,7 +615,15 @@ public class TPSProcessor {
 
             tokenRecord.setKeyInfo(key_info_data.toHexStringPlain());
 
+        } else if (platProtInfo.isSCP03()) {
+            card_challenge = initUpdateResp.substr(CARD_CHALLENGE_OFFSET_GP211_SC03,CARD_CHALLENGE_SIZE);
+            card_cryptogram = initUpdateResp.substr(CARD_CRYPTOGRAM_OFFSET_GP211_SC03, CARD_CRYPTOGRAM_SIZE);
+
+            CMS.debug("TPSProcessor.setupSecureChannel 03: card cryptogram: " + card_cryptogram.toHexString());
+            CMS.debug("TPSProcessor.setupSecureChannel 03: card challenge: " + card_challenge.toHexString());
+            CMS.debug("TPSProcessor.setupSecureChannel 03: host challenge: " + randomData.toHexString());
         } else {
+
             card_challenge = initUpdateResp.substr(CARD_CHALLENGE_OFFSET, CARD_CHALLENGE_SIZE);
         }
         //CMS.debug("TPSProcessor.setupSecureChannel: card challenge: " + card_challenge.toHexString());
@@ -628,6 +649,8 @@ public class TPSProcessor {
             TPSBuffer sequenceCounter,AppletInfo appletInfo)
             throws EBaseException, TPSException, IOException {
 
+        String method = "TPSProcessor.generateSecureChannel:";
+
         if (connId == null || keyDiversificationData == null || keyInfoData == null || cardChallenge == null
                 || cardCryptogram == null || hostChallenge == null || appletInfo == null) {
             throw new TPSException("TPSProcessor.generateSecureChannel: Invalid input data!",
@@ -654,6 +677,11 @@ public class TPSProcessor {
         PK11SymKey cmacSessionKeySCP02 = null;
         PK11SymKey rmacSessionKeySCP02 = null;
 
+        PK11SymKey encSessionKeySCP03 = null;
+        PK11SymKey macSessionKeySCP03 = null;
+        PK11SymKey kekSessionKeySCP03 = null;
+        PK11SymKey rmacSessionKeySCP03 = null;
+
         SymmetricKey sharedSecret = null;
 
         //Sanity checking
@@ -680,7 +708,14 @@ public class TPSProcessor {
                     TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
         }
 
-        SecureChannelProtocol protocol = new SecureChannelProtocol();
+        SecureChannelProtocol protocol =  null; //new SecureChannelProtocol();
+
+
+        if(platProtInfo.isSCP01() || platProtInfo.isSCP02() ) {
+            protocol = new SecureChannelProtocol(1);
+        } else if (platProtInfo.isSCP03()) {
+            protocol = new SecureChannelProtocol(3);
+        }
 
         String tokenName = CryptoUtil.INTERNAL_TOKEN_FULL_NAME;
 
@@ -715,7 +750,6 @@ public class TPSProcessor {
             if (hostCryptogram == null) {
                 throw new TPSException("TPSProcessor.generateSecureChannel: No host cryptogram returned from token!",
                         TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
-
             }
 
             try {
@@ -725,9 +759,7 @@ public class TPSProcessor {
              /* sessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName, (PK11SymKey) sharedSecret,
                         sessionKeyWrapped.toBytesArray()); */
 
-
-               sessionKey =  (PK11SymKey) protocol.unwrapWrappedSymKeyOnToken(token, sharedSecret, sessionKeyWrapped.toBytesArray(), false);
-
+               sessionKey =  (PK11SymKey) protocol.unwrapWrappedSymKeyOnToken(token, sharedSecret, sessionKeyWrapped.toBytesArray(), false,SymmetricKey.DES3);
 
                 if (sessionKey == null) {
                     CMS.debug("TPSProcessor.generateSecureChannel: Can't extract session key!");
@@ -740,7 +772,7 @@ public class TPSProcessor {
               /*  encSessionKey = SessionKey.UnwrapSessionKeyWithSharedSecret(tokenName,(PK11SymKey) sharedSecret,
                         encSessionKeyWrapped.toBytesArray()); */
 
-                encSessionKey = (PK11SymKey) protocol.unwrapWrappedSymKeyOnToken(token, sharedSecret,encSessionKeyWrapped.toBytesArray(),false);
+                encSessionKey = (PK11SymKey) protocol.unwrapWrappedSymKeyOnToken(token, sharedSecret,encSessionKeyWrapped.toBytesArray(),false,SymmetricKey.DES3);
 
                 if (encSessionKey == null) {
                     CMS.debug("TPSProcessor.generateSecureChannel: Can't extract enc session key!");
@@ -781,7 +813,7 @@ public class TPSProcessor {
 
         }
 
-        if (platProtInfo.isGP211() && platProtInfo.isSCP02()) {
+        if (platProtInfo.isSCP02()) {
             //Generate the 4 keys we need for SCP02, Impl 15
 
             if (sequenceCounter == null) {
@@ -805,8 +837,8 @@ public class TPSProcessor {
             }
 
             respCMac02 = engine.computeSessionKeySCP02(keyDiversificationData, appletInfo.getCUID(), keyInfoData,
-                    sequenceCounter, new TPSBuffer(SecureChannel.C_MACDerivationConstant),
-                    connId, getSelectedTokenType(), getSelectedKeySet());
+                    sequenceCounter, new TPSBuffer(SecureChannel.C_MACDerivationConstant), connId,
+                    getSelectedTokenType(), getSelectedKeySet());
 
             TPSBuffer cmacSessionKeyWrappedSCP02 = respCMac02.getSessionKey();
 
@@ -875,6 +907,43 @@ public class TPSProcessor {
 
         }
 
+        if (platProtInfo.isSCP03()) {
+            CMS.debug("TPSProcessor.generateSecureChannel Trying secure channel protocol 03");
+
+            resp = engine.computeSessionKeysSCP03(keyDiversificationData, appletInfo.getCUID(), keyInfoData,
+                    cardChallenge, hostChallenge, cardCryptogram, connId, getSelectedTokenType(), getSelectedKeySet());
+
+            TPSBuffer encSessionKeyBuff = resp.getEncSessionKey();
+            TPSBuffer kekSessionKeyBuff = resp.getKekSessionKey();
+            TPSBuffer macSessionKeyBuff = resp.getMacSessionKey();
+            TPSBuffer hostCryptogramBuff = resp.getHostCryptogram();
+            TPSBuffer keyCheckBuff = resp.getKeyCheck();
+
+            TPSBuffer drmDesKeyBuff = resp.getDRM_Trans_DesKey();
+            TPSBuffer kekDesKeyBuff = resp.getKekWrappedDesKey();
+
+            CMS.debug(method + " encSessionKeyBuff: " + encSessionKeyBuff.toHexString());
+            CMS.debug(method + " kekSessionKeyBuff: " + kekSessionKeyBuff.toHexString());
+            CMS.debug(method + " macSessionKeyBuff: " + macSessionKeyBuff.toHexString());
+            CMS.debug(method + " hostCryptogramBuff: " + hostCryptogramBuff.toHexString());
+            CMS.debug(method + " keyCheckBuff: " + keyCheckBuff.toHexString());
+            CMS.debug(method + " drmDessKeyBuff: " + drmDesKeyBuff.toHexString());
+            CMS.debug(method + " kekDesKeyBuff: " + kekDesKeyBuff.toHexString());
+
+            encSessionKeySCP03 = (PK11SymKey) protocol.unwrapWrappedSymKeyOnToken(token, sharedSecret,
+                    encSessionKeyBuff.toBytesArray(), false, SymmetricKey.AES);
+            macSessionKeySCP03 = (PK11SymKey) protocol.unwrapWrappedSymKeyOnToken(token, sharedSecret,
+                    macSessionKeyBuff.toBytesArray(), false, SymmetricKey.AES);
+            kekSessionKeySCP03 = (PK11SymKey) protocol.unwrapWrappedSymKeyOnToken(token, sharedSecret,
+                    kekSessionKeyBuff.toBytesArray(), false, SymmetricKey.AES);
+
+            channel = new SecureChannel(this, encSessionKeySCP03, macSessionKeySCP03, kekSessionKeySCP03,
+                    drmDesKeyBuff, kekDesKeyBuff,
+                    keyCheckBuff, keyDiversificationData, cardChallenge,
+                    cardCryptogram, hostChallenge, hostCryptogramBuff, keyInfoData,
+                    platProtInfo);
+        }
+
         if (channel == null) {
             throw new TPSException(
                     "TPSProcessor.generateSecureChannel: Can't create Secure Channel, possibly invalid secure channel protocol requested.",
@@ -3141,9 +3210,6 @@ public class TPSProcessor {
                  The second byte is the key offset, which is always 1
                 */
 
-                byte[] nv = { (byte) requiredVersion, 0x01 };
-                TPSBuffer newVersion = new TPSBuffer(nv);
-
                 // GetKeyInfoData will return a buffer which is bytes 11,12 of
                 // the data structure on page 89 of Cyberflex Access Programmer's
                 // Guide
@@ -3157,8 +3223,21 @@ public class TPSProcessor {
                 int protocol = 1;
                 if (channel.isSCP02()) {
                     protocol = 2;
+                } if (channel.isSCP03()) {
+                    protocol = 3;
                 }
 
+                byte[] nv = null;
+
+                if(protocol == 3) {
+                    nv = new byte[] { (byte) requiredVersion,curKeyInfo.at(1),curKeyInfo.at(2) };
+
+                } else {
+                    nv = new byte[] { (byte) requiredVersion, 0x01 };
+                }
+
+                TPSBuffer newVersion = new TPSBuffer(nv);
+
                 //Sanity checking
 
                 boolean cuidOK = checkCUIDMatchesKDD(appletInfo.getCUIDhexStringPlain(), appletInfo.getKDDhexStringPlain());
@@ -3574,14 +3653,21 @@ public class TPSProcessor {
         try {
             gp211GetSecureChannelProtocolDetails();
         } catch (TPSException e) {
+
+            if(platProtInfo.getProtocol() == SecureChannel.SECURE_PROTO_03) {
+                CMS.debug("PSProcessor.acquireChannelPlatformProtocolInfo: card is reporting SCP03, bail, we don't yet support!");
+                throw 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()) {
+        if (platProtInfo.isSCP02()) {
             // We only support impl 15, the most common, at this point.
 
             if (platProtInfo.getImplementation() != SecureChannel.GP211_SCP02_IMPL_15) {
@@ -3690,16 +3776,17 @@ public class TPSProcessor {
         byte protocol = oidSecureChannelProtocol.at(length - 2);
         byte implementation = oidSecureChannelProtocol.at(length - 1);
 
-        if (protocol == SecureChannel.SECURE_PROTO_03) {
-            throw new TPSException("TPSProcessor.gp211GetSecureChannelProtocolDetails: No support for SCP03 as of yet, bailing.",
-                    TPSStatus.STATUS_ERROR_SECURE_CHANNEL);
-        }
+
 
         platProtInfo.setProtocol(protocol);
         platProtInfo.setImplementation(implementation);
         platProtInfo.setKeysetInfoData(keyData);
 
-        if (protocol == SecureChannel.SECURE_PROTO_02)
+        if (protocol == SecureChannel.SECURE_PROTO_03) {
+            CMS.debug("TPSProcessor.gp211GetSecureChannelProtocolDetails: Found protocol 03!");
+        }
+
+        if ((protocol == SecureChannel.SECURE_PROTO_02) || (protocol == SecureChannel.SECURE_PROTO_03))
             platProtInfo.setPlatform(SecureChannel.GP211);
         else
             platProtInfo.setPlatform(SecureChannel.GP201);
@@ -3714,6 +3801,12 @@ public class TPSProcessor {
         return platProtInfo;
     }
 
+    public int getProtocol() {
+        if(platProtInfo == null)
+            return SecureChannel.SECURE_PROTO_01;
+        return platProtInfo.getProtocol();
+    }
+
     boolean checkCardGPKeyVersionIsInRange(String CUID, String KDD, String keyInfoData) throws TPSException {
         boolean result = true;
 
@@ -3772,11 +3865,14 @@ public class TPSProcessor {
 
             CMS.debug(method + " minVersion: " + minVersion + " maxVersion: " + maxVersion);
 
-            if (keyInfoData.length() != 4) {
+            if( keyInfoData.length() != 4 && keyInfoData.length() != 6) {
                 result = false;
             } else {
+
+
                 // Actually check the version range;
 
+
                 String keyInfoVer = keyInfoData.substring(0, 2);
 
                 CMS.debug(method + " Version reported from key Info Data: " + keyInfoVer);
-- 
2.5.0

