diff options
12 files changed, 2343 insertions, 226 deletions
diff --git a/base/server/cms/src/com/netscape/cms/selftests/tks/TKSKnownSessionKey.java b/base/server/cms/src/com/netscape/cms/selftests/tks/TKSKnownSessionKey.java index 06a6398c5..d5e7c11ad 100644 --- a/base/server/cms/src/com/netscape/cms/selftests/tks/TKSKnownSessionKey.java +++ b/base/server/cms/src/com/netscape/cms/selftests/tks/TKSKnownSessionKey.java @@ -67,6 +67,9 @@ public class TKSKnownSessionKey private byte[] mCUID = null; private byte[] mMacKey = null; private byte[] mSessionKey = null; + private byte mNistSP800_108KdfOnKeyVersion = 0; // AC: KDF SPEC CHANGE + private boolean mNistSP800_108KdfUseCuidAsKdd = false; // AC: KDF SPEC CHANGE + private byte[] mKDD = null; // AC: KDF SPEC CHANGE /** * Initializes this subsystem with the configuration store @@ -101,6 +104,46 @@ public class TKSKnownSessionKey mMacKey = getConfigByteArray("macKey", 16); mUseSoftToken = getConfigString("useSoftToken"); + // AC: KDF SPEC CHANGE + // read CUID for the KDD field + mKDD = getConfigByteArray("CUID", 10); + // + // + // read self-test configuration item for nistSP800-108KdfOnKeyVersion + // + // read setting as string + String nistSP800_108KdfOnKeyVersion_str = getConfigString("nistSP800-108KdfOnKeyVersion"); + short nistSP800_108KdfOnKeyVersion_short; + // convert setting value (in ASCII-hex) to short + try{ + nistSP800_108KdfOnKeyVersion_short = Short.parseShort(nistSP800_108KdfOnKeyVersion_str,16); + if ((nistSP800_108KdfOnKeyVersion_short < 0) || (nistSP800_108KdfOnKeyVersion_short > (short)0x00FF)){ + throw new Exception("Out of range."); + } + }catch(Throwable t){ + mSelfTestSubsystem.log (mSelfTestSubsystem.getSelfTestLogger(), + CMS.getLogMessage("SELFTESTS_MISSING_VALUES", + getSelfTestName(), mPrefix + ".nistSP800-108KdfOnKeyVersion")); + throw new EMissingSelfTestException("nistSP800-108KdfOnKeyVersion"); + } + // convert to byte (anything higher than 0x7F is represented as negative) + mNistSP800_108KdfOnKeyVersion = (byte)nistSP800_108KdfOnKeyVersion_short; + // + // + // read self-test configuration item for nistSP800-108KdfUseCuidAsKdd + // + // read setting as string + String nistSP800_108KdfUseCuidAsKdd_str = getConfigString("nistSP800-108KdfUseCuidAsKdd"); + // convert setting value to boolean + try{ + mNistSP800_108KdfUseCuidAsKdd = Boolean.parseBoolean(nistSP800_108KdfUseCuidAsKdd_str); + }catch(Throwable t){ + mSelfTestSubsystem.log (mSelfTestSubsystem.getSelfTestLogger(), + CMS.getLogMessage("SELFTESTS_MISSING_VALUES", + getSelfTestName(), mPrefix + ".nistSP800-108KdfUseCuidAsKdd")); + throw new EMissingSelfTestException("nistSP800-108KdfUseCuidAsKdd"); + } + String defKeySetMacKey = null; tks = CMS.getSubsystem(mTksSubId); if (tks != null) { @@ -132,7 +175,12 @@ public class TKSKnownSessionKey if (mSessionKey == null) { mSessionKey = SessionKey.ComputeSessionKey(mToken, mKeyName, mCardChallenge, mHostChallenge, - mKeyInfo, mCUID, mMacKey, mUseSoftToken, null, null); + mKeyInfo, + mNistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration self-test value + mNistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration self-test value + mCUID, + mKDD, // AC: KDF SPEC CHANGE - pass in KDD + mMacKey, mUseSoftToken, null, null); if (mSessionKey == null || mSessionKey.length != 16) { mSelfTestSubsystem.log(mSelfTestSubsystem.getSelfTestLogger(), CMS.getLogMessage("SELFTESTS_MISSING_VALUES", @@ -314,8 +362,13 @@ public class TKSKnownSessionKey String keySet = "defKeySet"; byte[] sessionKey = SessionKey.ComputeSessionKey( - mToken, mKeyName, mCardChallenge, mHostChallenge, mKeyInfo, - mCUID, mMacKey, mUseSoftToken, keySet, sharedSecretName); + mToken, mKeyName, mCardChallenge, mHostChallenge, + mKeyInfo, + mNistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration self-test value + mNistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration self-test value + mCUID, + mKDD, // AC: KDF SPEC CHANGE - pass in KDD + mMacKey, mUseSoftToken, keySet, sharedSecretName); // Now we just see if we can successfully generate a session key. // For FIPS compliance, the routine now returns a wrapped key, which can't be extracted and compared. diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java index 4a6503897..0b1c6f6e2 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java +++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java @@ -275,6 +275,8 @@ public class ConfigurationUtils { HttpResponse httpresponse = httpclient.send(httprequest); c = httpresponse.getContent(); + //cfu + } catch (ConnectException e) { CMS.debug("getHttpResponse: " + e.toString()); throw new IOException("The server you tried to contact is not running."); @@ -4104,6 +4106,7 @@ public class ConfigurationUtils { CMS.debug("registerUser: response is empty or null."); throw new IOException("The server " + targetURI + "is not available"); } else { + CMS.debug("registerUser: response: " + response); ByteArrayInputStream bis = new ByteArrayInputStream(response.getBytes()); XMLObject parser = new XMLObject(bis); 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 9b0c2bcf2..2cca81bcf 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 @@ -72,25 +72,25 @@ public class TokenServlet extends CMSServlet { IPrettyPrintFormat pp = CMS.getPrettyPrintFormat(":"); private final static String LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST = - "LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_3"; + "LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_4"; // AC: KDF SPEC CHANGE: Need to log both KDD and CUID. private final static String LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS = - "LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS_8"; + "LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS_13"; // AC: KDF SPEC CHANGE: Need to log both KDD and CUID. Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd. private final static String LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE = - "LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE_9"; + "LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE_14"; // AC: KDF SPEC CHANGE: Need to log both KDD and CUID. Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd. private final static String LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST = - "LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_5"; + "LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_6"; // AC: KDF SPEC CHANGE: Need to log both KDD and CUID. private final static String LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS = - "LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS_6"; + "LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS_12"; // AC: KDF SPEC CHANGE: Need to log both KDD and CUID. Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd. private final static String LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE = - "LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE_7"; + "LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE_13"; // AC: KDF SPEC CHANGE: Need to log both KDD and CUID. Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd. private final static String LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST = - "LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_4"; + "LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_5"; // AC: KDF SPEC CHANGE: Need to log both KDD and CUID. private final static String LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS = "LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS_7"; @@ -216,9 +216,110 @@ public class TokenServlet extends CMSServlet { } + // AC: KDF SPEC CHANGE - read new setting value from config file + // (This value allows configuration of which master keys use the NIST SP800-108 KDF and which use the original KDF for backwards compatibility) + // CAREFUL: Result returned may be negative due to java's lack of unsigned types. + // Negative values need to be treated as higher key numbers than positive key numbers. + private static byte read_setting_nistSP800_108KdfOnKeyVersion(String keySet) throws Exception{ + String nistSP800_108KdfOnKeyVersion_map = "tks." + keySet + ".nistSP800-108KdfOnKeyVersion"; + // KDF phase1: default to 00 + String nistSP800_108KdfOnKeyVersion_value = + CMS.getConfigStore().getString(nistSP800_108KdfOnKeyVersion_map, "00" /*null*/); + short nistSP800_108KdfOnKeyVersion_short = 0; + // if value does not exist in file + if (nistSP800_108KdfOnKeyVersion_value == null){ + // throw + // (we want admins to pay attention to this configuration item rather than guessing for them) + throw new Exception("Required configuration value \"" + nistSP800_108KdfOnKeyVersion_map + "\" missing from configuration file."); + } + // convert setting value (in ASCII-hex) to short + try{ + nistSP800_108KdfOnKeyVersion_short = Short.parseShort(nistSP800_108KdfOnKeyVersion_value, 16); + if ((nistSP800_108KdfOnKeyVersion_short < 0) || (nistSP800_108KdfOnKeyVersion_short > (short)0x00FF)){ + throw new Exception("Out of range."); + } + } catch(Throwable t){ + throw new Exception("Configuration value \"" + nistSP800_108KdfOnKeyVersion_map + "\" is in incorrect format. " + + "Correct format is \"" + nistSP800_108KdfOnKeyVersion_map + "=xx\" where xx is key version specified in ASCII-HEX format.", t); + } + // convert to byte (anything higher than 0x7F is represented as a negative) + byte nistSP800_108KdfOnKeyVersion_byte = (byte)nistSP800_108KdfOnKeyVersion_short; + return nistSP800_108KdfOnKeyVersion_byte; + } + + // AC: KDF SPEC CHANGE - read new setting value from config file + // (This value allows configuration of the NIST SP800-108 KDF: + // If "true" we use the CUID parameter within the NIST SP800-108 KDF. + // If "false" we use the KDD parameter within the NIST SP800-108 KDF. + private static boolean read_setting_nistSP800_108KdfUseCuidAsKdd(String keySet) throws Exception{ + String setting_map = "tks." + keySet + ".nistSP800-108KdfUseCuidAsKdd"; + // KDF phase1: default to "false" + String setting_str = + CMS.getConfigStore().getString(setting_map, "false" /*null*/); + boolean setting_boolean = false; + // if value does not exist in file + if (setting_str == null){ + // throw + // (we want admins to pay attention to this configuration item rather than guessing for them) + throw new Exception("Required configuration value \"" + setting_map + "\" missing from configuration file."); + } + // convert setting value to boolean + try{ + setting_boolean = Boolean.parseBoolean(setting_str); + }catch(Throwable t){ + throw new Exception("Configuration value \"" + setting_map + "\" is in incorrect format. Should be either \"true\" or \"false\".", t); + } + return setting_boolean; + } + + // AC: KDF SPEC CHANGE - Audit logging helper functions. + // Converts a byte array to an ASCII-hex string. + // We implemented this ourselves rather than using this.pp.toHexArray() because + // the team preferred CUID and KDD strings to be without ":" separators every byte. + final char[] bytesToHex_hexArray = "0123456789ABCDEF".toCharArray(); + private String bytesToHex(byte[] bytes){ + char[] hexChars = new char[bytes.length * 2]; + for (int i = 0; i < bytes.length; i++){ + int thisChar = bytes[i] & 0x000000FF; + hexChars[i * 2] = bytesToHex_hexArray[thisChar >>> 4]; // div 16 + hexChars[i*2 + 1] = bytesToHex_hexArray[thisChar & 0x0F]; + } + return new String(hexChars); + } + + // AC: KDF SPEC CHANGE - Audit logging helper functions. + // Safely converts a keyInfo byte array to a Key version hex string in the format: 0xa + // Since key version is always the first byte, this function returns the unsigned hex string representation of parameter[0]. + // Returns "null" if parameter is null. + // Returns "invalid" if parameter.length < 1 + private String log_string_from_keyInfo(byte[] xkeyInfo){ + return (xkeyInfo == null) ? "null" : (xkeyInfo.length < 1 ? "invalid" : "0x" + Integer.toHexString((int)(xkeyInfo[0]) & 0x000000FF) ); + } + + // AC: KDF SPEC CHANGE - Audit logging helper functions. + // Safely converts a byte array containing specialDecoded information to an ASCII-hex string. + // Parameters: + // specialDecoded - byte array containing data. May be null. + // Returns: + // if specialDecoded is blank, returns "null" + // if specialDecoded != null, returns <ASCII-HEX string representation of specialDecoded> + private String log_string_from_specialDecoded_byte_array(byte[] specialDecoded){ + if (specialDecoded == null){ + return "null"; + }else{ + return bytesToHex(specialDecoded); + } + } + + private void processComputeSessionKey(HttpServletRequest req, HttpServletResponse resp) throws EBaseException { - byte[] card_challenge, host_challenge, keyInfo, xCUID, CUID, session_key; + byte[] card_challenge ,host_challenge,keyInfo, xCUID, session_key, xKDD; // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD + + // AC: KDF SPEC CHANGE - new config file values (needed for symkey) + byte nistSP800_108KdfOnKeyVersion = (byte)0xff; + boolean nistSP800_108KdfUseCuidAsKdd = false; + byte[] card_crypto, host_cryptogram, input_card_crypto; byte[] xcard_challenge, xhost_challenge; byte[] enc_session_key, xkeyInfo; @@ -228,6 +329,15 @@ public class TokenServlet extends CMSServlet { String transportKeyName = ""; String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID); + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + 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"; @@ -243,6 +353,10 @@ public class TokenServlet extends CMSServlet { IConfigStore sconfig = CMS.getConfigStore(); boolean isCryptoValidate = true; boolean missingParam = false; + + // AC: KDF SPEC CHANGE - flag for if there is an error reading our new setting + Exception missingSetting_exception = null; + session_key = null; card_crypto = null; host_cryptogram = null; @@ -257,9 +371,11 @@ public class TokenServlet extends CMSServlet { (String) sContext.get(SessionContext.USER_ID); } + // AC: KDF SPEC CHANGE: Need to log both KDD and CUID auditMessage = CMS.getLogMessage( LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST, rCUID, + rKDD, // AC: KDF SPEC CHANGE - Log both CUID and KDD. ILogger.SUCCESS, agentId); @@ -299,6 +415,13 @@ public class TokenServlet extends CMSServlet { badParams += " CUID,"; missingParam = true; } + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + if ((rKDD == null) || (rKDD.length() == 0)) { + CMS.debug("TokenServlet: ComputeSessionKey(): missing request parameter: KDD"); + badParams += " KDD,"; + missingParam = true; + } if ((rcard_challenge == null) || (rcard_challenge.equals(""))) { badParams += " card_challenge,"; @@ -322,6 +445,11 @@ public class TokenServlet extends CMSServlet { String keyNickName = null; boolean sameCardCrypto = true; + // AC: KDF SPEC CHANGE + xCUID = null; // avoid errors about non-initialization + xKDD = null; // avoid errors about non-initialization + xkeyInfo = null; // avoid errors about non-initialization + if (!missingParam) { xCUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); @@ -330,6 +458,15 @@ public class TokenServlet extends CMSServlet { CMS.debug("TokenServlet: Invalid CUID length"); missingParam = true; } + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + xKDD = com.netscape.cmsutil.util.Utils.SpecialDecode(rKDD); + if (xKDD == null || xKDD.length != 10) { + badParams += " KDD length,"; + CMS.debug("TokenServlet: Invalid KDD length"); + missingParam = true; + } + xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo); if (xkeyInfo == null || xkeyInfo.length != 2) { badParams += " KeyInfo length,"; @@ -353,7 +490,9 @@ public class TokenServlet extends CMSServlet { } - CUID = null; + // AC: KDF SPEC CHANGE - Remove duplicative variable. + // CUID = null; + if (!missingParam) { card_challenge = com.netscape.cmsutil.util.Utils.SpecialDecode(rcard_challenge); @@ -361,7 +500,33 @@ public class TokenServlet extends CMSServlet { host_challenge = com.netscape.cmsutil.util.Utils.SpecialDecode(rhost_challenge); keyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo); - CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); + // CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); + // AC: KDF SPEC CHANGE: Removed duplicative variable/processing. + + // AC: KDF SPEC CHANGE - read new config file values (needed for symkey) + try{ + nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet); + nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet); + + // log settings read in to debug log along with xkeyInfo + CMS.debug("TokenServlet: ComputeSessionKey(): xkeyInfo[0] = 0x" + + Integer.toHexString((int)(xkeyInfo[0]) & 0x0000000FF) + + ", xkeyInfo[1] = 0x" + + Integer.toHexString((int)(xkeyInfo[1]) & 0x0000000FF) + ); + CMS.debug("TokenServlet: ComputeSessionKey(): Nist SP800-108 KDF will be used for key versions >= 0x" + + Integer.toHexString((int)(nistSP800_108KdfOnKeyVersion) & 0x0000000FF) + ); + if (nistSP800_108KdfUseCuidAsKdd == true){ + CMS.debug("TokenServlet: ComputeSessionKey(): Nist SP800-108 KDF (if used) will use CUID instead of KDD."); + }else{ + CMS.debug("TokenServlet: ComputeSessionKey(): Nist SP800-108 KDF (if used) will use KDD."); + } + // conform to the set-an-error-flag mentality + }catch(Exception e){ + missingSetting_exception = e; + CMS.debug("TokenServlet: ComputeSessionKey(): Exception reading Nist SP800-108 KDF config values: " + e.toString()); + } String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo; //#xx#xx String mappingValue = CMS.getConfigStore().getString(keyInfoMap, null); @@ -377,7 +542,9 @@ public class TokenServlet extends CMSServlet { keyNickName = st.nextToken(); } - if (selectedToken != null && keyNickName != null) { + if (selectedToken != null && keyNickName != null + // AC: KDF SPEC CHANGE - check for error flag + && missingSetting_exception == null) { try { @@ -388,7 +555,12 @@ public class TokenServlet extends CMSServlet { + selectedToken + " keyNickName=" + keyNickName); session_key = SessionKey.ComputeSessionKey( selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, CUID, macKeyArray, useSoftToken_s, keySet, transportKeyName); + host_challenge,keyInfo, + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + macKeyArray, useSoftToken_s, keySet, transportKeyName ); if (session_key == null) { CMS.debug("TokenServlet:Tried ComputeSessionKey, got NULL "); @@ -401,7 +573,12 @@ public class TokenServlet extends CMSServlet { + keySet + ".auth_key")); enc_session_key = SessionKey.ComputeEncSessionKey( selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, CUID, encKeyArray, useSoftToken_s, keySet); + host_challenge,keyInfo, + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + encKeyArray, useSoftToken_s, keySet); if (enc_session_key == null) { CMS.debug("TokenServlet:Tried ComputeEncSessionKey, got NULL "); @@ -426,7 +603,12 @@ public class TokenServlet extends CMSServlet { kek_key = SessionKey.ComputeKekKey( selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, CUID, kekKeyArray, useSoftToken_s, keySet); + host_challenge,keyInfo, + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + kekKeyArray, useSoftToken_s,keySet); CMS.debug("TokenServlet: called ComputeKekKey"); @@ -462,10 +644,19 @@ public class TokenServlet extends CMSServlet { CMS.debug("TokenServlet: key encryption key generated on " + selectedToken); desKey = SessionKey.GenerateSymkey(selectedToken); } - if (desKey != null) - CMS.debug("TokenServlet: key encryption key generated for " + rCUID); - else { - CMS.debug("TokenServlet: key encryption key generation failed for " + rCUID); + if (desKey != null) { + // AC: KDF SPEC CHANGE - Output using CUID and KDD + CMS.debug("TokenServlet: key encryption key generated for CUID=" + + trim(pp.toHexString(xCUID)) + + ", KDD=" + + trim(pp.toHexString(xKDD))); + } else { + // AC: KDF SPEC CHANGE - Output using CUID and KDD + CMS.debug("TokenServlet: key encryption key generation failed for CUID=" + + trim(pp.toHexString(xCUID)) + + ", KDD=" + + trim(pp.toHexString(xKDD))); + throw new Exception("can't generate key encryption key"); } @@ -538,7 +729,12 @@ public class TokenServlet extends CMSServlet { + keySet + ".auth_key")); host_cryptogram = SessionKey.ComputeCryptogram( selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, CUID, 0, authKeyArray, useSoftToken_s, keySet); + host_challenge,keyInfo, + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + 0, authKeyArray, useSoftToken_s, keySet); if (host_cryptogram == null) { CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL "); @@ -547,7 +743,13 @@ public class TokenServlet extends CMSServlet { } card_crypto = SessionKey.ComputeCryptogram( selectedToken, keyNickName, card_challenge, - host_challenge, keyInfo, CUID, 1, authKeyArray, useSoftToken_s, keySet); + host_challenge,keyInfo, + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + 1, authKeyArray, useSoftToken_s, keySet); + if (card_crypto == null) { CMS.debug("TokenServlet:Tried ComputeCryptogram, got NULL "); @@ -575,10 +777,13 @@ public class TokenServlet extends CMSServlet { } } + // AC: KDF SPEC CHANGE - print both KDD and CUID CMS.getLogger().log(ILogger.EV_AUDIT, ILogger.S_TKS, ILogger.LL_INFO, "processComputeSessionKey for CUID=" + - trim(pp.toHexString(CUID))); + trim(pp.toHexString(xCUID)) + + ", KDD=" + + trim(pp.toHexString(xKDD))); } catch (Exception e) { CMS.debug(e); CMS.debug("TokenServlet Computing Session Key: " + e.toString()); @@ -625,18 +830,35 @@ public class TokenServlet extends CMSServlet { cryptogram = com.netscape.cmsutil.util.Utils.SpecialEncode(host_cryptogram); } else { - status = "2"; + // AC: Bugfix: Don't override status's value if an error was already flagged + if (status.equals("0") == true){ + status = "2"; + } } if (selectedToken == null || keyNickName == null) { - status = "4"; + // AC: Bugfix: Don't override status's value if an error was already flagged + if (status.equals("0") == true){ + status = "4"; + } } if (!sameCardCrypto) { - status = "3"; + // AC: Bugfix: Don't override status's value if an error was already flagged + if (status.equals("0") == true){ + // AC: Bugfix: Don't mis-represent host cryptogram mismatch errors as TPS parameter issues + status = "5"; + } } + // AC: KDF SPEC CHANGE - check for settings file issue (flag) + if (missingSetting_exception != null){ + // AC: Intentionally override previous errors if config file settings were missing. + status = "6"; + } + if (missingParam) { + // AC: Intentionally override previous errors if parameters were missing. status = "3"; } @@ -650,10 +872,20 @@ public class TokenServlet extends CMSServlet { errorMsg = "Problem creating host_cryptogram."; } + // AC: Bugfix: Don't mis-represent card cryptogram mismatch errors as TPS parameter issues + if (status.equals("5")) { + errorMsg = "Card cryptogram mismatch. Token likely has incorrect keys."; + } + if (status.equals("4")) { errorMsg = "Problem obtaining token information."; } + // AC: KDF SPEC CHANGE - handle missing configuration item + if (status.equals("6")) { + errorMsg = "Problem reading required configuration value."; + } + if (status.equals("3")) { if (badParams.endsWith(",")) { badParams = badParams.substring(0, badParams.length() - 1); @@ -706,31 +938,45 @@ public class TokenServlet extends CMSServlet { } if (status.equals("0")) { - - auditMessage = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, - rCUID, - ILogger.SUCCESS, - status, - agentId, - isCryptoValidate ? "true" : "false", - serversideKeygen ? "true" : "false", - selectedToken, - keyNickName); - + // AC: KDF SPEC CHANGE - Log both CUID and KDD. + // Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd + // Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available. + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.SUCCESS, // Outcome + status, // status + agentId, // AgentID + isCryptoValidate? "true":"false", // IsCryptoValidate + serversideKeygen? "true":"false", // IsServerSideKeygen + selectedToken, // SelectedToken + keyNickName, // KeyNickName + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion + "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion + Boolean.toString(nistSP800_108KdfUseCuidAsKdd) // NistSP800_108KdfUseCuidAsKdd + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, logParams); } else { + // AC: KDF SPEC CHANGE - Log both CUID and KDD + // Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd + // Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available. + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.FAILURE, // Outcome + status, // status + agentId, // AgentID + isCryptoValidate? "true":"false", // IsCryptoValidate + serversideKeygen? "true":"false", // IsServerSideKeygen + selectedToken, // SelectedToken + keyNickName, // KeyNickName + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion + "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion + Boolean.toString(nistSP800_108KdfUseCuidAsKdd), // NistSP800_108KdfUseCuidAsKdd + errorMsg // Error + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE, logParams); - auditMessage = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE, - rCUID, - ILogger.FAILURE, - status, - agentId, - isCryptoValidate ? "true" : "false", - serversideKeygen ? "true" : "false", - selectedToken, - keyNickName, - errorMsg); } audit(auditMessage); @@ -768,8 +1014,22 @@ public class TokenServlet extends CMSServlet { private void processDiversifyKey(HttpServletRequest req, HttpServletResponse resp) throws EBaseException { - byte[] KeySetData, CUID, xCUID; - byte[] xkeyInfo, xnewkeyInfo; + byte[] KeySetData,KeysValues,xCUID,xKDD; // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD + + // AC: BUGFIX: Record the actual parameters to DiversifyKey in the audit log. + String oldKeyNickName = null; + String newKeyNickName = null; + + // AC: KDF SPEC CHANGE - new config file values (needed for symkey) + byte nistSP800_108KdfOnKeyVersion = (byte)0xff; + boolean nistSP800_108KdfUseCuidAsKdd = false; + + // AC: BUGFIX for key versions higher than 09: We need to initialize these variables in order for the compiler not to complain when we pass them to DiversifyKey. + byte[] xkeyInfo = null,xnewkeyInfo = null; + + // AC: KDF SPEC CHANGE - flag for if there is an error reading our new setting + Exception missingSetting_exception = null; + boolean missingParam = false; String errorMsg = ""; String badParams = ""; @@ -779,6 +1039,15 @@ public class TokenServlet extends CMSServlet { String newMasterKeyName = req.getParameter(IRemoteRequest.TOKEN_NEW_KEYINFO); String oldMasterKeyName = req.getParameter(IRemoteRequest.TOKEN_KEYINFO); String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID); + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + String rKDD = req.getParameter("KDD"); + if ((rKDD == null) || (rKDD.length() == 0)) { + // temporarily make it friendly before TPS change + CMS.debug("TokenServlet: KDD not supplied, set to CUID before TPS change"); + rKDD = rCUID; + } + String auditMessage = ""; String keySet = req.getParameter(IRemoteRequest.TOKEN_KEYSET); @@ -795,9 +1064,11 @@ public class TokenServlet extends CMSServlet { (String) sContext.get(SessionContext.USER_ID); } + // AC: KDF SPEC CHANGE: Need to log both KDD and CUID auditMessage = CMS.getLogMessage( LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST, rCUID, + rKDD, // AC: KDF SPEC CHANGE - Log both CUID and KDD. ILogger.SUCCESS, agentId, oldMasterKeyName, @@ -810,6 +1081,14 @@ public class TokenServlet extends CMSServlet { CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: CUID"); missingParam = true; } + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + if ((rKDD == null) || (rKDD.length() == 0)) { + CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: KDD"); + badParams += " KDD,"; + missingParam = true; + } + if ((rnewKeyInfo == null) || (rnewKeyInfo.equals(""))) { badParams += " newKeyInfo,"; CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: newKeyInfo"); @@ -821,6 +1100,12 @@ public class TokenServlet extends CMSServlet { missingParam = true; } + // AC: KDF SPEC CHANGE + xCUID = null; // avoid errors about non-initialization + xKDD = null; // avoid errors about non-initialization + xkeyInfo = null; // avoid errors about non-initialization + xnewkeyInfo = null; // avoid errors about non-initialization + if (!missingParam) { xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(oldMasterKeyName); if (xkeyInfo == null || xkeyInfo.length != 2) { @@ -847,9 +1132,46 @@ public class TokenServlet extends CMSServlet { CMS.debug("TokenServlet: Invalid CUID length"); missingParam = true; } + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + xKDD = com.netscape.cmsutil.util.Utils.SpecialDecode(rKDD); + if (xKDD == null || xKDD.length != 10) { + badParams += " KDD length,"; + CMS.debug("TokenServlet: Invalid KDD length"); + missingParam = true; + } } if (!missingParam) { - CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); + // CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); // AC: KDF SPEC CHANGE: Removed duplicative variable/processing. + + // AC: KDF SPEC CHANGE - read new config file values (needed for symkey) + try{ + nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet); + nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet); + + // log settings read in to debug log along with xkeyInfo and xnewkeyInfo + CMS.debug("TokenServlet: processDiversifyKey(): xkeyInfo[0] (old) = 0x" + + Integer.toHexString((int)(xkeyInfo[0]) & 0x0000000FF) + + ", xkeyInfo[1] (old) = 0x" + + Integer.toHexString((int)(xkeyInfo[1]) & 0x0000000FF) + + ", xnewkeyInfo[0] = 0x" + + Integer.toHexString((int)(xnewkeyInfo[0]) & 0x000000FF) + + ", xnewkeyInfo[1] = 0x" + + Integer.toHexString((int)(xnewkeyInfo[1]) & 0x000000FF) + ); + CMS.debug("TokenServlet: processDiversifyKey(): Nist SP800-108 KDF will be used for key versions >= 0x" + + Integer.toHexString((int)(nistSP800_108KdfOnKeyVersion) & 0x0000000FF) + ); + if (nistSP800_108KdfUseCuidAsKdd == true){ + CMS.debug("TokenServlet: processDiversifyKey(): Nist SP800-108 KDF (if used) will use CUID instead of KDD."); + }else{ + CMS.debug("TokenServlet: processDiversifyKey(): Nist SP800-108 KDF (if used) will use KDD."); + } + // conform to the set-an-error-flag mentality + }catch(Exception e){ + missingSetting_exception = e; + CMS.debug("TokenServlet: processDiversifyKey(): Exception reading Nist SP800-108 KDF config values: " + e.toString()); + } if (mKeyNickName != null) oldMasterKeyName = mKeyNickName; @@ -859,7 +1181,6 @@ public class TokenServlet extends CMSServlet { String oldKeyInfoMap = "tks." + keySet + ".mk_mappings." + req.getParameter(IRemoteRequest.TOKEN_KEYINFO); //#xx#xx String oldMappingValue = CMS.getConfigStore().getString(oldKeyInfoMap, null); String oldSelectedToken = null; - String oldKeyNickName = null; if (oldMappingValue == null) { oldSelectedToken = CMS.getConfigStore().getString("tks.defaultSlot", "internal"); oldKeyNickName = req.getParameter(IRemoteRequest.TOKEN_KEYINFO); @@ -872,7 +1193,6 @@ public class TokenServlet extends CMSServlet { String newKeyInfoMap = "tks.mk_mappings." + rnewKeyInfo; //#xx#xx String newMappingValue = CMS.getConfigStore().getString(newKeyInfoMap, null); String newSelectedToken = null; - String newKeyNickName = null; if (newMappingValue == null) { newSelectedToken = CMS.getConfigStore().getString("tks.defaultSlot", "internal"); newKeyNickName = rnewKeyInfo; @@ -888,10 +1208,20 @@ public class TokenServlet extends CMSServlet { newKeyNickName); byte kekKeyArray[] = - com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); + com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); + + // AC: KDF SPEC CHANGE - check for error reading settings + if (missingSetting_exception == null){ KeySetData = SessionKey.DiversifyKey(oldSelectedToken, newSelectedToken, oldKeyNickName, - newKeyNickName, rnewKeyInfo, CUID, kekKeyArray, useSoftToken_s, keySet); + newKeyNickName, + xkeyInfo, // AC: KDF SPEC CHANGE - pass in old key info so symkey can make decision about which KDF version to use + xnewkeyInfo, // AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be byte[] + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + kekKeyArray, useSoftToken_s, keySet); if (KeySetData == null || KeySetData.length <= 1) { CMS.getLogger().log(ILogger.EV_AUDIT, @@ -901,11 +1231,18 @@ public class TokenServlet extends CMSServlet { CMS.getLogger().log(ILogger.EV_AUDIT, ILogger.S_TKS, - ILogger.LL_INFO, "process DiversifyKey for CUID =" + trim(pp.toHexString(CUID)) + ILogger.LL_INFO, + "process DiversifyKey for CUID=" + + trim(pp.toHexString(xCUID)) + // AC: KDF SPEC CHANGE: Log both CUID and KDD + ", KDD=" + + trim(pp.toHexString(xKDD)) + ";from oldMasterKeyName=" + oldSelectedToken + ":" + oldKeyNickName + ";to newMasterKeyName=" + newSelectedToken + ":" + newKeyNickName); resp.setContentType("text/html"); + + } // AC: KDF SPEC CHANGE - endif no error reading settings from settings file + } // ! missingParam //CMS.debug("TokenServlet:processDiversifyKey " +outputString); @@ -918,6 +1255,11 @@ public class TokenServlet extends CMSServlet { value = IRemoteRequest.RESPONSE_STATUS+"=0&" + IRemoteRequest.TKS_RESPONSE_KeySetData+"=" + com.netscape.cmsutil.util.Utils.SpecialEncode(KeySetData); CMS.debug("TokenServlet:process DiversifyKey.encode " + value); + // AC: KDF SPEC CHANGE - check for settings file issue (flag) + } else if (missingSetting_exception != null){ + status = "6"; + errorMsg = "Problem reading required configuration value."; + value = "status=" + status; } else if (missingParam) { status = "3"; if (badParams.endsWith(",")) { @@ -945,26 +1287,48 @@ public class TokenServlet extends CMSServlet { if (status.equals("0")) { - auditMessage = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, - rCUID, - ILogger.SUCCESS, - status, - agentId, - oldMasterKeyName, - newMasterKeyName); - + // AC: KDF SPEC CHANGE - Log both CUID and KDD + // Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd + // Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available. + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.SUCCESS, // Outcome + status, // status + agentId, // AgentID + + // AC: BUGFIX: Record the actual parameters to DiversifyKey in the audit log. + oldKeyNickName, // oldMasterKeyName + newKeyNickName, // newMasterKeyName + + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // OldKeyInfo_KeyVersion + log_string_from_keyInfo(xnewkeyInfo), // NewKeyInfo_KeyVersion + "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion + Boolean.toString(nistSP800_108KdfUseCuidAsKdd) // NistSP800_108KdfUseCuidAsKdd + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, logParams); } else { - - auditMessage = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE, - rCUID, - ILogger.FAILURE, - status, - agentId, - oldMasterKeyName, - newMasterKeyName, - errorMsg); + // AC: KDF SPEC CHANGE - Log both CUID and KDD + // Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd + // Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available. + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.FAILURE, // Outcome + status, // status + agentId, // AgentID + + // AC: BUGFIX: Record the actual parameters to DiversifyKey in the audit log. + oldKeyNickName, // oldMasterKeyName + newKeyNickName, // newMasterKeyName + + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // OldKeyInfo_KeyVersion + log_string_from_keyInfo(xnewkeyInfo), // NewKeyInfo_KeyVersion + "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion + Boolean.toString(nistSP800_108KdfUseCuidAsKdd), // NistSP800_108KdfUseCuidAsKdd + errorMsg // Error + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE, logParams); } audit(auditMessage); @@ -972,7 +1336,15 @@ public class TokenServlet extends CMSServlet { private void processEncryptData(HttpServletRequest req, HttpServletResponse resp) throws EBaseException { - byte[] keyInfo, CUID, xCUID, encryptedData, xkeyInfo; + byte[] keyInfo, xCUID, encryptedData, xkeyInfo, xKDD; // AC: KDF SPEC CHANGE: removed duplicative 'CUID' variable and added xKDD + + // AC: KDF SPEC CHANGE - new config file values (needed for symkey) + byte nistSP800_108KdfOnKeyVersion = (byte)0xff; + boolean nistSP800_108KdfUseCuidAsKdd = false; + + // AC: KDF SPEC CHANGE - flag for if there is an error reading our new setting + Exception missingSetting_exception = null; + boolean missingParam = false; byte[] data = null; boolean isRandom = true; // randomly generate the data to be encrypted @@ -984,6 +1356,15 @@ public class TokenServlet extends CMSServlet { String rdata = req.getParameter(IRemoteRequest.TOKEN_DATA); String rKeyInfo = req.getParameter(IRemoteRequest.TOKEN_KEYINFO); String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID); + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + String rKDD = req.getParameter("KDD"); + if ((rKDD == null) || (rKDD.length() == 0)) { + // temporarily make it friendly before TPS change + 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"; @@ -1008,9 +1389,11 @@ public class TokenServlet extends CMSServlet { isRandom = true; } + // AC: KDF SPEC CHANGE: Need to log both KDD and CUID String auditMessage = CMS.getLogMessage( LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST, rCUID, + rKDD, // AC: KDF SPEC CHANGE - Log both CUID and KDD. ILogger.SUCCESS, agentId, s_isRandom); @@ -1044,12 +1427,24 @@ public class TokenServlet extends CMSServlet { missingParam = true; } + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + if ((rKDD == null) || (rKDD.length() == 0)) { + CMS.debug("TokenServlet: processDiversifyKey(): missing request parameter: KDD"); + badParams += " KDD,"; + missingParam = true; + } + if ((rKeyInfo == null) || (rKeyInfo.equals(""))) { badParams += " KeyInfo,"; CMS.debug("TokenServlet: processEncryptData(): missing request parameter: key info"); missingParam = true; } + // AC: KDF SPEC CHANGE + xCUID = null; // avoid errors about non-initialization + xKDD = null; // avoid errors about non-initialization + xkeyInfo = null; // avoid errors about non-initialization + if (!missingParam) { xCUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); if (xCUID == null || xCUID.length != 10) { @@ -1057,6 +1452,15 @@ public class TokenServlet extends CMSServlet { CMS.debug("TokenServlet: Invalid CUID length"); missingParam = true; } + + // AC: KDF SPEC CHANGE - read new KDD parameter from TPS + xKDD = com.netscape.cmsutil.util.Utils.SpecialDecode(rKDD); + if (xKDD == null || xKDD.length != 10) { + badParams += " KDD length,"; + CMS.debug("TokenServlet: Invalid KDD length"); + missingParam = true; + } + xkeyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo); if (xkeyInfo == null || xkeyInfo.length != 2) { badParams += " KeyInfo length,"; @@ -1072,10 +1476,35 @@ public class TokenServlet extends CMSServlet { String selectedToken = null; String keyNickName = null; if (!missingParam) { + + // AC: KDF SPEC CHANGE - read new config file values (needed for symkey + try{ + nistSP800_108KdfOnKeyVersion = TokenServlet.read_setting_nistSP800_108KdfOnKeyVersion(keySet); + nistSP800_108KdfUseCuidAsKdd = TokenServlet.read_setting_nistSP800_108KdfUseCuidAsKdd(keySet); + + // log settings read in to debug log along with xkeyInfo + CMS.debug("TokenServlet: processEncryptData(): xkeyInfo[0] = 0x" + + Integer.toHexString((int)(xkeyInfo[0]) & 0x0000000FF) + + ", xkeyInfo[1] = 0x" + + Integer.toHexString((int)(xkeyInfo[1]) & 0x0000000FF) + ); + CMS.debug("TokenServlet: processEncryptData(): Nist SP800-108 KDF will be used for key versions >= 0x" + + Integer.toHexString((int)(nistSP800_108KdfOnKeyVersion) & 0x0000000FF) + ); + if (nistSP800_108KdfUseCuidAsKdd == true){ + CMS.debug("TokenServlet: processEncryptData(): Nist SP800-108 KDF (if used) will use CUID instead of KDD."); + }else{ + CMS.debug("TokenServlet: processEncryptData(): Nist SP800-108 KDF (if used) will use KDD."); + } + // conform to the set-an-error-flag mentality + }catch(Exception e){ + missingSetting_exception = e; + CMS.debug("TokenServlet: processEncryptData(): Exception reading Nist SP800-108 KDF config values: " + e.toString()); + } + if (!isRandom) data = com.netscape.cmsutil.util.Utils.SpecialDecode(rdata); keyInfo = com.netscape.cmsutil.util.Utils.SpecialDecode(rKeyInfo); - CUID = com.netscape.cmsutil.util.Utils.SpecialDecode(rCUID); String keyInfoMap = "tks." + keySet + ".mk_mappings." + rKeyInfo; String mappingValue = CMS.getConfigStore().getString(keyInfoMap, null); @@ -1090,12 +1519,30 @@ public class TokenServlet extends CMSServlet { byte kekKeyArray[] = com.netscape.cmsutil.util.Utils.SpecialDecode(sconfig.getString("tks." + keySet + ".kek_key")); + // AC: KDF SPEC CHANGE - check for error reading settings + if (missingSetting_exception == null){ + encryptedData = SessionKey.EncryptData( - selectedToken, keyNickName, data, keyInfo, CUID, kekKeyArray, useSoftToken_s, keySet); + selectedToken,keyNickName,data,keyInfo, + nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE - pass in configuration file value + nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE - pass in configuration file value + xCUID, // AC: KDF SPEC CHANGE - removed duplicative 'CUID' variable and replaced with 'xCUID' + xKDD, // AC: KDF SPEC CHANGE - pass in KDD so symkey can make decision about which value (KDD,CUID) to use + kekKeyArray, useSoftToken_s, keySet); + + // AC: KDF SPEC CHANGE - Log both CUID and KDD + CMS.getLogger().log(ILogger.EV_AUDIT, ILogger.S_TKS, - ILogger.LL_INFO, "process EncryptData for CUID =" + trim(pp.toHexString(CUID))); + ILogger.LL_INFO,"process EncryptData for CUID="+ + trim(pp.toHexString(xCUID)) + + ", KDD=" + + trim(pp.toHexString(xKDD))); + + } // AC: KDF SPEC CHANGE - endif no error reading settings from settings file + + } // !missingParam resp.setContentType("text/html"); @@ -1109,6 +1556,11 @@ public class TokenServlet extends CMSServlet { com.netscape.cmsutil.util.Utils.SpecialEncode(data) + "&"+IRemoteRequest.TKS_RESPONSE_EncryptedData+"=" + com.netscape.cmsutil.util.Utils.SpecialEncode(encryptedData); + // AC: KDF SPEC CHANGE - check for settings file issue (flag) + } else if (missingSetting_exception != null){ + status = "6"; + errorMsg = "Problem reading required configuration value."; + value = "status=" + status; } else if (missingParam) { if (badParams.endsWith(",")) { badParams = badParams.substring(0, badParams.length() - 1); @@ -1137,29 +1589,42 @@ public class TokenServlet extends CMSServlet { } if (status.equals("0")) { - - auditMessage = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS, - rCUID, - ILogger.SUCCESS, - status, - agentId, - s_isRandom, - selectedToken, - keyNickName); - + // AC: KDF SPEC CHANGE - Log both CUID and KDD + // Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd + // Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available. + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.SUCCESS, // Outcome + status, // status + agentId, // AgentID + s_isRandom, // isRandom + selectedToken, // SelectedToken + keyNickName, // KeyNickName + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion + "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion + Boolean.toString(nistSP800_108KdfUseCuidAsKdd) // NistSP800_108KdfUseCuidAsKdd + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS, logParams); } else { - - auditMessage = CMS.getLogMessage( - LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE, - rCUID, - ILogger.FAILURE, - status, - agentId, - s_isRandom, - selectedToken, - keyNickName, - errorMsg); + // AC: KDF SPEC CHANGE - Log both CUID and KDD + // Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd + // Finally, log CUID and KDD in ASCII-HEX format, as long as special-decoded version is available. + String[] logParams = { log_string_from_specialDecoded_byte_array(xCUID), // CUID_decoded + log_string_from_specialDecoded_byte_array(xKDD), // KDD_decoded + ILogger.FAILURE, // Outcome + status, // status + agentId, // AgentID + s_isRandom, // isRandom + selectedToken, // SelectedToken + keyNickName, // KeyNickName + keySet, // TKSKeyset + log_string_from_keyInfo(xkeyInfo), // KeyInfo_KeyVersion + "0x" + Integer.toHexString((int)nistSP800_108KdfOnKeyVersion & 0x000000FF), // NistSP800_108KdfOnKeyVersion + Boolean.toString(nistSP800_108KdfUseCuidAsKdd), // NistSP800_108KdfUseCuidAsKdd + errorMsg // Error + }; + auditMessage = CMS.getLogMessage(LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE, logParams); } audit(auditMessage); diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties index dfa23c15b..ef3872c8d 100644 --- a/base/server/cmsbundle/src/LogMessages.properties +++ b/base/server/cmsbundle/src/LogMessages.properties @@ -2263,6 +2263,13 @@ LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE_4=<type=COMPU # SubjectID must be the CUID of the token establishing the secure channel # AgentID must be the trusted agent id used to make the request LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_3=<type=COMPUTE_SESSION_KEY_REQUEST>:[AuditEvent=COMPUTE_SESSION_KEY_REQUEST][SubjectID={0}][Outcome={1}][AgentID={2}] TKS Compute session key request +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the +## CUID. Renamed to "CUID_encoded" and "KDD_encoded" to reflect fact that +## encoded parameters are being logged. +# CUID_encoded must be the special-encoded CUID of the token establishing the secure channel +# KDD_encoded must be the special-encoded KDD of the token establishing the secure channel +LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_4=<type=COMPUTE_SESSION_KEY_REQUEST>:[AuditEvent=COMPUTE_SESSION_KEY_REQUEST][CUID_encoded={0}][KDD_encoded={1}][Outcome={2}][AgentID={3}] TKS Compute session key request + # # # LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS @@ -2277,6 +2284,19 @@ LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_3=<type=COMPUTE_SESSION_KEY_REQ # KeyNickName is the number keyset ex: #01#01 # LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS_8=<type=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS>:[AuditEvent=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS][SubjectID={0}][Outcome={1}][status={2}][AgentID={3}][IsCryptoValidate={4}][IsServerSideKeygen={5}][SelectedToken={6}][KeyNickName={7}] TKS Compute session key request processed successfully +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the +## CUID. Renamed to "CUID_decoded" and "KDD_decoded" to reflect fact +## that decoded parameters are now logged. +## Also added TKSKeyset, KeyInfo_KeyVersion, +## NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd +# CUID_decoded must be the ASCII-HEX representation of the CUID of the token establishing the secure channel +# KDD_decoded must be the ASCII-HEX representation of the KDD of the token establishing the secure channel +# TKSKeyset is the name of the TKS keyset being used for this request. +# KeyInfo_KeyVersion is the key version number requested in hex. +# NistSP800_108KdfOnKeyVersion lists the value of the corresponding setting in hex. +# NistSP800_108KdfUseCuidAsKdd lists the value of the corresponding setting in hex. +LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS_13=<type=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS>:[AuditEvent=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS][CUID_decoded={0}][KDD_decoded={1}][Outcome={2}][status={3}][AgentID={4}][IsCryptoValidate={5}][IsServerSideKeygen={6}][SelectedToken={7}][KeyNickName={8}][TKSKeyset={9}][KeyInfo_KeyVersion={10}][NistSP800_108KdfOnKeyVersion={11}][NistSP800_108KdfUseCuidAsKdd={12}] TKS Compute session key request processed successfully + # # # LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE @@ -2293,6 +2313,16 @@ LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS_8=<type=COMPU # Error gives the error message LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE_9=<type=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE>:[AuditEvent=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE][SubjectID={0}][Outcome={1}][status={2}][AgentID={3}][IsCryptoValidate={4}][IsServerSideKeygen={5}][SelectedToken={7}][KeyNickName={7}][Error={8}] TKS Compute session key request failed # +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the CUID. Renamed to "CUID_decoded" and "KDD_decoded" to reflect fact that decoded parameters are now logged. +## Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd +# CUID_decoded must be the ASCII-HEX representation of the CUID of the token establishing the secure channel +# KDD_decoded must be the ASCII-HEX representation of the KDD of the token establishing the secure channel +# TKSKeyset is the name of the TKS keyset being used for this request. +# KeyInfo_KeyVersion is the key version number requested in hex. +# NistSP800_108KdfOnKeyVersion lists the value of the corresponding setting in hex. +# NistSP800_108KdfUseCuidAsKdd lists the value of the corresponding setting in hex +LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE_14=<type=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE>:[AuditEvent=COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE][CUID_decoded={0}][KDD_decoded={1}][Outcome={2}][status={3}][AgentID={4}][IsCryptoValidate={5}][IsServerSideKeygen={6}][SelectedToken={7}][KeyNickName={8}][TKSKeyset={9}][KeyInfo_KeyVersion={10}][NistSP800_108KdfOnKeyVersion={11}][NistSP800_108KdfUseCuidAsKdd={12}][Error={13}] TKS Compute session key request failed + # LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST # - request for TPS to TKS to do key change over @@ -2303,6 +2333,11 @@ LOGGING_SIGNED_AUDIT_COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE_9=<type=COMPU # newMasterKeyName is the new master key name LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_5=<type=DIVERSIFY_KEY_REQUEST>:[AuditEvent=DIVERSIFY_KEY_REQUEST][SubjectID={0}][Outcome={1}][AgentID={2}][oldMasterKeyName={3}][newMasterKeyName={4}] TKS Key Change Over request # +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the CUID. Renamed to "CUID_encoded" and "KDD_encoded" to reflect fact that encoded parameters are being logged. +# CUID_encoded must be the special-encoded CUID of the token establishing the secure channel +# KDD_encoded must be the special-encoded KDD of the token establishing the secure channel +LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_6=<type=DIVERSIFY_KEY_REQUEST>:[AuditEvent=DIVERSIFY_KEY_REQUEST][CUID_encoded={0}][KDD_encoded={1}][Outcome={2}][AgentID={3}][oldMasterKeyName={4}][newMasterKeyName={5}] TKS Key Change Over request + ########################### # LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS # - request for TPS to TKS to do key change over request processed @@ -2314,6 +2349,17 @@ LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_5=<type=DIVERSIFY_KEY_REQUEST>:[Audit # newMasterKeyName is the new master key name LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS_6=<type=DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS>:[AuditEvent=DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS][SubjectID={0}][Outcome={1}][status={2}][AgentID={3}][oldMasterKeyName={4}][newMasterKeyName={5}] TKS Key Change Over request processed successfully # +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the CUID. Renamed to "CUID_decoded" and "KDD_decoded" to reflect fact that decoded parameters are now logged. +## Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd +# CUID_decoded must be the ASCII-HEX representation of the CUID of the token establishing the secure channel +# KDD_decoded must be the ASCII-HEX representation of the KDD of the token establishing the secure channel +# TKSKeyset is the name of the TKS keyset being used for this request. +# OldKeyInfo_KeyVersion is the old key version number in hex. +# NewKeyInfo_KeyVersion is the new key version number in hex. +# NistSP800_108KdfOnKeyVersion lists the value of the corresponding setting in hex. +# NistSP800_108KdfUseCuidAsKdd lists the value of the corresponding setting in hex. +LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS_12=<type=DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS>:[AuditEvent=DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS][CUID_decoded={0}][KDD_decoded={1}][Outcome={2}][status={3}][AgentID={4}][oldMasterKeyName={5}][newMasterKeyName={6}][TKSKeyset={7}][OldKeyInfo_KeyVersion={8}][NewKeyInfo_KeyVersion={9}][NistSP800_108KdfOnKeyVersion={10}][NistSP800_108KdfUseCuidAsKdd={11}] TKS Key Change Over request processed successfully + # ########################### # LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE @@ -2327,6 +2373,16 @@ LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS_6=<type=DIVERSIFY_K # Error gives the error message LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE_7=<type=DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE>:[AuditEvent=DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE][SubjectID={0}][Outcome={1}][status={2}][AgentID={3}][oldMasterKeyName={4}][newMasterKeyName={5}][Error={6}] TKS Key Change Over request failed # +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the CUID. Renamed to "CUID_decoded" and "KDD_decoded" to reflect fact that decoded parameters are now logged. +## Also added TKSKeyset, OldKeyInfo_KeyVersion, NewKeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd +# CUID_decoded must be the ASCII-HEX representation of the CUID of the token establishing the secure channel +# KDD_decoded must be the ASCII-HEX representation of the KDD of the token establishing the secure channel +# TKSKeyset is the name of the TKS keyset being used for this request. +# OldKeyInfo_KeyVersion is the old key version number in hex. +# NewKeyInfo_KeyVersion is the new key version number in hex. +# NistSP800_108KdfOnKeyVersion lists the value of the corresponding setting in hex. +# NistSP800_108KdfUseCuidAsKdd lists the value of the corresponding setting in hex +LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE_13=<type=DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE>:[AuditEvent=DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE][CUID_decoded={0}][KDD_decoded={1}][Outcome={2}][status={3}][AgentID={4}][oldMasterKeyName={5}][newMasterKeyName={6}][TKSKeyset={7}][OldKeyInfo_KeyVersion={8}][NewKeyInfo_KeyVersion={9}][NistSP800_108KdfOnKeyVersion={10}][NistSP800_108KdfUseCuidAsKdd={11}][Error={12}] TKS Key Change Over request failed # LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST # - request from TPS to TKS to encrypt data @@ -2337,6 +2393,11 @@ LOGGING_SIGNED_AUDIT_DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE_7=<type=DIVERSIFY_K # isRandom tells if the data is randomly generated on TKS LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_4=<type=ENCRYPT_DATA_REQUEST>:[AuditEvent=ENCRYPT_DATA_REQUEST][SubjectID={0}][status={1}][AgentID={2}][isRandom={3}] TKS encrypt data request # +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the CUID. Renamed to "CUID_encoded" and "KDD_encoded" to reflect fact that encoded parameters are being logged. +# CUID_encoded must be the special-encoded CUID of the token establishing the secure channel +# KDD_encoded must be the special-encoded KDD of the token establishing the secure channel +LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_5=<type=ENCRYPT_DATA_REQUEST>:[AuditEvent=ENCRYPT_DATA_REQUEST][CUID_encoded={0}][KDD_encoded={1}][status={2}][AgentID={3}][isRandom={4}] TKS encrypt data request + # # LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS # - request from TPS to TKS to encrypt data @@ -2350,6 +2411,16 @@ LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_4=<type=ENCRYPT_DATA_REQUEST>:[AuditEv # KeyNickName is the numeric keyset ex: #01#01 LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS_7=<type=ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS>:[AuditEvent=ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS][SubjectID={0}][Outcome={1}][status={2}][AgentID={3}][isRandom={4}][SelectedToken={5}][KeyNickName={6}] TKS encrypt data request processed successfully # +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the CUID. Renamed to "CUID_decoded" and "KDD_decoded" to reflect fact that decoded parameters are now logged. +## Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd +# CUID_decoded must be the ASCII-HEX representation of the CUID of the token establishing the secure channel +# KDD_decoded must be the ASCII-HEX representation of the KDD of the token establishing the secure channel +# TKSKeyset is the name of the TKS keyset being used for this request. +# KeyInfo_KeyVersion is the key version number requested in hex. +# NistSP800_108KdfOnKeyVersion lists the value of the corresponding setting in hex. +# NistSP800_108KdfUseCuidAsKdd lists the value of the corresponding setting in hex. +LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS_12=<type=ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS>:[AuditEvent=ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS][CUID_decoded={0}][KDD_decoded={1}][Outcome={2}][status={3}][AgentID={4}][isRandom={5}][SelectedToken={6}][KeyNickName={7}][TKSKeyset={8}][KeyInfo_KeyVersion={9}][NistSP800_108KdfOnKeyVersion={10}][NistSP800_108KdfUseCuidAsKdd={11}] TKS encrypt data request processed successfully + # # LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE # - request from TPS to TKS to encrypt data @@ -2364,6 +2435,15 @@ LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS_7=<type=ENCRYPT_DATA # Error gives the error message LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE_8=<type=ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE>:[AuditEvent=ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE][SubjectID={0}][Outcome={1}][status={2}][AgentID={3}][isRandom={4}][SelectedToken={5}][KeyNickName={6}][Error={7}] TKS encrypt data request failed # +## AC: KDF SPEC CHANGE - Need to log both the KDD and CUID, not just the CUID. Renamed to "CUID_decoded" and "KDD_decoded" to reflect fact that decoded parameters are now logged. +## Also added TKSKeyset, KeyInfo_KeyVersion, NistSP800_108KdfOnKeyVersion, NistSP800_108KdfUseCuidAsKdd +# CUID_decoded must be the ASCII-HEX representation of the CUID of the token establishing the secure channel +# KDD_decoded must be the ASCII-HEX representation of the KDD of the token establishing the secure channel +# TKSKeyset is the name of the TKS keyset being used for this request. +# KeyInfo_KeyVersion is the key version number requested in hex. +# NistSP800_108KdfOnKeyVersion lists the value of the corresponding setting in hex. +# NistSP800_108KdfUseCuidAsKdd lists the value of the corresponding setting in hex. +LOGGING_SIGNED_AUDIT_ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE_13=<type=ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE>:[AuditEvent=ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE][CUID_decoded={0}][KDD_decoded={1}][Outcome={2}][status={3}][AgentID={4}][isRandom={5}][SelectedToken={6}][KeyNickName={7}][TKSKeyset={8}][KeyInfo_KeyVersion={9}][NistSP800_108KdfOnKeyVersion={10}][NistSP800_108KdfUseCuidAsKdd={11}][Error={12}] TKS encrypt data request failed # # # LOGGING_SIGNED_AUDIT_SECURITY_DOMAIN_UPDATE diff --git a/base/symkey/src/com/netscape/symkey/CMakeLists.txt b/base/symkey/src/com/netscape/symkey/CMakeLists.txt index 84c8e086a..63c31903d 100644 --- a/base/symkey/src/com/netscape/symkey/CMakeLists.txt +++ b/base/symkey/src/com/netscape/symkey/CMakeLists.txt @@ -25,6 +25,7 @@ set(SYMKEY_LINK_LIBRARIES set(symkey_library_HDRS SessionKey.h + NistSP800_108KDF.h ) set(symkey_library_SRCS @@ -32,6 +33,7 @@ set(symkey_library_SRCS EncryptData.cpp SessionKey.cpp SymKey.cpp + NistSP800_108KDF.cpp ) include_directories(${SYMKEY_PRIVATE_INCLUDE_DIRS}) diff --git a/base/symkey/src/com/netscape/symkey/EncryptData.cpp b/base/symkey/src/com/netscape/symkey/EncryptData.cpp index ccb817f7c..3963b5026 100644 --- a/base/symkey/src/com/netscape/symkey/EncryptData.cpp +++ b/base/symkey/src/com/netscape/symkey/EncryptData.cpp @@ -37,6 +37,10 @@ extern "C" #include <stdlib.h> #include "Buffer.h" #include "SymKey.h" + +// AC: KDF SPEC CHANGE: Include headers for NIST SP800-108 KDF functions. +#include "NistSP800_108KDF.h" + #define DES2_WORKAROUND PRFileDesc *d = NULL; @@ -66,17 +70,24 @@ void GetKeyName(jbyte *keyVersion, char *keyname) sprintf(keyname+index+4,"%.2d", keyVersion[1]); } - +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_EncryptData -(JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jstring, jstring); +(JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, jbyteArray, jstring, jstring); +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD extern "C" JNIEXPORT jbyteArray JNICALL -Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstring j_tokenName, jstring j_keyName, jbyteArray j_in, jbyteArray keyInfo, jbyteArray CUID, jbyteArray kekKeyArray, jstring useSoftToken_s,jstring keySet) +Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstring j_tokenName, jstring j_keyName, jbyteArray j_in, jbyteArray keyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUID, jbyteArray KDD, jbyteArray kekKeyArray, jstring useSoftToken_s,jstring keySet) { jbyte * kek_key = NULL; PK11SymKey *masterKey = NULL; - PK11SymKey *kekKey = NULL; + + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Kek) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + // KDF output keys + PK11SymKey* macKey = NULL; + PK11SymKey* encKey = NULL; + PK11SymKey* kekKey = NULL; Buffer out = Buffer(KEYLENGTH, (BYTE)0); BYTE kekData[KEYLENGTH]; @@ -86,7 +97,13 @@ Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstr jbyte *cc = NULL; int cc_len = 0; - jbyte * cuidValue = NULL; + + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + jbyte* cuidValue = NULL; + jsize cuidValue_len = -1; + jbyte* kddValue = NULL; + jsize kddValue_len = -1; if( kekKeyArray != NULL) { kek_key = (jbyte*)(env)->GetByteArrayElements(kekKeyArray, NULL); @@ -122,13 +139,30 @@ Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstr goto done; } - if( CUID != NULL) { - cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); - } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + if ( CUID != NULL ) { + cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); + cuidValue_len = env->GetArrayLength(CUID); + } if( cuidValue == NULL) { goto done; } + if ( cuidValue_len <= 0){ // check that CUID is at least 1 byte in length + goto done; + } + if ( KDD != NULL ){ + kddValue = env->GetByteArrayElements(KDD, NULL); + kddValue_len = env->GetArrayLength(KDD); + } + if ( kddValue == NULL ){ + goto done; + } + if ( kddValue_len != static_cast<jsize>(NistSP800_108KDF::KDD_SIZE_BYTES) ){ // check that KDD is expected size + goto done; + } + if( j_in != NULL) { cc = (jbyte*)(env)->GetByteArrayElements( j_in, NULL); @@ -139,7 +173,8 @@ Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstr goto done; } - GetDiversificationData(cuidValue,kekData,kek); + // AC: KDF SPEC CHANGE: Moved this call down. (We don't necessarily need it anymore depending on the KDF we're going to use.) + //GetDiversificationData(cuidValue,kekData,kek); PR_fprintf(PR_STDOUT,"In SessionKey: EncryptData! \n"); @@ -181,12 +216,75 @@ Java_com_netscape_symkey_SessionKey_EncryptData(JNIEnv * env, jclass this2, jstr { masterKey = ReturnSymKey( slot,keyname); - /* We need to use internal so that the key - * can be exported by using PK11_GetKeyData() - */ if (masterKey != NULL) { - kekKey = ComputeCardKeyOnToken(masterKey,kekData); + + + // --------------------------------- + // AC KDF SPEC CHANGE: Determine which KDF to use. + // + // Convert to unsigned types + BYTE nistSP800_108KdfOnKeyVersion_byte = static_cast<BYTE>(nistSP800_108KdfOnKeyVersion); + BYTE requestedKeyVersion_byte = static_cast<BYTE>(keyVersion[0]); + // if requested key version meets setting value, use NIST SP800-108 KDF + if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, requestedKeyVersion_byte) == true){ + + PR_fprintf(PR_STDOUT,"EncryptData NistSP800_108KDF code: Using NIST SP800-108 KDF.\n"); + + // react to "UseCUIDAsKDD" setting value + jbyte* context_jbyte = NULL; + jsize context_len_jsize = 0; + if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){ + context_jbyte = cuidValue; + context_len_jsize = cuidValue_len; + }else{ + context_jbyte = kddValue; + context_len_jsize = kddValue_len; + } + + // Converting this way is safe since jbyte is guaranteed to be 8 bits + // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical), + // but it looks like this assumption is also made in GetDiversificationData + const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte); + + // Convert jsize to size_t + const size_t context_len = static_cast<size_t>(context_len_jsize); + if (context_len > 0x000000FF){ // sanity check (CUID should never be larger than 255 bytes) + PR_fprintf(PR_STDERR, "EncryptData NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n"); + goto done; + } + + // call NIST SP800-108 KDF routine + try{ + NistSP800_108KDF::ComputeCardKeys(masterKey, context, context_len, &encKey, &macKey, &kekKey); + }catch(std::runtime_error& ex){ + PR_fprintf(PR_STDERR, "EncryptData NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: "); + PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what()); + goto done; + }catch(...){ + PR_fprintf(PR_STDERR, "EncryptData NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n"); + goto done; + } + + // if not a key version where we use the NIST SP800-108 KDF, use the original KDF + }else{ + + PR_fprintf(PR_STDOUT,"EncryptData NistSP800_108KDF code: Using original KDF.\n"); + + // AC: KDF SPEC CHANGE: Moved this call down from the original location. + // (We don't always need to call it anymore; it depends on the KDF we're going to use.) + // + // Note the change from "cuidValue" to "kddValue". + // This change is necessary due to the semantics change in the parameters passed between TPS and TKS. + GetDiversificationData(kddValue,kekData,kek); + + // AC: Derives the Kek key for the token. + kekKey = ComputeCardKeyOnToken(masterKey,kekData); + + } // endif use original KDF + // --------------------------------- + + if (kekKey != NULL) { Buffer input = Buffer((BYTE*)cc, cc_len); @@ -213,6 +311,16 @@ done: internal = NULL; } + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Kek) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + if( macKey ) { + PK11_FreeSymKey(macKey); + macKey = NULL; + } + if ( encKey ) { + PK11_FreeSymKey(encKey); + encKey = NULL; + } if ( kekKey != NULL) { PK11_FreeSymKey( kekKey); kekKey = NULL; @@ -246,5 +354,11 @@ done: env->ReleaseByteArrayElements(CUID, cuidValue, JNI_ABORT); } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + if ( kddValue != NULL){ + env->ReleaseByteArrayElements(KDD, kddValue, JNI_ABORT); + kddValue = NULL; + } + return handleBA; } diff --git a/base/symkey/src/com/netscape/symkey/NistSP800_108KDF.cpp b/base/symkey/src/com/netscape/symkey/NistSP800_108KDF.cpp new file mode 100644 index 000000000..9f89dd372 --- /dev/null +++ b/base/symkey/src/com/netscape/symkey/NistSP800_108KDF.cpp @@ -0,0 +1,470 @@ +/* + * NistSP800_108KDF.cpp - Implements the new Key Diversification Function (KDF) as required + * by the latest Department of Defense SIPRnet token interface + * specification. The functions in this file are internally called + * by other functions in the Symkey library. We have made patches + * to these other Symkey functions to trigger this new KDF routine + * at the appropriate times. + * + * Also provides a utility function for adding DES key parity. + */ + +//******************************************************************************* + +#include "NistSP800_108KDF.h" + +//******************************************************************************* + +#include <cstring> // memset() +#include <sstream> // std::ostringstream + +#ifdef NISTSP800_108_KDF_DEBUG +#include <iostream> +#endif + +#include "pk11pub.h" + +//******************************************************************************* + +namespace NistSP800_108KDF{ + +//******************************************************************************* +// 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. +// +//******************************************************************************* +void ComputeCardKeys( PK11SymKey* masterKey, // Key Derivation Key + const BYTE* context, // unique data passed to the kdf (kdd) + const size_t context_length, // length of context + PK11SymKey** encKey, // output parameter: generated enc/auth key + PK11SymKey** macKey, // output parameter: generated mac key + PK11SymKey** kekKey) // output parameter: generated kek key +{ + // sanity check output parameters + if (*encKey != NULL){ + throw std::runtime_error("Output parameter \"encKey\" wasn't initialized to NULL. Overwriting may result in a memory leak."); + } + if (*macKey != NULL){ + throw std::runtime_error("Output parameter \"macKey\" wasn't initialized to NULL. Overwriting may result in a memory leak."); + } + if (*kekKey != NULL){ + throw std::runtime_error("Output parameter \"kekKey\" wasn't initialized to NULL. Overwriting may result in a memory leak."); + } + + // allocate space for KDF output + BYTE kdf_output[KDF_OUTPUT_SIZE_BYTES]; + + try{ + // generate 384 bits of key data from the master key + KDF_CM_SHA256HMAC_L384(masterKey, context, context_length, KDF_LABEL, kdf_output, KDF_OUTPUT_SIZE_BYTES); + }catch(std::runtime_error& ex){ + std::ostringstream msg; + msg << "Exception invoking NistSP800_108KDF::KDF_CM_SHA256HMAC_L384: "; + if (ex.what() == NULL){ + msg << "NULL"; + }else{ + msg << ex.what(); + } + throw std::runtime_error(msg.str()); + }catch(...){ + throw std::runtime_error("Unknown exception invoking NistSP800_108KDF::KDF_CM_SHA256HMAC_L384."); + } + + try{ + // get slot from master key + // (we need the slot to be able to generate our temp key and unwrap our generated bytes + PK11SlotInfo* slot = PK11_GetSlotFromKey(masterKey); + if (slot == NULL){ + throw std::runtime_error("Failed to get slot from masterKey."); + } + try{ + // generate a temp key to import the key data with + PK11SymKey* tmpKey = PK11_TokenKeyGenWithFlags(slot, // slot handle + CKM_DES3_KEY_GEN, // mechanism type + NULL, // pointer to params (SECItem structure) + 0, // keySize (per documentation in pk11skey.c, must be 0 for fixed key length algorithms) + 0, // pointer to keyid (SECItem structure) + CKF_WRAP | CKF_UNWRAP | CKF_ENCRYPT | CKF_DECRYPT, // opFlags + PK11_ATTR_PRIVATE | PK11_ATTR_UNEXTRACTABLE | PK11_ATTR_SENSITIVE, // attrFlags (AC: this is my "best guess" as to what flags should be set) + NULL); // pointer to wincx (AC: also my "best guess" - per pkix_sample_modules.h line 265, this should always be NULL on non-Windows) + if (tmpKey == NULL) { + throw std::runtime_error("Unable to create temp key (for use with importing the key data)."); + } + try{ + + // set parity on each of the 3 generated **16 byte** keys + set_des_parity(kdf_output + (0 * KEY_DATA_SIZE_BYTES), KEY_DATA_SIZE_BYTES); + set_des_parity(kdf_output + (1 * KEY_DATA_SIZE_BYTES), KEY_DATA_SIZE_BYTES); + set_des_parity(kdf_output + (2 * KEY_DATA_SIZE_BYTES), KEY_DATA_SIZE_BYTES); + + try{ + // copy byte array information into 2-key 3DES PK11 keys on token + *encKey = Copy2Key3DESKeyDataToToken(slot, tmpKey, kdf_output + (0 * KEY_DATA_SIZE_BYTES), KEY_DATA_SIZE_BYTES); + *macKey = Copy2Key3DESKeyDataToToken(slot, tmpKey, kdf_output + (1 * KEY_DATA_SIZE_BYTES), KEY_DATA_SIZE_BYTES); + *kekKey = Copy2Key3DESKeyDataToToken(slot, tmpKey, kdf_output + (2 * KEY_DATA_SIZE_BYTES), KEY_DATA_SIZE_BYTES); + }catch(...){ + // free any keys we created before rethrowing + if (*encKey != NULL){ + PK11_FreeSymKey(*encKey); + *encKey = NULL; + } + if (*macKey != NULL){ + PK11_FreeSymKey(*macKey); + *macKey = NULL; + } + if (*kekKey != NULL){ + PK11_FreeSymKey(*kekKey); + *kekKey = NULL; + } + + throw; + } + + // clean up + PK11_FreeSymKey(tmpKey); + tmpKey = NULL; + }catch(...){ + // clean up + PK11_FreeSymKey(tmpKey); + tmpKey = NULL; + + throw; + } + // clean up + PK11_FreeSlot(slot); + slot = NULL; + }catch(...){ + // clean up + PK11_FreeSlot(slot); + slot = NULL; + + throw; + } + + // erase key data from RAM + memset(kdf_output, 0, KDF_OUTPUT_SIZE_BYTES); + }catch(...){ + // erase key data from RAM before rethrowing + memset(kdf_output, 0, KDF_OUTPUT_SIZE_BYTES); + + throw; + } +} + +// uses the specified temporary key to encrypt and then unwrap (decrypt) the specified binary data onto the specified token +// this has the net effect of copying the raw key data to the token +PK11SymKey* Copy2Key3DESKeyDataToToken( PK11SlotInfo* slot, // slot to unwrap key onto + PK11SymKey* tmpKey, // temporary key to use (must already be on the slot) + const BYTE* const data, // pointer to array containing the key data to encrypt and then unwrap (decrypt) on the token + const size_t data_len) // length of data in above array +{ + + // ensure expected input data size + if (data_len != KEY_DATA_SIZE_BYTES){ + throw std::runtime_error("Invalid data length value (should be 16) (Copy2Key3DESKeyDataToToken)."); + } + + // create encryption context + SECItem noParams = { siBuffer, NULL, 0 }; + PK11Context* context = PK11_CreateContextBySymKey(CKM_DES3_ECB, // mechanism type + CKA_ENCRYPT, // operation type + tmpKey, // symKey to operate on + &noParams); // pointer to param (SECItem structure) + if (context == NULL) { + throw std::runtime_error("Unable to create context (Copy2Key3DESKeyDataToToken)."); + } + try{ + BYTE encryptedData[KEY_DATA_SIZE_BYTES + 8]; + BYTE unencryptedData[KEY_DATA_SIZE_BYTES + 8]; + + // copy the key data to a new (larger) buffer + memcpy(unencryptedData, data, KEY_DATA_SIZE_BYTES); + + // copy first DES key (of the two) into the end of the buffer + // (key1-key2-key1) + memcpy(unencryptedData + KEY_DATA_SIZE_BYTES, data, 8); + + try{ + + // encrypt key data with the temp key + int encryptedData_result_len = -1; + SECStatus result = PK11_CipherOp( context, // [in] pointer to PK11Context object + encryptedData, // [out] pointer to output buffer for encrypted data + &encryptedData_result_len, // [out] pointer to output buffer length + KEY_DATA_SIZE_BYTES + 8, // [in] size of output buffer + unencryptedData, // [in] pointer to input buffer for unencrypted data + KEY_DATA_SIZE_BYTES + 8); // [in] size of input buffer + if (result != SECSuccess){ + throw std::runtime_error("Unable to encrypt plaintext key data with temporary key (Copy2Key3DESKeyDataToToken)."); + } + if (encryptedData_result_len != KEY_DATA_SIZE_BYTES + 8){ + throw std::runtime_error("Invalid output encrypting plaintext key data with temporary key (Copy2Key3DESKeyDataToToken)."); + } + + // now "unwrap" the encrypted key data onto the token with the temporary key + SECItem wrappeditem; + wrappeditem.type = siBuffer; + wrappeditem.data = encryptedData; + wrappeditem.len = encryptedData_result_len; + noParams.type = siBuffer; + noParams.data = NULL; + noParams.len = 0; + PK11SymKey* const resultingKey = PK11_UnwrapSymKeyWithFlags(tmpKey, // pointer to wrappingKey (PK11SymKey) + CKM_DES3_ECB, // wrapType (CK_MECHANISM_TYPE) + &noParams, // pointer to param (SECItem struct) + &wrappeditem, // pointer to wrappedKey data (SECItem struct) + CKM_DES3_KEY_GEN, // target (CK_MECHANISM_TYPE) + CKA_DECRYPT, // operation (CK_ATTRIBUTE_TYPE) + KEY_DATA_SIZE_BYTES + 8, // keySize (int) + CKF_SIGN | CKF_WRAP | CKF_UNWRAP | CKF_ENCRYPT | CKF_DECRYPT); // flags (CK_FLAGS) + if (resultingKey == NULL){ + throw std::runtime_error("Unable to unwrap key onto token (Copy2Key3DESKeyDataToToken)."); + } + + // zeroize unencrypted key data before returning + memset(unencryptedData, 0, KEY_DATA_SIZE_BYTES + 8); + + // clean up + PK11_DestroyContext(context, PR_TRUE); + + return resultingKey; + }catch(...){ + // zeroize unencrypted key data before rethrowing + memset(unencryptedData, 0, KEY_DATA_SIZE_BYTES + 8); + + throw; + } + + }catch(...){ + // clean up + PK11_DestroyContext(context, PR_TRUE); + + throw; + } +} + +//******************************************************************************* +// 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) +//******************************************************************************* +void KDF_CM_SHA256HMAC_L384( PK11SymKey* K_I, // Key Derivation Key + const BYTE* context, // unique data passed to the kdf (kdd) + const size_t context_length, // length of context + const BYTE label, // one BYTE label parameter + BYTE* const output, // output is a L-bit array of BYTEs + const size_t output_length) // output length must be at least 48 bytes +{ + //unsigned int h_bits = SHA256_LENGTH * 8; // SHA256_HMAC output size = 256 bits + //unsigned int h_bytes = SHA256_LENGTH; // SHA256_HMAC output size = 32 bytes + //const unsigned int r_bits = 8; // The counter will be representable in 8 bits + //unsigned int n = L / h_bits; // Number of iterations of the PRF + //unsigned int L_BYTE_array_length = (int)ceil(L/256.0); + + const BYTE n = 2; // ceil(384 / (SHA256LENGTH * 8)) == 2 + const size_t L_BYTE_array_length = 2; // 384 = 0x0180 hex; 2 byte long representation + + // sanity check that output buffer is large enough to contain 384 bits + if (output_length < KDF_OUTPUT_SIZE_BYTES){ + throw std::runtime_error("Array \"output\" must be at least 48 bytes in size."); + } + + // calculate size of temporary buffer + size_t 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 std::runtime_error("Input parameter \"context_length\" too large."); + } + BYTE* hmac_data_input = new BYTE[HMAC_DATA_INPUT_SIZE]; // Hash Input = context + 5 BYTES + + BYTE K[n * SHA256_LENGTH]; // BYTE K[n * h_bytes]; - Buffer to store PRF output + try{ + const BYTE L_BYTE_array[L_BYTE_array_length] = {0x01, 0x80}; // Array to store L in BYTES + + /* Establish HMAC Input */ + memset(hmac_data_input, 0, HMAC_DATA_INPUT_SIZE); + hmac_data_input[1] = label; + hmac_data_input[2] = 0x00; + memcpy(&hmac_data_input[3], context, context_length); + memcpy(&hmac_data_input[context_length+3], L_BYTE_array, 2); + + for(BYTE i = 1; i <= n; i++){ + // hmac_data_input = i || label || 0x00 || context || L + hmac_data_input[0] = i; + +#ifdef NISTSP800_108_KDF_DEBUG + std::cout << "hmac_data_input:\n"; + print_BYTE_array(hmac_data_input, HMAC_DATA_INPUT_SIZE); // 5 bytes added to context +#endif + + SHA256HMAC(K_I, hmac_data_input, HMAC_DATA_INPUT_SIZE, &K[(i - 1) * SHA256_LENGTH]); + } + + // clean up + delete[] hmac_data_input; + hmac_data_input = NULL; + + // upon exception, clean up before rethrowing + }catch(...){ + // clean up + delete[] hmac_data_input; + hmac_data_input = NULL; + + throw; + } + +#ifdef NISTSP800_108_KDF_DEBUG + std::cout << "KDF Output (untrimmed):\n"; + print_BYTE_array(K, n * SHA256_LENGTH); +#endif + + // copy result to output buffer, trimming it to 384 bits + memcpy(output, K, KDF_OUTPUT_SIZE_BYTES); + + // clear K before returning + memset(K, 0, n * SHA256_LENGTH); +} + +//******************************************************************************* + +void SHA256HMAC( PK11SymKey* key, // HMAC Secret Key (K_I) + const BYTE* input, // HMAC Input (i||04||00||context||0180) + const size_t input_length, // Input Length + BYTE* const output) // Output Buffer (32 BYTES written) +{ + unsigned int len = 32; + PK11Context *context = 0; + SECStatus s; + SECItem noParams; + noParams.type = siBuffer; + noParams.data = 0; + noParams.len = 0; + + context = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, key, &noParams); + if (!context) { + throw std::runtime_error("CreateContextBySymKey failed"); + } + try{ + + s = PK11_DigestBegin(context); + if (s != SECSuccess) { + throw std::runtime_error("DigestBegin failed"); + } + + s = PK11_DigestOp(context, input, input_length); + if (s != SECSuccess) { + throw std::runtime_error("DigestOp failed"); + } + + s = PK11_DigestFinal(context, output, &len, 32); + if (s != SECSuccess) { + throw std::runtime_error("DigestFinal failed"); + } + +/* Debug Output */ +#ifdef NISTSP800_108_KDF_DEBUG + std::cout << "********************SHA256HMAC_NSS********************\n"; + std::cout << "\nInput Data:\n"; + print_BYTE_array(input, input_length); + std::cout << "\nSHA256HMAC_NSS output:\n"; + print_BYTE_array(output, SHA256_LENGTH); +#endif + + PK11_DestroyContext(context, PR_TRUE); + }catch(...){ + PK11_DestroyContext(context, PR_TRUE); + throw; + } +} + +//******************************************************************************* +// DES Parity Functions +//******************************************************************************* + +/* DES KEY Parity conversion table. Takes each byte >> 1 as an index, returns + * that byte with the proper parity bit set*/ +const unsigned char parityTable[256] = + { + /* 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, }; + +void set_des_parity(BYTE* const key, const size_t length) +{ + if(length != 2*8){ + throw std::runtime_error("set_des_parity failed: wrong key size"); + } + + for (size_t i=0; i < length; i++) + { + key[i] = parityTable[key[i]>>1]; + } +} + +//******************************************************************************* +// BYTE Array Management Functions +//******************************************************************************* +#ifdef NISTSP800_108_KDF_DEBUG +void print_BYTE_array(const BYTE *array2, const size_t length) +{ + for (size_t i = 0; i < length; i++){ + printf("%02x ", array2[i]); + if((i+1)%16 == 0) + printf("\n"); + } + std::cout << std::endl; +} +#endif + +//******************************************************************************* +// NistSP800_108KDF Decision-Making Functions +//******************************************************************************* +// Returns true if the new KDF should be used, otherwise false. +bool useNistSP800_108KDF(BYTE nistSP800_108KDFonKeyVersion, BYTE requestedKeyVersion){ + return (requestedKeyVersion >= nistSP800_108KDFonKeyVersion); +} + +//******************************************************************************* + +} // end namespace NistSP800_108KDF + +//******************************************************************************* diff --git a/base/symkey/src/com/netscape/symkey/NistSP800_108KDF.h b/base/symkey/src/com/netscape/symkey/NistSP800_108KDF.h new file mode 100644 index 000000000..f26edd5d2 --- /dev/null +++ b/base/symkey/src/com/netscape/symkey/NistSP800_108KDF.h @@ -0,0 +1,115 @@ +/* + * NistSP800_108KDF.H - Implements the new Key Diversification Function (KDF) as required + * by the latest Department of Defense SIPRnet token interface + * specification. The functions in this file are internally called + * by other functions in the Symkey library. We have made patches + * to these other Symkey functions to trigger this new KDF routine + * at the appropriate times. + * + * Also provides a utility function for adding DES key parity. + */ + +#ifndef NISTSP800_108KDF_H_ +#define NISTSP800_108KDF_H_ + +//******************************************************************************* +// Defines +//******************************************************************************* +// Debug Flag - Enabling this includes <iostream> and results in the NIST SP800-108 +// KDF code printing out lots of stuff (including key material!) to stdout. +//#define NISTSP800_108_KDF_DEBUG 1 + +//******************************************************************************* +// Includes +//******************************************************************************* +#include <cstddef> // typedef size_t +#include <stdexcept> // std::runtime_error + +#include "pk11pub.h" + +#include "Base.h" // typedef BYTE + +//******************************************************************************* + +namespace NistSP800_108KDF{ + +//******************************************************************************* +// Constants +//******************************************************************************* + +// might already be defined by NSS +#ifndef SHA256_LENGTH +#define SHA256_LENGTH 32 +#endif + +// AC: don't change any of these constants without validating the code that uses them +const size_t KDF_OUTPUT_SIZE_BITS = 384; +const size_t KDF_OUTPUT_SIZE_BYTES = KDF_OUTPUT_SIZE_BITS / 8; +const size_t KEY_DATA_SIZE_BYTES = KDF_OUTPUT_SIZE_BYTES / 3; + +const size_t KDD_SIZE_BYTES = 10; // expected KDD field length in bytes + +const BYTE KDF_LABEL = 0x04; // arbitrary input to crypto routine (see documentation) + +//******************************************************************************* +// Function Headers +//******************************************************************************* + +// 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. +void ComputeCardKeys( PK11SymKey* masterKey, // Key Derivation Key + const BYTE* context, // unique data passed to the kdf (kdd) + const size_t context_length, // length of context + PK11SymKey** encKey, // output parameter: generated enc/auth key + PK11SymKey** macKey, // output parameter: generated mac key + PK11SymKey** kekKey); // output parameter: generated kek key + +// uses the specified temporary key to encrypt and then unwrap (decrypt) the specified binary data onto the specified token +// this has the net effect of copying the raw key data to the token +PK11SymKey* Copy2Key3DESKeyDataToToken( PK11SlotInfo* slot, // slot to unwrap key onto + PK11SymKey* tmpKey, // temporary key to use (must already be on the slot) + const BYTE* const data, // pointer to array containing the key data to encrypt and then unwrap (decrypt) on the token + const size_t data_len); // length of data in above array + +// calculates 384 bits of diversified output from the provided master key (K_I) +void KDF_CM_SHA256HMAC_L384( PK11SymKey* K_I, // Key Derivation Key + const BYTE* context, // unique data passed to the kdf (kdd) + const size_t context_length, // length of context + const BYTE label, // one BYTE label parameter + BYTE* const output, // output is a L-bit array of BYTEs + const size_t output_length); // output length must be at least 48 bytes + +void SHA256HMAC( PK11SymKey* key, // HMAC Secret Key (K_I) + const BYTE* input, // HMAC Input (i||04||00||context||0180) + const size_t input_length, // Input Length + BYTE* const output); // Output Buffer (32 BYTES written) + +/* DES KEY Parity conversion table. Takes each byte >> 1 as an index, returns + * that byte with the proper parity bit set*/ +void set_des_parity(BYTE* const key, const size_t length); + +#ifdef NISTSP800_108_KDF_DEBUG +void print_BYTE_array(const BYTE *array2, const size_t len); +#endif + +// Returns true if the new KDF should be used, otherwise false. +bool useNistSP800_108KDF(BYTE nistSP800_108KDFonKeyVersion, BYTE requestedKeyVersion); + +//******************************************************************************* + +} // end namespace NistSP800_108KDF + +//******************************************************************************* + +#endif /* NISTSP800_108KDF_H_ */ diff --git a/base/symkey/src/com/netscape/symkey/SessionKey.cpp b/base/symkey/src/com/netscape/symkey/SessionKey.cpp index 9f3a353a3..2c146730f 100644 --- a/base/symkey/src/com/netscape/symkey/SessionKey.cpp +++ b/base/symkey/src/com/netscape/symkey/SessionKey.cpp @@ -51,6 +51,10 @@ extern "C" #include "Buffer.h" #include "SymKey.h" +// AC: KDF SPEC CHANGE: Include headers for NIST SP800-108 KDF functions. +#include "NistSP800_108KDF.h" + + #define STEAL_JSS #ifdef STEAL_JSS // stealing code from JSS to handle DRM support @@ -573,13 +577,15 @@ extern "C" * Method: ComputeSessionKey * Signature: ([B[B[B[B)[B */ +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeSessionKey - (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jstring, jstring, jstring); + (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, jbyteArray, jstring, jstring, jstring); #ifdef __cplusplus } #endif #define KEYLENGTH 16 -extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeSessionKey(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyteArray CUID, jbyteArray macKeyArray, jstring useSoftToken_s, jstring keySet, jstring sharedSecretKeyName) +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD +extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeSessionKey(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUID, jbyteArray KDD, jbyteArray macKeyArray, jstring useSoftToken_s, jstring keySet, jstring sharedSecretKeyName) { /* hardcore permanent mac key */ jbyte *mac_key = NULL; @@ -608,8 +614,13 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp PK11SymKey *macSymKey = NULL; PK11SymKey *symkey16 = NULL; - PK11SymKey *macKey = NULL; + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Mac) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + // KDF output keys + PK11SymKey* macKey = NULL; + PK11SymKey* encKey = NULL; + PK11SymKey* kekKey = NULL; BYTE macData[KEYLENGTH]; char keyname[KEYNAMELENGTH]; @@ -625,7 +636,12 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp jbyteArray handleBA=NULL; jbyte *handleBytes=NULL; - jbyte * cuidValue = NULL; + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + jbyte* cuidValue = NULL; + jsize cuidValue_len = -1; + jbyte* kddValue = NULL; + jsize kddValue_len = -1; jbyte *cc = NULL; int cc_len = 0; @@ -693,13 +709,30 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp goto done; } + + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). if ( CUID != NULL ) { cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); + cuidValue_len = env->GetArrayLength(CUID); } - if( cuidValue == NULL) { goto done; } + if ( cuidValue_len <= 0){ // check that CUID is at least 1 byte in length + goto done; + } + if ( KDD != NULL ){ + kddValue = env->GetByteArrayElements(KDD, NULL); + kddValue_len = env->GetArrayLength(KDD); + } + if ( kddValue == NULL ){ + goto done; + } + if ( kddValue_len != static_cast<jsize>(NistSP800_108KDF::KDD_SIZE_BYTES) ){ // check that KDD is expected size + goto done; + } + /* copy card and host challenge into input buffer */ for (i = 0; i < 8; i++) @@ -711,7 +744,8 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp input[8+i] = hc[i]; } - GetDiversificationData(cuidValue,macData,mac);//keytype is mac + // AC: KDF SPEC CHANGE: Moved this call down. (We don't necessarily need it anymore depending on the KDF we're going to use.) + //GetDiversificationData(cuidValue,macData,mac);//keytype is mac if(tokenName) { @@ -753,21 +787,90 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp goto done; } - macKey =ComputeCardKeyOnToken(masterKey,macData); + + // --------------------------------- + // AC KDF SPEC CHANGE: Determine which KDF to use. + // + // Convert to unsigned types + BYTE nistSP800_108KdfOnKeyVersion_byte = static_cast<BYTE>(nistSP800_108KdfOnKeyVersion); + BYTE requestedKeyVersion_byte = static_cast<BYTE>(keyVersion[0]); + // if requested key version meets setting value, use NIST SP800-108 KDF + if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, requestedKeyVersion_byte) == true){ + + PR_fprintf(PR_STDOUT,"ComputeSessionKey NistSP800_108KDF code: Using NIST SP800-108 KDF.\n"); + + // react to "UseCUIDAsKDD" setting value + jbyte* context_jbyte = NULL; + jsize context_len_jsize = 0; + if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){ + context_jbyte = cuidValue; + context_len_jsize = cuidValue_len; + }else{ + context_jbyte = kddValue; + context_len_jsize = kddValue_len; + } + + // Converting this way is safe since jbyte is guaranteed to be 8 bits + // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical), + // but it looks like this assumption is also made in GetDiversificationData + const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte); + + // Convert jsize to size_t + const size_t context_len = static_cast<size_t>(context_len_jsize); + if (context_len > 0x000000FF){ // sanity check (CUID should never be larger than 255 bytes) + PR_fprintf(PR_STDERR, "ComputeSessionKey NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n"); + goto done; + } + + // call NIST SP800-108 KDF routine + try{ + NistSP800_108KDF::ComputeCardKeys(masterKey, context, context_len, &encKey, &macKey, &kekKey); + }catch(std::runtime_error& ex){ + PR_fprintf(PR_STDERR, "ComputeSessionKey NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: "); + PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what()); + goto done; + }catch(...){ + PR_fprintf(PR_STDERR, "ComputeSessionKey NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n"); + goto done; + } + + // if not a key version where we use the NIST SP800-108 KDF, use the original KDF + }else{ + + PR_fprintf(PR_STDOUT,"ComputeSessionKey NistSP800_108KDF code: Using original KDF.\n"); + + // AC: KDF SPEC CHANGE: Moved this call down from the original location. + // (We don't always need to call it anymore; it depends on the KDF we're going to use.) + // + // Note the change from "cuidValue" to "kddValue". + // This change is necessary due to the semantics change in the parameters passed between TPS and TKS. + GetDiversificationData(kddValue,macData,mac);//keytype is mac + + // AC: Derives the mac key for the token. + macKey =ComputeCardKeyOnToken(masterKey,macData); + + } // endif use original KDF + // --------------------------------- + + if(macKey == NULL) { goto done; } - + + // AC: This computes the GP session key using the card-specific MAC key we previously derived. symkey = DeriveKey(macKey, Buffer((BYTE*)hc, hc_len), Buffer((BYTE*)cc, cc_len)); - if(symkey == NULL) - { - goto done; - } } - //Now wrap the key for the trip back to TPS with shared secret transport key + // AC: Moved this check out of the else block so we catch NULL keys in the developer key case + // (The call already exists outside the "else" block for ComputeEncSessionKey and ComputeKekKey.) + if(symkey == NULL) + { + goto done; + } + + //Now wrap the key for the trip back to TPS with shared secret transport key symkey16 = NULL; transportKey = ReturnSymKey( internal, GetSharedSecretKeyName(NULL)); if ( transportKey == NULL ) { @@ -829,10 +932,20 @@ done: masterKey = NULL; } + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Mac) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. if( macKey ) { PK11_FreeSymKey( macKey); macKey = NULL; } + if ( encKey ) { + PK11_FreeSymKey(encKey); + encKey = NULL; + } + if ( kekKey ) { + PK11_FreeSymKey(kekKey); + kekKey = NULL; + } if( macSymKey ) { PK11_FreeSymKey( macSymKey ); @@ -849,7 +962,9 @@ done: sharedSecretKeyNameChars = NULL; } - if ( handleBA != NULL) { + // AC BUGFIX: Check the value of handleBytes (not handleBA) before freeing handleBytes! + //if ( handleBA != NULL) { + if ( handleBytes != NULL) { (env)->ReleaseByteArrayElements( handleBA, handleBytes, 0); } @@ -869,11 +984,22 @@ done: (env)->ReleaseByteArrayElements(CUID, cuidValue, JNI_ABORT); } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + if ( kddValue != NULL){ + env->ReleaseByteArrayElements(KDD, kddValue, JNI_ABORT); + kddValue = NULL; + } + if( mac_key != NULL) { (env)->ReleaseByteArrayElements(macKeyArray, mac_key, JNI_ABORT); } - return handleBA; + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + if (wrapStatus != SECFailure ){ + return handleBA; + }else{ + return NULL; + } } @@ -886,13 +1012,15 @@ extern "C" * Method: ComputeEncSessionKey * Signature: ([B[B[B[B)[B */ +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeEncSessionKey - (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jstring, jstring); + (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, jbyteArray, jstring, jstring); #ifdef __cplusplus } #endif #define KEYLENGTH 16 -extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeEncSessionKey(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyteArray CUID, jbyteArray encKeyArray, jstring useSoftToken_s, jstring keySet) +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD +extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeEncSessionKey(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUID, jbyteArray KDD, jbyteArray encKeyArray, jstring useSoftToken_s, jstring keySet) { /* hardcoded permanent enc key */ jbyte *enc_key = NULL; @@ -919,9 +1047,15 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp PK11SymKey *masterKey = NULL; PK11SymKey *encSymKey = NULL; - PK11SymKey *encKey = NULL; PK11SymKey *symkey16 = NULL; + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Enc) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + // KDF output keys + PK11SymKey* macKey = NULL; + PK11SymKey* encKey = NULL; + PK11SymKey* kekKey = NULL; + BYTE encData[KEYLENGTH]; char keyname[KEYNAMELENGTH]; @@ -934,7 +1068,12 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp jbyteArray handleBA=NULL; jbyte *handleBytes=NULL; - jbyte * cuidValue = NULL; + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + jbyte* cuidValue = NULL; + jsize cuidValue_len = -1; + jbyte* kddValue = NULL; + jsize kddValue_len = -1; jbyte *cc = NULL; int cc_len = 0; @@ -989,13 +1128,30 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp goto done; } - if( CUID != NULL) { - cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); - } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + if ( CUID != NULL ) { + cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); + cuidValue_len = env->GetArrayLength(CUID); + } if( cuidValue == NULL) { goto done; } + if ( cuidValue_len <= 0){ // check that CUID is at least 1 byte in length + goto done; + } + if ( KDD != NULL ){ + kddValue = env->GetByteArrayElements(KDD, NULL); + kddValue_len = env->GetArrayLength(KDD); + } + if ( kddValue == NULL ){ + goto done; + } + if ( kddValue_len != static_cast<jsize>(NistSP800_108KDF::KDD_SIZE_BYTES) ){ // check that KDD is expected size + goto done; + } + /* copy card and host challenge into input buffer */ for (i = 0; i < 8; i++) @@ -1007,7 +1163,8 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp input[8+i] = hc[i]; } - GetDiversificationData(cuidValue,encData,enc); + // AC: KDF SPEC CHANGE: Moved this call down. (We don't necessarily need it anymore depending on the KDF we're going to use.) + //GetDiversificationData(cuidValue,encData,enc); if(tokenName) { @@ -1044,17 +1201,81 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp { masterKey = ReturnSymKey( slot,keyname); - /* We need to use internal so that the key - * can be exported by using PK11_GetKeyData() - */ if(masterKey == NULL) { goto done; } - encKey =ComputeCardKeyOnToken(masterKey,encData); + + // --------------------------------- + // AC KDF SPEC CHANGE: Determine which KDF to use. + // + // Convert to unsigned types + BYTE nistSP800_108KdfOnKeyVersion_byte = static_cast<BYTE>(nistSP800_108KdfOnKeyVersion); + BYTE requestedKeyVersion_byte = static_cast<BYTE>(keyVersion[0]); + // if requested key version meets setting value, use NIST SP800-108 KDF + if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, requestedKeyVersion_byte) == true){ + + PR_fprintf(PR_STDOUT,"ComputeEncSessionKey NistSP800_108KDF code: Using NIST SP800-108 KDF.\n"); + + // react to "UseCUIDAsKDD" setting value + jbyte* context_jbyte = NULL; + jsize context_len_jsize = 0; + if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){ + context_jbyte = cuidValue; + context_len_jsize = cuidValue_len; + }else{ + context_jbyte = kddValue; + context_len_jsize = kddValue_len; + } + + // Converting this way is safe since jbyte is guaranteed to be 8 bits + // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical), + // but it looks like this assumption is also made in GetDiversificationData + const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte); + + // Convert jsize to size_t + const size_t context_len = static_cast<size_t>(context_len_jsize); + if (context_len > 0x000000FF){ // sanity check (CUID should never be larger than 255 bytes) + PR_fprintf(PR_STDERR, "ComputeEncSessionKey NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n"); + goto done; + } + + // call NIST SP800-108 KDF routine + try{ + NistSP800_108KDF::ComputeCardKeys(masterKey, context, context_len, &encKey, &macKey, &kekKey); + }catch(std::runtime_error& ex){ + PR_fprintf(PR_STDERR, "ComputeEncSessionKey NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: "); + PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what()); + goto done; + }catch(...){ + PR_fprintf(PR_STDERR, "ComputeEncSessionKey NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n"); + goto done; + } + + // if not a key version where we use the NIST SP800-108 KDF, use the original KDF + }else{ + + PR_fprintf(PR_STDOUT,"ComputeEncSessionKey NistSP800_108KDF code: Using original KDF.\n"); + + // AC: KDF SPEC CHANGE: Moved this call down from the original location. + // (We don't always need to call it anymore; it depends on the KDF we're going to use.) + // + // Note the change from "cuidValue" to "kddValue". + // This change is necessary due to the semantics change in the parameters passed between TPS and TKS. + GetDiversificationData(kddValue,encData,enc); + + // AC: Derives the enc key for the token. + encKey =ComputeCardKeyOnToken(masterKey,encData); + + } // endif use original KDF + // --------------------------------- + + if(encKey == NULL) { goto done; } + + // AC: This computes the GP session key using the card-specific ENC key we previously derived. symkey = DeriveKey(encKey, Buffer((BYTE*)hc, hc_len), Buffer((BYTE*)cc, cc_len)); } @@ -1127,9 +1348,19 @@ done: encSymKey = NULL; } - if( encKey) { - PK11_FreeSymKey( encKey); - encKey = NULL; + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Enc) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + if( macKey ) { + PK11_FreeSymKey(macKey); + macKey = NULL; + } + if ( encKey) { + PK11_FreeSymKey( encKey); + encKey = NULL; + } + if ( kekKey ) { + PK11_FreeSymKey(kekKey); + kekKey = NULL; } if( keySetStringChars ) { @@ -1156,11 +1387,22 @@ done: (env)->ReleaseByteArrayElements(CUID, cuidValue, JNI_ABORT); } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + if ( kddValue != NULL){ + env->ReleaseByteArrayElements(KDD, kddValue, JNI_ABORT); + kddValue = NULL; + } + if( enc_key != NULL) { (env)->ReleaseByteArrayElements(encKeyArray, enc_key, JNI_ABORT); } - return handleBA; + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + if (wrapStatus != SECFailure ){ + return handleBA; + }else{ + return NULL; + } } #ifdef __cplusplus @@ -1172,14 +1414,16 @@ extern "C" * Method: ComputeKekKey * Signature: ([B[B[B[B)[B */ +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_ComputeKekKey - (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jstring, jstring); + (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, jbyteArray, jstring, jstring); #ifdef __cplusplus } #endif #define KEYLENGTH 16 -extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_ComputeKekKey(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyteArray CUID, jbyteArray kekKeyArray, jstring useSoftToken_s, jstring keySet) +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD +extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_ComputeKekKey(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUID, jbyteArray KDD, jbyteArray kekKeyArray, jstring useSoftToken_s, jstring keySet) { /* hardcoded permanent kek key */ jbyte *kek_key = NULL; @@ -1210,13 +1454,25 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_Compute jbyte *hc = NULL; jbyte * keyVersion = NULL; int keyVersion_len = 0; - jbyte * cuidValue = NULL; + + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + jbyte* cuidValue = NULL; + jsize cuidValue_len = -1; + jbyte* kddValue = NULL; + jsize kddValue_len = -1; char *keyNameChars=NULL; char *tokenNameChars = NULL; PK11SlotInfo *slot = NULL; - PK11SymKey *kekKey = NULL; + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (KEK) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + // KDF output keys + PK11SymKey* macKey = NULL; + PK11SymKey* encKey = NULL; + PK11SymKey* kekKey = NULL; + PK11SymKey *masterKey = NULL; BYTE kekData[KEYLENGTH]; @@ -1249,13 +1505,30 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_Compute goto done; } - if( CUID != NULL) { - cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); - } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + if ( CUID != NULL ) { + cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); + cuidValue_len = env->GetArrayLength(CUID); + } if( cuidValue == NULL) { goto done; } + if ( cuidValue_len <= 0){ // check that CUID is at least 1 byte in length + goto done; + } + if ( KDD != NULL ){ + kddValue = env->GetByteArrayElements(KDD, NULL); + kddValue_len = env->GetArrayLength(KDD); + } + if ( kddValue == NULL ){ + goto done; + } + if ( kddValue_len != static_cast<jsize>(NistSP800_108KDF::KDD_SIZE_BYTES) ){ // check that KDD is expected size + goto done; + } + /* copy card and host challenge into input buffer */ for (i = 0; i < 8; i++) @@ -1267,7 +1540,8 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_Compute input[8+i] = hc[i]; } - GetDiversificationData(cuidValue,kekData,kek);//keytype is kek + // AC: KDF SPEC CHANGE: Moved this call down. (We don't necessarily need it anymore depending on the KDF we're going to use.) + //GetDiversificationData(cuidValue,kekData,kek);//keytype is kek if (tokenName) { @@ -1301,7 +1575,71 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_netscape_symkey_SessionKey_Compute goto done; } - kekKey =ComputeCardKeyOnToken(masterKey,kekData); + + // --------------------------------- + // AC KDF SPEC CHANGE: Determine which KDF to use. + // + // Convert to unsigned types + BYTE nistSP800_108KdfOnKeyVersion_byte = static_cast<BYTE>(nistSP800_108KdfOnKeyVersion); + BYTE requestedKeyVersion_byte = static_cast<BYTE>(keyVersion[0]); + // if requested key version meets setting value, use NIST SP800-108 KDF + if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, requestedKeyVersion_byte) == true){ + + PR_fprintf(PR_STDOUT,"ComputeKekKey NistSP800_108KDF code: Using NIST SP800-108 KDF.\n"); + + // react to "UseCUIDAsKDD" setting value + jbyte* context_jbyte = NULL; + jsize context_len_jsize = 0; + if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){ + context_jbyte = cuidValue; + context_len_jsize = cuidValue_len; + }else{ + context_jbyte = kddValue; + context_len_jsize = kddValue_len; + } + + // Converting this way is safe since jbyte is guaranteed to be 8 bits + // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical), + // but it looks like this assumption is also made in GetDiversificationData + const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte); + + // Convert jsize to size_t + const size_t context_len = static_cast<size_t>(context_len_jsize); + if (context_len > 0x000000FF){ // sanity check (CUID should never be larger than 255 bytes) + PR_fprintf(PR_STDERR, "ComputeKekKey NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n"); + goto done; + } + + // call NIST SP800-108 KDF routine + try{ + NistSP800_108KDF::ComputeCardKeys(masterKey, context, context_len, &encKey, &macKey, &kekKey); + }catch(std::runtime_error& ex){ + PR_fprintf(PR_STDERR, "ComputeKekKey NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: "); + PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what()); + goto done; + }catch(...){ + PR_fprintf(PR_STDERR, "ComputeKekKey NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n"); + goto done; + } + + // if not a key version where we use the NIST SP800-108 KDF, use the original KDF + }else{ + + PR_fprintf(PR_STDOUT,"ComputeKekKey NistSP800_108KDF code: Using original KDF.\n"); + + // AC: KDF SPEC CHANGE: Moved this call down from the original location. + // (We don't always need to call it anymore; it depends on the KDF we're going to use.) + // + // Note the change from "cuidValue" to "kddValue". + // This change is necessary due to the semantics change in the parameters passed between TPS and TKS. + GetDiversificationData(kddValue,kekData,kek);//keytype is kek + + // AC: Derives the mac key for the token. + kekKey =ComputeCardKeyOnToken(masterKey,kekData); + + } // endif use original KDF + // --------------------------------- + } @@ -1323,6 +1661,16 @@ done: masterKey = NULL; } + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Kek) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + if( macKey ) { + PK11_FreeSymKey(macKey); + macKey = NULL; + } + if ( encKey ) { + PK11_FreeSymKey(encKey); + encKey = NULL; + } if(kekKey) { PK11_FreeSymKey( kekKey); kekKey = NULL; @@ -1349,6 +1697,12 @@ done: (env)->ReleaseByteArrayElements(CUID, cuidValue, JNI_ABORT); } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + if ( kddValue != NULL){ + env->ReleaseByteArrayElements(KDD, kddValue, JNI_ABORT); + kddValue = NULL; + } + return keyObj; } @@ -1498,13 +1852,15 @@ extern "C" * Method: ComputeCryptogram * Signature: ([B[B[B[B)[B */ +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeCryptogram - (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyteArray, int, jbyteArray, jstring, jstring); + (JNIEnv *, jclass, jstring, jstring, jbyteArray, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, int, jbyteArray, jstring, jstring); #ifdef __cplusplus } #endif #define KEYLENGTH 16 -extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeCryptogram(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyteArray CUID, int type, jbyteArray authKeyArray, jstring useSoftToken_s, jstring keySet) +// AC: KDF SPEC CHANGE: function signature change - added jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD +extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_ComputeCryptogram(JNIEnv * env, jclass this2, jstring tokenName, jstring keyName, jbyteArray card_challenge, jbyteArray host_challenge, jbyteArray keyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUID, jbyteArray KDD, int type, jbyteArray authKeyArray, jstring useSoftToken_s, jstring keySet) { /* hardcore permanent mac key */ jbyte *auth_key = NULL; @@ -1542,7 +1898,13 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp int hc_len = 0; jbyte * keyVersion = NULL; int keyVersion_len = 0; - jbyte * cuidValue = NULL; + + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + jbyte* cuidValue = NULL; + jsize cuidValue_len = -1; + jbyte* kddValue = NULL; + jsize kddValue_len = -1; char *tokenNameChars = NULL; char *keyNameChars=NULL; @@ -1551,13 +1913,22 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp jbyte * session_key = NULL; PK11SymKey *symkey = NULL; PK11SymKey *masterKey = NULL; - PK11SymKey *authKey = NULL; PK11SymKey *authSymKey = NULL; + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Enc/Auth) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + // KDF output keys + PK11SymKey* macKey = NULL; + PK11SymKey* authKey = NULL; + PK11SymKey* kekKey = NULL; + BYTE authData[KEYLENGTH]; char keyname[KEYNAMELENGTH]; Buffer input_x = Buffer(KEYLENGTH); + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + bool error_computing_result = true; + if( card_challenge != NULL ) { cc = (jbyte*)(env)->GetByteArrayElements( card_challenge, NULL); cc_len = (env)->GetArrayLength(card_challenge); @@ -1587,13 +1958,30 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp goto done; } - if( CUID != NULL) { - cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); - } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + if ( CUID != NULL ) { + cuidValue = (jbyte*)(env)->GetByteArrayElements( CUID, NULL); + cuidValue_len = env->GetArrayLength(CUID); + } if( cuidValue == NULL) { goto done; } + if ( cuidValue_len <= 0){ // check that CUID is at least 1 byte in length + goto done; + } + if ( KDD != NULL ){ + kddValue = env->GetByteArrayElements(KDD, NULL); + kddValue_len = env->GetArrayLength(KDD); + } + if ( kddValue == NULL ){ + goto done; + } + if ( kddValue_len != static_cast<jsize>(NistSP800_108KDF::KDD_SIZE_BYTES) ){ // check that KDD is expected size + goto done; + } + if (type == 0) // compute host cryptogram { @@ -1621,7 +2009,8 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp input_x.replace(0, (BYTE*) input, KEYLENGTH); - GetDiversificationData(cuidValue,authData,enc); + // AC: KDF SPEC CHANGE: Moved this call down. (We don't necessarily need it anymore depending on the KDF we're going to use.) + //GetDiversificationData(cuidValue,authData,enc); if (tokenName) { @@ -1660,12 +2049,78 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp goto done; } - authKey = ComputeCardKeyOnToken(masterKey,authData); + + // --------------------------------- + // AC KDF SPEC CHANGE: Determine which KDF to use. + // + // Convert to unsigned types + BYTE nistSP800_108KdfOnKeyVersion_byte = static_cast<BYTE>(nistSP800_108KdfOnKeyVersion); + BYTE requestedKeyVersion_byte = static_cast<BYTE>(keyVersion[0]); + // if requested key version meets setting value, use NIST SP800-108 KDF + if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, requestedKeyVersion_byte) == true){ + + PR_fprintf(PR_STDOUT,"ComputeCryptogram NistSP800_108KDF code: Using NIST SP800-108 KDF.\n"); + + // react to "UseCUIDAsKDD" setting value + jbyte* context_jbyte = NULL; + jsize context_len_jsize = 0; + if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){ + context_jbyte = cuidValue; + context_len_jsize = cuidValue_len; + }else{ + context_jbyte = kddValue; + context_len_jsize = kddValue_len; + } + + // Converting this way is safe since jbyte is guaranteed to be 8 bits + // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical), + // but it looks like this assumption is also made in GetDiversificationData + const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte); + + // Convert jsize to size_t + const size_t context_len = static_cast<size_t>(context_len_jsize); + if (context_len > 0x000000FF){ // sanity check (CUID should never be larger than 255 bytes) + PR_fprintf(PR_STDERR, "ComputeCryptogram NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n"); + goto done; + } + + // call NIST SP800-108 KDF routine + try{ + NistSP800_108KDF::ComputeCardKeys(masterKey, context, context_len, &authKey, &macKey, &kekKey); + }catch(std::runtime_error& ex){ + PR_fprintf(PR_STDERR, "ComputeCryptogram NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: "); + PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what()); + goto done; + }catch(...){ + PR_fprintf(PR_STDERR, "ComputeCryptogram NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n"); + goto done; + } + + // if not a key version where we use the NIST SP800-108 KDF, use the original KDF + }else{ + + PR_fprintf(PR_STDOUT,"ComputeCryptogram NistSP800_108KDF code: Using original KDF.\n"); + + // AC: KDF SPEC CHANGE: Moved this call down from the original location. + // (We don't always need to call it anymore; it depends on the KDF we're going to use.) + // + // Note the change from "cuidValue" to "kddValue". + // This change is necessary due to the semantics change in the parameters passed between TPS and TKS. + GetDiversificationData(kddValue,authData,enc); + + // AC: Derives the mac key for the token. + authKey = ComputeCardKeyOnToken(masterKey,authData); + + } // endif use original KDF + // --------------------------------- + + if (authKey == NULL) { goto done; } + // AC: This computes the GP session key using the card-specific ENC key we previously derived. symkey = DeriveKey(authKey, Buffer((BYTE*)hc, hc_len), Buffer((BYTE*)cc, cc_len)); @@ -1678,6 +2133,10 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Comp handleBytes = (env)->GetByteArrayElements(handleBA, NULL); if( handleBytes ) { memcpy(handleBytes, session_key, EIGHT_BYTES); + + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + // Set flag that we've successfully copied. + error_computing_result = false; } done: @@ -1697,10 +2156,20 @@ done: authSymKey = NULL; } + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF, we build all 3 keys despite only using one of them (Enc/Auth) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + if( macKey ) { + PK11_FreeSymKey(macKey); + macKey = NULL; + } if( authKey) { PK11_FreeSymKey( authKey); authKey = NULL; } + if ( kekKey ) { + PK11_FreeSymKey(kekKey); + kekKey = NULL; + } if( masterKey) { PK11_FreeSymKey( masterKey); @@ -1732,7 +2201,18 @@ done: (env)->ReleaseByteArrayElements(CUID, cuidValue, JNI_ABORT); } - return handleBA; + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + if ( kddValue != NULL){ + env->ReleaseByteArrayElements(KDD, kddValue, JNI_ABORT); + kddValue = NULL; + } + + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + if (error_computing_result == false){ + return handleBA; + }else{ + return NULL; + } } diff --git a/base/symkey/src/com/netscape/symkey/SessionKey.java b/base/symkey/src/com/netscape/symkey/SessionKey.java index 56782aad9..d44cc5479 100644 --- a/base/symkey/src/com/netscape/symkey/SessionKey.java +++ b/base/symkey/src/com/netscape/symkey/SessionKey.java @@ -79,7 +79,10 @@ public class SessionKey { byte[] card_challenge, byte[] host_challenge, byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE byte[] CUID, + byte[] KDD, // AC: KDF SPEC CHANGE byte[] macKeyArray, String useSoftToken, String keySet, @@ -90,11 +93,15 @@ public class SessionKey { byte[] card_challenge, byte[] host_challenge, byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE byte[] CUID, + byte[] KDD, // AC: KDF SPEC CHANGE byte[] encKeyArray, String useSoftToken, String keySet); + /* AC: KDF SPEC CHANGE; unused method with no JNI implementation public static native PK11SymKey ComputeKekSessionKey(String tokenName, String keyName, byte[] card_challenge, @@ -104,13 +111,17 @@ public class SessionKey { byte[] kekKeyArray, String useSoftToken, String keySet); + */ public static native PK11SymKey ComputeKekKey(String tokenName, String keyName, byte[] card_challenge, byte[] host_challenge, byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE byte[] CUID, + byte[] KDD, // AC: KDF SPEC CHANGE byte[] kekKeyArray, String useSoftToken, String keySet); @@ -130,7 +141,10 @@ public class SessionKey { byte[] card_challenge, byte[] host_challenge, byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE byte[] CUID, + byte[] KDD, // AC: KDF SPEC CHANGE int type, byte[] authKeyArray, String useSoftToken, String keySet); @@ -139,7 +153,10 @@ public class SessionKey { String keyName, byte[] in, byte[] keyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE byte[] CUID, + byte[] KDD, // AC: KDF SPEC CHANGE byte[] kekKeyArray, String useSoftToken, String keySet); @@ -147,8 +164,14 @@ public class SessionKey { String newTokenName, String oldMasterKeyName, String newMasterKeyName, - String keyInfo, + byte[] oldKeyInfo, // AC: KDF SPEC CHANGE + // AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be jbyteArray's + // -- Changed parameter "jstring keyInfo" to "jbyteArray newKeyInfo" + byte[] newKeyInfo, + byte nistSP800_108KdfOnKeyVersion, // AC: KDF SPEC CHANGE + boolean nistSP800_108KdfUseCuidAsKdd, // AC: KDF SPEC CHANGE byte[] CUIDValue, + byte[] KDD, // AC: KDF SPEC CHANGE byte[] kekKeyArray, String useSoftToken, String keySet); diff --git a/base/symkey/src/com/netscape/symkey/SymKey.cpp b/base/symkey/src/com/netscape/symkey/SymKey.cpp index 758156677..6e206db54 100644 --- a/base/symkey/src/com/netscape/symkey/SymKey.cpp +++ b/base/symkey/src/com/netscape/symkey/SymKey.cpp @@ -69,6 +69,9 @@ extern "C" #include "Buffer.h" #include "SymKey.h" +// AC: KDF SPEC CHANGE: Include headers for NIST SP800-108 KDF functions. +#include "NistSP800_108KDF.h" + typedef unsigned char BYTE; typedef struct @@ -152,15 +155,17 @@ PK11SymKey * ReturnSymKey( PK11SlotInfo *slot, char *keyname) firstSymKey = PK11_ListFixedKeysInSlot( slot , NULL, ( void *) &pwdata ); /* scan through the symmetric key list for a key matching our nickname */ sk = firstSymKey; - while( sk != NULL ) + // AC: Stop iteration if we've found the key + while(( sk != NULL ) && (foundSymKey == NULL)) { /* get the nickname of this symkey */ name = PK11_GetSymKeyNickname( sk ); /* if the name matches, make a 'copy' of it */ - if ( name != NULL && !strcmp( keyname, name )) + // AC BUGFIX: Don't leak key name string memory if name isn't equal to keyname + if ( name != NULL ) { - if (foundSymKey == NULL) + if ((foundSymKey == NULL) && (strcmp( keyname, name ) == 0)) { foundSymKey = PK11_ReferenceSymKey(sk); } @@ -659,6 +664,8 @@ PRStatus EncryptData(const Buffer &kek_key,PK11SymKey *cardKey, Buffer &input, B #ifdef DES2_WORKAROUND unsigned char masterKeyData[DES3_LENGTH]; #else +// AC: Prevent broken code from compiling. +#error "This code will not work unless DES2_WORKAROUND is defined!!! (memcpy below writes beyond array bounds)" unsigned char masterKeyData[KEYLENGTH]; #endif unsigned char result[EIGHT_BYTES]; @@ -987,20 +994,22 @@ void GetDiversificationData(jbyte *cuidValue,BYTE *KDC,keyType keytype) } -static int getMasterKeyVersion(char *newMasterKeyNameChars) -{ - if( newMasterKeyNameChars == NULL || - strlen( newMasterKeyNameChars) < 3) { - return 0; - } - - char masterKeyVersionNumber[3]; - masterKeyVersionNumber[0]=newMasterKeyNameChars[1]; - masterKeyVersionNumber[1]=newMasterKeyNameChars[2]; - masterKeyVersionNumber[2]=0; - int newMasterKeyVesion = atoi(masterKeyVersionNumber); - return newMasterKeyVesion; -} +// AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! +// (atoi doesn't do the same thing as specialDecode does; since we're decoding on the Java side, this function is unnecessary) +//static int getMasterKeyVersion(char *newMasterKeyNameChars) +//{ +// if( newMasterKeyNameChars == NULL || +// strlen( newMasterKeyNameChars) < 3) { +// return 0; +// } +// +// char masterKeyVersionNumber[3]; +// masterKeyVersionNumber[0]=newMasterKeyNameChars[1]; +// masterKeyVersionNumber[1]=newMasterKeyNameChars[2]; +// masterKeyVersionNumber[2]=0; +// int newMasterKeyVesion = atoi(masterKeyVersionNumber); +// return newMasterKeyVesion; +//} char *GetSharedSecretKeyName(char *newKeyName) { if ( newKeyName && strlen( newKeyName ) > 0 ) { @@ -1030,10 +1039,16 @@ void getFullName(char * fullMasterKeyName, char * masterKeyNameChars ) * Method: DiversifyKey * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[B)[B */ +// AC: KDF SPEC CHANGE: function signature change - added jstring oldKeyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD +// AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be jbyteArray's +// -- Changed parameter "jstring keyInfo" to "jbyteArray newKeyInfo" extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_DiversifyKey -(JNIEnv *, jclass, jstring, jstring, jstring, jstring, jstring, jbyteArray, jbyteArray, jstring, jstring); +(JNIEnv *, jclass, jstring, jstring, jstring, jstring, jbyteArray, jbyteArray, jbyte, jboolean, jbyteArray, jbyteArray, jbyteArray, jstring, jstring); -extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_DiversifyKey( JNIEnv * env, jclass this2, jstring tokenName,jstring newTokenName, jstring oldMasterKeyName, jstring newMasterKeyName, jstring keyInfo, jbyteArray CUIDValue, jbyteArray kekKeyArray, jstring useSoftToken_s, jstring keySet) +// AC: KDF SPEC CHANGE: function signature change - added jstring oldKeyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, and jbyteArray KDD +// AC: BUGFIX for key versions higher than 09: We need to specialDecode keyInfo parameters before sending them into symkey! This means the parameters must be jbyteArray's +// -- Changed parameter "jstring keyInfo" to "jbyteArray newKeyInfo" +extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_DiversifyKey( JNIEnv * env, jclass this2, jstring tokenName,jstring newTokenName, jstring oldMasterKeyName, jstring newMasterKeyName, jbyteArray oldKeyInfo, jbyteArray newKeyInfo, jbyte nistSP800_108KdfOnKeyVersion, jboolean nistSP800_108KdfUseCuidAsKdd, jbyteArray CUIDValue, jbyteArray KDD, jbyteArray kekKeyArray, jstring useSoftToken_s, jstring keySet) { PK11SymKey *encKey = NULL; PK11SymKey *macKey = NULL; @@ -1047,7 +1062,13 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive char fullMasterKeyName[KEYNAMELENGTH]; char fullNewMasterKeyName[KEYNAMELENGTH]; PRBool specified_key_is_present = PR_TRUE; - PK11SymKey *old_kek_sym_key = NULL; + + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF being used for old key version, we build all 3 old keys despite only using one of them (Kek) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + // KDF output keys + PK11SymKey* old_mac_sym_key = NULL; + PK11SymKey* old_enc_sym_key = NULL; + PK11SymKey* old_kek_sym_key = NULL; char *keySetStringChars = NULL; if ( keySet != NULL ) { @@ -1062,7 +1083,19 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive jbyteArray handleBA=NULL; jbyte *handleBytes=NULL; - int newMasterKeyVesion = 1; + + + // AC: BUGFIX for key versions higher than 09 + // No longer need this variable (it's misspelled anyway) and it's the wrong type. + // int newMasterKeyVesion = 1; + + // AC: BUGFIX for key versions higher than 09 + // New variables used for JNI retrieval. + jbyte* oldKeyInfo_jbyteptr = NULL; + jbyte* newKeyInfo_jbyteptr = NULL; + jsize oldKeyInfo_jbyteptr_len = -1; + jsize newKeyInfo_jbyteptr_len = -1; + /* find slot */ char *tokenNameChars = NULL; @@ -1075,7 +1108,19 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive char * newTokenNameChars = NULL; char *keyInfoChars = NULL; - jbyte * cuidValue = NULL; + // AC: KDF SPEC CHANGE: Need to retrieve old key info from JNI. + char* oldKeyInfoChars = NULL; + + // AC: KDF SPEC CHANGE: Convert new setting value to BYTE (unsigned). + BYTE nistSP800_108KdfOnKeyVersion_byte = static_cast<BYTE>(nistSP800_108KdfOnKeyVersion); + + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + jbyte* cuidValue = NULL; + jsize cuidValue_len = -1; + jbyte* kddValue = NULL; + jsize kddValue_len = -1; + jbyte * old_kek_key = NULL; PK11SymKey * masterKey = NULL; @@ -1085,13 +1130,37 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive BYTE KDCmac[KEYLENGTH]; BYTE KDCkek[KEYLENGTH]; - if( CUIDValue != NULL) { - cuidValue = (jbyte*)(env)->GetByteArrayElements( CUIDValue, NULL); - } + // AC: BUGFIX for key versions higher than 09: New code to retrieve oldKeyInfo and newKeyInfo byte arrays from JNI. + BYTE oldKeyVersion; + BYTE newKeyVersion; + + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + bool error_computing_result = true; + + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + // Also added "len" variable for CUID (for sanity check). + if ( CUIDValue != NULL ) { + cuidValue = (jbyte*)(env)->GetByteArrayElements( CUIDValue, NULL); + cuidValue_len = env->GetArrayLength(CUIDValue); + } if( cuidValue == NULL) { goto done; } + if ( cuidValue_len <= 0){ // check that CUID is at least 1 byte in length + goto done; + } + if ( KDD != NULL ){ + kddValue = env->GetByteArrayElements(KDD, NULL); + kddValue_len = env->GetArrayLength(KDD); + } + if ( kddValue == NULL ){ + goto done; + } + if ( kddValue_len != static_cast<jsize>(NistSP800_108KDF::KDD_SIZE_BYTES) ){ // check that KDD is expected size + goto done; + } + if( kekKeyArray != NULL) { old_kek_key = (jbyte*)(env)->GetByteArrayElements(kekKeyArray, NULL); @@ -1103,9 +1172,12 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive PR_fprintf(PR_STDOUT,"In SessionKey.DiversifyKey! \n"); - GetDiversificationData(cuidValue,KDCenc,enc); - GetDiversificationData(cuidValue,KDCmac,mac); - GetDiversificationData(cuidValue,KDCkek,kek); + // AC: KDF SPEC CHANGE: + // Changed from "cuidValue" to "kddValue". + // This change is necessary due to the semantics change in the parameters passed between TPS and TKS. + GetDiversificationData(kddValue,KDCenc,enc); + GetDiversificationData(kddValue,KDCmac,mac); + GetDiversificationData(kddValue,KDCkek,kek); if(tokenName) { @@ -1142,20 +1214,56 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive (env)->ReleaseStringUTFChars(newMasterKeyName, (const char *)newMasterKeyNameChars); } - /* packing return */ - if( keyInfo != NULL) { - keyInfoChars = (char *)(env)->GetStringUTFChars(keyInfo, NULL); - } - newMasterKeyVesion = getMasterKeyVersion(keyInfoChars); - if(keyInfoChars) - { - (env)->ReleaseStringUTFChars(keyInfo, (const char *)keyInfoChars); + // AC: BUGFIX for key versions higher than 09: Since "jstring keyInfo" is now passed in as "jbyteArray newKeyInfo", we no longer need this code. + // + ///* packing return */ + //if( keyInfo != NULL) { + // keyInfoChars = (char *)(env)->GetStringUTFChars(keyInfo, NULL); + //} + // + //newMasterKeyVesion = getMasterKeyVersion(keyInfoChars); + // + //if(keyInfoChars) + //{ + // (env)->ReleaseStringUTFChars(keyInfo, (const char *)keyInfoChars); + //} + // + ///* NEW MASTER KEY VERSION */ + //newMasterKeyBuffer = Buffer((unsigned int) 1, (BYTE)newMasterKeyVesion); + + + + // AC: BUGFIX for key versions higher than 09: New code to retrieve oldKeyInfo and newKeyInfo byte arrays from JNI. + if (oldKeyInfo != NULL){ + oldKeyInfo_jbyteptr = env->GetByteArrayElements(oldKeyInfo, NULL); + oldKeyInfo_jbyteptr_len = env->GetArrayLength(oldKeyInfo); + } + if(oldKeyInfo_jbyteptr == NULL){ + goto done; + } + if (oldKeyInfo_jbyteptr_len != 2){ + goto done; } + if (newKeyInfo != NULL){ + newKeyInfo_jbyteptr = env->GetByteArrayElements(newKeyInfo, NULL); + newKeyInfo_jbyteptr_len = env->GetArrayLength(newKeyInfo); + } + if(newKeyInfo_jbyteptr == NULL){ + goto done; + } + if (newKeyInfo_jbyteptr_len != 2){ + goto done; + } + // now get the key versions from the byte arrays we got from JNI + oldKeyVersion = oldKeyInfo_jbyteptr[0]; + newKeyVersion = newKeyInfo_jbyteptr[0]; + // for compatibility with old code: wrap newKeyVersion inside Buffer object + newMasterKeyBuffer = Buffer((unsigned int) 1, newKeyVersion); + + - /* NEW MASTER KEY VERSION */ - newMasterKeyBuffer = Buffer((unsigned int) 1, (BYTE)newMasterKeyVesion); if(oldMasterKeyName) { oldMasterKeyNameChars = (char *)(env)->GetStringUTFChars(oldMasterKeyName, NULL); @@ -1169,24 +1277,108 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive if(strcmp( oldMasterKeyNameChars, "#01#01") == 0 || strcmp( oldMasterKeyNameChars, "#FF#01") == 0) { old_kek_key_buff = Buffer((BYTE*)old_kek_key, KEYLENGTH); - }else if(strcmp( oldMasterKeyNameChars, "#00#00") == 0) - { - /* print Debug message - do not create real keysetdata */ - old_kek_key_buff = Buffer((BYTE*)"#00#00", 6); - output = Buffer((BYTE*)old_kek_key, KEYLENGTH); + + + // AC: BUGFIX: Remove garbage code. + // I believe that this code is a no-op as long as the system is working correctly + // (with the developer keyset data populated in the config file & copied to HSM). + // + // Notes: + // "old_kek_key_buff" appears to only be used if unable to read/load the developer keys into HSM. + // "old_kek_key_buff" is populated with incorrect data (not appropriate key-length) + // "output" is overwritten when "CreateKeySetDataWithSymKeys" is called + // + // As a result, only when there is some failure (i.e. we execute a "goto" and skip assignment + // to "output") do we return a keyset data that is 16 bytes in length (the default KEK). + // This is unlikely to work and even if it does, is a horrible idea as the caller has no way + // of knowing that we've now essentially inserted a "backdoor" on the token. So, instead of + // this, we treat #00#00 just like any other "normal" case. + // + //}else if(strcmp( oldMasterKeyNameChars, "#00#00") == 0) + //{ + // /* print Debug message - do not create real keysetdata */ + // old_kek_key_buff = Buffer((BYTE*)"#00#00", 6); + // output = Buffer((BYTE*)old_kek_key, KEYLENGTH); + + } else { oldMasterKey = ReturnSymKey(slot,fullMasterKeyName); - old_kek_sym_key = ComputeCardKeyOnToken(oldMasterKey,KDCkek); - if (oldMasterKey) { - PK11_FreeSymKey( oldMasterKey ); - oldMasterKey = NULL; + + + // AC: BUGFIX: Check for nonexistent master key instead of (potentially) crashing. + if (oldMasterKey == NULL){ + goto done; } + + + // --------------------------------- + // AC KDF SPEC CHANGE: Determine which KDF to use. + // + // if old key version meets setting value, use NIST SP800-108 KDF for deriving old keys + if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, oldKeyVersion) == true){ + + PR_fprintf(PR_STDOUT,"DiversifyKey old key NistSP800_108KDF code: Using NIST SP800-108 KDF for old keyset.\n"); + + // react to "UseCUIDAsKDD" setting value + jbyte* context_jbyte = NULL; + jsize context_len_jsize = 0; + if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){ + context_jbyte = cuidValue; + context_len_jsize = cuidValue_len; + }else{ + context_jbyte = kddValue; + context_len_jsize = kddValue_len; + } + + // Converting this way is safe since jbyte is guaranteed to be 8 bits + // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical), + // but it looks like this assumption is also made in GetDiversificationData + const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte); + + // Convert jsize to size_t + const size_t context_len = static_cast<size_t>(context_len_jsize); + if (context_len > 0x000000FF){ // sanity check (CUID should never be larger than 255 bytes) + PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n"); + goto done; + } + + // call NIST SP800-108 KDF routine + try{ + NistSP800_108KDF::ComputeCardKeys(oldMasterKey, context, context_len, &old_enc_sym_key, &old_mac_sym_key, &old_kek_sym_key); + }catch(std::runtime_error& ex){ + PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: "); + PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what()); + goto done; + }catch(...){ + PR_fprintf(PR_STDERR, "DiversifyKey old key NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n"); + goto done; + } + + // if not a key version where we use the NIST SP800-108 KDF, use the original KDF + }else{ + + PR_fprintf(PR_STDOUT,"DiversifyKey old key NistSP800_108KDF code: Using original KDF for old keyset.\n"); + + // AC: Derives the kek key for the token. + old_kek_sym_key = ComputeCardKeyOnToken(oldMasterKey,KDCkek); + + } // endif use original KDF + // --------------------------------- + + + // AC KDF SPEC CHANGE: Moved this code down so we don't skip it during "goto done". + //if (oldMasterKey) { + // PK11_FreeSymKey( oldMasterKey ); + // oldMasterKey = NULL; + //} } - if(oldMasterKeyNameChars) { - (env)->ReleaseStringUTFChars(oldMasterKeyName, (const char *)oldMasterKeyNameChars); - } + + // AC KDF SPEC CHANGE: Moved this code down so we don't skip it during "goto done". + //if(oldMasterKeyNameChars) { + // (env)->ReleaseStringUTFChars(oldMasterKeyName, (const char *)oldMasterKeyNameChars); + //} /* special case #01#01 */ if (fullNewMasterKeyName != NULL && strcmp(fullNewMasterKeyName, "#01#01") == 0) @@ -1213,10 +1405,65 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive } else { PR_fprintf(PR_STDOUT,"DiversifyKey: Compute card key on token case ! \n"); - /* compute card key */ - encKey = ComputeCardKeyOnSoftToken(masterKey, KDCenc); - macKey = ComputeCardKeyOnSoftToken(masterKey, KDCmac); - kekKey = ComputeCardKeyOnSoftToken(masterKey, KDCkek); + + + // --------------------------------- + // AC KDF SPEC CHANGE: Determine which KDF to use. + // + // if old key version meets setting value, use NIST SP800-108 KDF for deriving new keys + if (NistSP800_108KDF::useNistSP800_108KDF(nistSP800_108KdfOnKeyVersion_byte, newKeyVersion) == true){ + + PR_fprintf(PR_STDOUT,"DiversifyKey new key NistSP800_108KDF code: Using NIST SP800-108 KDF for new keyset.\n"); + + // react to "UseCUIDAsKDD" setting value + jbyte* context_jbyte = NULL; + jsize context_len_jsize = 0; + if (nistSP800_108KdfUseCuidAsKdd == JNI_TRUE){ + context_jbyte = cuidValue; + context_len_jsize = cuidValue_len; + }else{ + context_jbyte = kddValue; + context_len_jsize = kddValue_len; + } + + // Converting this way is safe since jbyte is guaranteed to be 8 bits + // Of course, this assumes that "char" is 8 bits (not guaranteed, but typical), + // but it looks like this assumption is also made in GetDiversificationData + const BYTE* const context = reinterpret_cast<const BYTE*>(context_jbyte); + + // Convert jsize to size_t + const size_t context_len = static_cast<size_t>(context_len_jsize); + if (context_len > 0x000000FF){ // sanity check (CUID should never be larger than 255 bytes) + PR_fprintf(PR_STDERR, "DiversifyKey new key NistSP800_108KDF code: Error; context_len larger than 255 bytes.\n"); + goto done; + } + + // call NIST SP800-108 KDF routine + try{ + NistSP800_108KDF::ComputeCardKeys(masterKey, context, context_len, &encKey, &macKey, &kekKey); + }catch(std::runtime_error& ex){ + PR_fprintf(PR_STDERR, "DiversifyKey new key NistSP800_108KDF code: Exception invoking NistSP800_108KDF::ComputeCardKeys: "); + PR_fprintf(PR_STDERR, "%s\n", ex.what() == NULL ? "null" : ex.what()); + goto done; + }catch(...){ + PR_fprintf(PR_STDERR, "DiversifyKey new key NistSP800_108KDF code: Unknown exception invoking NistSP800_108KDF::ComputeCardKeys.\n"); + goto done; + } + + // if not a key version where we use the NIST SP800-108 KDF, use the original KDF + }else{ + + PR_fprintf(PR_STDOUT,"DiversifyKey new key NistSP800_108KDF code: Using original KDF for new keyset.\n"); + + // AC: Derives the kek key for the token. + /* compute card key */ + encKey = ComputeCardKeyOnSoftToken(masterKey, KDCenc); + macKey = ComputeCardKeyOnSoftToken(masterKey, KDCmac); + kekKey = ComputeCardKeyOnSoftToken(masterKey, KDCkek); + + } // endif use original KDF + // --------------------------------- + /* Fixes Bugscape Bug #55855: TKS crashes if specified key * is not present -- for each portion of the key, check if @@ -1257,6 +1504,17 @@ extern "C" JNIEXPORT jbyteArray JNICALL Java_com_netscape_symkey_SessionKey_Dive } done: + + // AC: BUGFIX for key versions higher than 09: Release oldKeyInfo and newKeyInfo JNI byte arrays. + if ( oldKeyInfo_jbyteptr != NULL){ + env->ReleaseByteArrayElements(oldKeyInfo, oldKeyInfo_jbyteptr, JNI_ABORT); + oldKeyInfo_jbyteptr = NULL; + } + if ( newKeyInfo_jbyteptr != NULL){ + env->ReleaseByteArrayElements(newKeyInfo, newKeyInfo_jbyteptr, JNI_ABORT); + newKeyInfo_jbyteptr = NULL; + } + if (masterKey != NULL) { PK11_FreeSymKey( masterKey); masterKey = NULL; @@ -1277,6 +1535,32 @@ done: kekKey = NULL; } + // AC: KDF SPEC CHANGE: For the NIST SP800-108 KDF being used for old key version, we build all 3 old keys despite only using one of them (Kek) in this function. + // We do this because our NIST SP800-108 KDF outputs the data for all three keys simultaneously. + // AC: BUGFIX: Note that there was previously no PK11_FreeSymKey(old_kek_sym_key) call. This most likely resulted in a memory / keyhandle leak. + if( old_mac_sym_key ) { + PK11_FreeSymKey(old_mac_sym_key); + old_mac_sym_key = NULL; + } + if ( old_enc_sym_key ) { + PK11_FreeSymKey(old_enc_sym_key); + old_enc_sym_key = NULL; + } + if ( old_kek_sym_key ) { + PK11_FreeSymKey(old_kek_sym_key); + old_kek_sym_key = NULL; + } + + // AC KDF SPEC CHANGE: Moved this code down so we don't skip it during "goto done". + if (oldMasterKey) { + PK11_FreeSymKey( oldMasterKey ); + oldMasterKey = NULL; + } + if(oldMasterKeyNameChars) { + (env)->ReleaseStringUTFChars(oldMasterKeyName, (const char *)oldMasterKeyNameChars); + oldMasterKeyNameChars = NULL; + } + if( keySetStringChars ) { (env)->ReleaseStringUTFChars(keySet, (const char *)keySetStringChars); keySetStringChars = NULL; @@ -1286,10 +1570,21 @@ done: { if(output.size()>0) handleBA = (env)->NewByteArray( output.size()); - else - handleBA = (env)->NewByteArray(1); - handleBytes = (env)->GetByteArrayElements(handleBA, NULL); - memcpy(handleBytes, (BYTE*)output,output.size()); + + // AC: Bugfix: Return NULL if no output is present. + //else + // handleBA = (env)->NewByteArray(1); + + // AC: Bugfix: Don't crash if we couldn't allocate array. + if (handleBA != NULL){ + handleBytes = (env)->GetByteArrayElements(handleBA, NULL); + + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + if (handleBytes != NULL){ + memcpy(handleBytes, (BYTE*)output,output.size()); + error_computing_result = false; + } + } if( handleBytes != NULL) { (env)->ReleaseByteArrayElements( handleBA, handleBytes, 0); @@ -1300,6 +1595,12 @@ done: (env)->ReleaseByteArrayElements(CUIDValue, cuidValue, JNI_ABORT); } + // AC: KDF SPEC CHANGE: Need to retrieve KDD as well as CUID from JNI. + if ( kddValue != NULL){ + env->ReleaseByteArrayElements(KDD, kddValue, JNI_ABORT); + kddValue = NULL; + } + if( kekKeyArray != NULL) { (env)->ReleaseByteArrayElements(kekKeyArray, old_kek_key, JNI_ABORT); } @@ -1319,7 +1620,12 @@ done: internal = NULL; } - return handleBA; + // AC: BUGFIX: Don't return a java array with uninitialized or zero'd data. + if (error_computing_result == false){ + return handleBA; + }else{ + return NULL; + } } PK11SymKey *CreateUnWrappedSymKeyOnToken( PK11SlotInfo *slot, PK11SymKey * unWrappingKey, BYTE *keyToBeUnWrapped, int sizeOfKeyToBeUnWrapped, PRBool isPerm) diff --git a/base/tks/shared/conf/CS.cfg.in b/base/tks/shared/conf/CS.cfg.in index be8213788..2f7606f44 100644 --- a/base/tks/shared/conf/CS.cfg.in +++ b/base/tks/shared/conf/CS.cfg.in @@ -302,6 +302,8 @@ selftests.plugin.TKSKnownSessionKey.TksSubId=tks selftests.plugin.TKSKnownSessionKey.cardChallenge=#bd#6d#19#85#6e#54#0f#cd selftests.plugin.TKSKnownSessionKey.hostChallenge=#77#57#62#e4#5e#23#66#7d selftests.plugin.TKSKnownSessionKey.keyName=#01#01 +selftests.plugin.TKSKnownSessionKey.nistSP800-108KdfOnKeyVersion=FF +selftests.plugin.TKSKnownSessionKey.nistSP800-108KdfUseCuidAsKdd=false selftests.plugin.TKSKnownSessionKey.macKey=#40#41#42#43#44#45#46#47#48#49#4a#4b#4c#4d#4e#4f selftests.plugin.TKSKnownSessionKey.sessionKey=#d1#be#b8#26#dc#56#20#25#8c#93#e7#de#f0#ab#4f#5b selftests.plugin.TKSKnownSessionKey.token=Internal Key Storage Token @@ -339,6 +341,8 @@ tks.defKeySet._004=## tks.defKeySet.auth_key=#40#41#42#43#44#45#46#47#48#49#4a#4b#4c#4d#4e#4f tks.defKeySet.mac_key=#40#41#42#43#44#45#46#47#48#49#4a#4b#4c#4d#4e#4f tks.defKeySet.kek_key=#40#41#42#43#44#45#46#47#48#49#4a#4b#4c#4d#4e#4f +tks.defKeySet.nistSP800-108KdfOnKeyVersion=00 +tks.defKeySet.nistSP800-108KdfUseCuidAsKdd=false tks.jForte._000=## tks.jForte._001=## SAFLink's jForte default key set: tks.jForte._002=## @@ -347,6 +351,8 @@ tks.jForte._004=## tks.jForte.auth_key=#30#31#32#33#34#35#36#37#38#39#3a#3b#3c#3d#3e#3f tks.jForte.mac_key=#40#41#42#43#44#45#46#47#48#49#4a#4b#4c#4d#4e#4f tks.jForte.kek_key=#50#51#52#53#54#55#56#57#58#59#5a#5b#5c#5d#5e#5f +tks.jForte.nistSP800-108KdfOnKeyVersion=00 +tks.jForte.nistSP800-108KdfUseCuidAsKdd=false multiroles._000=## multiroles._001=## multiroles multiroles._002=## |