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/> -- Sent via pgsql-general mailing list (pgsql-general@xxxxxxxxxxxxxx) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-general