[Source] C# Authentication Port (port of phpbb_hash)

Discussion forum for MOD Writers regarding MOD Development.
Razchek
Registered User
Posts: 15
Joined: Sat Sep 05, 2009 2:48 am

[Source] C# Authentication Port (port of phpbb_hash)

Post by Razchek »

Okay! =) Here we go, supporting only new password hashes (if requested, I'll update it to support the old ones too):

cPhpBB.cs:

Code: Select all

using System;
using System.Security.Cryptography;
using System.Text;

namespace phpBB
{
    /// <summary>
    /// Computes the phpBB/SubMD5 hash value for the input data using the implementation provided by http://openwall.com/phpass/ modified by http://www.phpbb.com/.
    /// </summary>
    /// <remarks>
    /// Ported by Ryan Irecki
    /// Website: http://www.digilitepc.net/
    /// E-mail: razchek@gmail.com
    /// </remarks>
    class phpBBCryptoServiceProvider
    {
        /// <summary>
        /// The encryption string base.
        /// </summary>
        private string itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

        /// <summary>
        /// Compares the password string given with the hash retrieved from your database.
        /// </summary>
        /// <param name="password">Plaintext password.</param>
        /// <param name="hash">Hash from a SQL database</param>
        /// <returns>True if the password is correct, False otherwise.</returns>
        public bool phpbbCheckHash(string password, string hash)
        {
            if (hash.Length == 34) return (hashCryptPrivate(ASCIIEncoding.ASCII.GetBytes(password), hash, itoa64) == hash);
            return false;
        }

        /// <summary>
        /// This function will return the resulting hash from the password string you specify.
        /// </summary>
        /// <param name="password">String to hash.</param>
        /// <returns>Encrypted hash.</returns>
        /// <remarks>
        /// Although this will return the md5 for an older password, I have not added
        /// support for older passwords, so they will not work with this class unless
        /// I or someone else updates it.
        /// </remarks>
        public string phpbb_hash(string password)
        {
            // Generate a random string from a random number with the length of 6.
            // You could use a static string instead, doesn't matter. E.g.
            // byte[] random = ASCIIEncoding.ASCII.GetBytes("abc123");
            byte[] random =  ASCIIEncoding.ASCII.GetBytes(new Random().Next(100000, 999999).ToString());

            string hash = hashCryptPrivate(ASCIIEncoding.ASCII.GetBytes(password), hashGensaltPrivate(random, itoa64), itoa64);

            if (hash.Length == 34) return hash;

            return sMD5(password);
        }

        /// <summary>
        /// The workhorse that encrypts your hash.
        /// </summary>
        /// <param name="password">String to be encrypted. Use: ASCIIEncoding.ASCII.GetBytes();</param>
        /// <param name="genSalt">Generated salt.</param>
        /// <param name="itoa64">The itoa64 string.</param>
        /// <returns>The encrypted hash ready to be compared.</returns>
        /// <remarks>
        /// password:  Saves conversion inside the function, lazy coding really.
        /// genSalt:   Returns from hashGensaltPrivate(random, itoa64);
        /// return:    Compare with phpbbCheckHash(password, hash)
        /// </remarks>
        private string hashCryptPrivate(byte[] password, string genSalt, string itoa64)
        {
            string output = "*";
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            if (!genSalt.StartsWith("$H$")) return output;
            //	$count_log2 = strpos($itoa64, $setting[3]);
            int count_log2 = itoa64.IndexOf(genSalt[3]);
            if (count_log2 < 7 || count_log2 > 30) return output;

            int count = 1 << count_log2;
            byte[] salt = ASCIIEncoding.ASCII.GetBytes(genSalt.Substring(4, 8));

            if (salt.Length != 8) return output;

            byte[] hash = md5.ComputeHash(Combine(salt, password));

            do
            {
                hash = md5.ComputeHash(Combine(hash, password));
            } while (count-- > 1);

            output = genSalt.Substring(0, 12);
            output += hashEncode64(hash, 16, itoa64);

            return output;
        }

        /// <summary>
        /// Private function to concat byte arrays.
        /// </summary>
        /// <param name="b1">Source array.</param>
        /// <param name="b2">Array to add to the source array.</param>
        /// <returns>Combined byte array.</returns>
        private byte[] Combine(byte[] b1, byte[] b2)
        {
            byte[] retVal = new byte[b1.Length + b2.Length];
            Array.Copy(b1, 0, retVal, 0, b1.Length);
            Array.Copy(b2, 0, retVal, b1.Length, b2.Length);
            return retVal;
        }

        /// <summary>
        /// Encode the hash.
        /// </summary>
        /// <param name="input">The hash to encode.</param>
        /// <param name="count">[This parameter needs documentation].</param>
        /// <param name="itoa64">The itoa64 string.</param>
        /// <returns>Encoded hash.</returns>
        private string hashEncode64(byte[] input, int count, string itoa64)
        {
            string output = "";
            int i = 0; int value = 0;

            do
            {
                value = input[i++];
                output += itoa64[value & 0x3f];

                if (i < count) value |= input[i] << 8;
                output += itoa64[(value >> 6) & 0x3f];
                if (i++ >= count)
                    break;

                if (i < count) value |= input[i] << 16;
                output += itoa64[(value >> 12) & 0x3f];
                if (i++ >= count)
                    break;

                output += itoa64[(value >> 18) & 0x3f];

            } while (i < count);
            
            return output;
        }

        /// <summary>
        /// Generate salt for hash generation.
        /// </summary>
        /// <param name="input">Any random information.</param>
        /// <param name="itoa64">The itoa64 string.</param>
        /// <returns>Generated salt string</returns>
        private string hashGensaltPrivate(byte[] input, string itoa64)
        {
            int iteration_count_log2 = 6;

            string output = "$H$";
            output += itoa64[Math.Min(iteration_count_log2 + 5, 30)];
            output += hashEncode64(input, 6, itoa64);

            return output;
        }

        /// <summary>
        /// Returns a hexadecimal string representation for the encrypted MD5 parameter.
        /// </summary>
        /// <param name="password">String to be encrypted.</param>
        /// <returns>String</returns>
        private string sMD5(string password)
        { return sMD5(password, false); }
        /// <summary>
        /// Returns a hexadecimal string representation for the encrypted MD5 parameter.
        /// </summary>
        /// <param name="password">String to be encrypted.</param>
        /// <param name="raw">Whether or not to produce a raw string.</param>
        /// <returns>String</returns>
        private string sMD5(string password, bool raw)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            if (raw) return Encoding.ASCII.GetString(md5.ComputeHash(Encoding.ASCII.GetBytes(password)));
            else return BitConverter.ToString(md5.ComputeHash(Encoding.ASCII.GetBytes(password))).Replace("-", "");
        }

    }
}
Usage:
Call auth() from any function event. (e.g. button_click())

