\df *crypt in psql reveals the argument types of the pgcrypto
decrypt functions (as do the PgCrypto docs):
List of functions
Schema | Name | Result data type | Argument data types | Type
public | decrypt | bytea | bytea, bytea, text | normal
public | encrypt | bytea | bytea, bytea, text | normal
so both the
decrypt functions expect the key to be
bytea. As per the error message, "you might need to add explicit type casts".
However, it works fine here on Pg 9.1, so I suspect there's more to it than you've shown. Perhaps you have another function also named
encrypt with three arguments?
Here's how it works on a clean Pg 9.1:
regress=# create table demo(pw bytea);
regress=# insert into demo(pw) values ( encrypt( 'data', 'key', 'aes') );
INSERT 0 1
regress=# select decrypt(pw, 'key', 'aes') FROM demo;
regress=# select convert_from(decrypt(pw, 'key', 'aes'), 'utf-8') FROM demo;
Awooga! Awooga! Key exposure risk, extreme admin caution required!
BTW, please think carefully about whether PgCrypto is really the right choice. Keys in your queries can be revealed in
pg_stat_activity and the system logs via
log_statement or via crypto statements that fail with an error. IMO it's frequently better to do crypto in the application.
Witness this session, with
client_min_messages enabled so you can see what'd appear in the logs:
regress# SET client_min_messages = 'DEBUG'; SET log_statement = 'all';
regress=# select decrypt(pw, 'key', 'aes') from demo;
LOG: statement: select decrypt(pw, 'key', 'aes') from demo;
LOG: duration: 0.710 ms
Whoops, key possibly exposed in the logs if
log_min_messages is low enough. It's now on the server's storage, along with the encrypted data. Fail. Same issue without
log_statement if an error occurs to cause the statement to get logged, or possibly if
auto_explain is enabled.
pg_stat_activity is also possible.. Open two sessions, and:
LOCK TABLE demo;
select decrypt(pw, 'key', 'aes') from demo;
select * from pg_stat_activity where current_query ILIKE '%decrypt%' AND procpid <> pg_backend_pid();
Whoops! There goes the key again. It can be reproduced without the
LOCK TABLE by an unprivileged attacker, it's just harder to time it right. The attack via
pg_stat_activity can be avoided by revoking access to
public, but it just goes to show that it might not be best to send your key to the DB unless you know your app is the only thing ever accessing it. Even then, I don't like to.
If it's passwords, should you store them at all?
Furthermore, if you're storing passwords, don't two-way encrypt them; if at all possible salt passwords then hash them and store the result. You usually don't need to be able to recover the password cleartext, only confirm that the stored hash matches the password the user sends you to log in when it's hashed with the same salt.
If it's auth, let someone else do it for you
Even better, don't store the password at all, authenticate against LDAP, SASL, Active Directory, an OAuth or OpenID provider, or some other external system that's already designed and working.
and lots more.