Mangiare Senza Glutine disponibile su App Store

Per altre informazioni scrivi a fabriziocaldarelli@negusweb.it

Blowfish-based bcrypt in C

Da Programmazione Software.

Descrizione

Durante la conversione da una piattaforma Snitz Forum (ASP su Sql Server) a PHPBB3 (PHP su MySql) mi sono imbattutto nella necessità di portare le vecchie password in chiaro di Snitz alle password hashate di phpbb. Il tutto fatto attraverso un sistema-ponte in C#,

La prima cosa è stata cercare di capire il metodo di hashing utilizzato da PHPBB3. L’hashing della password è eseguito dalla funzione phpbb_hash, la cui documentazione è abbastanza semplice e chiara.

Il sistema di hashing usato da PHPBB3 è chiaramente leggibile nella parte di codice citato ma anche direttamente da questo sito: http://www.openwall.com/phpass/.

Così’ ad una prima occhiata ho sottavalutato un pò il codice, pensando che la conversione mi prendesse solo qualche minuto … in realtà dopo 2 ore il codice da me implementato in C# ancora non funzionava correttamente. Per questo qui di seguito vi posto la conversione di questo sistema in C#, con la speranza che sia utile anche a qualcun’altro.

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
 
namespace Snitz2PhpBB3.Classes
{
    class CHashPassword
    {
        private static CHashPassword instance = null;
        public static CHashPassword getSingleton() { if (instance == null) instance = new CHashPassword(); return instance; }
 
        // Hash an input string and return the hash as
        // a 32 character hexadecimal string.
        string md5(string input)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
 
            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
 
            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();
 
            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }
 
            // Return the hexadecimal string.
            return sBuilder.ToString();
        }
 
        // Hash an input string and return the hash as
        // a 32 character hexadecimal string.
        byte[] md5bin(string input)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
 
            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
 
            return data;
        }
 
        private int ord(byte ch)
        {
            return ((int)ch);
        }
 
        private double ConvertToUnixTimestamp(DateTime date)
        {
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            TimeSpan diff = date - origin;
            return Math.Floor(diff.TotalSeconds);
        }
 
        /**
        * Return unique id
        * @param string $extra additional entropy
        */
        private string unique_id()
        {
            //char extra = 'c';
 
            double val = ConvertToUnixTimestamp(DateTime.UtcNow);
            String vals = md5(val.ToString());
 
            return vals.Substring(4,16);
        }
        /**
        *
        * @version Version 0.1 / slightly modified for phpBB 3.0.x (using $H$ as hash type identifier)
        *
        * Portable PHP password hashing framework.
        *
        * Written by Solar Designer  in 2004-2006 and placed in
        * the public domain.
        *
        * There's absolutely no warranty.
        *
        * The homepage URL for this framework is:
        *
        *    http://www.openwall.com/phpass/
        *
        * Please be sure to update the Version line if you edit this file in any way.
        * It is suggested that you leave the main version number intact, but indicate
        * your project name (after the slash) and add your own revision information.
        *
        * Please do not change the "private" password hashing method implemented in
        * here, thereby making your hashes incompatible.  However, if you must, please
        * change the hash type identifier (the "$P$") to something different.
        *
        * Obviously, since this code is in the public domain, the above are not
        * requirements (there can be none), but merely suggestions.
        *
        *
        * Hash the password
        */
        public String phpbb_hash(String password)
        {
            String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
            String random_state = unique_id();
            List random = new List();
            int count = 6;
 
            if (random.Count < count)
            {
                random = new List();
 
                for (int i = 0; i < count; i += 16)
                {
                    random_state = md5(unique_id() + random_state);
                    random.AddRange(md5bin(random_state));
                }
                random = random.GetRange(0, count);
            }
 
            String hashGensaltPrivate = _hash_gensalt_private(random.ToArray(), itoa64, 6);
            String hash = _hash_crypt_private(password, hashGensaltPrivate, itoa64);
 
            if (hash.Length == 34)
            {
                return hash;
            }
 
            return md5(password);
        }
 
        /**
        * Check for correct password
        */
        public bool phpbb_check_hash(String password, String hash)
        {
            String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
            if (hash.Length == 34)
            {
                return (_hash_crypt_private(password, hash, itoa64) == hash) ? true : false;
            }
 
            return (md5(password) == hash) ? true : false;
        }
 
        /**
        * Generate salt for hash generation
        */
        private string _hash_gensalt_private(byte[] input, String itoa64, int iteration_count_log2)
        {
            if (iteration_count_log2  31)
            {
                iteration_count_log2 = 8;
            }
 
            String output = "$H$";
            output += itoa64[Math.Min(iteration_count_log2 + 5, 30)];
            output += _hash_encode64(input, 6, itoa64);
 
            return output;
        }
 
        /**
        * Encode hash
        */
        private string _hash_encode64(byte[] input, int count, String itoa64)
        {
            String output = "";
            int i = 0;
 
            do
            {
                int value = ord(input[i++]);
                output += itoa64[value & 0x3f];
 
                if (i < count)
                {
                    value |= ord(input[i]) << 8;
                }
 
                output += itoa64[(value >> 6) & 0x3f];
 
                if (i++ >= count)
                {
                    break;
                }
 
                if (i < count)
                {
                    value |= ord(input[i]) << 16;
                }
 
                output += itoa64[(value >> 12) & 0x3f];
 
                if (i++ >= count)
                {
                    break;
                }
 
                output += itoa64[(value >> 18) & 0x3f];
            }
            while (i < count);
 
            return output;
        }
 
        /**
        * The crypt function/replacement
        */
        private string _hash_crypt_private(String password, String setting, String itoa64)
        {
            String output = "*";
 
            // Check for correct hash
            if (setting.Substring(0,3) != "$H$")
            {
                return output;
            }
 
            int count_log2 = itoa64.IndexOf(setting[3]);
 
            if (count_log2  30)
            {
                return output;
            }
 
            int count = 1 << count_log2;
            string salt = setting.Substring(4,8);
 
            if (salt.Length != 8)
            {
                return output;
            }
 
            /**
            * We're kind of forced to use MD5 here since it's the only
            * cryptographic primitive available in all versions of PHP
            * currently in use.  To implement our own low-level crypto
            * in PHP would result in much worse performance and
            * consequently in lower iteration counts and hashes that are
            * quicker to crack (by non-PHP code).
            */
            byte[] hashBin = md5bin(salt + password);
            do
            {
                hashBin = md5bin(ASCIIEncoding.Default.GetString(hashBin) + password);
            }
            while (--count!=0);
 
            output = setting.Substring(0,12);
            output += _hash_encode64(hashBin, 16, itoa64);
 
            return output;
        }
    }
}