Code: Select all

        void auth()
        {
            // Initialize the class
            phpBBCryptoServiceProvider cPhpBB = new phpBBCryptoServiceProvider();
            
            // Incase your curious. =)
            //string localHash = cPhpBB.phpbb_hash("myPassword"); 
            
            // remoteHash is a hash from your SQL database.
            // getSQLhash is a function to query the database and retrieve that hash.
            string remoteHash = getSQLhash("username");
            
            // Set the result to see if the password matches or not.
            bool result = cPhpBB.phpbbCheckHash("myPassword", remoteHash);
            
            if(result) // If true, the password matches!
                MessageBox.Show("Authentication successful.");
            else // If false, the password does not!
                MessageBox.Show("Authentication failed.");
        }
Example:

Code: Select all

        void auth()
        {
            phpBBCryptoServiceProvider cPhpBB = new phpBBCryptoServiceProvider();

            string remoteHash = "$H$9uAiKWrdcDomn7FEqujoPLYuBXvkzV0";
            
            bool result = cPhpBB.phpbbCheckHash("q1w2e3", remoteHash);
            
            if(result) MessageBox.Show("Authentication successful.");
            else MessageBox.Show("Authentication failed.");
        }
The example would set 'result' to True, indicating the authentication was successful!

Enjoy! If anyone needs any help with the C#, please post here. I'll keep an eye on the thread. =)
dnsuser
Registered User
Posts: 3
Joined: Mon Sep 14, 2009 5:40 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by dnsuser »

