diff options
author | Jack Magne <jmagne@dhcp-16-206.sjc.redhat.com> | 2016-06-01 10:23:33 -0700 |
---|---|---|
committer | Jack Magne <jmagne@dhcp-16-206.sjc.redhat.com> | 2017-03-14 15:25:34 -0700 |
commit | 6d6b6f954a5bf6730d4b53875c7cc122eb3ab5eb (patch) | |
tree | 666adf630ac8316eb509bd9b32c7a36d12056879 | |
parent | 648361bac96996e76339b9390b8a8882dcde8ad7 (diff) | |
download | pki-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.
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); |