constantes PBKDF2

0

Recientemente encontré este código PBKDF2:

<?php
/*
 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
 * Copyright (c) 2013, Taylor Hornby
 * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTE_SIZE", 24);
define("PBKDF2_HASH_BYTE_SIZE", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" .
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTE_SIZE,
            true
        ));
}

function validate_password($password, $correct_hash)
{
    $params = explode(":", $correct_hash);
    if(count($params) < HASH_SECTIONS)
       return false;
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0;
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
    if($count <= 0 || $key_length <= 0)
        trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);

    if (function_exists("hash_pbkdf2")) {
        // The output length is in NIBBLES (4-bits) if $raw_output is false!
        if (!$raw_output) {
            $key_length = $key_length * 2;
        }
        return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
    }

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>

Sin embargo, no encontré en ningún lugar qué hacen estas constantes y cuáles son sus valores recomendados:

define("PBKDF2_SALT_BYTE_SIZE", 24);
define("PBKDF2_HASH_BYTE_SIZE", 24);
define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

¿Puedes explicarme esto por favor?

    
pregunta Vilican 21.11.2015 - 14:01
fuente

1 respuesta

2

Tomando primero el segundo bloque

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

es solo una forma detallada de especificar el formato de almacenamiento "de cadena". La función create_hash concatena el algoritmo hash, el recuento de iteraciones, la sal y el resultado pbkdf2 (hash de contraseña) en ese orden separados por dos puntos en una sola cadena para su almacenamiento. validate_password divide esa cadena almacenada en los dos puntos para obtener 4 piezas en una matriz, por lo que el algoritmo hash está en el índice 0 en la matriz y se accede mediante [HASH_ALGORITHM_INDEX] donde HASH_ALGORITHM_INDEX es 0, y el recuento de iteraciones está en el índice 1 y se accede usando [HASH_ITERATION_INDEX] donde HASH_ITERATION_INDEX es 1, etc.

El primer bloque son los parámetros de seguridad reales:

Se recomendó

PBKDF2_ITERATIONS = 1000 cuando rfc2898 se escribió en 2000, pero como el hardware de la computadora ha avanzado, ahora es menos seguro. Si puedes, debes probar valores significativamente mayores; intente al menos 100k, y un millón o más sería mejor si no afecta el rendimiento de su sistema para el inicio de sesión (y el cambio de contraseña, pero eso debería ser más raro).

PBKDF2_SALT_BYTE_SIZE = 24 es el tamaño en bytes de la sal, que debe ser lo suficientemente grande como para ser único con una probabilidad abrumadora para cada contraseña que hash; debido a la paradoja del cumpleaños, esto debería abarcar al menos el cuadrado del número de contraseñas más un margen de seguridad. 24 bytes = 192 bits es generoso; me permite pensar que cada átomo del sistema solar tiene una contraseña única en tu sistema, y no creo que ni siquiera Google planifique eso. Usted podría reducirlo a 16 o incluso 12 si lo desea, y obtener en promedio un pequeño aumento en el rendimiento, pero probablemente no valga la pena. Definitivamente, no vaya por debajo de 8 o corre el riesgo real de duplicados.

PBKDF2_HASH_BYTE_SIZE = 24 es el tamaño en bytes del hash generado. Debe ser lo suficientemente grande como para estar fuera del rango de fuerza bruta y como sal único para cada contraseña, pero no debe ser más que el tamaño de salida del hash (y por lo tanto HMAC) utilizado, porque eso incrementa diferencialmente el costo de defensa para el ataque fuera de línea (donde el tiempo constante igual no se puede imponer). Para sha256, el tamaño de salida es de 32 bytes, por lo que 24 está bien; En realidad, cualquier cosa de alrededor de 16 a 32 está bien. Si quisieras usar sha1, deberías reducir el tamaño de hash a 20 bytes, pero no hay una buena razón para usar sha1 y hacerlo probablemente atraerá críticas y quizás auditorías de que sería una pérdida de tiempo. con.

    
respondido por el dave_thompson_085 22.11.2015 - 22:10
fuente

Lea otras preguntas en las etiquetas