root/src/system/libroot/posix/crypt/pbkdf2.cpp
/* This file is distributed under the following terms:

 * Copyright 2005-2014 Colin Percival.  All rights reserved.
 * Copyright 2014 Sean Kelly.  All rights reserved.

 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <ByteOrder.h>

#include "pbkdf2.h"

/* Function which does the zeroing. */
static void
insecure_memzero_func(volatile void * buf, size_t len)
{
        volatile uint8_t * _buf = (volatile uint8_t *)buf;
        size_t i;

        for (i = 0; i < len; i++)
                _buf[i] = 0;
}

/* Pointer to memory-zeroing function. */
void (* volatile insecure_memzero_ptr)(volatile void *, size_t) =
    insecure_memzero_func;

/**
 * HMAC_SHA256_Init(ctx, K, Klen):
 * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from
 * ${K}.
 */
void
HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen)
{
        uint8_t pad[64];
        uint8_t khash[32];
        const uint8_t * K = (const uint8_t *)_K;
        size_t i;

        /* If Klen > 64, the key is really SHA256(K). */
        if (Klen > 64) {
                ctx->ictx.Init();
                ctx->ictx.Update(K, Klen);
                memcpy(khash, ctx->ictx.Digest(), 32);
                K = khash;
                Klen = 32;
        }

        /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */
        ctx->ictx.Init();
        memset(pad, 0x36, 64);
        for (i = 0; i < Klen; i++)
                pad[i] ^= K[i];
        ctx->ictx.Update(pad, 64);

        /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */
        ctx->octx.Init();
        memset(pad, 0x5c, 64);
        for (i = 0; i < Klen; i++)
                pad[i] ^= K[i];
        ctx->octx.Update(pad, 64);

        /* Clean the stack. */
        insecure_memzero(khash, 32);
        insecure_memzero(pad, 64);
}

/**
 * HMAC_SHA256_Update(ctx, in, len):
 * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}.
 */
void
HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len)
{

        /* Feed data to the inner SHA256 operation. */
        ctx->ictx.Update(in, len);
}

/**
 * HMAC_SHA256_Final(digest, ctx):
 * Output the HMAC-SHA256 of the data input to the context ${ctx} into the
 * buffer ${digest}.
 */
void
HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx)
{
        uint8_t ihash[32];

        /* Finish the inner SHA256 operation. */
        memcpy(ihash, ctx->ictx.Digest(), 32);

        /* Feed the inner hash to the outer SHA256 operation. */
        ctx->octx.Update(ihash, 32);

        /* Finish the outer SHA256 operation. */
        memcpy(digest, ctx->octx.Digest(), 32);

        /* Clean the stack. */
        insecure_memzero(ihash, 32);
}

/**
 * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
 * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
 * write the output to buf.  The value dkLen must be at most 32 * (2^32 - 1).
 */
void
PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt,
    size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen)
{
        HMAC_SHA256_CTX PShctx, hctx;
        size_t i;
        uint32_t ivec;
        uint8_t U[32];
        uint8_t T[32];
        uint64_t j;
        int k;
        size_t clen;

        /* Sanity-check. */
        assert(dkLen <= 32 * (size_t)(UINT32_MAX));

        /* Compute HMAC state after processing P and S. */
        HMAC_SHA256_Init(&PShctx, passwd, passwdlen);
        HMAC_SHA256_Update(&PShctx, salt, saltlen);

        /* Iterate through the blocks. */
        for (i = 0; i * 32 < dkLen; i++) {
                /* Generate INT(i + 1). */
                ivec = B_HOST_TO_BENDIAN_INT32((uint32_t)(i + 1));

                /* Compute U_1 = PRF(P, S || INT(i)). */
                memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX));
                HMAC_SHA256_Update(&hctx, &ivec, 4);
                HMAC_SHA256_Final(U, &hctx);

                /* T_i = U_1 ... */
                memcpy(T, U, 32);

                for (j = 2; j <= c; j++) {
                        /* Compute U_j. */
                        HMAC_SHA256_Init(&hctx, passwd, passwdlen);
                        HMAC_SHA256_Update(&hctx, U, 32);
                        HMAC_SHA256_Final(U, &hctx);

                        /* ... xor U_j ... */
                        for (k = 0; k < 32; k++)
                                T[k] ^= U[k];
                }

                /* Copy as many bytes as necessary into buf. */
                clen = dkLen - i * 32;
                if (clen > 32)
                        clen = 32;
                memcpy(&buf[i * 32], T, clen);
        }

        /* Clean PShctx, since we never called _Final on it. */
        insecure_memzero(&PShctx, sizeof(HMAC_SHA256_CTX));
}