Decrypt an with PHP encrypted Password lazarus / 16-Databases ---------------------------------------------------------------------- vodanet · 2018-05-05 13:31 Hello, I encrypted a password with php and would now like to decrypt it with lazarus, or compare whether the input is correct. I have an input field where the user enters his password and I would like to compare it with the password stored in the mysql table. I tried this example with blowfish, but it does't work. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  { 1 }    key := 'testkey';    value := 'this is a string';    { 2 }    s1 := TStringStream.Create('');    en := TBlowFishEncryptStream.Create(key,s1);    { 3 }    en.WriteAnsiString(value);    en.Free;    showmessage('encrypted: ' + pchar(s1.DataString));    { 4 }    s2 := TStringStream.Create(s1.DataString);    s1.Free;    { 5 }    de := TBlowFishDeCryptStream.Create(key,s2);    { 6 }    temp := de.ReadAnsiString;    showmessage('decrypted: ' + temp);    de.Free;  s2.Free;   Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   showmessage('encrypted: ' + pchar(s1.DataString)); shows nothing (Showmodal is empty). What is the problem? Or do I need a completely different approach? Thanks for your help! ---------------------------------------------------------------------- ASerge · 2018-05-05 17:54 Quote from: vodanet on May 05, 2018, 03:31:57 pmI have an input field where the user enters his password and I would like to compare it with the password stored in the mysql table. I tried this example with blowfish, but it does't work. I think the encrypted text is not for display. It is correct, but only for storage or comparison, but not for display. Here is a simple working example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;{$MODE OBJFPC}{$APPTYPE CONSOLE}{$LONGSTRINGS ON} uses Classes, BlowFish, SysUtils; var  Key: string = 'testkey';  Value: string = 'this is a string';  HexStr: string;  s1: TStringStream;  en: TBlowFishEncryptStream;  de: TBlowFishDeCryptStream;begin  s1 := TStringStream.Create('');  try    en := TBlowFishEncryptStream.Create(Key, s1);    try      en.WriteAnsiString(Value);    finally      en.Free;    end;    Writeln('Encrypted:', s1.DataString);    SetLength(HexStr, Length(s1.DataString) * 2);    BinToHex(PChar(s1.DataString), PChar(HexStr), Length(s1.DataString));    Writeln('Encrypted hex:', HexStr);    s1.Position := 0;    de := TBlowFishDeCryptStream.Create(Key, s1);    try      Writeln('Decrypted: ', de.ReadAnsiString);    finally      de.Free;  end;  finally    s1.Free;  end;  Readln;end. ---------------------------------------------------------------------- vodanet · 2018-05-05 20:21 OK. Thank you. And how do I apply it in my concrete example? I have the value from the table (the saved password) and the value of the input. How do I check if it is correct? Somehow do not get any further ... ---------------------------------------------------------------------- ASerge · 2018-05-05 22:16 Quote from: vodanet on May 05, 2018, 10:21:01 pmOK. Thank you. And how do I apply it in my concrete example? No need to store passwords in the database. Store the encrypted string. If the database does not support blob, save, for example, as base64. And then compare the encrypted strings. ---------------------------------------------------------------------- vodanet · 2018-05-06 08:27 QuoteNo need to store passwords in the database. I do not do that either. I have an existing database of saved passwords. I used php crypt () for that. Now I have saved a password that looks like this: $2y$10$LAyJngmoCbV/YT2BGBx.kubjpBXLFLB2hGjARZRERTubkM5agLCtO I have the hash = $2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG And now I'm trying to compare the user's input with the stored value. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  Key: string = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';  HexStr: string;  s1: TStringStream;  en: TBlowFishEncryptStream;  de: TBlowFishDeCryptStream; value:string;begin  value:=edit2.text;   s1 := TStringStream.Create('');  try    en := TBlowFishencryptStream.Create(Key, s1);    try      en.WriteAnsiString(Value);    finally      en.Free;    end; Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  if s1.DataString = my_saved_string // wich is $2y$10$LAyJngmoCbV/YT2BGBx.kubjpBXLFLB2hGjARZRERTubkM5agLCtOthen.... OK   Or? ---------------------------------------------------------------------- ASerge · 2018-05-06 11:30 Quote from: vodanet on May 06, 2018, 10:27:46 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  if s1.DataString = my_saved_string // wich is $2y$10$LAyJngmoCbV/YT2BGBx.kubjpBXLFLB2hGjARZRERTubkM5agLCtOthen.... OK   That's right. Do not forget that Lazarus stores Utf8 encoded strings, and if passwords contain not ASCII characters, it is important that the encodings match. ---------------------------------------------------------------------- vodanet · 2018-05-06 12:11 QuoteDo not forget that Lazarus stores Utf8 encoded strings, and if passwords contain not ASCII characters, it is important that the encodings match.And probably there is my problem somewhere, because Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i s1.datastring is always empty. For example:  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i showmessage  ('Value:'+s1.DataString); shows nothing. only the empty messagebox. So when i try to compare Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if s1.DataString =my_password then ... it's always <> Is encoding the problem? How do I encode it "correctly"? ---------------------------------------------------------------------- Thaddy · 2018-05-06 12:34 Can you first try with another algorithm? The default PHP crypt() function is still DES - not Blowfish AFAIK -, but I suggest to test with SHA256 and SHA512 first and report back. Maybe it s the salt that is expected. In general encryption and secure hashing should be done on byte arrays, not strings..... The string type and/or codepage does not matter at all that way. Note that only goes for compare and not for display: that needs the same codepage both sides as explained above. ---------------------------------------------------------------------- ASerge · 2018-05-06 14:38 Quote from: vodanet on May 06, 2018, 02:11:51 pm Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i showmessage  ('Value:'+s1.DataString); shows nothing. only the empty messagebox. So when i try to compare Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if s1.DataString =my_password then ... it's always <> Reading from the beginning My example is work. Encoding/decoding work correctly. So the mistake is somewhere else. Are you suggesting guessing? ---------------------------------------------------------------------- engkin · 2018-05-06 19:38 If TEncoding.Default.EncodingName is *NOT* utf8 while DefaultSystemCodePage is 65001 - as in Lazarus - you need to call: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   TEncoding.FreeEncodings;  e := TMBCSEncoding.Create(DefaultSystemCodePage); If you use recent trunk, it is sufficient to call TEncoding.FreeEncodings. BlowFish uses StringStream which depends on TEncoding.Default for encoding. ---------------------------------------------------------------------- Thaddy · 2018-05-06 20:15 Sigh. ---------------------------------------------------------------------- engkin · 2018-05-06 20:17 Quote from: Thaddy on May 06, 2018, 10:15:29 pmSigh. What?!! ---------------------------------------------------------------------- vodanet · 2018-05-07 18:37 QuoteCan you first try with another algorithm? The default PHP crypt() function is still DES - not Blowfish AFAIK -, but I suggest to test with SHA256 and SHA512 first and report back. Maybe it s the salt that is expected. In general encryption and secure hashing should be done on byte arrays, not strings..... The string type and/or codepage does not matter at all that way. Note that only goes for compare and not for display: that needs the same codepage both sides as explained above.That would mean that all users have to create their password again, right? Would it help if I post the PHP file ? Unfortunately, I'm very unconcerned in the field of encryption and I did not think it would be that difficult. Quote So the mistake is somewhere else But where? QuoteAre you suggesting guessing? Excuse please my bad english, but what do you mean? I do not understand the question.  Thank you all for your help. As I said / asked - would the PHP be useful to see how it was encrypted? ---------------------------------------------------------------------- Thaddy · 2018-05-07 18:48 It would be very helpful when we can see the PHP code. Many of us are not constrained to one programming language. So, yes, please. That would make it a lot easier to help you. And in the context of interfacing with FreePascal, PHP code is allowed... ---------------------------------------------------------------------- vodanet · 2018-05-07 20:21 OK  There is a simple query via a form. After SUBMIT comes the password check... Code: PHP  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i require_once("inc/functions.inc.php"); $error_msg = "";if(isset($_POST['email']) && isset($_POST['passwort'])) {        $email = $_POST['email'];        $passwort = $_POST['passwort'];       $statement = $pdo->prepare("SELECT * FROM users WHERE email = :email");        $result = $statement->execute(array('email' => $email));        $user = $statement->fetch();  $ip = getenv ("REMOTE_ADDR");        //Überprüfung des Passworts        if ($user !== false && password_verify($passwort, $user['passwort'])) { The script I had from here ... http://www.php-einfach.de/experte/php-codebeispiele/loginscript/ and here the password.inc.php Code: PHP  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  * @license http://www.opensource.org/licenses/mit-license.html MIT License * @copyright 2012 The Authors */ namespace {     if (!defined('PASSWORD_BCRYPT')) {        /**         * PHPUnit Process isolation caches constants, but not function declarations.         * So we need to check if the constants are defined separately from        * the functions to enable supporting process isolation in userland         * code.         */        define('PASSWORD_BCRYPT', 1);        define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);        define('PASSWORD_BCRYPT_DEFAULT_COST', 10);    }     if (!function_exists('password_hash')) {         /**         * Hash the password using the specified algorithm         *         * @param string $password The password to hash         * @param int    $algo   The algorithm to use (Defined by PASSWORD_* constants)         * @param array  $options  The options for the algorithm to use         *         * @return string|false The hashed password, or false on error.         */        function password_hash($password, $algo, array $options = array()) {            if (!function_exists('crypt')) {                trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);                return null;            }            if (is_null($password) || is_int($password)) {              $password = (string) $password;            }            if (!is_string($password)) {                trigger_error("password_hash(): Password must be a string", E_USER_WARNING);                return null;            }            if (!is_int($algo)) {                trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);                return null;            }            $resultLength = 0;            switch ($algo) {                case PASSWORD_BCRYPT:                    $cost = PASSWORD_BCRYPT_DEFAULT_COST;                    if (isset($options['cost'])) {                        $cost = (int) $options['cost'];                        if ($cost < 4 || $cost > 31) {                            trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);                            return null;                      }                    }                    // The length of salt to generate                    $raw_salt_len = 16;                  // The length required in the final serialization                  $required_salt_len = 22;                    $hash_format = sprintf("$2y$%02d$", $cost);                    // The expected length of the final crypt() output                    $resultLength = 60;                  break;                default:                    trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);                    return null;            }            $salt_req_encoding = false;            if (isset($options['salt'])) {                switch (gettype($options['salt'])) {                    case 'NULL':                  case 'boolean':                    case 'integer':                  case 'double':                    case 'string':                      $salt = (string) $options['salt'];                        break;                    case 'object':                        if (method_exists($options['salt'], '__tostring')) {                          $salt = (string) $options['salt'];                            break;                        }                    case 'array':                  case 'resource':                    default:                      trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);                        return null;              }                if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {                    trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);                    return null;                } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {                    $salt_req_encoding = true;                }          } else {                $buffer = '';                $buffer_valid = false;                if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {                  $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);                    if ($buffer) {                      $buffer_valid = true;                    }                }              if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {                    $strong = false;                    $buffer = openssl_random_pseudo_bytes($raw_salt_len, $strong);                  if ($buffer && $strong) {                        $buffer_valid = true;                    }                }                if (!$buffer_valid && @is_readable('/dev/urandom')) {                    $file = fopen('/dev/urandom', 'r');                    $read = 0;                  $local_buffer = '';                    while ($read < $raw_salt_len) {                        $local_buffer .= fread($file, $raw_salt_len - $read);                        $read = PasswordCompat\binary\_strlen($local_buffer);                    }                  fclose($file);                    if ($read >= $raw_salt_len) {                        $buffer_valid = true;                  }                    $buffer = str_pad($buffer, $raw_salt_len, "\0") ^ str_pad($local_buffer, $raw_salt_len, "\0");              }                if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {                  $buffer_length = PasswordCompat\binary\_strlen($buffer);                  for ($i = 0; $i < $raw_salt_len; $i++) {                      if ($i < $buffer_length) {                            $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));                        } else {                            $buffer .= chr(mt_rand(0, 255));                      }                    }                }                $salt = $buffer;                $salt_req_encoding = true;            }            if ($salt_req_encoding) {                // encode string with the Base64 variant used by crypt                $base64_digits =                    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';              $bcrypt64_digits =                    './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';               $base64_string = base64_encode($salt);                $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);            }            $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);           $hash = $hash_format . $salt;             $ret = crypt($password, $hash);             if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {              return false;            }             return $ret;        }         /**         * Get information about the password hash. Returns an array of the information         * that was used to generate the password hash.         *         * array(         *    'algo' => 1,       *    'algoName' => 'bcrypt',         *    'options' => array(       *        'cost' => PASSWORD_BCRYPT_DEFAULT_COST,         *    ),       * )         *         * @param string $hash The password hash to extract info from         *         * @return array The array of information about the hash.         */        function password_get_info($hash) {            $return = array(                'algo' => 0,                'algoName' => 'unknown',                'options' => array(),            );            if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {                $return['algo'] = PASSWORD_BCRYPT;                $return['algoName'] = 'bcrypt';                list($cost) = sscanf($hash, "$2y$%d$");              $return['options']['cost'] = $cost;            }          return $return;        }         /**         * Determine if the password hash needs to be rehashed according to the options provided       *         * If the answer is true, after validating the password using password_verify, rehash it.         *         * @param string $hash    The hash to test         * @param int    $algo    The algorithm used for new password hashes         * @param array  $options The options array passed to password_hash         *         * @return boolean True if the password needs to be rehashed.         */        function password_needs_rehash($hash, $algo, array $options = array()) {            $info = password_get_info($hash);            if ($info['algo'] !== (int) $algo) {                return true;          }            switch ($algo) {                case PASSWORD_BCRYPT:                    $cost = isset($options['cost']) ? (int) $options['cost'] : PASSWORD_BCRYPT_DEFAULT_COST;                  if ($cost !== $info['options']['cost']) {                        return true;                    }                    break;          }            return false;        }         /**         * Verify a password against a hash using a timing attack resistant approach       *         * @param string $password The password to verify         * @param string $hash     The hash to verify against         *         * @return boolean If the password matches the hash         */        function password_verify($password, $hash) {            if (!function_exists('crypt')) {                trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);              return false;            }            $ret = crypt($password, $hash);            if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {                return false;            }             $status = 0;            for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {                $status |= (ord($ret[$i]) ^ ord($hash[$i]));            }             return $status === 0;        }    } } namespace PasswordCompat\binary {     if (!function_exists('PasswordCompat\\binary\\_strlen')) {       /**         * Count the number of bytes in a string         *       * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.         * In this case, strlen() will count the number of *characters* based on the internal encoding. A         * sequence of bytes might be regarded as a single multibyte character.         *         * @param string $binary_string The input string         *         * @internal         * @return int The number of bytes         */        function _strlen($binary_string) {            if (function_exists('mb_strlen')) {                return mb_strlen($binary_string, '8bit');            }            return strlen($binary_string);        }         /**         * Get a substring based on byte limits         *         * @see _strlen()         *       * @param string $binary_string The input string         * @param int    $start         * @param int    $length         *         * @internal         * @return string The substring         */        function _substr($binary_string, $start, $length) {            if (function_exists('mb_substr')) {                return mb_substr($binary_string, $start, $length, '8bit');            }          return substr($binary_string, $start, $length);        }         /**         * Check if current PHP version is compatible with the library         *         * @return boolean the check result         */        function check() {            static $pass = NULL;           if (is_null($pass)) {                if (function_exists('crypt')) {                    $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';                  $test = crypt("password", $hash);                    $pass = $test == $hash;                } else {                    $pass = false;                }            }            return $pass;        }     }} ---------------------------------------------------------------------- snorkel · 2018-05-11 16:04 You shouldn't be encrypting password for storage in a DB, you should be one way hashing them with a salt. and MD5 hashing is not recommended anymore, use bcrypt hashing as some one mentioned or SHA256 ---------------------------------------------------------------------- Thaddy · 2018-05-11 19:14 Indeed. Good advice. https://en.wikipedia.org/wiki/MD5 ---------------------------------------------------------------------- Thaddy · 2018-05-11 19:22 I have some code working for SHA families (PHP-FPC-PHP-FPC w/o error). Will put it here tomorrow. ---------------------------------------------------------------------- vodanet · 2018-05-12 06:09 OK. Then I'll wait a bit.  Many Thanks to all!