Hi Razchek,

I have tried your C# port of phpbb_hash. But it looks like the hash generated from your port is not the same from the phpbb.

I have manually compared the SQL table phpbb_users.user_password field with the hashed text generated from a aspx webpage. The code is shown below

protected void Button1_Click(object sender, EventArgs e)
{
PhpBBCryptoServiceProvider hText;
hText = new PhpBBCryptoServiceProvider();

hashText.Text = hText.phpbb_hash(clearText.Text);
}

hastText and clearText are just asp.net TextBox. Could you give me any insight what am I missing?

thanks in advance.
dnsuser
ToonArmy
Former Team Member
Posts: 4608
Joined: Sat Mar 06, 2004 5:29 pm
Location: Worcestershire, UK
Name: Chris Smith
Contact:

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by ToonArmy »

The hash changes on every invocation as it is salted.

Example: http://www.cs278.org/tools/phpbb/hash.php reload the page and you will notice it changes with the same data. ;)
Chris SmithGitHub
Razchek
Registered User
Posts: 15
Joined: Sat Sep 05, 2009 2:48 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Razchek »

dnsuser wrote:Hi Razchek,

I have tried your C# port of phpbb_hash. But it looks like the hash generated from your port is not the same from the phpbb.

I have manually compared the SQL table phpbb_users.user_password field with the hashed text generated from a aspx webpage. The code is shown below

protected void Button1_Click(object sender, EventArgs e)
{
PhpBBCryptoServiceProvider hText;
hText = new PhpBBCryptoServiceProvider();

hashText.Text = hText.phpbb_hash(clearText.Text);
}

hastText and clearText are just asp.net TextBox. Could you give me any insight what am I missing?

thanks in advance.
dnsuser
Hi dnsuser,

Instead of calling: hText.phpbb_hash(clearText.Text);
Call: cPhpBB.phpbbCheckHash(clearText.Text, aspxHashString);

The final hashing is performed in this function using the remote hash as a salt.

Once that is done, it will compare the hash from clearText.Text with the hash provided from the ASPX text. If the password is correct, then it will return true.

E.g.
Plain-text Password: q1w2e3
SQL Hash: $H$9uAiKWrdcDomn7FEqujoPLYuBXvkzV0 (in your case, the hash on the aspx page)
phpbbCheckHash("q1w2e3", "$H$9uAiKWrdcDomn7FEqujoPLYuBXvkzV0") will return true because the password is correct.
phpbbCheckHash("fakepass", "$H$9uAiKWrdcDomn7FEqujoPLYuBXvkzV0") will return false because the password is incorrect.

Is there anything specific you are trying to achieve? Or will the comparison suffice?

Code: Select all

        /// <summary>
        /// Compares the password string given with the hash retrieved from your database.
        /// </summary>
        /// <param name="password">Plaintext password.</param>
        /// <param name="hash">Hash from a SQL database</param>
        /// <returns>True if the password is correct, False otherwise.</returns>
        public bool phpbbCheckHash(string password, string hash)
        {
            if (hash.Length == 34) return (hashCryptPrivate(ASCIIEncoding.ASCII.GetBytes(password), hash, itoa64) == hash);
            return false;
        }
Phe0n1x
Registered User
Posts: 8
Joined: Tue Sep 15, 2009 7:22 pm

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Phe0n1x »

