void CsrCryptoWpaPskHash(char *passphrase, CsrUint8 *ssid, CsrUint8 ssidLength, CsrUint8 *pmk) { CsrUint8 input[36]; CsrUint8 hash[20]; /* The SSID is concatenated with a four-octet encoding of an integer block index, most significant octet first. As the underlying HMAC-SHA1 only provides 160 bits, it is necessary to run the PBKDF2 F function twice with block indexes of 1 and 2 to construct the required 256 bits for the PMK. The block index needs to be assigned with either 1 or 2 for the two invocations of PBKDF2_F */ /* Concatenate SSID and block index of 1 for first invocation of PBKDF2_F */ CsrMemCpy( input, ssid, ssidLength ); /* Add four-octet encoding of block index, most significant octet first */ input[ssidLength + 0] = 0; input[ssidLength + 1] = 0; input[ssidLength + 2] = 0; input[ssidLength + 3] = 1; PBKDF2_F((CsrUint8 *)passphrase, CsrStrLen(passphrase), input, ssidLength + 4, hash); /* Copy 160 bits to first part of 256 bit PMK */ CsrMemCpy( pmk, hash, 20 ); /* Update block index to 2 for the second invocation of PBKDF2_F */ input[ssidLength + 3] = 2; PBKDF2_F((CsrUint8 *)passphrase, CsrStrLen(passphrase), input, ssidLength + 4, hash); /* Copy 96 bits to complete remainder of 256 bit PMK */ CsrMemCpy( pmk+20, hash, 12 ); }
/** * @brief PKCS5-PBKDF2 password-based key derivation. * * The underlying PRF used is HMAC-SHA1. * * @param[out] key pointer to the derived key * @param[in] dkey_len desired length for key in bytes * @param[in] pw pointer to passphrase to use * @param[in] pw_len length of pw in bytes * @param[in] salt salt to use * @param[in] salt_len length of salt in bytes * @param[in] c number of iterations * @return 0 on success, non-zero on error **/ int PBKDF2(unsigned char **key, size_t dkey_len, unsigned char *pw, size_t pw_len, unsigned char *salt, size_t salt_len, unsigned int c) { int i; unsigned int l = 0; // unsigned int r = 0; // @TODO appears unused unsigned char *dk = NULL; unsigned char T[SHA_DIGEST_LENGTH]; size_t h_len = SHA_DIGEST_LENGTH; size_t remaining_bytes = dkey_len; if (!key || !dkey_len || !pw || !pw_len || !salt || !salt_len || !c ) return -1; // @NOTE the spec says we should ensure dkey_len <= (2^32 - 1) * hLen // but size_t is small enough on most arch that this is not the case if ((dk = malloc(dkey_len)) == NULL) return -1; memset(dk, 0, dkey_len); memset(T, 0, sizeof(T)); // derive l, the number of SHA blocks in the derived key l = (dkey_len/h_len); if (dkey_len % h_len) { l++; } // derive remainder r, the number of bytes in the last block // r = dkey_len - ((l - 1) * h_len); // @TODO appears unused // Compute T_i for (i = 0; i < l; i++) { if (PBKDF2_F(T, pw, pw_len, salt, salt_len, c, i) != 0) goto cleanup; // Add T_i to the derived key if (remaining_bytes >= h_len) { memcpy(dk + (i * h_len), T, h_len); remaining_bytes -= h_len; } else { memcpy(dk + (i * h_len), T, remaining_bytes); remaining_bytes -= remaining_bytes; } } *key = dk; return 0; cleanup: sfree(dk, dkey_len); return -1; }
// ========================================================================= // Generate PBKDF2 key based on HMAC NTSTATUS ImplKDFPBKDF2( IN PDataHashFn FnHash, IN GUID HashGUID, IN HASH HashDetails, IN unsigned int PLength, // In bits IN unsigned char* P, IN unsigned int SLength, // In bits IN unsigned char* S, IN unsigned int c, // Iterations IN int dkLenBits, // In *bits* IN OUT unsigned int* DerivedKeyLength, // In bits OUT unsigned char* DerivedKey ) { NTSTATUS status; unsigned int i; unsigned int l; // unsigned int r; // In *bytes* unsigned int dkLen; // In *bytes* unsigned int hLen; // In *bytes* unsigned char* T_; unsigned int T_SizeBytes; // In *bytes* DEBUGOUTKDFDRV(DEBUGLEV_ENTER, ("ImplKDFPBKDF2\n")); status = STATUS_SUCCESS; T_ = NULL; T_SizeBytes = 0; // Sanity check // This should be picked up by the caller; this is belt & braces if ( (HashDetails.BlockSize <= 0) || (HashDetails.Length <= 0) ) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("PBKDF2 implementation called with a hash that has an undefined/zero length/blocksize\n")); status = STATUS_INTERNAL_ERROR; } else { // This is PBKDF2 based on HMAC - the HMAC function returns the same // number of bytes as is in the hash it uses hLen = (HashDetails.Length / 8); } if (dkLenBits < 0) { dkLenBits = DEFAULT_PBKDF2_KEY_SIZE; } dkLen = (dkLenBits / 8); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("dkLenBits = %d bits\n", dkLenBits)); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("dkLen = %d bytes\n", dkLen)); // Sanity check // This should be picked up by the caller; this is belt & braces if ((unsigned int)dkLenBits > *DerivedKeyLength) { // The output buffer can store the requested number of bits, right? // Note that dkLenBits can be -ve to indicate the full key length is to be // returned DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("Requested number of bits is larger than supplied buffer\n")); status = STATUS_BUFFER_TOO_SMALL; } // Step 1 // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and // stop. DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 1: n/a; skipped...\n")); // (Step skipped - because dkLen is an integer, it can never be more // than 2^(31-1) - far less than (2^32 - 1) * hLen) // Step 2 // 2. Let l be the number of hLen-octet blocks in the derived key, // rounding up, and let r be the number of octets in the last // block: // // l = CEIL (dkLen / hLen) , // r = dkLen - (l - 1) * hLen . // // Here, CEIL (x) is the "ceiling" function, i.e. the smallest // integer greater than, or equal to, x. if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 2: Executing...\n")); // Note: div always rounds down towards zero l = (dkLen / hLen); // Because div always rounds down towards zero, we may need to increment it // by one if (dkLen > (l * hLen)) { l++; } DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("dkLen = %d bytes\n", dkLen)); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("hLen = %d bytes\n", hLen)); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("l = %d\n", l)); // r = dkLen - ((l - 1) * hLen); // DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("r = %d bytes\n", r)); } // Step 3 // 3. For each block of the derived key apply the function F if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 3: Executing...\n")); T_SizeBytes = (hLen * l); T_ = FREEOTFE_MEMALLOC(T_SizeBytes); for(i = 1; i<=l; i++) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Loop: i = %d\n", i)); if (!(NT_SUCCESS(PBKDF2_F( FnHash, HashGUID, HashDetails, PLength, P, SLength, S, c, i, // This is correct (-1), as our loop starts // from 1 &T_[((i-1) * hLen)] )))) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("Call to PBKDF2 function \"F\" failed.\n")); status = STATUS_INTERNAL_ERROR; break; } } } // Step 4 // 4. Concatenate the blocks and extract the first dkLen octets to // produce a derived key DK: // // DK = T_1 || T_2 || ... || T_l<0..r-1> if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 4: Executing...\n")); FREEOTFE_MEMCPY( DerivedKey, T_, (dkLenBits / 8) ); } // Step 5 // 5. Output the derived key DK. if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 5: Executing...\n")); *DerivedKeyLength = dkLenBits; } // Cleanup... DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Freeing off any used buffers...\n")); if (T_ != NULL) { SecZeroMemory(T_, T_SizeBytes); FREEOTFE_FREE(T_); } DEBUGOUTKDFDRV(DEBUGLEV_EXIT, ("ImplKDFPBKDF2\n")); return status; }