Contraseñas y entropía

Contraseñas y entropía

Hoy un amigo nos ha enviado un mensaje al grupo de la Promoción para ver que opinábamos al respecto de la siguiente imagen

IMG-20200902-WA0018.jpg

y yo que la he visto, instantáneamente me he acordado del cálculo necesario para rellenar esta tabla.

Lo primero de todo se debe calcular la entropía. La entropía de una contraseña es calculo matemático que representa un indicador de como se encuentra esta contraseña sobre una población, si incluimos en nuestra contraseña mayúsculas, minúsculas, números y símbolos, la entropía de nuestra contraseña será mayor puesto que la población de caracteres usados es mucho mayor, en este caso, unos 94 (26 minúsculas, 26 mayúsculas, 10 números y 32 símbolos). La fórmula es la siguiente:

entropia.png

o lo que es lo mismo, es el logaritmo en base de 2 de la cantidad de elementos de la población por la longitud de la cadena. Si efectuamos el cálculo inverso, 2 elevado a la entropía debe ser igual a la población elevada al número de caracteres de  nuestra contraseña, que esto serían el número máximo de operaciones necesarias para intentar reventar nuestra password por fuerza bruta.

Si nuestra contraseña usa solo las minúsculas, su «pool» sería de 26 y por tanto la contraseña tendría una entropía baja y fácil de descubrir.

Si hay alguien que quiera comprobar la fuerza de su contraseña online puede hacerlo desde Kaspersky
Bien, pero lo que a mi me importa es como sabemos, con código. En la clase le he incluido una penalización si nuestra contraseña se encuentra entre las más conocidas.

En C#

    /*
************************************************************************************************************************************************
    * © JOAQUIN MARTINEZ RUS 2020
    * Archivo:         PasswordEntropy.cs
    * Descripción:     Clase que calcula la entropia y el tiempo en descifrar por fuerza bruta de de una contraseña
    * Historial:
    *                  1. Joaquin Martínez Rus - 02 sep 2020. Creación
    *
    * Comentarios:
    *
    *
    ****************************************************************/
    public class PasswordEntropy
    {
        public PasswordEntropy(string password)
        {
            Password = password;

        }

        public string Password { get; set; }
        public double Entropy => GetEntropy();
        public string EntropyToString => GetTimeByEntropy(Entropy);

        private double GetEntropy()
        {
            //Pools
            double pool = 0;

            // Numbers
            pool += Password.Any(k => char.IsDigit(k)) ? 10 : 0;
            // Lower 26
            pool += Password.Any(k => char.IsLower(k)) ? 26 : 0;
            // Upper 26
            pool += Password.Any(k => char.IsUpper(k)) ? 26 : 0;
            // Simbols 30
            pool += Password.Any(k => (char.IsSymbol(k) || char.IsPunctuation(k))) ? 32 : 0;

            if (pool == 0) return pool;
            // entropy = Lenght * log pool / log2
            var p = Math.Log(pool, 2);
            var entropy = Password.Length * Math.Log(pool, 2);

            // el uso de una contraseña famosa, indica una falta de seguridad absoluta
            // por lo que no se incluye como deducción de entropía, sino que se incluye en el
            // mismo cálculo de la entropía
            if (IsFamous(Password))
            {
                entropy = (int)Entropies.NoSecurity - 1; // Inexistencia de seguridad
            }
            return entropy;
        }

        private const double OPERATIONPERSECOND = 4*10E12;

        private bool IsFamous(string password)
        {
            var list = new List()
            {
                "1234",
                "2000",
                "12345",
                "111111",
                "123123",
                "123456",
                "654321",
                "696969",
                "1234567",
                "12345678",
                "abc123",
                "alejandra",
                "america",
                "baseball",
                "bonita",
                "daniel",
                "dragon",
                "estrella",
                "football",
                "harley",
                "iloveyou",
                "jennifer",
                "jesus",
                "jordan",
                "letmein",
                "mariposa",
                "master",
                "michael",
                "monkey",
                "mustang",
                "password",
                "pussy",
                "qwerty",
                "roberto",
                "sebastian",
                "shadow",
                "superman",
                "Tequiero"
            };
            return list.Contains(password);
        }

        public enum Entropies
        {
            Blank = 0,
            NoSecurity =10,
            TooWeak=20,
            VeryWeak=28,
            Weak=35,
            Medium=55,
            Strong=75,
            Stronger=127,
            OverTheTopStrong = 190
        }

        private string GetTimeByEntropy(double entropy)
        {
            if (entropy == 0) return string.Empty;
            string output = "";

            double value = Math.Pow(2, entropy) / OPERATIONPERSECOND;
            double ms = 0;
            double seconds = value;
            double minutes = seconds / 60;
            double hours = minutes / 60;
            double days = hours / 24;
            double months = days / 30;
            double years = months / 12;
            double centuries = years / 100;
            double millennium = centuries / 10;

            seconds = value;
            if (value == 0) return "";

            if (seconds > 0 && seconds  59 && hours > 24 && days  11 && years < 100 && centuries < 1000000)
            {
                output = "Tu contraseña puede ser descifrada en más de un millón de años!";
            }
            else
            {
                millennium = Math.Round(millennium, 1);
                output = $"Tu contraseña puede ser descifrada en {millennium}, {(millennium == 1 ? "milenio" : "milenios")}";
            }

            return output;
        }
    }

