Non-Fun with SSHA Password scheme - Mailing list pgsql-general

From Chris Browne
Subject Non-Fun with SSHA Password scheme
Date
Msg-id 87y6vnyjai.fsf_-_@dba2.int.libertyrms.com
Whole thread Raw
In response to HELP all women were raped during the May riots in Jakarta  (adm@pu.go.id)
List pgsql-general
I have been trying to set up pl/pgsql code to generate and evaluate
{SSHA} passwords, with somewhat limited success.

{SSHA} is a password scheme that uses SHA-1 along with salting to ward
off dictionary attacks.  Apparently it's used quite a bit with LDAP.

There does not seem to be a "claimed authoritative" source on it; the
nearest is an OpenLDAP FAQ entry that points to a now-dead Netscape
source; apparently the idea was created by someone in Netscape's LDAP
group.

  http://www.openldap.org/faq/data/cache/347.html

  http://developer.netscape.com/docs/technote/ldap/pass_sha.html  (dead)

 (So instead, see the WayBack Machine...)
  http://web.archive.org/web/20040810221808/http://developer.netscape.com/docs/technote/ldap/pass_sha.html

The OpenLDAP FAQ shows examples in Python, Perl, PHP, and Ruby;
they're not very self-explanatory :-).

A Sun Blog has a very nice algorithmic description:
   http://blogs.sun.com/DirectoryManager/entry/the_ssha_password_storage_scheme

I've been trying to create a pair of functions to encode & decode
them.  I get something *internally* consistent, but that doesn't
consistently evaluate other tools' SSHA passwords rightly.

The "consistent" part is the odd part.

If I take the Python/Ruby implementations in the OpenLDAP FAQ, and
choose a human-readable-text salt value, those values seem to (near as
I can tell with a limited selection set :-)) evaluate successfully in
the function ssha_verify(), below.

If, instead, I allow them to use arbitrary binary salts (e.g. - a
series of randomly chosen bytes), then ssha_verify() is quite sure
they don't match.

I suspect I'm doing something dumb surrounding the string smashing,
but just what is not occurring to me.

--------- Set phasers to Cut Here ----------------------
create or replace function ssha_encode (i_secret text) returns text as $$
declare
    c_salt bytea;
    c_shaed_pw bytea;
begin
    -- 1.  Generate 8 bytes of random data to use as salt
    c_salt := public.digest(now()::text || i_secret || random()::text, 'sha1');
    -- 2+3 Append salt, generate SHA1 digest
    c_shaed_pw := public.digest((i_secret||c_salt), 'sha1');
    -- 4-5 - append salt, and encode in base64
    return '{SSHA}' || pg_catalog.encode(c_shaed_pw || c_salt, 'base64');
end $$ language plpgsql;

create or replace function ssha_verify (i_secret text, i_hash text) returns boolean as $$
declare
    c_decoded bytea;
    c_body text;
    c_chash bytea;
    c_salt bytea;
    c_hash bytea;
begin
    if not (i_hash like '{SSHA}%') then
        raise exception 'ssha_verify(%,%) - hash not of SSHA form!', i_secret, i_hash;
    end if;
    c_body := substr(i_hash, 7);
    c_chash := substr(pg_catalog.decode(c_body, 'base64')::bytea, 1, 20);
    c_salt := substr(pg_catalog.decode(c_body, 'base64')::bytea, 21);
    c_hash := public.digest((i_secret || c_salt), 'sha1');
    if c_hash = c_chash then
        return 't';
    else
        return 'f';
    end if;
end
$$ language plpgsql;
--------- Set phasers to Cut Here ----------------------
--
select 'cbbrowne' || '@' || 'linuxfinances.info';
http://cbbrowne.com/info/sap.html
Rules of the  Evil Overlord #22. "No matter how tempted  I am with the
prospect  of unlimited  power, I  will  not consume  any energy  field
bigger than my head. <http://www.eviloverlord.com/>

pgsql-general by date:

Previous
From: K D
Date:
Subject: plpython large result set
Next
From: Scott Marlowe
Date:
Subject: Re: Detemine database size on Postgres 8.0