Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

md5 hash/digest and sprintf

md5

This topic has been archived. This means that you cannot reply to this topic.
6 replies to this topic

#1 kimbo

kimbo

    CC Lurker

  • New Member
  • Pip
  • 4 posts

Posted 09 January 2013 - 02:27 AM

I am trying to port some code from C to Python. I have most of it done and working but I'm stuck on a C sprintf statement. It looks simple but I just can't get it. Could someone tell me how to translate that sprintf() or if you feel I'm off on entirely the wrong direction please feel free to deflect me onto a better direction. I'm using Python 2.7 but would update to 3.x if it would make the job easier.

The C code is in a .cpp module for a C++-ncurses app called boinctui. I'll provide the code snippet in question further down but if you want the entire 50KB source package you can download it here. If you are familiar with the BOINC distributed computing platform then you already familiar with BOINC client. BOINC client (the client for short) can be controlled/monitored over an RPC (remote procedure call) interface via TCP. My Python script is exactly for that purpose. The client opens a socket in listen mode on port 31416 and waits for managing software, for example my script, to connect and issue directives or request state information. The connection/login is secure i.e. one must jump through some hoops before the client will accept a connection. The sequence is:

1) manager (my script) sends "<auth1>" to signal it wishes a connection
2) client responds with a nonce
3) manager responds with a hash of the nonce, the password, length of the nonce and length of the password

The hash is essentially an md5 digest. I have my code working up to the point of creating the md5 digest. Then that digest must be massaged one more way that I cannot understand. The sprintf statement from the boinctui application does the massaging but I cannot translate it into Python for love nor money. Please help.

The following code snippet is from boinctui. It's the relevant portion of the Srv::login() function in the srvdata.cpp module in the source package I linked to above. I quote most of the function here to put the sprintf statement I stumble over into context though you might not find that necessary. The sprintf is the 4th line from the bottom. My port to Python follows this first snippet

bool Srv::login() //авторизоваться на сервере
{
bool result = false;
if (strlen(pwd) == 0)
return true; //пароль не задан (считаем что логин серверу не требуется)
//получить случайную строку (nonce)
Item* r1 = req("<auth1/>");
if (r1 == NULL)
return result;
kLogPrintf("login() nonce='%s'\n", r1->toxmlstring().c_str());
Item* nonce = r1->findItem("nonce");
if ( nonce == NULL )
{
delete r1;
return result;
}
const char* snonce = r1->findItem("nonce")->getsvalue();
//расчитать хэш md5 от nonce+pwd
unsigned char md5digest[MD5_DIGEST_LENGTH];
MD5_CTX c;
MD5_Init(&c);
MD5_Update(&c, snonce, strlen(snonce));
MD5_Update(&c, pwd , strlen(pwd));
MD5_Final(md5digest,&c);
char shash[1024]; //строковое представление хэша
for (int i=0;i<MD5_DIGEST_LENGTH;i++)
	 sprintf(shash+i*2,"%02x",md5digest[i]);
kLogPrintf("login() md5_hash '%s%s' = %d\n",snonce,pwd,shash);
//вторая фаза авторизации
Item* r2 = req("<auth2>\n<nonce_hash>%s</nonce_hash>\n</auth2>",shash);


My "somewhat equivalent" to Srv::login():

def login(host, port, passwd):

# print 'passwd:', passwd
s = socket()
s.connect((host, port))
s.send(mak_req('<auth1/>'))
r = s.recv(1024)
print 'r:', r
r2 = r.replace('boinc_gui_rpc_reply', '').replace('nonce','').replace('/', '').replace('\n', '').replace('\03', '').replace('<>', '')
print 'r2:', r2
hash_phrase = r2 + str(len(r2)) + passwd + str(len(passwd))
shash = md5()

shash.update(hash_phrase)

s.send(mak_req('<auth2>\n<nonce_hash>' + shash.hexdigest() + '</nonce_hash>\n</auth2>'))
r3 = s.recv(1024)
print 'r3:', r3

return s

The script begins with these imports and the call to login():

from socket import socket
from hashlib import md5

socket = login('127.0.0.1', 31416, 'password')
socket.close()

Edited by kimbo, 09 January 2013 - 02:31 AM.


#2 Flying Dutchman

Flying Dutchman

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1090 posts

Posted 10 January 2013 - 06:59 AM

a = "date: %04d-%02d-%02d" % (2013, 1, 10)

Something like that.

The roots of education are bitter, but the fruit is sweet.


#3 kimbo

kimbo

    CC Lurker

  • New Member
  • Pip
  • 4 posts

Posted 10 January 2013 - 05:12 PM

Lol, yes Flying Dutchman, something like that.

Thanks for your clue but I knew that when I posted. I should have posted all the things something like that that I tried.

Of course the problem is the python interpreter doesn't turn "something like that" into "exactly what I want". I don't know why it cannot do that, I have tried so many different things "like that" it should know what I want by now and just do it for me, lol. Like my wife always knows what I want. No, wait... it's not like that... the reason my wife knows is not because I want only one thing. I want many things.

Anyway. Maybe a woman should write the Python interpreter. It might be more understanding. Unfortunately it would never give an error message, it would just say "everything is just wonderful" then hang.

I'll keep experimenting.

#4 Flying Dutchman

Flying Dutchman

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1090 posts

Posted 11 January 2013 - 02:47 AM

Maybe something like this? :P
>>> hash = "7b96e636e4bd247fc6dfe3371a194766"
>>> s = "".join(["%02s" % x for x in hash])
>>> s
' 7 b 9 6 e 6 3 6 e 4 b d 2 4 7 f c 6 d f e 3 3 7 1 a 1 9 4 7 6 6'
>>> s.replace(' ', '0')
'070b09060e0603060e040b0d0204070f0c060d0f0e030307010a010904070606'

The roots of education are bitter, but the fruit is sweet.


#5 kimbo

kimbo

    CC Lurker

  • New Member
  • Pip
  • 4 posts

Posted 11 January 2013 - 10:34 PM

Maybe something like this? :P

>>> hash = "7b96e636e4bd247fc6dfe3371a194766"
>>> s = "".join(["%02s" % x for x in hash])
>>> s
' 7 b 9 6 e 6 3 6 e 4 b d 2 4 7 f c 6 d f e 3 3 7 1 a 1 9 4 7 6 6'
>>> s.replace(' ', '0')
'070b09060e0603060e040b0d0204070f0c060d0f0e030307010a010904070606'


Thanks. I already tried that using slightly different code but it didn't work. And your much nicer code doesn't work either to the extend that the code still can't connect to the BOINC client listening on port 31416.

I think it's because you and I both misinterpreted what the sprintf statement is doing. At first I thought same as you, that the sprintf is merely inserts a 0 between each char in the hash string. But I think what it actually does is take each char from the hash string and append that char's hex ASCII value to shash in a 2 char wide field. Python's binascii.hexlify() does that in one statement:

Python 2.7.3 (default, Sep 26 2012, 21:51:14)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from binascii import hexlify
>>> hexlify('62d86af0')
'3632643836616630'
>>>

Unfortunately that approach does not get me logged in either :-(

The possible reasons are:

1) neither of us has correctly reverse-engineered that sprintf statement yet
2) Python's md5 code has a bug in it and isn't generating a correct hash
3) I am not using Python's md5 functions correctly

