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 );
}
Example #2
0
/**
 * @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;
}