Non-Fun with SSHA Password scheme

From: Chris Browne <cbbrowne(at)acm(dot)org>
To: pgsql-general(at)postgresql(dot)org
Subject: Non-Fun with SSHA Password scheme
Date: 2009-03-02 16:35:33
Message-ID: 87y6vnyjai.fsf_-_@dba2.int.libertyrms.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: 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/>

Browse pgsql-general by date

  From Date Subject
Next Message Scott Marlowe 2009-03-02 17:52:40 Re: Detemine database size on Postgres 8.0
Previous Message K D 2009-03-02 16:10:20 plpython large result set