static void Main(string[] args)
        {
            var password = "x#Pr0m0cio_N";

            for (int i = 1; i <= password.Length; i++)
            {
                var pwd = new PasswordEntropy(password.Substring(0,i));
                Console.WriteLine($"Password: {pwd.Password}\n{pwd.EntropyToString}");
                Console.WriteLine("Press any key");
            }

            Console.ReadKey();
        }

el resultado iterando por la contraseña que he creado


Password: x
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#P
Tu contraseña puede ser descifrada en 0, milisegundos
Press any key
Password: x#Pr
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0m
Tu contraseña puede ser descifrada en 17 milisegundos
Press any key
Password: x#Pr0m0
Tu contraseña puede ser descifrada en 2 segundos
Press any key
Password: x#Pr0m0c
Tu contraseña puede ser descifrada en 3 minutos
Press any key
Password: x#Pr0m0ci
Tu contraseña puede ser descifrada en 4 horas
Press any key
Password: x#Pr0m0cio
Tu contraseña puede ser descifrada en 15,6 dias
Press any key
Password: x#Pr0m0cio_
Tu contraseña puede ser descifrada en 4,1 años
Press any key
Password: x#Pr0m0cio_N
Tu contraseña puede ser descifrada en 3,8 siglos
Press any key

y en Kotlin

class PasswordEntropy(val password: String) {

    init {

    }

    fun entropy(): Double {

        //Pools
        var pool = 0.0

        // Numbers
        pool += if (password.any { c -> c.isDigit() }) 10.0 else 0.0
        // Lower 26
        pool += if (password.any { c -> c.isLowerCase() }) 26.0 else 0.0
        // Upper 26
        pool += if (password.any { c -> c.isUpperCase() }) 26.0 else 0.0
        val (noSymbols, symbols) = password.partition { it.isLetter() || it.isDigit() }
        // Simbols 32
        pool += if (symbols.count()>0) 32.0 else 0.0
        if (pool == 0.0) return pool
        // entropy = Lenght * log pool / log2

        var entropy = password.length * log2(pool)

        // el uso de una contraseña famosa, indica una falta de seguridad absoluta
        // por lo que no se incluye como deducción de entropía, sino que se incluye en el
        // mismo cálculo de la entropía
        if (isFamous()) {
            entropy = 9.0 // Inexistencia de seguridad
        }
        return entropy
    }