Code: Select all

            // getSQLhash is a function to query the database and retrieve that hash.
            string remoteHash = getSQLhash("username");
where can I find the getSQLHash function? It doesn't appear anywhere that I can find.


Actually, I have the salt and I have the password. However, I don't have the hash. How would I accomplish hashing the password phpbb style? I can't quite understand that from your code.
Razchek
Registered User
Posts: 15
Joined: Sat Sep 05, 2009 2:48 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Razchek »

Phe0n1x wrote:

Code: Select all

            // getSQLhash is a function to query the database and retrieve that hash.
            string remoteHash = getSQLhash("username");
where can I find the getSQLHash function? It doesn't appear anywhere that I can find.

Actually, I have the salt and I have the password. However, I don't have the hash. How would I accomplish hashing the password phpbb style? I can't quite understand that from your code.
Okay, the getSQLhash function is my own interpretation, an example function if you will.
user_password stores the password in your SQL database.

User/Pass -> SQL Database -> phpbb_db -> phpbb_users -> user_password
In the final snippet of code, I provide the example. You need the hash to be able to compare with the password you're giving.

If you need code to access an SQL database, you might need to Google it. And make sure that your host allows remote SQL connections. I can give you an example of using the MySQL.net connector. But just understand that you need to retrieve the hash in-order to create a comparison.

The salt is random, unless you're talking about the hash from the SQL database, in-which case that's what you need. bool phpbbCheckHash(plaintext_password, sql_hash_stored_in_database)
dnsuser
Registered User
Posts: 3
Joined: Mon Sep 14, 2009 5:40 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by dnsuser »

Razchek wrote:
dnsuser wrote:

Hi dnsuser,

Instead of calling: hText.phpbb_hash(clearText.Text);
Call: cPhpBB.phpbbCheckHash(clearText.Text, aspxHashString);

The final hashing is performed in this function using the remote hash as a salt.

Once that is done, it will compare the hash from clearText.Text with the hash provided from the ASPX text. If the password is correct, then it will return true.

E.g.
Plain-text Password: q1w2e3
SQL Hash: $H$9uAiKWrdcDomn7FEqujoPLYuBXvkzV0 (in your case, the hash on the aspx page)
phpbbCheckHash("q1w2e3", "$H$9uAiKWrdcDomn7FEqujoPLYuBXvkzV0") will return true because the password is correct.
phpbbCheckHash("fakepass", "$H$9uAiKWrdcDomn7FEqujoPLYuBXvkzV0") will return false because the password is incorrect.

Is there anything specific you are trying to achieve? Or will the comparison suffice?

Code: Select all

        /// <summary>
        /// Compares the password string given with the hash retrieved from your database.
        /// </summary>
        /// <param name="password">Plaintext password.</param>
        /// <param name="hash">Hash from a SQL database</param>
        /// <returns>True if the password is correct, False otherwise.</returns>
        public bool phpbbCheckHash(string password, string hash)
        {
            if (hash.Length == 34) return (hashCryptPrivate(ASCIIEncoding.ASCII.GetBytes(password), hash, itoa64) == hash);
            return false;
        }

Hi Razchek,

Thanks for your reply. I should have read your sample code more thoroughly. That's exactly what I wanted to do.
I am integrating phpbb into my asp.net website and I want to just using phpbb's user management functions but some sections of the website need are for members only. So, I need to do the authentication on the asp.net side.

thanks
dnsuser
Razchek
Registered User
Posts: 15
Joined: Sat Sep 05, 2009 2:48 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Razchek »

You're most welcome. It's alright, I should have made the phpbb_hash() method private to avoid confusion, but I can find uses for it on a website so I left it public. I'm glad my code port helped.
If you have any other questions, feel free to ask.
User avatar
Kellanved
Former Team Member
Posts: 2635
Joined: Wed Jan 26, 2005 2:48 pm
Location: Meta-level

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Kellanved »

