9

I'd like to call some pgcrypto functions from python. Namely px_crypt. I can't seem to figure out the right object files to link it seems.

Here's my code:

#include <Python.h>

#include "postgres.h"

#include "pgcrypto/px-crypt.h"


static PyObject*
pgcrypt(PyObject* self, PyObject* args)
{
    const char* key;
    const char* setting;

    if (!PyArg_ParseTuple(args, "ss", &key, &setting))
        return NULL;

    return Py_BuildValue("s", px_crypt(key, setting, "", 0));
}

static PyMethodDef PgCryptMethods[] =
{
     {"pgcrypt", pgcrypt, METH_VARARGS, "Call pgcrypto's crypt"},
     {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initpypgcrypto(void)
{
     (void) Py_InitModule("pypgcrypto", PgCryptMethods);
}

and gcc commands and output:

x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/home/ionut/github/postgres/contrib/ -I/usr/include/postgresql/9.4/server/ -I/usr/include/python2.7 -c pypgcrypto.c -o build/temp.linux-x86_64-2.7/pypgcrypto.o
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wl,-z,relro -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/pypgcrypto.o /usr/lib/postgresql/9.4/lib/pgcrypto.so -lpgport -lpq -o build/lib.linux-x86_64-2.7/pypgcrypto.so

Error is:

python -c "import pypgcrypto; print pypgcrypto.pgcrypt('foo', 'bar')"

Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: /usr/lib/postgresql/9.4/lib/pgcrypto.so: undefined symbol: InterruptPending
Oin
  • 6,951
  • 2
  • 31
  • 55
  • It is complaining about `InterruptPending`, which library does this symbol belongs to? – fluter Apr 15 '16 at 00:28
  • Postgres. I can find it at: http://doxygen.postgresql.org/globals_8c_source.html#l00029 but I don't know how to link it. – Oin Apr 15 '16 at 09:30
  • 2
    Two questions, 1. Why should it be px_crypt? Can't you use *libssl.so* for example. 2. Do you understand that `px_crypt` is loaded at runtime by postgresql with `dlsym()` probably and that `InterruptPending` might be for example a global variable defined in the caller? – Iharob Al Asimi Apr 15 '16 at 22:56
  • 1
    1. I want to replicate pgcrypto's behaviour in order to be able to generate password hashes that match the ones already in my database. 2. I don't care about the value of InterruptPending. My question is mainly about how I can run px_crypt rather than about solving that specific InterruptPending undefined symbol error. – Oin Apr 16 '16 at 11:10
  • 2
    [Read this](http://www.postgresql.org/docs/9.4/static/pgcrypto.html) and just use [*psycopg2*](http://initd.org/psycopg/) to generate your passwords hashes. Also, postgresql uses algorithms that are present in *python* modules and *c* libraries too. So no need to force it to be the postgresql module. – Iharob Al Asimi Apr 16 '16 at 11:51
  • In one of the comments you said `I want to replicate pgcrypto's behaviour in order to be able to generate password hashes that match the ones already in my database`. My answer does exactly this for the algorithm you need. – Harry Apr 22 '16 at 18:32

1 Answers1

2

From one of your comments I got this...

I want to replicate pgcrypto's behavior in order to be able to generate password hashes that match the ones already in my database.

You can use python to do this already. I don't know what algorithm you're using, nor should I, here are two different methods using python to generate the exact same hash as Postgresql's pgcrypto

Crypt

=# select crypt('12345678', gen_salt('xdes')), md5('test');
        crypt         |               md5                
----------------------+----------------------------------
 _J9..b8FIoskMdlHvKjk | 098f6bcd4621d373cade4e832627b4f6

Here's the Python to check the password...

#!/usr/bin/env python
import crypt
from hmac import compare_digest as compare_hash

def login():
    hash_ = '_J9..OtC82a6snTAAqWg'
    print(compare_hash(crypt.crypt('123456789', hash_), hash_))
    #return True

if __name__ == '__main__':
  login()

MD5

For md5 you can use passlib's md5_crypt as follows...

=# select crypt('12345678', gen_salt('md5')), md5('test');
               crypt                |               md5                
------------------------------------+----------------------------------
 $1$UUVXoPbO$JMA7yhrKvaZcKqoFoi9jl. | 098f6bcd4621d373cade4e832627b4f6

Python would look something like...

#!/usr/bin/env python
from passlib.hash import md5_crypt

def login():
    hash_ = '$1$kOFl2EuX$QhhnPMAdx2/j2Tsk15nfQ0'
    print(md5_crypt.verify("12345678", hash_))

if __name__ == '__main__':
  login()

Blowfish

select crypt('12345678', gen_salt('bf')), md5('test');
                            crypt                             |               md5                
--------------------------------------------------------------+----------------------------------
 $2a$06$HLZUXMgqFhi/sl1D697il.lN8OMQFBWR2VBuZ5nTCd59jvGLU9pQ2 | 098f6bcd4621d373cade4e832627b4f6

Python code...

#!/usr/bin/env python
from passlib.hash import md5_crypt
from passlib.hash import bcrypt

def blowfish():
    hash_ = '$2a$06$HLZUXMgqFhi/sl1D697il.lN8OMQFBWR2VBuZ5nTCd59jvGLU9pQ2'
    print(bcrypt.verify("12345678", hash_))

if __name__ == '__main__':
  blowfish()
Harry
  • 11,298
  • 1
  • 29
  • 43
  • Thanks, but this is not an answer to the question I originally asked. I need to be sure I'm using the exact same algorithm to calculate the hashes that pgcrypto is using. And different implementations *can* differ. See e.g. the bug-compatibility comment at the bottom of http://doxygen.postgresql.org/crypt-blowfish_8c_source.html – Oin Apr 22 '16 at 09:17
  • 1
    I updated the answer to include some `extra` examples. – Harry Apr 22 '16 at 17:47
  • Still doesn't say anything about binding to pgcrypto. – Oin Apr 22 '16 at 18:23
  • The algorithm they use are identical. The "2a" variant. Look at the first few bytes '$2a$', 2a variant and passlib.hash.bcrypt uses this algorithm. – Harry Apr 22 '16 at 18:27
  • 2
    You don't need to bind to pgcrypto to do what you need to do. The algorithms used are identical or the code in my answer literally couldn't work. If two different cryptographic algorithms produce the same result there's something very wrong with the algorithms and the code above proves the algorithms used are indeed identical. – Harry Apr 22 '16 at 18:40