summaryrefslogtreecommitdiffstats
path: root/base/symkey
diff options
context:
space:
mode:
authorChristina Fu <cfu@redhat.com>2014-12-02 14:38:08 -0800
committerChristina Fu <cfu@redhat.com>2014-12-19 11:17:34 -0800
commit4c910296a6c6c8bf74fbdace740680db2f1fecab (patch)
tree1d0d53e8f72c7f219830d387f228003074faf98a /base/symkey
parent00b1c33272900613687448ccab7809ba794679f6 (diff)
downloadpki-4c910296a6c6c8bf74fbdace740680db2f1fecab.tar.gz
pki-4c910296a6c6c8bf74fbdace740680db2f1fecab.tar.xz
pki-4c910296a6c6c8bf74fbdace740680db2f1fecab.zip
Ticket #864 866 (part 1 symkey, common) NIST SP800-108 KDF
- this patch does not include TPS side of changes: (#865 needs to be rewritten in Java)
Diffstat (limited to 'base/symkey')
-rw-r--r--base/symkey/src/com/netscape/symkey/CMakeLists.txt2
-rw-r--r--base/symkey/src/com/netscape/symkey/EncryptData.cpp140
-rw-r--r--base/symkey/src/com/netscape/symkey/NistSP800_108KDF.cpp470
-rw-r--r--base/symkey/src/com/netscape/symkey/NistSP800_108KDF.h115
-rw-r--r--base/symkey/src/com/netscape/symkey/SessionKey.cpp580
-rw-r--r--base/symkey/src/com/netscape/symkey/SessionKey.java25
-rw-r--r--base/symkey/src/com/netscape/symkey/SymKey.cpp424
7 files changed, 1633 insertions, 123 deletions
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)