The phpass author might be interested in this:
http://www.openwall.com/phpass/
Nocando is in Idontwanna county. No support via PM
Razchek
Registered User
Posts: 15
Joined: Sat Sep 05, 2009 2:48 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Razchek »

Kellanved wrote:The phpass author might be interested in this:
http://www.openwall.com/phpass/
Sent a quick comment their way.
I created a C# port of phpBB's method using phpass, a developer there told me you may be interested in this: http://www.phpbb.com/community/viewtopi ... &t=1771165. You can contact me there or via: x@x.com
dnsuser
Registered User
Posts: 3
Joined: Mon Sep 14, 2009 5:40 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by dnsuser »

ToonArmy wrote:The hash changes on every invocation as it is salted.

Example: http://www.cs278.org/tools/phpbb/hash.php reload the page and you will notice it changes with the same data. ;)

Hi,

I am not very good at this "security" area. I thought the hash is just a one-way function to produce the same result if it is given the same input each time.......
thanks for the insight :)
ToonArmy
Former Team Member
Posts: 4608
Joined: Sat Mar 06, 2004 5:29 pm
Location: Worcestershire, UK
Name: Chris Smith
Contact:

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by ToonArmy »

dnsuser wrote:I am not very good at this "security" area. I thought the hash is just a one-way function to produce the same result if it is given the same input each time.......
thanks for the insight :)
The phpBB hash is a salted hash it contains both the salt and the salted hash hence for the same password will produce different hashes.
Chris SmithGitHub
Phe0n1x
Registered User
Posts: 8
Joined: Tue Sep 15, 2009 7:22 pm

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Phe0n1x »

Ok, so I've been messing around with this for the last two weeks and I can't seem to get it to work. I've retrieved the hash, but I continue to get a login failed. In the database snapshot I have, there is a column (index is 49) which contains the salt, a 4 character/number combination. How would I incorporate that into your code?
Razchek
Registered User
Posts: 15
Joined: Sat Sep 05, 2009 2:48 am

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Razchek »

Phe0n1x wrote:Ok, so I've been messing around with this for the last two weeks and I can't seem to get it to work. I've retrieved the hash, but I continue to get a login failed. In the database snapshot I have, there is a column (index is 49) which contains the salt, a 4 character/number combination. How would I incorporate that into your code?
1. Retrieve remote hash from db.
2. call phpbbCheckHash("passwordplaintext", hashfromstep1)
3. If step 2 == true, login.

Does not matter what the salt is, are you using any mods?

Please give me a little code to work from. =)
Phe0n1x
Registered User
Posts: 8
Joined: Tue Sep 15, 2009 7:22 pm

Re: [Source] C# Authentication Port (port of phpbb_hash)

Post by Phe0n1x »

I edited a .NET Membership Provider to only have ValidateUser and MaxInvalidPasswordAttempts.

Validate user had your usage code in it.

I then had the MySQL Connector connect to my DB to retrieve the hash and finally executed the phpbbCheckHash function.

Code: Select all

            string MyConString = "SERVER=localhost;" +
            "DATABASE=forums;" +
            "UID=root;" +
            "PASSWORD=toor;";

        public string getSQLhash(string username)
        {
            MySql.Data.MySqlClient.MySqlConnection connection = new MySql.Data.MySqlClient.MySqlConnection(MyConString);
            MySql.Data.MySqlClient.MySqlCommand command = connection.CreateCommand();
            command.CommandText = String.Format("select {0} from user", username);
            connection.Open();
            using (MySql.Data.MySqlClient.MySqlDataReader Reader = command.ExecuteReader())
            {
                while (Reader.Read())
                {
                    try
                    {
                        //hash is in the 5th column
                        string hash = Reader.GetValue(5).ToString();
                        connection.Close();
                        return hash;
                    }
                    catch
                    {
                        connection.Close();
                        return null;
                    }
                }
            }
            return null;
        }
Even if I put breaks in the code, the program never hits them...
Locked

Return to “[3.0.x] MOD Writers Discussion”