    private fun isFamous(): Boolean{
        return listOf(
            "1234",
            "2000",
            "12345",
            "111111",
            "123123",
            "123456",
            "654321",
            "696969",
            "1234567",
            "12345678",
            "abc123",
            "alejandra",
            "america",
            "baseball",
            "bonita",
            "daniel",
            "dragon",
            "estrella",
            "football",
            "harley",
            "iloveyou",
            "jennifer",
            "jesus",
            "jordan",
            "letmein",
            "mariposa",
            "master",
            "michael",
            "monkey",
            "mustang",
            "password",
            "pussy",
            "qwerty",
            "roberto",
            "sebastian",
            "shadow",
            "superman",
            "Tequiero").contains(password)

    }
    private val OPERATION_PER_SECOND: Double = 4*10E12

    fun entropyToString(): String {
        val entropy = entropy()
        if (entropy == 0.0) return ""

        val value = 2.0.pow(entropy) / OPERATION_PER_SECOND
        var ms = 0
        var seconds = value
        val minutes = seconds / 60.0
        val hours = minutes / 60.0
        val days = hours / 24.0
        val months = days / 30.0
        val years = months / 12.0
        val centuries = years / 100.0
        val millennium = centuries / 10.0

        seconds = value
        if (value == 0.0) return ""

        return "Contraseña: $password\n${if (ifa) "Es una contraseña conocida!\n" else ""}${when {
                    ms in 1..999 ->"Tu contraseña puede ser descifrada inmediatamente, yo no lo llamaría contraseña"
                    seconds > 0 && seconds  "Tu contraseña puede ser descifrada en ${seconds.toInt()} ${if (seconds.toInt() == 1) "segundo" else "segundos"}"
                    seconds > 59 && minutes   "Tu contraseña puede ser descifrada en ${minutes.toInt()} ${if (minutes.toInt() == 1) "minuto" else "minutos"}"
                    minutes > 59 && hours "Tu contraseña puede ser descifrada en ${hours.toInt()} ${if (hours.toInt() == 1) "hora" else "horas"}"
                    hours > 24 && days  "Tu contraseña puede ser descifrada en ${days.toInt()} ${if (days.toInt() == 1) "dia" else "dias"}"
                    days > 30 && months  "Tu contraseña puede ser descifrada en ${months.toInt()} ${if (months.toInt() == 1) "mes" else "meses"}"
                    months > 11 && years   "Tu contraseña puede ser descifrada en ${years.toInt()} ${if (years.toInt() == 1) "año" else "años"}"
                    years > 99 && centuries  "Tu contraseña puede ser descifrada en ${"%.1f".format(centuries)} ${if (centuries == 1.0) "siglo" else "siglos"}"
                    millennium > 1000000 -> "Tu contraseña puede ser descifrada en más de un millón de años!"
            else -> "Tu contraseña puede ser descifrada en ${millennium.toInt()} ${if (millennium.toInt() == 1) "milenio" else "milenios"}"}}"
}

fun main(){
    val password = "x#Pr0m0cio_N";

    for (i in (1..password.length)) {
        val pwd = PasswordEntropy(password.substring(0,i))
        println("Password: ${pwd.password}\n${pwd.entropyToString()}\n")

    }
}

el resultado para Kotlin


Password: x
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#P
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0m
Tu contraseña puede ser descifrada en 17 milisegundos
Press any key
Password: x#Pr0m0
Tu contraseña puede ser descifrada en 2 segundos
Press any key
Password: x#Pr0m0c
Tu contraseña puede ser descifrada en 3 minutos
Press any key
Password: x#Pr0m0ci
Tu contraseña puede ser descifrada en 4 horas
Press any key
Password: x#Pr0m0cio
Tu contraseña puede ser descifrada en 15,6 dias
Press any key
Password: x#Pr0m0cio_
Tu contraseña puede ser descifrada en 4,1 años
Press any key
Password: x#Pr0m0cio_N
Tu contraseña puede ser descifrada en 3,8 siglos
Press any key

El archivo jar podéis descargarlo desde aquí

Archivo ZIP

SHA256 9314879A535675E813EAC13CEC9B056CAADC4C080E5E033E239E2D88A60F55E3

Archivo jar

SHA256 CA942FB971E34CB775E14F12C5B25E5E456044F02DD54447F5690FA05469C3C8

Saludos a la X Promoción del IPE 2!!!

 

Anuncio publicitario