Bind to pgcrypto from python
Asked Answered
E

1

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
Egress answered 11/4, 2016 at 14:52 Comment(6)
It is complaining about InterruptPending, which library does this symbol belongs to?Flatfish
Postgres. I can find it at: doxygen.postgresql.org/globals_8c_source.html#l00029 but I don't know how to link it.Egress
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?Volcanism
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.Egress
Read this and just use psycopg2 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.Volcanism
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.Mendelssohn
M
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()
Mendelssohn answered 20/4, 2016 at 23:48 Comment(5)
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 doxygen.postgresql.org/crypt-blowfish_8c_source.htmlEgress
I updated the answer to include some extra examples.Mendelssohn
Still doesn't say anything about binding to pgcrypto.Egress
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.Mendelssohn
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.Mendelssohn

© 2022 - 2024 — McMap. All rights reserved.