diff options
Diffstat (limited to 'base/server/cms/src/com')
5 files changed, 2080 insertions, 64 deletions
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 new file mode 100644 index 000000000..f8a5b1f6a --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/KDF.java @@ -0,0 +1,123 @@ +package com.netscape.cms.servlet.tks; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; + +public class KDF { + /* DES KEY Parity conversion table. Takes each byte >> 1 as an index, returns + * that byte with the proper parity bit set*/ + static final int parityTable[] = + { + /* Even...0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e */ + /* E */0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e, + /* Odd....0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e */ + /* O */0x10, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x1f, + /* Odd....0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e */ + /* O */0x20, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x2f, + /* Even...0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e */ + /* E */0x31, 0x32, 0x34, 0x37, 0x38, 0x3b, 0x3d, 0x3e, + /* Odd....0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e */ + /* O */0x40, 0x43, 0x45, 0x46, 0x49, 0x4a, 0x4c, 0x4f, + /* Even...0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e */ + /* E */0x51, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, 0x5e, + /* Even...0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e */ + /* E */0x61, 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x6e, + /* Odd....0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e */ + /* O */0x70, 0x73, 0x75, 0x76, 0x79, 0x7a, 0x7c, 0x7f, + /* Odd....0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e */ + /* O */0x80, 0x83, 0x85, 0x86, 0x89, 0x8a, 0x8c, 0x8f, + /* Even...0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e */ + /* E */0x91, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, 0x9e, + /* Even...0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae */ + /* E */0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xae, + /* Odd....0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe */ + /* O */0xb0, 0xb3, 0xb5, 0xb6, 0xb9, 0xba, 0xbc, 0xbf, + /* Even...0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce */ + /* E */0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xcb, 0xcd, 0xce, + /* Odd....0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde */ + /* O */0xd0, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, 0xdf, + /* Odd....0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee */ + /* O */0xe0, 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xef, + /* 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 { + + String method = "KDF.getDiversificationData:"; + + CMS.debug(method + " entering ..."); + + if (context == null || type == null) { + throw new EBaseException(method + "Invalid input parameters!"); + } + + byte[] KDC = new byte[SecureChannelProtocol.DES2_LENGTH]; + + // BYTE *lastTwoBytesOfAID = (BYTE *)cuidValue; + // BYTE *ICFabricationDate = (BYTE *)cuidValue + 2; + // BYTE *ICSerialNumber = (BYTE *)cuidValue + 4 + // BYTE *ICBatchIdentifier = (BYTE *)cuidValue + 8; + + // Last 2 bytes of AID + KDC[0] = context[0]; + KDC[1] = context[1]; + KDC[2] = context[4 + 0]; + KDC[3] = context[4 + 1]; + KDC[4] = context[4 + 2]; + KDC[5] = context[4 + 3]; + KDC[6] = (byte) 0xF0; + KDC[7] = 0x01; + KDC[8] = context[0]; + KDC[9] = context[1]; + KDC[10] = context[4 + 0]; + KDC[11] = context[4 + 1]; + KDC[12] = context[4 + 2]; + KDC[13] = context[4 + 3]; + KDC[14] = 0x0F; + KDC[15] = 0x01; + + if (type.equals(SecureChannelProtocol.encType)) + return KDC; + + KDC[6] = (byte) 0xF0; + KDC[7] = 0x02; + KDC[14] = 0x0F; + KDC[15] = 0x02; + if (type.equals(SecureChannelProtocol.macType)) + return KDC; + + KDC[6] = (byte) 0xF0; + KDC[7] = 0x03; + KDC[14] = 0x0F; + KDC[15] = 0x03; + if (type.equals(SecureChannelProtocol.kekType)) + return KDC; + + return KDC; + + } + + static public byte[] getDesParity(byte[] key) throws EBaseException { + String method = "KDF.getDesParity"; + if (key == null || (key.length != SecureChannelProtocol.DES2_LENGTH && + key.length != SecureChannelProtocol.EIGHT_BYTES && key.length != SecureChannelProtocol.DES3_LENGTH)) { + throw new EBaseException(method + " Incorrect input key !"); + } + + byte[] desKey = new byte[key.length]; + + for (int i = 0; i < key.length; i++) { + int index = key[i] & 0xff; + int finalIndex = index >> 1; + + byte val = (byte) parityTable[finalIndex]; + desKey[i] = val; + + } + + CMS.debug("desKey: len: " + desKey.length); + + return desKey; + } + +} 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 new file mode 100644 index 000000000..e392ce1a3 --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/NistSP800_108KDF.java @@ -0,0 +1,218 @@ +package com.netscape.cms.servlet.tks; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.HMACAlgorithm; +import org.mozilla.jss.crypto.JSSMessageDigest; +import org.mozilla.jss.crypto.SymmetricKey; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; + +public class NistSP800_108KDF extends KDF { + + static final int KDF_OUTPUT_SIZE_BITS = 384; + static final int KDF_OUTPUT_SIZE_BYTES = KDF_OUTPUT_SIZE_BITS / 8; + static final int KEY_DATA_SIZE_BYTES = KDF_OUTPUT_SIZE_BYTES / 3; + + static final int KDD_SIZE_BYTES = 10; // expected KDD field length in bytes + + static final byte KDF_LABEL = 0x04; // arbitra + + static final int SHA256_LENGTH = 32; + + SecureChannelProtocol protocol = null; + + NistSP800_108KDF(SecureChannelProtocol protocol) { + this.protocol = protocol; + } + + static public boolean useThisKDF(byte nistSP800_108KDFonKeyVersion, byte requestedKeyVersion) { + return (requestedKeyVersion >= nistSP800_108KDFonKeyVersion); + } + + /******************************************************************************* + Generates three PK11SymKey objects using the KDF_CM_SHA256HMAC_L384() function for key data. + After calling KDF_CM_SHA256HMAC_L384, the function splits up the output, sets DES parity, + and imports the keys into the token. + + Careful: This function currently generates the key data **IN RAM** using calls to NSS sha256. + The key data is then "unwrapped" (imported) to the NSS token and then erased from RAM. + (This means that a malicious actor on the box could steal the key data.) + + Note: Returned key material from the KDF is converted into keys according to the following: + * Bytes 0 - 15 : enc/auth key + * Bytes 16 - 31 : mac key + * Bytes 32 - 47 : kek key + We chose this order to conform with the key order used by the PUT KEY command. + + *******************************************************************************/ + + public Map<String, SymmetricKey> computeCardKeys(SymmetricKey masterKey, byte[] context, CryptoToken token) + throws EBaseException { + + String method = "NistSP800_108KDF.computeCardKeys:"; + + if (masterKey == null || context == null || token == null) { + throw new EBaseException(method + " Invlalid input parameters!"); + } + + Map<String, SymmetricKey> keys = new HashMap<String, SymmetricKey>(); + + byte[] kdf_output = null; + + kdf_output = kdf_CM_SHA256_HMAC_L384(masterKey, context, KDF_LABEL, KDF_OUTPUT_SIZE_BYTES, token); + + //Now create the 3 keys from only 48 of the 64 bytes... + + byte[] enc = new byte[16]; + byte[] mac = new byte[16]; + byte[] kek = new byte[16]; + + System.arraycopy(kdf_output, 0 * SecureChannelProtocol.DES2_LENGTH, enc, 0, SecureChannelProtocol.DES2_LENGTH); + System.arraycopy(kdf_output, 1 * SecureChannelProtocol.DES2_LENGTH, mac, 0, SecureChannelProtocol.DES2_LENGTH); + System.arraycopy(kdf_output, 2 * SecureChannelProtocol.DES2_LENGTH, kek, 0, SecureChannelProtocol.DES2_LENGTH); + + byte[] encFinal = KDF.getDesParity(enc); + byte[] macFinal = KDF.getDesParity(mac); + byte[] kekFinal = KDF.getDesParity(kek); + + boolean showKeysOnlyForDebug = false; + + if (showKeysOnlyForDebug) { + + SecureChannelProtocol.debugByteArray(kdf_output, "kdf_CM_SHA256_HMAC_L384 output: "); + + SecureChannelProtocol.debugByteArray(mac, " Nist mac before parity: "); + SecureChannelProtocol.debugByteArray(enc, " Nist enc before parity: "); + SecureChannelProtocol.debugByteArray(kek, " Nist kek before parityl: "); + + + SecureChannelProtocol.debugByteArray(macFinal, " Nist macFinal: "); + SecureChannelProtocol.debugByteArray(encFinal, " Nist encFinal: "); + SecureChannelProtocol.debugByteArray(kekFinal, " Nist kekFinal: "); + } + + Arrays.fill(enc, (byte) 0); + Arrays.fill(mac, (byte) 0); + 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); + + Arrays.fill(encFinal, (byte) 0); + Arrays.fill(macFinal, (byte) 0); + Arrays.fill(kekFinal, (byte) 0); + + keys.put(SecureChannelProtocol.macType, macKey); + keys.put(SecureChannelProtocol.encType, encKey); + keys.put(SecureChannelProtocol.kekType, kekKey); + + return keys; + + } + + /******************************************************************************* + 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) + *******************************************************************************/ + + private byte[] kdf_CM_SHA256_HMAC_L384(SymmetricKey masterKey, byte[] context, byte kdfLabel, + int kdfOutputSizeBytes, CryptoToken token) throws EBaseException { + + String method = "NistSP800_108KDF.kdf_CM_SHA256_HMAC_L384:"; + + CMS.debug(method + " entering.."); + final byte n = 2; // ceil(384 / (SHA256LENGTH * 8)) == 2 + 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) { + throw new EBaseException(method + " Array \"output\" must be at least 48 bytes in size."); + } + + // calculate size of temporary buffer + int HMAC_DATA_INPUT_SIZE = context.length + 3 + L_BYTE_array_length; // Don't change without reviewing code below. + + // prevent integer overflow + if (HMAC_DATA_INPUT_SIZE < context.length) { + throw new EBaseException(method + " Input parameter context size is too large."); + } + + byte L_BYTE_array[] = new byte[L_BYTE_array_length]; // Array to store L in BYTES + L_BYTE_array[0] = 0x01; + L_BYTE_array[1] = (byte) 0x80; + + // Hash Input = context + 5 BYTES + byte[] hmac_data_input = new byte[HMAC_DATA_INPUT_SIZE]; + + byte[] K = new byte[n * SHA256_LENGTH]; + + hmac_data_input[1] = kdfLabel; + hmac_data_input[2] = 0x0; + System.arraycopy(context, 0, hmac_data_input, 3, context.length); + System.arraycopy(L_BYTE_array, 0, hmac_data_input, context.length + 3, 2); + + byte[] outputHMAC256 = null; + + for (byte i = 1; i <= n; i++) { + hmac_data_input[0] = i; + outputHMAC256 = sha256HMAC(masterKey, hmac_data_input, HMAC_DATA_INPUT_SIZE, token); + CMS.debug(method + "outputHMAC256 len: " + outputHMAC256.length); + System.arraycopy(outputHMAC256, 0, K, (i - 1) * SHA256_LENGTH, SHA256_LENGTH); + Arrays.fill(outputHMAC256, (byte)0); + } + + CMS.debug(method + " Full array: " + K.length + " bytes..."); + + byte[] finalOutput = new byte[KDF_OUTPUT_SIZE_BYTES]; + + System.arraycopy(K, 0, finalOutput, 0, KDF_OUTPUT_SIZE_BYTES); + + Arrays.fill(K, (byte) 0); + + CMS.debug(method + " finalOutput: " + finalOutput.length + " bytes..."); + + return finalOutput; + } + + private byte[] sha256HMAC(SymmetricKey masterKey, // HMAC Secret Key (K_I) + byte[] hmac_data_input, // HMAC Input (i||04||00||context||0180) + int hMAC_DATA_INPUT_SIZE, // Input Length + CryptoToken token) throws EBaseException { + + String method = "NistSP800_108KDF.sha256HMAC:"; + + CMS.debug(method + " Entering..."); + + byte[] digestBytes = null; + + if (token == null) { + throw new EBaseException(method = " Invalid Crypto Token input!"); + } + + try { + JSSMessageDigest digest = token.getDigestContext(HMACAlgorithm.SHA256); + digest.initHMAC(masterKey); + + digestBytes = digest.digest(hmac_data_input); + } catch (Exception e) { + + CMS.debug(method + " Failure to HMAC the input data: " + e); + throw new EBaseException(method + e); + } + + // SecureChannelProtocol.debugByteArray(digestBytes, " output of sha256HMAC: "); + + return digestBytes; + } + +} 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 new file mode 100644 index 000000000..83dd93c6e --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/SecureChannelProtocol.java @@ -0,0 +1,1528 @@ +package com.netscape.cms.servlet.tks; + +import java.io.ByteArrayOutputStream; +import java.io.CharConversionException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Map; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.CryptoManager.NotInitializedException; +import org.mozilla.jss.NoSuchTokenException; +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.IllegalBlockSizeException; +import org.mozilla.jss.crypto.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyGenerator; +import org.mozilla.jss.crypto.KeyWrapAlgorithm; +import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.SymmetricKey; +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; + +public class SecureChannelProtocol { + + static String sharedSecretKeyName = null; + static String masterKeyPrefix = null; + + static final int KEYLENGTH = 16; + static final int PREFIXLENGHT = 128; + static final int DES2_LENGTH = 16; + static final int DES3_LENGTH = 24; + static final int EIGHT_BYTES = 8; + static final int KEYNAMELENGTH = PREFIXLENGHT + 7; + static final String TRANSPORT_KEY_NAME = "sharedSecret"; + static final String DEFKEYSET_NAME = "defKeySet"; + + 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 int PROTOCOL_ONE = 1; + static final int PROTOCOL_TWO = 2; + static final int PROTOCOL_THREE = 3; + static final int HOST_CRYPTOGRAM = 0; + static final int CARD_CRYPTOGRAM = 1; + + private SymmetricKey transportKey = null; + CryptoManager cryptoManager = null; + + public SecureChannelProtocol() { + } + + public byte[] computeCryptogram_SCP01( + String selectedToken, String keyNickName, byte[] card_challenge, + byte[] host_challenge, byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + byte[] xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + byte[] xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + int cryptogramType, byte[] authKeyArray, String useSoftToken_s, String keySet, String transportKeyName) + throws EBaseException { + + String method = "SecureChannelProtocol.computeCryptogram_SCP01:"; + + CMS.debug(method + " Entering: Type: HOST=0 , CARD=1 : TYPE: " + cryptogramType); + + if ((card_challenge == null || card_challenge.length != EIGHT_BYTES) + || (host_challenge == null || host_challenge.length != EIGHT_BYTES)) { + + throw new EBaseException(method + " Invalid card challenge or host challenge!"); + + } + + if (cryptogramType != HOST_CRYPTOGRAM && cryptogramType != CARD_CRYPTOGRAM) { + throw new EBaseException(method + " Invalid cyrptgram type!"); + } + + byte[] cryptogram = null; + + SymmetricKey authKey = this.computeSessionKey_SCP01(SecureChannelProtocol.encType, selectedToken, keyNickName, + card_challenge, host_challenge, keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, + xCUID, xKDD, authKeyArray, useSoftToken_s, keySet, transportKeyName); + + byte[] input = new byte[DES2_LENGTH]; + byte[] icv = new byte[EIGHT_BYTES]; + + if (cryptogramType == HOST_CRYPTOGRAM) // compute host cryptogram + { + /* copy card and host challenge into input buffer */ + for (int i = 0; i < EIGHT_BYTES; i++) + { + input[i] = card_challenge[i]; + } + for (int i = 0; i < EIGHT_BYTES; i++) + { + input[EIGHT_BYTES + i] = host_challenge[i]; + } + } // compute card cryptogram + else if (cryptogramType == CARD_CRYPTOGRAM) + { + for (int i = 0; i < EIGHT_BYTES; i++) + { + input[i] = host_challenge[i]; + } + for (int i = 0; i < EIGHT_BYTES; i++) + { + input[EIGHT_BYTES + i] = card_challenge[i]; + } + + } + cryptogram = computeMAC_SCP01(authKey, input, icv, selectedToken); + + // SecureChannelProtocol.debugByteArray(cryptogram, " Output of computeCrytptogram type: " + cryptogramType); + + return cryptogram; + } + + public SymmetricKey computeSessionKey_SCP02( + String selectedToken, String keyNickName, + byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + boolean nistSP800_108KdfUseCuidAsKdd, byte[] xCUID, byte[] xKDD, byte[] macKeyArray, + byte[] sequenceCounter, byte[] derivationConstant, + String useSoftToken_s, String keySet, + String transportKeyName) throws EBaseException { + + String method = "SecureChannelProtocol.computeSessionKey_SCP01:"; + + CMS.debug(method + " entering... "); + + throw new EBaseException(method + " Not yet implemented!"); + } + + public SymmetricKey computeKEKKey_SCP01( + String selectedToken, String keyNickName, + byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + byte[] xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + 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... "); + + return computeSessionKey_SCP01(SecureChannelProtocol.kekType, selectedToken, keyNickName, null, null, keyInfo, + nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, devKeyArray, useSoftToken_s, + keySet, transportKeyName); + + } + + public SymmetricKey computeSessionKey_SCP01(String keyType, + String selectedToken, String keyNickName, byte[] card_challenge, + byte[] host_challenge, byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + byte[] xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + 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.computeSessionKey_SCP01:"; + + CMS.debug(method + " entering... requested type: " + keyType); + + // This gets set if there is no input card challenge and host challenge + // Allows this routine to be used for the "encryptData" routine built on top. + + boolean noDerive = false; + + if (keyType == null || devKeyArray == null || keyInfo == null + || keySet == null || transportKeyName == null || (keyInfo == null || keyInfo.length < 2)) { + 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!"); + } + + if (card_challenge == null && host_challenge == null) { + noDerive = true; + CMS.debug(method + " NoDerive mode: true"); + } else { + if (card_challenge == null || host_challenge == null) { + throw new EBaseException(method + " Invalid input!"); + } + + CMS.debug(method + " NoDerive mode: false"); + } + + CMS.debug(method + " entering. nickname: " + keyNickName + " selectedToken: " + selectedToken); + CMS.debug(method + " nistSP800_108kdfOnKeyVersion: " + nistSP800_108KdfOnKeyVersion); + + 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); + + String keyNameStr = null; + + SymmetricKey sessionKey = null; + SymmetricKey masterKey = null; + + if (keyNickName == null) { + keyNameStr = this.getKeyName(keyInfo); + } else { + keyNameStr = keyNickName; + } + + byte[] context = null; + + if (nistSP800_108KdfUseCuidAsKdd == true) { + context = xCUID; + } else { + context = xKDD; + } + + if ((keyInfo[0] == 0x1 && keyInfo[1] == 0x1 && keyNameStr.equals("#01#01")) || + (keyInfo[0] == -1 && keyNameStr.indexOf("#FF") != -1)) + + { + /* default manufacturers key */ + + String finalKeyType = keyType; + + SymmetricKey devSymKey = returnDeveloperSymKey(token, finalKeyType, keySet, devKeyArray); + + // Create the auth with is the same as enc, might need it later. + if (keyType.equals(encType)) { + returnDeveloperSymKey(token, authType, keySet, devKeyArray); + } + + if (noDerive == true) { + sessionKey = devSymKey; + } else { + sessionKey = deriveKey_SCP01(token, devSymKey, host_challenge, card_challenge); + } + + } else { + + SymmetricKey devKey = null; + CMS.debug(method + "In master key mode."); + + masterKey = getSymKeyByName(token, keyNameStr); + + if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, keyInfo[1])) { + CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using NIST SP800-108 KDF."); + + NistSP800_108KDF nistKDF = new NistSP800_108KDF(this); + + Map<String, SymmetricKey> keys = null; + try { + keys = nistKDF.computeCardKeys(masterKey, context, token); + } catch (EBaseException e) { + CMS.debug(method + "Can't compute card keys! " + e); + throw e; + } + + devKey = keys.get(keyType); + + } else { + StandardKDF standardKDF = new StandardKDF(this); + CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using original KDF."); + byte[] data = KDF.getDiversificationData(context, keyType); + devKey = standardKDF.computeCardKey(masterKey, data, token, PROTOCOL_ONE); + } + + if (noDerive == true) { + sessionKey = devKey; + } else { + sessionKey = deriveKey_SCP01(token, devKey, host_challenge, card_challenge); + } + } + + return sessionKey; + } + + private SymmetricKey deriveKey_SCP01(CryptoToken token, SymmetricKey cardKey, byte[] host_challenge, + byte[] card_challenge) + throws EBaseException { + String method = "SecureChannelProtocol.deriveKey_SCP01:"; + CMS.debug(method + "entering.."); + + if (cardKey == null || token == null) { + throw new EBaseException(method + " Invalid input data!"); + } + + byte[] derivationData = new byte[KEYLENGTH]; + + SymmetricKey derivedKey = null; + + for (int i = 0; i < 4; i++) + { + derivationData[i] = card_challenge[i + 4]; + derivationData[i + 4] = host_challenge[i]; + derivationData[i + 8] = card_challenge[i]; + derivationData[i + 12] = host_challenge[i + 4]; + } + + SymmetricKeyDeriver encryptDes3; + byte[] encrypted = null; + try { + encryptDes3 = token.getSymmetricKeyDeriver(); + + encryptDes3.initDerive( + cardKey, /* PKCS11Constants.CKM_DES3_ECB_ENCRYPT_DATA */4354L, derivationData, null, + PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE, 16); + + try { + derivedKey = encryptDes3.derive(); + } catch (TokenException e) { + CMS.debug(method + "Unable to derive the key with the proper mechanism!" + e); + CMS.debug(method + "Now try this the old fashioned way"); + + encrypted = computeDes3EcbEncryption(cardKey, token.getName(), derivationData); + byte[] parityEncrypted = KDF.getDesParity(encrypted); + CMS.debug(method + "encryption completed"); + + derivedKey = this.unwrapSymKeyOnToken(token, null, parityEncrypted, false); + } + + } catch (TokenException | InvalidKeyException | EBaseException e) { + CMS.debug(method + "Unable to derive the key with the proper mechanism!"); + throw new EBaseException(e); + } + + return derivedKey; + } + + public SymmetricKey getSharedSecretKey(CryptoToken token) throws EBaseException { + + String method = "SecureChannelProtocol.getSharedSecretKey:"; + CMS.debug(method + "entering: transportKey: " + transportKey); + + CryptoToken finalToken = token; + CryptoToken internalToken = null; + if (token == null) { + + CMS.debug(method + " No token provided assume internal "); + CryptoManager cm = null; + try { + cm = CryptoManager.getInstance(); + internalToken = returnTokenByName("internal", cm); + finalToken = internalToken; + } catch (NotInitializedException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + + } catch (NoSuchTokenException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + } + + if (transportKey == null) { + transportKey = getSymKeyByName(finalToken, sharedSecretKeyName); + } + + if (transportKey == null) { + throw new EBaseException(method + "Can't locate shared secret key in token db."); + } + + return transportKey; + } + + private String getKeyName(byte[] keyVersion) { + String method = "SecureChannelProtocol.getKeyName:"; + CMS.debug(method + " Entering..."); + String keyName = null; + + if (keyVersion == null || keyVersion.length != 2) { + return null; + } + + SecureChannelProtocol.debugByteArray(keyVersion, "keyVersion array:"); + keyName = "#" + String.format("%02X", keyVersion[0]) + "#" + String.format("%02X", keyVersion[1]); + + CMS.debug(method + " returning: " + keyName); + + return keyName; + } + + public static String getSharedSecretKeyName(String name) throws EBaseException { + + String method = "SecureChannelProtocol.getSharedSecretKeyName:"; + CMS.debug(method + " Entering..."); + + if (name != null && SecureChannelProtocol.sharedSecretKeyName == null) { + SecureChannelProtocol.sharedSecretKeyName = name; + } + + if (SecureChannelProtocol.sharedSecretKeyName == null) { + throw new EBaseException(method + " Can not find shared secret key name!"); + } + + return SecureChannelProtocol.sharedSecretKeyName; + } + + public static String setSharedSecretKeyName(String name) throws EBaseException { + return SecureChannelProtocol.getSharedSecretKeyName(name); + } + + /* This routine will attempt to return one of the well known developer symmetric keys from the token. + Each key, is merely stored on the token for convenience. + If the given key is not found on the token it is put there and left on as a permanent key. + 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) + throws EBaseException { + + SymmetricKey devKey = null; + + String method = "SecureChannelProtocol.returnDeveloperSymKey:"; + + String devKeyName = keySet + "-" + keyType + "Key"; + CMS.debug(method + " entering.. searching for key: " + devKeyName); + + if (token == null || keyType == null || keySet == null) { + throw new EBaseException(method + "Invalid input data!"); + } + + try { + CMS.debug(method + " requested token: " + token.getName()); + } catch (TokenException e) { + throw new EBaseException(method + e); + } + + devKey = getSymKeyByName(token, devKeyName); + + if (devKey == null) { + //Put the key there and leave it + + byte[] des3InputKey = null; + + if (inputKeyArray == null) { + throw new EBaseException(method + "Input key is null and has to be non null when importing..."); + } + int inputLen = inputKeyArray.length; + + CMS.debug(method + " inputKeyArray.length: " + inputLen); + + if (inputLen != DES3_LENGTH && inputLen != DES2_LENGTH) { + throw new EBaseException(method + "invalid input key length!"); + } + + if (inputLen == DES2_LENGTH) { + des3InputKey = new byte[DES3_LENGTH]; + System.arraycopy(inputKeyArray, 0, des3InputKey, 0, DES2_LENGTH); + System.arraycopy(inputKeyArray, 0, des3InputKey, DES2_LENGTH, EIGHT_BYTES); + + } else { + System.arraycopy(inputKeyArray, 0, des3InputKey, 0, DES3_LENGTH); + } + + SecureChannelProtocol.debugByteArray(des3InputKey, "Developer key to import: " + keyType + ": "); + + devKey = unwrapSymKeyOnToken(token, des3InputKey, true); + devKey.setNickName(devKeyName); + } else { + CMS.debug(method + " Found sym key: " + devKeyName); + } + return devKey; + } + + public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray, + boolean isPerm) + throws EBaseException { + + String method = "SecureChannelProtocol.unwrapSymKeyOnToken:"; + CMS.debug(method + "Entering..."); + SymmetricKey unwrapped = null; + SymmetricKey tempKey = null; + + if (token == null) { + throw new EBaseException(method + "Invalid input!"); + } + + if (inputKeyArray == null || (inputKeyArray.length != DES3_LENGTH && inputKeyArray.length != DES2_LENGTH)) { + throw new EBaseException(method + "No raw array to use to create key!"); + } + + if (unwrappingKey == null) { + try { + KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3); + + 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); + tempKey = kg.generate(); + } catch (NoSuchAlgorithmException | TokenException | IllegalStateException | CharConversionException e) { + throw new EBaseException(method + "Can't generate temporary key to unwrap the key."); + } + + } + + byte[] finalKeyArray = null; + + if (inputKeyArray.length == DES2_LENGTH) { + finalKeyArray = SecureChannelProtocol.makeDes3FromDes2(inputKeyArray); + } + + Cipher encryptor = null; + byte[] wrappedKey = null; + + SymmetricKey encUnwrapKey = null; + + if (tempKey != null) { + encUnwrapKey = tempKey; + } else { + encUnwrapKey = unwrappingKey; + } + + try { + encryptor = token.getCipherContext(EncryptionAlgorithm.DES3_ECB); + + encryptor.initEncrypt(encUnwrapKey); + + if (finalKeyArray != null) { + wrappedKey = encryptor.doFinal(finalKeyArray); + } else { + wrappedKey = encryptor.doFinal(inputKeyArray); + } + + CMS.debug(method + " done enrypting data"); + + // SecureChannelProtocol.debugByteArray(wrappedKey, " encrypted key"); + + KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB); + keyWrap.initUnwrap(encUnwrapKey, null); + + if (isPerm == true) { + unwrapped = keyWrap.unwrapSymmetricPerm(wrappedKey, + SymmetricKey.DES3, 0); + } else { + unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES3, 0); + } + + } catch (Exception e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } finally { + if (finalKeyArray != null) { + Arrays.fill(finalKeyArray, (byte) 0); + } + } + + CMS.debug(method + "Returning symkey: " + unwrapped); + + return unwrapped; + } + + public SymmetricKey unwrapWrappedSymKeyOnToken(CryptoToken token, SymmetricKey unwrappingKey, byte[] inputKeyArray, + boolean isPerm) + throws EBaseException { + + String method = "SecureChannelProtocol.unwrapWrappedSymKeyOnToken:"; + CMS.debug(method + "Entering..."); + SymmetricKey unwrapped = null; + SymmetricKey finalUnwrapped = null; + + if (token == null || unwrappingKey == null) { + throw new EBaseException(method + "Invalid input!"); + } + + if (inputKeyArray == null || (inputKeyArray.length != DES3_LENGTH && inputKeyArray.length != DES2_LENGTH)) { + throw new EBaseException(method + "No raw array to use to create key!"); + } + + try { + KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES3_ECB); + keyWrap.initUnwrap(unwrappingKey, null); + + if (isPerm) { + unwrapped = keyWrap.unwrapSymmetricPerm(inputKeyArray, + SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, inputKeyArray.length); + } else { + unwrapped = keyWrap.unwrapSymmetric(inputKeyArray, SymmetricKey.DES3, SymmetricKey.Usage.UNWRAP, + inputKeyArray.length); + } + + finalUnwrapped = makeDes3KeyDerivedFromDes2(unwrapped, token.getName()); + + } catch (Exception e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + CMS.debug(method + "Returning symkey: " + unwrapped); + + return finalUnwrapped; + } + + public SymmetricKey unwrapSymKeyOnToken(CryptoToken token, byte[] inputKeyArray, boolean isPerm) + throws EBaseException { + + String method = "SecureChannelProtocol.unwrapSymKeyOnToken:"; + CMS.debug(method + "Entering..."); + SymmetricKey unwrapped = null; + + if (token == null) { + throw new EBaseException(method + "Invalide crypto token!"); + } + + if (inputKeyArray == null || (inputKeyArray.length != DES3_LENGTH && inputKeyArray.length != DES2_LENGTH)) { + throw new EBaseException(method + "No raw array to use to create key!"); + } + + SymmetricKey transport = getSharedSecretKey(token); + unwrapped = this.unwrapSymKeyOnToken(token, transport, inputKeyArray, isPerm); + + CMS.debug(method + "Returning symkey: " + unwrapped); + + return unwrapped; + } + + public static SymmetricKey getSymKeyByName(CryptoToken token, String name) throws EBaseException { + + String method = "SecureChannelProtocol.getSymKeyByName:"; + if (token == null || name == null) { + throw new EBaseException(method + "Invalid input data!"); + } + SymmetricKey[] keys; + + CMS.debug(method + "Searching for sym key: " + name); + try { + keys = token.getCryptoStore().getSymmetricKeys(); + } catch (TokenException e) { + throw new EBaseException(method + "Can't get the list of symmetric keys!"); + } + int len = keys.length; + for (int i = 0; i < len; i++) { + SymmetricKey cur = keys[i]; + if (cur != null) { + if (name.equals(cur.getNickName())) { + CMS.debug(method + "Found key: " + name); + return cur; + } + } + } + + CMS.debug(method + " Sym Key not found."); + return null; + } + + public CryptoToken returnTokenByName(String name, CryptoManager manager) throws NoSuchTokenException { + + if (name == null || manager == null) + throw new NoSuchTokenException(); + + if (name.equals("internal") || name.equals("Internal KeyStorage Token")) { + return manager.getInternalKeyStorageToken(); + } else { + return manager.getTokenByName(name); + } + + } + + public static byte[] makeDes3FromDes2(byte[] des2) { + + if (des2 == null || des2.length != SecureChannelProtocol.DES2_LENGTH) { + return null; + } + + byte[] des3 = new byte[SecureChannelProtocol.DES3_LENGTH]; + + System.arraycopy(des2, 0, des3, 0, SecureChannelProtocol.DES2_LENGTH); + System.arraycopy(des2, 0, des3, DES2_LENGTH, EIGHT_BYTES); + + return des3; + } + + public static void debugByteArray(byte[] array, String message) { + + CMS.debug("About to dump array: " + message); + + if (array == null) { + CMS.debug("Array to dump is empty!"); + return; + } + + CMS.debug("################### "); + + String result = getHexString(array); + CMS.debug(result); + } + + final protected static char[] hex = "0123456789abcdef".toCharArray(); + + public static String getHexString(byte[] bytes) { + + char[] hexChars = new char[bytes.length * 3]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 3] = hex[v >>> 4]; + hexChars[j * 3 + 1] = hex[v & 0x0F]; + hexChars[j * 3 + 2] = ':'; + } + return new String(hexChars); + } + + public CryptoManager getCryptoManger() throws EBaseException { + String method = "SecureChannelProtocol.getCryptoManager"; + CryptoManager cm = null; + + if (cryptoManager != null) + return cryptoManager; + + try { + cm = CryptoManager.getInstance(); + } catch (NotInitializedException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + + } + + cryptoManager = cm; + + return cryptoManager; + + } + + public static byte[] longToBytes(long x) { + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); + buffer.putLong(x); + return buffer.array(); + } + + /* Generate 24 key, but with a DES2 key converted to DES3 + This needed to appease the server side keygen and the coollkey applet. + */ + public SymmetricKey generateSymKey(String selectedToken) throws EBaseException { + String method = "SecureChannelProtocol.generateSymKey:"; + + CMS.debug(method + " entering , token: " + selectedToken); + SymmetricKey symKey = null; + SymmetricKey symKeyFinal = null; + + if (selectedToken == null) { + throw new EBaseException(method + " Invalid input data!"); + } + + try { + CryptoManager cm = this.getCryptoManger(); + CryptoToken token = returnTokenByName(selectedToken, cm); + + KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3); + + symKey = kg.generate(); + + symKeyFinal = this.makeDes3KeyDerivedFromDes2(symKey, selectedToken); + + } catch (NoSuchAlgorithmException | TokenException | NoSuchTokenException | IllegalStateException + | CharConversionException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + return symKeyFinal; + + } + + public byte[] ecbEncrypt(SymmetricKey devKey, SymmetricKey symKey, String selectedToken) throws EBaseException { + byte[] result = null; + String method = "SecureChannelProtocol.ecbEncrypt:"; + CMS.debug(method + " Entering..."); + + if (devKey == null || symKey == null || selectedToken == null) { + throw new EBaseException(method + " Invalid input parameters."); + } + + String devKeyToken = null; + try { + devKeyToken = symKey.getOwningToken().getName(); + CMS.debug(method + " symKey token: " + devKeyToken); + CMS.debug(method + " devKey token: " + devKey.getOwningToken().getName()); + + } catch (TokenException e) { + } + SymmetricKey des2 = this.extractDes2FromDes3(symKey, devKeyToken); + + result = this.wrapSessionKey(selectedToken, des2, devKey); + + // SecureChannelProtocol.debugByteArray(result, " Wrapped des2 key"); + + return result; + } + + /* Convenience routine to create a 3DES key from a 2DES key. + This is done by taking the first 8 bytes of the 2DES key and copying it to the end, making + a faux 3DES key. This is required due to applet requirements. + */ + public SymmetricKey makeDes3KeyDerivedFromDes2(SymmetricKey des3Key, String selectedToken) throws EBaseException { + SymmetricKey des3 = null; + + String method = "SecureChannelProtocol.makeDes3KeyDerivedFromDes2:"; + + CMS.debug(method + " Entering ..."); + + if (des3Key == null || selectedToken == null) { + throw new EBaseException(method + " Invalid input data!"); + } + + try { + CryptoManager cm = this.getCryptoManger(); + CryptoToken token = returnTokenByName(selectedToken, cm); + + long bitPosition = 0; + + byte[] param = SecureChannelProtocol.longToBytes(bitPosition); + + SymmetricKey extracted16 = this.extractDes2FromDes3(des3Key, selectedToken); + + SymmetricKeyDeriver extract8 = token.getSymmetricKeyDeriver(); + + extract8.initDerive( + extracted16, PKCS11Constants.CKM_EXTRACT_KEY_FROM_KEY, param, null, + PKCS11Constants.CKA_ENCRYPT, PKCS11Constants.CKA_DERIVE, 8); + + SymmetricKey extracted8 = extract8.derive(); + + CMS.debug(method + " extracted8 key: " + extracted8); + + SymmetricKeyDeriver concat = token.getSymmetricKeyDeriver(); + concat.initDerive( + extracted16, extracted8, PKCS11Constants.CKM_CONCATENATE_BASE_AND_KEY, null, null, + PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE, 0); + + des3 = concat.derive(); + + } catch (NoSuchTokenException | IllegalStateException | TokenException | InvalidKeyException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + return des3; + } + + public SymmetricKey extractDes2FromDes3(SymmetricKey baseKey, String selectedToken) throws EBaseException { + String method = "SecureChannelProtocol.extractDes2FromDes3:"; + CMS.debug(method + " Entering: "); + + SymmetricKey extracted16 = null; + + if (baseKey == null || selectedToken == null) { + throw new EBaseException(method + " Invalid input data."); + } + + try { + CryptoManager cm = this.getCryptoManger(); + CryptoToken token = returnTokenByName(selectedToken, cm); + + long bitPosition = 0; + + byte[] param = SecureChannelProtocol.longToBytes(bitPosition); + + SymmetricKeyDeriver extract16 = token.getSymmetricKeyDeriver(); + extract16.initDerive( + baseKey, PKCS11Constants.CKM_EXTRACT_KEY_FROM_KEY, param, null, + PKCS11Constants.CKA_ENCRYPT, PKCS11Constants.CKA_DERIVE, 16); + + extracted16 = extract16.derive(); + + } catch (NoSuchTokenException | IllegalStateException | TokenException | InvalidKeyException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + return extracted16; + } + + /* If wrappingKey is not null, use it, otherwise use the shared secret key + */ + public byte[] wrapSessionKey(String tokenName, SymmetricKey sessionKey, SymmetricKey wrappingKey) + throws EBaseException { + //Now wrap the key for the trip back to TPS with shared secret transport key + + String method = "SecureChannelProtocol.wrapSessionKey"; + + KeyWrapper keyWrap = null; + byte[] wrappedSessKeyData = null; + + if (tokenName == null || sessionKey == null) { + throw new EBaseException(method + " Invalid input data."); + } + + SymmetricKey wrapper = null; + + if (wrappingKey == null) { + wrapper = transportKey; + } else { + wrapper = wrappingKey; + } + + 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 (NoSuchAlgorithmException | TokenException | InvalidKeyException | InvalidAlgorithmParameterException + | NoSuchTokenException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + CMS.debug(method + " About to return session key: " + wrappedSessKeyData); + + return wrappedSessKeyData; + + } + + public byte[] computeDes3EcbEncryption(SymmetricKey desKey, String selectedToken, byte[] input) + throws EBaseException { + + String method = "SecureChannelProtocol.computeDes3EcbEncryption"; + byte[] output = null; + + if (desKey == null || selectedToken == null) { + throw new EBaseException(method + " Invalid input data."); + } + + try { + CryptoManager cm = this.getCryptoManger(); + CryptoToken token = returnTokenByName(selectedToken, cm); + CMS.debug("desKey: owning token: " + desKey.getOwningToken().getName()); + CMS.debug("desKey: current token: " + token.getName()); + Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.DES3_ECB); + CMS.debug("got encryptor"); + encryptor.initEncrypt(desKey); + CMS.debug("done initEncrypt"); + output = encryptor.doFinal(input); + CMS.debug("done doFinal " + output); + + // SecureChannelProtocol.debugByteArray(output, "Encrypted data:"); + } catch (EBaseException | NoSuchTokenException | NoSuchAlgorithmException | TokenException + | InvalidKeyException | InvalidAlgorithmParameterException | + IllegalStateException | IllegalBlockSizeException | BadPaddingException e) { + + CMS.debug(method + e); + throw new EBaseException(method + e); + } + CMS.debug("returning encrypted output."); + // SecureChannelProtocol.debugByteArray(output, "Encrypted data before leaving:"); + + return output; + } + + public byte[] computeKeyCheck(SymmetricKey desKey, String selectedToken) throws EBaseException { + + String method = "SecureChannelProtocol.computeKeyCheck:"; + + CMS.debug(method + " Entering..."); + + byte[] input = new byte[EIGHT_BYTES]; + byte[] finalOutput = new byte[3]; + + if (desKey == null || selectedToken == null) { + throw new EBaseException(method + " Invalid input data."); + } + + byte[] output = null; + String keysToken = null; + try { + keysToken = desKey.getOwningToken().getName(); + } catch (TokenException e1) { + throw new EBaseException(e1 + " Can't get owning token for key/"); + } + + try { + output = computeDes3EcbEncryption(desKey, keysToken, input); + } catch (EBaseException e) { + CMS.debug(method + e); + throw e; + + } + + //Get the 3 bytes needed + System.arraycopy(output, 0, finalOutput, 0, 3); + + SecureChannelProtocol.debugByteArray(finalOutput, "Calculated KeyCheck Value:"); + + return finalOutput; + } + + public byte[] computeMAC_SCP01(SymmetricKey symKey, byte[] input, byte[] icv, String selectedToken) + throws EBaseException { + byte[] output = null; + byte[] result = null; + + String method = "SecureChannelProtocol.computeMAC_SCP01:"; + + CMS.debug(method + " Entering..."); + + if (symKey == null || input == null || icv == null || icv.length != EIGHT_BYTES) { + throw new EBaseException(method + " invalid input data!"); + } + int inputLen = input.length; + + byte[] macPad = new byte[8]; + macPad[0] = (byte) 0x80; + + CryptoToken token = null; + + try { + + CryptoManager cm = this.getCryptoManger(); + token = returnTokenByName(selectedToken, cm); + + Cipher cipher = token.getCipherContext(EncryptionAlgorithm.DES3_ECB); + cipher.initEncrypt(symKey); + + result = new byte[EIGHT_BYTES]; + System.arraycopy(icv, 0, result, 0, EIGHT_BYTES); + + /* Process whole blocks */ + int inputOffset = 0; + while (inputLen >= 8) + { + for (int i = 0; i < 8; i++) + { + //Xor implicitly converts bytes to ints, we convert answer back to byte. + byte a = (byte) (result[i] ^ input[inputOffset + i]); + result[i] = a; + } + + byte[] ciphResult = cipher.update(result); + + if (ciphResult.length != result.length) { + throw new EBaseException(method + " Invalid cipher!"); + } + + System.arraycopy(ciphResult, 0, result, 0, EIGHT_BYTES); + + inputLen -= 8; + inputOffset += 8; + } + + /* + * Fold in remaining data (if any) + * Set i to number of bytes processed + */ + int i = 0; + for (i = 0; i < inputLen; i++) + { + byte a = (byte) (result[i] ^ input[inputOffset + i]); + result[i] = a; + } + + /* + * Fill remainder of last block. There + * will be at least one byte handled here. + */ + + //Start at the beginning of macPad + // Keep going with i in result where we left off. + int padOffset = 0; + while (i < 8) + { + byte a = (byte) (result[i] ^ macPad[padOffset++]); + result[i] = a; + i++; + } + + output = cipher.doFinal(result); + + if (output.length != result.length) { + throw new EBaseException(method + " Invalid cipher!"); + } + + } catch (Exception e) { + throw new EBaseException(method + " Cryptographic problem encountered! " + e.toString()); + } + + // SecureChannelProtocol.debugByteArray(output, method + " output: "); + + return output; + } + + public byte[] diversifyKey(String tokenName, + String newTokenName, + String oldMasterKeyName, + String newMasterKeyName, + byte[] oldKeyInfo, + byte[] newKeyInfo, + byte nistSP800_108KdfOnKeyVersion, + boolean nistSP800_108KdfUseCuidAsKdd, + byte[] CUIDValue, + byte[] KDD, + byte[] kekKeyArray, + String useSoftToken, String keySet, byte protocol) throws EBaseException { + + String method = "SecureChannelProtocol.diversifyKey:"; + + CMS.debug(method + " Entering ... newTokenName: " + newTokenName); + + SymmetricKey masterKey = null; + SymmetricKey oldMasterKey = null; + + byte[] KDCenc = null; + byte[] KDCmac = null; + byte[] KDCkek = null; + + SymmetricKey old_mac_sym_key = null; + SymmetricKey old_enc_sym_key = null; + SymmetricKey old_kek_sym_key = null; + + SymmetricKey encKey = null; + SymmetricKey macKey = null; + SymmetricKey kekKey = null; + + // The final answer + byte[] output = null; + + if (oldMasterKeyName == null || oldKeyInfo == null || newKeyInfo == null + || keySet == null) { + throw new EBaseException(method + "Invalid input!"); + } + + if (oldKeyInfo.length < 2 || newKeyInfo.length < 2) { + throw new EBaseException(method + " Invalid input length for keyinfo versions."); + } + + String fullNewMasterKeyName = getFullMasterKeyName(newMasterKeyName); + String fullOldMasterKeyName = getFullMasterKeyName(oldMasterKeyName); + + CryptoManager cm = null; + CryptoToken token = null; + CryptoToken newToken = null; + try { + cm = CryptoManager.getInstance(); + token = returnTokenByName(tokenName, cm); + if (newTokenName != null) { + newToken = returnTokenByName(newTokenName, cm); + } + } catch (NotInitializedException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + + } catch (NoSuchTokenException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + try { + if (newToken != null) { + masterKey = getSymKeyByName(newToken, fullNewMasterKeyName); + } + oldMasterKey = getSymKeyByName(token, fullOldMasterKeyName); + } catch (EBaseException e) { + masterKey = null; + CMS.debug(method + " Master key is null, possibly ok in moving from keyset 2 to 1"); + + if (oldMasterKey == null) { + throw new EBaseException(method + " Can't retrieve old master key!"); + } + } + + SecureChannelProtocol.debugByteArray(oldKeyInfo, " oldKeyInfo: "); + SecureChannelProtocol.debugByteArray(newKeyInfo, " newKeyInfo: "); + + byte oldKeyVersion = oldKeyInfo[0]; + byte newKeyVersion = newKeyInfo[0]; + + byte[] context = null; + + if (nistSP800_108KdfUseCuidAsKdd == true) { + context = CUIDValue; + } else { + context = KDD; + } + + if (context == null) { + throw new EBaseException(method + "Invalid token id information included!"); + } + + // We may need either or both of these + + 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); + + if (protocol == PROTOCOL_ONE) { + if (checkForDeveloperKeySet(oldMasterKeyName) == true) { + CMS.debug(method + " Developer key set case: "); + } else { + CMS.debug(method + " Not Developer key set case: "); + + if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, oldKeyVersion)) { + CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF."); + + Map<String, SymmetricKey> keys = null; + try { + keys = nistKDF.computeCardKeys(oldMasterKey, context, token); + } catch (EBaseException e) { + CMS.debug(method + "Can't compute card keys! " + e); + throw e; + } + + old_enc_sym_key = keys.get(SecureChannelProtocol.encType); + old_mac_sym_key = keys.get(SecureChannelProtocol.macType); + old_kek_sym_key = keys.get(SecureChannelProtocol.kekType); + + if (old_enc_sym_key == null || old_mac_sym_key == null || old_kek_sym_key == null) { + throw new EBaseException(method + " Can't derive session keys with Nist KDF."); + } + + } else { + CMS.debug(method + " ComputeSessionKey NistSP800_108KDF code: Using original KDF."); + + old_kek_sym_key = standardKDF.computeCardKey(oldMasterKey, KDCkek, token, PROTOCOL_ONE); + } + + } + + /* special case #01#01 */ + if (fullNewMasterKeyName != null && fullNewMasterKeyName.equals("#01#01")) + { + CMS.debug(method + " Special case dev key set for DiversifyKey!"); + + 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"); + + if (NistSP800_108KDF.useThisKDF(nistSP800_108KdfOnKeyVersion, newKeyVersion)) { + CMS.debug(method + " NistSP800_108KDF code: Using NIST SP800-108 KDF. For new key version."); + + Map<String, SymmetricKey> keys = null; + try { + keys = nistKDF.computeCardKeys(masterKey, context, newToken); + } catch (EBaseException e) { + CMS.debug(method + "Can't compute card keys! For new key version. " + e); + throw e; + } + + encKey = keys.get(SecureChannelProtocol.encType); + macKey = keys.get(SecureChannelProtocol.macType); + kekKey = keys.get(SecureChannelProtocol.kekType); + + } else { + CMS.debug(method + + " ComputeSessionKey NistSP800_108KDF code: Using original KDF. For new key version."); + + encKey = standardKDF.computeCardKeyOnToken(masterKey, KDCenc, protocol); + macKey = standardKDF.computeCardKeyOnToken(masterKey, KDCmac, protocol); + 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 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; + + if (showKeysForDebug == true) { + try { + SecureChannelProtocol.debugByteArray(encKey.getKeyData(), "DiversifyKey: new encKey: "); + SecureChannelProtocol.debugByteArray(macKey.getKeyData(), "DiversifyKey: new macKey:"); + SecureChannelProtocol.debugByteArray(kekKey.getKeyData(), "DiversifyKey: new kekKey"); + } catch (NotExtractableException e) { + CMS.debug(method + " Can not display debugging info for key"); + } + } + + if (old_kek_sym_key != null) { + + CMS.debug(method + " old kek sym key is not null"); + output = createKeySetDataWithSymKeys(newKeyVersion, (byte[]) null, + old_kek_sym_key, + encKey, + macKey, + kekKey, + protocol, tokenName); + + } 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, + macKey, + kekKey, + protocol, tokenName); + + } + + return output; + } + + 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) + throws EBaseException { + + SymmetricKey wrappingKey = null; + + String method = "SecureChannelProtocol.createKeySetDataWithSymKeys:"; + + byte alg = (byte) 0x81; + + byte[] output = null; + + if (encKey == null || macKey == null || kekKey == null || tokenName == null) { + throw new EBaseException(method + " Invalid input data!"); + } + + CryptoManager cm = null; + CryptoToken token = null; + try { + cm = CryptoManager.getInstance(); + token = returnTokenByName(tokenName, cm); + } catch (NotInitializedException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + + } catch (NoSuchTokenException e) { + CMS.debug(method + " " + e); + throw new EBaseException(e); + } + + SymmetricKey encKey16 = null; + SymmetricKey macKey16 = null; + SymmetricKey kekKey16 = null; + + byte[] encrypted_enc_key = null; + byte[] encrypted_mac_key = null; + byte[] encrypted_kek_key = null; + + byte[] keycheck_enc_key = null; + byte[] keycheck_mac_key = null; + byte[] keycheck_kek_key = null; + + if (protocol == PROTOCOL_ONE) { + if (old_kek_sym_key == null) { + CMS.debug(method + " Using old kek key array."); + wrappingKey = unwrapSymKeyOnToken(token, old_kek_key_array, false); + } else { + CMS.debug(method + " Using input old key key sym key."); + wrappingKey = old_kek_sym_key; + } + + alg = (byte) 0x81; + encKey16 = extractDes2FromDes3(encKey, tokenName); + macKey16 = extractDes2FromDes3(macKey, tokenName); + kekKey16 = extractDes2FromDes3(kekKey, tokenName); + + encrypted_enc_key = this.wrapSessionKey(tokenName, encKey16, wrappingKey); + encrypted_mac_key = this.wrapSessionKey(tokenName, macKey16, wrappingKey); + encrypted_kek_key = this.wrapSessionKey(tokenName, kekKey16, wrappingKey); + + 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 { + 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 }; + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + try { + outputStream.write(newKeyVersion); + outputStream.write(b1); + outputStream.write(encrypted_enc_key); + outputStream.write(b2); + outputStream.write(keycheck_enc_key); + + outputStream.write(b1); + outputStream.write(encrypted_mac_key); + outputStream.write(b2); + outputStream.write(keycheck_mac_key); + + outputStream.write(b1); + outputStream.write(encrypted_kek_key); + outputStream.write(b2); + outputStream.write(keycheck_kek_key); + + output = outputStream.toByteArray(); + + } catch (IOException e) { + throw new EBaseException(method + " Can't compose final output byte array!"); + } + + SecureChannelProtocol.debugByteArray(output, " Final output to createKeySetData: "); + + return output; + } + + private String getFullMasterKeyName(String masterKeyName) + { + if (masterKeyName == null) + { + return null; + } + + String fullMasterKeyName = null; + + fullMasterKeyName = ""; + + if (masterKeyName.length() > 0) { + fullMasterKeyName += masterKeyName; + } + + return fullMasterKeyName; + } + + private boolean checkForDeveloperKeySet(String keyInfo) + { + if (keyInfo == null) + return true; + + //SCP01 or SCP02 + if (keyInfo.equals("#01#01") || keyInfo.equals("#FF#01")) + return true; + + //SCP02 + if (keyInfo.equals("#01#02") || keyInfo.equals("#FF#02")) + return true; + + return false; + } + + public static void setDefaultPrefix(String masterkeyPrefix) { + if (SecureChannelProtocol.masterKeyPrefix == null) { + SecureChannelProtocol.masterKeyPrefix = masterkeyPrefix; + } + } + + 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 { + + String method = "SecureChannelProtocol.encryptData:"; + + CMS.debug(method + " Entering ...."); + + String transportKeyName = SecureChannelProtocol.getSharedSecretKeyName(null); + + if (keyInfo == null || keySet == null || (keyInfo == null || keyInfo.length < 2)) { + 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_SCP01(kekType, selectedToken, keyNickName, null, + null, keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, + kekKeyArray, useSoftToken_s, keySet, transportKeyName); + + byte[] output = computeDes3EcbEncryption(kekKey, selectedToken, data); + + // debugByteArray(output, " encryptData: Output: "); + + return output; + } + +} 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 new file mode 100644 index 000000000..1d61a465d --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/servlet/tks/StandardKDF.java @@ -0,0 +1,135 @@ +package com.netscape.cms.servlet.tks; + +import java.security.InvalidKeyException; + +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.SymmetricKey; +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; + +public class StandardKDF extends KDF { + SecureChannelProtocol protocol = null; + + StandardKDF(SecureChannelProtocol protocol) { + this.protocol = protocol; + } + + public SymmetricKey computeCardKey(SymmetricKey masterKey, byte[] derivationData, CryptoToken token, int protocol) + throws EBaseException { + + String method = "StandardKDF.computeCardKeys:"; + + SymmetricKey result = null; + + CMS.debug(method + " entering ..."); + + if (masterKey == null || derivationData == null + || derivationData.length != SecureChannelProtocol.DES2_LENGTH || token == null) { + + throw new EBaseException(method + " Invlalid input parameters!"); + } + + SymmetricKeyDeriver encryptDes3; + try { + + encryptDes3 = token.getSymmetricKeyDeriver(); + + encryptDes3.initDerive( + masterKey, /* PKCS11Constants.CKM_DES3_ECB_ENCRYPT_DATA */4354L, derivationData, null, + PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE, 16); + + + SymmetricKey derivedKey = null; + + try { + derivedKey = encryptDes3.derive(); + } catch (TokenException e) { + + CMS.debug(method + "Unable to derive the key with the proper mechanism!"); + CMS.debug(method + "Now try this the old fashioned way"); + + byte[] encrypted = this.protocol.computeDes3EcbEncryption(masterKey, token.getName(), derivationData); + byte[] parityEncrypted = KDF.getDesParity(encrypted); + CMS.debug(method + "done computeDes3EcbEncryptiong"); + derivedKey = this.protocol.unwrapSymKeyOnToken(token, null, + parityEncrypted, false); + + // The key this way is aleady des3, return + + return derivedKey; + } + + + CMS.debug(method + " derived card key first 16 :" + derivedKey); + + // byte[] extracted = derivedKey.getEncoded(); + // SecureChannelProtocol.debugByteArray(extracted, " Derived key 16."); + + CMS.debug(method + " derivedKey 16: owning token: " + derivedKey.getOwningToken().getName()); + + long bitPosition = 0; + + byte[] param = SecureChannelProtocol.longToBytes(bitPosition); + + SymmetricKeyDeriver extract8 = token.getSymmetricKeyDeriver(); + extract8.initDerive( + derivedKey, PKCS11Constants.CKM_EXTRACT_KEY_FROM_KEY,param,null, + PKCS11Constants.CKA_ENCRYPT, PKCS11Constants.CKA_DERIVE,8); + + + SymmetricKey extracted8 = extract8.derive(); + // byte [] extracted8Bytes = extracted8.getEncoded(); + // SecureChannelProtocol.debugByteArray(extracted8Bytes, " Derived key 8."); + + + SymmetricKeyDeriver concat = token.getSymmetricKeyDeriver(); + concat.initDerive( + derivedKey,extracted8, PKCS11Constants.CKM_CONCATENATE_BASE_AND_KEY,null,null, + PKCS11Constants.CKM_DES3_ECB, PKCS11Constants.CKA_DERIVE,0); + + result = concat.derive(); + CMS.debug(method + " final 24 byte key: " + result); + // byte [] extracted24Bytes = result.getEncoded(); + // SecureChannelProtocol.debugByteArray(extracted24Bytes, " Derived key 24."); + + } catch (TokenException | InvalidKeyException e) { + CMS.debug(method + "Unable to derive the key with the proper mechanism!"); + throw new EBaseException(e); + } + + return result; + + } + + public SymmetricKey computeCardKeyOnSoftToken(SymmetricKey masterKey, byte[] data, int protocol) + throws EBaseException { + String method = "StandardKDF.computeCardKeys:"; + + CMS.debug(method + " entering..."); + + if (masterKey == null || data == null) { + throw new EBaseException(method + " Invlalid input parameters!"); + } + + CryptoToken token = this.protocol.getCryptoManger().getInternalKeyStorageToken(); + + return this.computeCardKey(masterKey, data, token, protocol); + } + + public SymmetricKey computeCardKeyOnToken(SymmetricKey masterKey, byte[] data, int protocol) throws EBaseException { + String method = "StandardKDF.computeCardKeys:"; + + if (masterKey == null || data == null) { + throw new EBaseException(method + " Invlalid input parameters!"); + } + + CryptoToken token = masterKey.getOwningToken(); + + return this.computeCardKey(masterKey, data, token, protocol); + } +} 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 83b8bef92..00bb90594 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 @@ -35,6 +35,7 @@ import org.mozilla.jss.CryptoManager.NotInitializedException; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.KeyWrapAlgorithm; import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.SymmetricKey; import org.mozilla.jss.crypto.X509Certificate; import org.mozilla.jss.pkcs11.PK11SymKey; @@ -213,7 +214,10 @@ public class TokenServlet extends CMSServlet { } } - SessionKey.SetDefaultPrefix(masterKeyPrefix); + CMS.debug("Setting masteter keky prefix to: " + masterKeyPrefix); + + SecureChannelProtocol.setDefaultPrefix(masterKeyPrefix); + /*SessionKey.SetDefaultPrefix(masterKeyPrefix);*/ } catch (Exception e) { e.printStackTrace(); @@ -354,7 +358,6 @@ public class TokenServlet extends CMSServlet { String rKDD = req.getParameter(IRemoteRequest.TOKEN_KDD); - String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO); if ((rKeyInfo == null) || (rKeyInfo.equals(""))) { @@ -561,7 +564,8 @@ public class TokenServlet extends CMSServlet { selectedToken, keyNickName, keyInfo, nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value - nistSP800_108KdfUseCuidAsKdd,xCUID,xKDD, macKeyArray, sequenceCounter, derivationConstant, useSoftToken_s, keySet, + nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, macKeyArray, sequenceCounter, derivationConstant, + useSoftToken_s, keySet, transportKeyName); if (session_key == null) { @@ -889,9 +893,9 @@ public class TokenServlet extends CMSServlet { boolean serversideKeygen = false; byte[] drm_trans_wrapped_desKey = null; - PK11SymKey desKey = null; + SymmetricKey desKey = null; // PK11SymKey kek_session_key; - PK11SymKey kek_key; + SymmetricKey kek_key; IConfigStore sconfig = CMS.getConfigStore(); boolean isCryptoValidate = true; @@ -1092,14 +1096,15 @@ public class TokenServlet extends CMSServlet { + keySet + ".mac_key")); CMS.debug("TokenServlet about to try ComputeSessionKey selectedToken=" + selectedToken + " keyNickName=" + keyNickName); - session_key = SessionKey.ComputeSessionKey( - selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, - nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value - nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value - xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' - xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use - macKeyArray, useSoftToken_s, keySet, transportKeyName); + + SecureChannelProtocol protocol = new SecureChannelProtocol(); + SymmetricKey macKey = protocol.computeSessionKey_SCP01(SecureChannelProtocol.macType, + selectedToken, + keyNickName, card_challenge, + host_challenge, keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, + xKDD, macKeyArray, useSoftToken_s, keySet, transportKeyName); + + session_key = protocol.wrapSessionKey(selectedToken, macKey, null); if (session_key == null) { CMS.debug("TokenServlet:Tried ComputeSessionKey, got NULL "); @@ -1110,14 +1115,13 @@ public class TokenServlet extends CMSServlet { byte encKeyArray[] = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".auth_key")); - enc_session_key = SessionKey.ComputeEncSessionKey( - selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, - nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value - nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value - xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' - xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use - encKeyArray, useSoftToken_s, keySet); + SymmetricKey encKey = protocol.computeSessionKey_SCP01(SecureChannelProtocol.encType, + selectedToken, + keyNickName, card_challenge, host_challenge, keyInfo, nistSP800_108KdfOnKeyVersion, + nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, encKeyArray, useSoftToken_s, keySet, + transportKeyName); + + enc_session_key = protocol.wrapSessionKey(selectedToken, encKey, null); if (enc_session_key == null) { CMS.debug("TokenServlet:Tried ComputeEncSessionKey, got NULL "); @@ -1140,14 +1144,11 @@ public class TokenServlet extends CMSServlet { com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); - kek_key = SessionKey.ComputeKekKey( - selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, - nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value - nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value - xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' - xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use - kekKeyArray, useSoftToken_s, keySet); + kek_key = protocol.computeKEKKey_SCP01(selectedToken, + keyNickName, + keyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, + xCUID, + xKDD, kekKeyArray, useSoftToken_s, keySet, transportKeyName); CMS.debug("TokenServlet: called ComputeKekKey"); @@ -1177,11 +1178,12 @@ public class TokenServlet extends CMSServlet { if (useSoftToken_s.equals("true")) { CMS.debug("TokenServlet: key encryption key generated on internal"); //cfu audit here? sym key gen - desKey = SessionKey.GenerateSymkey("internal"); + + desKey = protocol.generateSymKey("internal"); //cfu audit here? sym key gen done } else { CMS.debug("TokenServlet: key encryption key generated on " + selectedToken); - desKey = SessionKey.GenerateSymkey(selectedToken); + desKey = protocol.generateSymKey(selectedToken); } if (desKey != null) { // AC: KDF SPEC CHANGE - Output using CUID and KDD @@ -1204,9 +1206,9 @@ public class TokenServlet extends CMSServlet { * and discard the last 8 bytes before it encrypts. * This is done so that the applet can digest it */ - byte[] encDesKey = - SessionKey.ECBencrypt(kek_key, - desKey); + + byte[] encDesKey = protocol.ecbEncrypt(kek_key, desKey, selectedToken); + /* CMS.debug("computeSessionKey:encrypted desKey size = "+encDesKey.length); CMS.debug(encDesKey); @@ -1216,8 +1218,8 @@ public class TokenServlet extends CMSServlet { com.netscape.cmsutil.util.Utils.SpecialEncode(encDesKey); // get keycheck - byte[] keycheck = - SessionKey.ComputeKeyCheck(desKey); + + byte[] keycheck = protocol.computeKeyCheck(desKey, selectedToken); /* CMS.debug("computeSessionKey:keycheck size = "+keycheck.length); CMS.debug(keycheck); @@ -1266,28 +1268,21 @@ public class TokenServlet extends CMSServlet { byte authKeyArray[] = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".auth_key")); - host_cryptogram = SessionKey.ComputeCryptogram( - selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, - nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value - nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value - xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' - xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use - 0, authKeyArray, useSoftToken_s, keySet); + + host_cryptogram = protocol.computeCryptogram_SCP01(selectedToken, keyNickName, card_challenge, + host_challenge, + xkeyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, SecureChannelProtocol.HOST_CRYPTOGRAM, + authKeyArray, useSoftToken_s, keySet, transportKeyName); if (host_cryptogram == null) { CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL "); throw new Exception("Can't compute host cryptogram!"); } - card_crypto = SessionKey.ComputeCryptogram( - selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, - nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value - nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value - xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' - xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use - 1, authKeyArray, useSoftToken_s, keySet); + + card_crypto = protocol.computeCryptogram_SCP01(selectedToken, keyNickName, card_challenge, + host_challenge, xkeyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, + xCUID, xKDD, SecureChannelProtocol.CARD_CRYPTOGRAM, authKeyArray, useSoftToken_s, keySet, transportKeyName); if (card_crypto == null) { CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL "); @@ -1302,6 +1297,10 @@ public class TokenServlet extends CMSServlet { } input_card_crypto = com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_cryptogram); + + SecureChannelProtocol.debugByteArray(input_card_crypto, "input_card_crypto"); + SecureChannelProtocol.debugByteArray(card_crypto, "card_crypto"); + if (card_crypto.length == input_card_crypto.length) { for (int i = 0; i < card_crypto.length; i++) { if (card_crypto[i] != input_card_crypto[i]) { @@ -1782,18 +1781,28 @@ public class TokenServlet extends CMSServlet { byte kekKeyArray[] = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); + SecureChannelProtocol secProtocol = new SecureChannelProtocol(); // AC: KDF SPEC CHANGE - check for error reading settings if (missingSetting_exception == null) { - KeySetData = SessionKey.DiversifyKey(oldSelectedToken, - newSelectedToken, oldKeyNickName, - newKeyNickName, - xkeyInfo, // AC: KDF SPEC CHANGE - pass in old key info so symkey can make decision about which KDF version to use - xnewkeyInfo, // AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be byte[] - nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value - nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value - xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' - xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use - (protocol == 2) ? xWrappedDekKey : kekKeyArray, useSoftToken_s, keySet, (byte) protocol); + if (protocol == 1) { + 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 + xnewkeyInfo, // AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be byte[] + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + (protocol == 2) ? xWrappedDekKey : kekKeyArray, useSoftToken_s, keySet, (byte) protocol); + + } else if (protocol == 2) { + KeySetData = SessionKey.DiversifyKey(oldSelectedToken, newSelectedToken, oldKeyNickName, + newKeyNickName, xkeyInfo, + xnewkeyInfo, nistSP800_108KdfOnKeyVersion, nistSP800_108KdfUseCuidAsKdd, xCUID, xKDD, + (protocol == 2) ? xWrappedDekKey : kekKeyArray, useSoftToken_s, keySet, (byte) protocol); + } + SecureChannelProtocol.debugByteArray(KeySetData, " New keyset data: "); if (KeySetData == null || KeySetData.length <= 1) { CMS.getLogger().log(ILogger.EV_AUDIT, @@ -1817,7 +1826,6 @@ public class TokenServlet extends CMSServlet { } // ! missingParam - String value = ""; String status = "0"; @@ -2092,7 +2100,9 @@ public class TokenServlet extends CMSServlet { // AC: KDF SPEC CHANGE - check for error reading settings if (missingSetting_exception == null) { - encryptedData = SessionKey.EncryptData( + 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 @@ -2100,6 +2110,8 @@ public class TokenServlet extends CMSServlet { 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.debugByteArray(encryptedData, "New Encrypt Data: "); + // AC: KDF SPEC CHANGE - Log both CUID and KDD CMS.getLogger().log(ILogger.EV_AUDIT, |
