freeotfekdfpbkdf2.c

来自「文件驱动加密,功能强大,可产生加密分区,支持AES,MD2,MD4,MD5MD2」· C语言 代码 · 共 421 行

C
421
字号
// Description: 
// By Sarah Dean
// Email: sdean12@sdean12.org
// WWW:   http://www.FreeOTFE.org/
//
// -----------------------------------------------------------------------------
//


#include "FreeOTFEKDFPBKDF2.h"

#include "FreeOTFEDebug.h"
#include "FreeOTFElib.h"
#include "FreeOTFEPlatform.h"
#include "FreeOTFEMACHMAC.h"


// =========================================================================
// 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
                     );
        }


    // 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;
}


// =========================================================================
// This is the PBKDF2 PRF function "F"  
// The PRF used is HMAC
NTSTATUS
PBKDF2_F(
    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,
    IN      unsigned int i,
    OUT     unsigned char* T_
)
{
    NTSTATUS status;
    unsigned char* U_;
    unsigned char* tmpU_Buffer;
    unsigned char* saltAndCount;
    unsigned int hLen;  // In *bytes*
    unsigned int hLenBits;  // In *Bits*
    unsigned int actualHLenBits;  // In *bits*
    unsigned int saltAndCountSizeBytes;  // In *bytes*
    unsigned int j, k;


    DEBUGOUTKDFDRV(DEBUGLEV_ENTER, ("PBKDF2_F\n"));


    status = STATUS_SUCCESS;

    U_ = NULL;
    tmpU_Buffer = NULL;
    saltAndCount = NULL;


    // From PKCS#5:

    // function F is defined as the exclusive-or sum of the
    // first c iterates of the underlying pseudorandom function PRF
    // applied to the password P and the concatenation of the salt S
    // and the block index i:
    //
    //           F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
    //
    // where
    //           U_1 = PRF (P, S || INT (i)) ,
    //           U_2 = PRF (P, U_1) ,
    //           ...
    //           U_c = PRF (P, U_{c-1}) .
    //
    // Here, INT (i) is a four-octet encoding of the integer i, most
    // significant octet first.


    DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Iterations requested: %d\n", c));

    // 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);
    hLenBits = HashDetails.Length;

    // Convert bits to bytes, and add 4 (specified in PKCS #5) for the "i"
    saltAndCountSizeBytes = ((SLength / 8) + 4);
    saltAndCount = FREEOTFE_MEMALLOC(saltAndCountSizeBytes);
    FREEOTFE_MEMCPY(
                    saltAndCount,
                    S,
                    (SLength / 8)
                    );
    // Concatenate the salt
    // Note: *Bitwise* AND :)
    saltAndCount[ (SLength / 8)   ] = ((i & 0xFF000000) / 0x01000000);
    saltAndCount[((SLength / 8)+1)] = ((i & 0x00FF0000) / 0x00010000);
    saltAndCount[((SLength / 8)+2)] = ((i & 0x0000FF00) / 0x00000100);
    saltAndCount[((SLength / 8)+3)] = ((i & 0x000000FF) / 0x00000001);

    U_ = FREEOTFE_MEMALLOC(hLen);
    tmpU_Buffer = FREEOTFE_MEMALLOC(hLen);

    // Process U_1
    if (NT_SUCCESS(status))
        {
        actualHLenBits = hLenBits;
        DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("First HMAC: SLength = %d\n", SLength));
        DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("First HMAC: actualHLenBits = %d\n", actualHLenBits));
        if (!(NT_SUCCESS(ImplMACHMAC(
                                    FnHash,
                                    HashGUID,
                                    HashDetails,
                                    -1,  // Retrieve full HMAC
                                    PLength,
                                    P,
                                    (saltAndCountSizeBytes * 8), // In *bits*
                                    saltAndCount,

                                    &actualHLenBits,  // In bits
                                    U_
                                ))))
            {
            DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("First call to HMAC function \"F\" failed.\n"));
            status = STATUS_INTERNAL_ERROR;
            }
        else
            {
            // Sanity check
            if (actualHLenBits != hLenBits)
                {
                DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("HMAC function didn't return expected number of bits?!\n"));
                status = STATUS_INTERNAL_ERROR;
                }
            else
                {
                // Copy to output buffer - note that this will be XORd if
                // there's more c>1
                FREEOTFE_MEMCPY(
                                T_,
                                U_,
                                (actualHLenBits / 8)
                                );
                }
            }
        }


    // Process subsequent U_n
    if (NT_SUCCESS(status))
        {
        for(j = 2; j <= c; j++)
            {
            actualHLenBits = hLenBits;
            DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Subsequent HMAC: actualHLenBits = %d\n", actualHLenBits));
            if (!(NT_SUCCESS(ImplMACHMAC(
                                        FnHash,
                                        HashGUID,
                                        HashDetails,
                                        -1,  // Retrieve full HMAC
                                        PLength,
                                        P,
                                        hLenBits,  // In bits
                                        U_,

                                        &actualHLenBits,  // In bits
                                        tmpU_Buffer
                                    ))))
                {
                DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("Call to HMAC function \"F\" failed.\n"));
                status = STATUS_INTERNAL_ERROR;
                break;
                }
            else
                {
                // Move from temp buffer to U_ buffer
                DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Copying (%d)\n", hLen));
                FREEOTFE_MEMCPY(
                            U_,
                            tmpU_Buffer,
                            hLen
                            );

                // XOR with previous iteration
                DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("XORing...\n"));
                for(k = 0; k < hLen; k++)
                    {
                    T_[k] ^= U_[k];
                    }
                DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("OK.\n"));
                }
            }
        }


    // Note: No need to set the output "T_" explicitly, as this is done during
    //       processing


    if (tmpU_Buffer != NULL)
        {
        SecZeroMemory(tmpU_Buffer, hLen);
        FREEOTFE_FREE(tmpU_Buffer);
        }
    if (U_ != NULL)
        {
        SecZeroMemory(U_, hLen);
        FREEOTFE_FREE(U_);
        }
    if (saltAndCount != NULL)
        {
        SecZeroMemory(saltAndCount, saltAndCountSizeBytes);
        FREEOTFE_FREE(saltAndCount);
        }



    DEBUGOUTKDFDRV(DEBUGLEV_EXIT, ("PBKDF2_F\n"));

    return status;
}


// =========================================================================
// =========================================================================


⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?