Finally I have the gcc compiler working so I think the smartest way for me to proceed is to stop guessing and compile the C code and see exactly what it produces for a hash and also see what the sprintf() does to that hash. Then I may as well write the whole app in C except I love Python's high level string and list handling abilities. I'm targeting Linux, OSX and Windows so Python solves some cross-platform issues too.

#6 Flying Dutchman

Flying Dutchman

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1090 posts

Posted 12 January 2013 - 04:24 AM

sprintf writes a formatted (2nd parameter) string, with arguments (3rd parameter) to destination (1st parameter). In your case

sprintf(shash+i*2,"%02x",md5digest[i]);

it writes to every even offset of shash a hex value (2 spaces wide with leading zeroes) of md5digest.

I think the problem is on this line:

hash_phrase = r2 + str(len(r2)) + passwd + str(len(passwd))
# should be
hash_phrase = r2 + passwd

In C function MD5_Update, 3rd argument is length of 2nd argument. You don't need to pass length in Python because the method update does that for you.

The roots of education are bitter, but the fruit is sweet.


#7 kimbo

kimbo

    CC Lurker

  • New Member
  • Pip
  • 4 posts

Posted 13 January 2013 - 11:38 PM

sprintf writes a formatted (2nd parameter) string, with arguments (3rd parameter) to destination (1st parameter). In your case

sprintf(shash+i*2,"%02x",md5digest[i]);

it writes to every even offset of shash a hex value (2 spaces wide with leading zeroes) of md5digest.


Yes, it writes a hex value but it is the hex value of the character's position in the ASCII table. When I compile the following code
char  shash[128]
char *md5digest = "49a64dc0"
sprintf(shash+i*2,"%02x",md5digest[i]);
printf(shash)

and run it the output is 3439613634646330 not 04090a06040d0c00 because the ASCII value of "4" is 34 (hex), the ASCII value of "9" is 39 (hex), the ASCII value of a is 61 (hex) and so on.

I think the problem is on this line:

hash_phrase = r2 + str(len(r2)) + passwd + str(len(passwd))
# should be
hash_phrase = r2 + passwd

In C function MD5_Update, 3rd argument is length of 2nd argument. You don't need to pass length in Python because the method update does that for you.


I suspect you are correct about that. I tried it but I still do not get a login so I cannot say for sure if it is right.

I suspect Python is generating an incorrect md5. At the moment I don't have the proper shared lib for the md5 funtion in the C code so I cannot compile and see what it generates and compare it with Python's md5 but I will find the required shared lib soon. I can also try Python 3.x instead of the 2.7 I am using at the moment. If there is a bug in the 2.7 md5 it might be fixed in 3.x.

Thank you for your suggestions. I won't give up until I make it work and when I do I will post the solution here.

Edited by kimbo, 13 January 2013 - 11:41 PM.





Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download