sqlpostgresqlhashcryptographypgcrypto

How does postgresql's crypt() function know that a password matches a stored one?


I am learning to use crypt() function from pgcrypto to crypt my passwords and store them into user tables.

However I don't understand how it is possible for the crypt function to generate the same password if we pass as a second parameter the existing password.

I want to understand how it does it please.

do $$
declare
   generated_salt text;
   hashResult text;
   authenticationPassword text;
begin
   -- select the number of actors from the actor table
   select public.gen_salt('bf')
   into generated_salt;
   
   select public.crypt('password', generated_salt)
   into hashResult;
   
   select public.crypt('password', hashResult)
   into authenticationPassword;

   -- show the number of actors
   raise notice 'Generated salt : %', generated_salt;
   raise notice 'Hash result : %', hashResult;
   raise notice 'authenticationPassword : %', authenticationPassword;
end; $$

In the exemple above if I take an execution and the stored variables's values.

I get : "$2a$06$.lub5s4Eqz4.epcg5zW4Ke" for generated_salt "$2a$06$.lub5s4Eqz4.epcg5zW4KervErzw//uARn8F2gchj2wM11ok.MJLK" for hashResult "$2a$06$.lub5s4Eqz4.epcg5zW4KervErzw//uARn8F2gchj2wM11ok.MJLK" for authenticationPassword

Why is the authenticationPassword the same as hashResult, I was expecting it to hash it again with hashResult as a salt.

How can it tell if it must recognize and calculate the same password or generate a new hash based on a salt?

P.S : I understand how to use the function for storing/retrieving passwords but I don't understand how it does it.

Thank you for your help,


Solution

  • I finally understand what is happening, the crypt function uses only the first 29 chars (passed as salt) to generate the hashOutput so even if you put anything behind those 29 characters the result will be the same. Example :

    public.crypt('password', '$2a$06$.lub5s4Eqz4.epcg5zW4Ke')
    public.crypt('password', '$2a$06$.lub5s4Eqz4.epcg5zW4KervErzw//uARn8F2gchj2wM11ok.MJLK')
    public.crypt('password', '$2a$06$.lub5s4Eqz4.epcg5zW4Keyadayadayadayada')
    

    The result of the 3 lines above is always $2a$06$.lub5s4Eqz4.epcg5zW4KervErzw//uARn8F2gchj2wM11ok.MJLK what really matters is the first 29 characters $2a$06$.lub5s4Eqz4.epcg5zW4Ke and the password value which decides of what is generated after the 29 characters : rvErzw//uARn8F2gchj2wM11ok.MJLK

    The source code of pgcrypto helped me understand what was happening under the hood : https://github.com/postgres/postgres/tree/master/contrib/pgcrypto