summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Magne <jmagne@dhcp-16-206.sjc.redhat.com>2016-06-01 10:23:33 -0700
committerJack Magne <jmagne@dhcp-16-206.sjc.redhat.com>2017-03-14 15:25:34 -0700
commit6d6b6f954a5bf6730d4b53875c7cc122eb3ab5eb (patch)
tree666adf630ac8316eb509bd9b32c7a36d12056879
parent648361bac96996e76339b9390b8a8882dcde8ad7 (diff)
downloadpki-6d6b6f954a5bf6730d4b53875c7cc122eb3ab5eb.tar.gz
pki-6d6b6f954a5bf6730d4b53875c7cc122eb3ab5eb.tar.xz
pki-6d6b6f954a5bf6730d4b53875c7cc122eb3ab5eb.zip
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.
-rw-r--r--base/common/src/org/dogtagpki/tps/TPSConnection.java8
-rw-r--r--base/common/src/org/dogtagpki/tps/apdu/APDU.java64
-rw-r--r--base/common/src/org/dogtagpki/tps/main/Util.java570
-rw-r--r--base/common/src/org/dogtagpki/tps/msg/TPSMessage.java6
-rw-r--r--base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java4
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/GPParams.java108
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java59
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java426
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java785
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java41
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/tks/TokenServlet.java800
-rw-r--r--base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java2
-rw-r--r--base/tps/src/org/dogtagpki/server/tps/channel/PlatformAndSecChannelProtoInfo.java8
-rw-r--r--base/tps/src/org/dogtagpki/server/tps/channel/SecureChannel.java234
-rw-r--r--base/tps/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java8
-rw-r--r--base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java157
-rw-r--r--base/tps/src/org/dogtagpki/server/tps/engine/TPSEngine.java58
-rw-r--r--base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java10
-rw-r--r--base/tps/src/org/dogtagpki/server/tps/processor/TPSProcessor.java162
19 files changed, 3347 insertions, 163 deletions
diff --git a/base/common/src/org/dogtagpki/tps/TPSConnection.java b/base/common/src/org/dogtagpki/tps/TPSConnection.java
index c5a971edd..dd9538b18 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 009c47094..e3d72c764 100644
--- a/base/common/src/org/dogtagpki/tps/apdu/APDU.java
+++ b/base/common/src/org/dogtagpki/tps/apdu/APDU.java
@@ -219,6 +219,70 @@ 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();
+
+ 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 b212478d7..1410c72a2 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)
+ 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 ) {
+ throw new EBaseException(method + " Invalid input!");
+ }
+
+ TPSBuffer data = new TPSBuffer();
+ int outputBits = 8 * 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 c7ad7f7c3..aee430247 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/authentication/SharedSecret.java b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
index 7a0784c53..48a23536a 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
@@ -35,4 +35,8 @@ public class SharedSecret implements ISharedToken {
public String getSharedToken(BigInteger serial) {
return "testing";
}
+
+ public String getSharedToken(String identification) {
+ return "testing";
+ }
}
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 000000000..f16481be5
--- /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 0407e2934..e7b183b4e 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 e392ce1a3..ad4a370c2 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.kdf_AES_CMAC_SCP03:";
+ // 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 1766f0459..371e734df 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,222 @@ 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);
+
+ Arrays.fill(finalKeyBytes,(byte) 0);
+
+ //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);
+
+ Arrays.fill(finalKeyBytes,(byte) 0);
+ }
+ }
+ }
+
+ //SecureChannelProtocol.debugByteArray(sessionKey.getEncoded(), keyType + " : session key");
+
+ return sessionKey;
+ }
+
public SymmetricKey computeKEKKey_SCP01(
String selectedToken, String keyNickName,
byte[] keyInfo,
@@ -153,7 +386,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 +526,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 +581,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 +672,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 +729,157 @@ 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);
+
+
+ Arrays.fill(wrappedKey,(byte) 0);
+
+ //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 +914,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 +949,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 +969,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 +993,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 +1011,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 +1033,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 +1095,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 +1220,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 +1336,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 +1458,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 +1645,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 +1658,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 +1690,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 +1701,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 +1759,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 +1805,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 +1867,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 +1919,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 +1934,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 +1988,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 +2002,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 +2107,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 +2120,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 1d61a465d..8c228a15c 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 a282cd26f..6a1746616 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 233124968..82482d09c 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 e5f38e108..e1de8f23d 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 f2e32368f..fc5472c79 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);
+ } 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,60 @@ 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());
+ this.incrementBuffer(encryptionCounter);
+ TPSBuffer currentEncryptionCounter = new TPSBuffer(encryptionCounter);
+ apdu.secureMessageSCP03(encSessionKey,currentEncryptionCounter);
+ ;
+ //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);
+ }
+
+ //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());
+ }
+
private void computeAPDU_SCP02(APDU apdu) throws TPSException {
CMS.debug("SecureChannel.computeAPDU_SCP02: entering..");
@@ -448,11 +602,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 +616,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 +767,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 +1559,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 +1700,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 2fe382539..910294e53 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 f38d7def5..65d0ed0ac 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 319ff67b5..540da4019 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 8b6370337..672f53d83 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 825df3f23..7d17f36b7 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);