Author | Message | Time |
---|---|---|
aton | i am looking for the python code. (before a flaming starts again, this is just a kind question, i am not expecting anyone to do anything for me. if nobody has done this yet, i will convert some c code on my own. no offense.) | June 29, 2009, 5:45 PM |
HdxBmx27 | Don't you already have a XSHA-1 Implementation in python? It's not hard to hash the password: XSHA1(ClientToken, ServerToken, XSHA1(Password)) [It may be Server, then client, but meh] No conversion of anything needed just implementation. | June 29, 2009, 7:51 PM |
aton | ah okay i was just looking at the bncsutil source and realised that its the same old bsha1 ;) | June 29, 2009, 9:32 PM |
aton | my conversion of the bncsutil function: [code] import struct import bsha1 def doubleHashPassword(password, clienttoken, servertoken): b1=bsha1.bsha() b1.update(password) b2=bsha1.bsha() b2.update(struct.pack("I", clienttoken)+struct.pack("I", servertoken)+b1.digest()) return b2.digest() [/code] works with the bsha1 module i posted here: https://davnit.net/bnet/vL/index.php?topic=17932.0 | June 30, 2009, 11:32 AM |
xpeh | Will some1 release the ready module? Even beta. | June 30, 2009, 11:50 PM |
aton | i just posted a link to the ready module... | July 1, 2009, 1:00 AM |
xpeh | i am sorry, but i dont see any download link :) Can you post ready downloadable module with some examples? | July 1, 2009, 7:49 AM |
xpeh | Actually, do some1 want to make global lib like BNCSUtil/MBNCSUtil, that can do almost anything, in python? | July 1, 2009, 7:53 AM |
ThePro | [quote author=aton link=topic=17995.msg182996#msg182996 date=1246361547] my conversion of the bncsutil function: [code] import struct import bsha1 def doubleHashPassword(password, clienttoken, servertoken): b1=bsha1.bsha() b1.update(password) b2=bsha1.bsha() b2.update(struct.pack("I", clienttoken)+struct.pack("I", servertoken)+b1.digest()) return b2.digest() [/code] works with the bsha1 module i posted here: https://davnit.net/bnet/vL/index.php?topic=17932.0 [/quote] Hm, this doesn't work for me. That what I've done: [code] import crypt import struct b1 = crypt.bsha() b1.update(password.lower()) b2 = crypt.bsha() b2.update(struct.pack("I", client_token) + struct.pack("I", server_token) + b1.digest() password_hash = b2.digest() [/code] I get a hash with a length of 20, but it is different from the hash of BNLS, so I always get an invalid password response from bnet. My hash: ClientToken: 45038296 ServerToken: 257613900 Password: funnypw Hash from lib (which is wrong): 2bce05fd6ce14a7dc726b1a81fc9eb63a87c00dd Aton, did you login successfully with that? Do you get the same wrong hash? If no, any Ideas what I made wrong? | July 5, 2009, 10:16 PM |
HdxBmx27 | Using those test values the final hash should be: DF63B84BC7B60DAD0EFFBFD3094C6C376949CBE3 The first hash of the password should be: DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1 Can you confirm that the 1st hash is the same, if not it's the SHA1 at fault, if it is the same then look into pack (though that *should* work) | July 5, 2009, 11:31 PM |
ThePro | [quote author=Hdx link=topic=17995.msg183039#msg183039 date=1246836714] Using those test values the final hash should be: DF63B84BC7B60DAD0EFFBFD3094C6C376949CBE3 The first hash of the password should be: DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1 Can you confirm that the 1st hash is the same, if not it's the SHA1 at fault, if it is the same then look into pack (though that *should* work) [/quote] Nope, its not: [code] b1 = crypt.bsha(True) #broken=True -> bsha1 b1.update("funnypw") b1.hexdigest() >>> 'ac80f2db821b8d6c4eb5d4f447026bfce1b8a904' #Which is wrong. Is has to be DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1 b1 = crypt.bsha(False) #broken=False -> sha1 b1.update("funnypw") b1.hexdigest() >>> '20edff4fc4edcc25e85b3f4e867008fb8ef8dac2' #Which is correct, as you can see here: http://www.tech-faq.com/sha-1-generator.shtml [/code] It seems this lib doesen't work. Since aton was releasing the module I'm sure it was working for him, so thats the confisuing part o_O. I'm using an intel 32Bit core dou processor, maybe he wrote this lib for a 64Bit processor!? Here's the code of Atons pythonmodule I used: [code] import struct import socket def _long2bytesBigEndian(n, blocksize=0): """Convert a long integer to a byte string. If optional blocksize is given and greater than zero, pad the front of the byte string with binary zeros so that the length is a multiple of blocksize. """ # After much testing, this algorithm was deemed to be the fastest. s = '' pack = struct.pack while n > 0: s = pack('>I', n & 0xffffffffL) + s n = n >> 32 # Strip off leading zeros. for i in range(len(s)): if s[i] <> '\000': break else: # Only happens when n == 0. s = '\000' i = 0 s = s[i:] # Add back some pad bytes. This could be done more efficiently # w.r.t. the de-padding being done above, but sigh... if blocksize > 0 and len(s) % blocksize: s = (blocksize - len(s) % blocksize) * '\000' + s return s def _bytelist2longBigEndian(list): "Transform a list of characters into a list of longs." imax = len(list)/4 hl = [0L] * imax j = 0 i = 0 while i < imax: b0 = long(ord(list[j])) << 24 b1 = long(ord(list[j+1])) << 16 b2 = long(ord(list[j+2])) << 8 b3 = long(ord(list[j+3])) hl[i] = b0 | b1 | b2 | b3 i = i+1 j = j+4 return hl def _rotateLeft(x, n): "Rotate x (32 bit) left n bits circularly." return (x << n) | (x >> (32-n)) # ====================================================================== # The SHA transformation functions # # ====================================================================== def f0_19(B, C, D): return (B & C) | ((~ B) & D) def f20_39(B, C, D): return B ^ C ^ D def f40_59(B, C, D): return (B & C) | (B & D) | (C & D) def f60_79(B, C, D): return B ^ C ^ D f = [f0_19, f20_39, f40_59, f60_79] # Constants to be used K = [ 0x5A827999L, # ( 0 <= t <= 19) 0x6ED9EBA1L, # (20 <= t <= 39) 0x8F1BBCDCL, # (40 <= t <= 59) 0xCA62C1D6L # (60 <= t <= 79) ] class bsha: "broken sha for blizzard stuff" digest_size = digestsize = 20 def __init__(self, broken=True): "Initialisation." self.broken=broken # Initial message length in bits(!). #self.length = 0L self.count = [0, 0] # Initial empty message as a sequence of bytes (8 bit characters). self.input = [] # Call a separate init function, that can be used repeatedly # to start from scratch on the same object. self.init() def init(self): "Initialize the message-digest and set all fields to zero." #self.length = 0L self.input = [] # Initial 160 bit message digest (5 times 32 bit). self.H0 = 0x67452301L self.H1 = 0xEFCDAB89L self.H2 = 0x98BADCFEL self.H3 = 0x10325476L self.H4 = 0xC3D2E1F0L def _transform(self, W): if self.broken: for t in range(0, 16): W[t]=socket.htonl(W[t]) for t in range(16, 80): if self.broken: W.append(_rotateLeft(1, (W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])&31) & 0xffffffffL) else: W.append(_rotateLeft(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffffL) A = self.H0 B = self.H1 C = self.H2 D = self.H3 E = self.H4 """ This loop was unrolled to gain about 10% in speed for t in range(0, 80): TEMP = _rotateLeft(A, 5) + f[t/20] + E + W[t] + K[t/20] E = D D = C C = _rotateLeft(B, 30) & 0xffffffffL B = A A = TEMP & 0xffffffffL """ for t in range(0, 20): TEMP = _rotateLeft(A, 5) + ((B & C) | ((~ B) & D)) + E + W[t] + K[0] E = D D = C C = _rotateLeft(B, 30) & 0xffffffffL B = A A = TEMP & 0xffffffffL for t in range(20, 40): TEMP = _rotateLeft(A, 5) + (B ^ C ^ D) + E + W[t] + K[1] E = D D = C C = _rotateLeft(B, 30) & 0xffffffffL B = A A = TEMP & 0xffffffffL for t in range(40, 60): TEMP = _rotateLeft(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2] E = D D = C C = _rotateLeft(B, 30) & 0xffffffffL B = A A = TEMP & 0xffffffffL for t in range(60, 80): TEMP = _rotateLeft(A, 5) + (B ^ C ^ D) + E + W[t] + K[3] E = D D = C C = _rotateLeft(B, 30) & 0xffffffffL B = A A = TEMP & 0xffffffffL self.H0 = (self.H0 + A) & 0xffffffffL self.H1 = (self.H1 + B) & 0xffffffffL self.H2 = (self.H2 + C) & 0xffffffffL self.H3 = (self.H3 + D) & 0xffffffffL self.H4 = (self.H4 + E) & 0xffffffffL # Down from here all methods follow the Python Standard Library # API of the sha module. def update(self, inBuf): """Add to the current message. Update the md5 object with the string arg. Repeated calls are equivalent to a single call with the concatenation of all the arguments, i.e. m.update(a); m.update(b) is equivalent to m.update(a+b). The hash is immediately calculated for all full blocks. The final calculation is made in digest(). It will calculate 1-2 blocks, depending on how much padding we have to add. This allows us to keep an intermediate value for the hash, so that we only need to make minimal recalculation if we call update() to add more data to the hashed string. """ leninBuf = long(len(inBuf)) # Compute number of bytes mod 64. index = (self.count[1] >> 3) & 0x3FL # Update number of bits. self.count[1] = self.count[1] + (leninBuf << 3) if self.count[1] < (leninBuf << 3): self.count[0] = self.count[0] + 1 self.count[0] = self.count[0] + (leninBuf >> 29) partLen = 64 - index if leninBuf >= partLen: self.input[index:] = list(inBuf[:partLen]) self._transform(_bytelist2longBigEndian(self.input)) i = partLen while i + 63 < leninBuf: self._transform(_bytelist2longBigEndian(list(inBuf[i:i+64]))) i = i + 64 else: self.input = list(inBuf[i:leninBuf]) else: i = 0 self.input = self.input + list(inBuf) def digest(self): """Terminate the message-digest computation and return digest. Return the digest of the strings passed to the update() method so far. This is a 20-byte string which may contain non-ASCII characters, including null bytes. """ H0 = self.H0 H1 = self.H1 H2 = self.H2 H3 = self.H3 H4 = self.H4 input = [] + self.input count = [] + self.count index = (self.count[1] >> 3) & 0x3fL if index < 56: padLen = 56 - index else: padLen = 120 - index if self.broken: padding = ['\000'] * 64 else: padding = ['\200'] + ['\000'] * 63 self.update(padding[:padLen]) if self.broken: bits = _bytelist2longBigEndian(self.input[:56]) + [0L,0L] else: # Append length bits = _bytelist2longBigEndian(self.input[:56])+count self._transform(bits) # Store state in digest. digest = _long2bytesBigEndian(self.H0, 4) + \ _long2bytesBigEndian(self.H1, 4) + \ _long2bytesBigEndian(self.H2, 4) + \ _long2bytesBigEndian(self.H3, 4) + \ _long2bytesBigEndian(self.H4, 4) self.H0 = H0 self.H1 = H1 self.H2 = H2 self.H3 = H3 self.H4 = H4 self.input = input self.count = count return digest def hexdigest(self): """Terminate and return digest in HEX form. Like digest() except the digest is returned as a string of length 32, containing only hexadecimal digits. This may be used to exchange the value safely in email or other non- binary environments. """ return ''.join(['%02x' % ord(c) for c in self.digest()]) # ====================================================================== # Mimic Python top-level functions from standard library API # for consistency with the md5 module of the standard library. # ====================================================================== # These are mandatory variables in the module. They have constant values # in the SHA standard. digest_size = digestsize = 20 blocksize = 1 [/code] | July 6, 2009, 12:14 AM |
Ringo | [quote author=ThePro link=topic=17995.msg183040#msg183040 date=1246839263] >>> 'ac80f2db821b8d6c4eb5d4f447026bfce1b8a904' #Which is wrong. Is has to be DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1 [/quote] Looks fine, apart from each dword having reversed byte order. | July 6, 2009, 4:36 AM |
xpeh | Instead of [code] def hexdigest(self): return ''.join(['%02x' % ord(c) for c in self.digest()]) [/code] you must use [code] def hexdigest(self): return ''.join(['%08x' % i for i in self.getintarray()]) [/code] There must be a way to get int array instead of byte array. | July 6, 2009, 6:32 AM |
ThePro | [quote author=Ringo link=topic=17995.msg183043#msg183043 date=1246854987] [quote author=ThePro link=topic=17995.msg183040#msg183040 date=1246839263] >>> 'ac80f2db821b8d6c4eb5d4f447026bfce1b8a904' #Which is wrong. Is has to be DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1 [/quote] Looks fine, apart from each dword having reversed byte order. [/quote] Ahhh Thanks alot, Ringo! I must be an idiot that I didn't see that! The solution was just changing ONE! byte in the sourcecode: [code] #original def _long2bytesBigEndian(n, blocksize=0): ... s = pack('>I', n 0xffffffffL) + s ... #changed def _long2bytesBigEndian(n, blocksize=0): ... s = pack('<I', n 0xffffffffL) + s ... [/code] Now it works perfectly. :) | July 6, 2009, 11:18 AM |
Yegg | The < states you want the result in little-endian. Before you used either, the struct module returns a value based on whichever endian your native system uses. Just curious, but what is your processor brand and its specs? | July 7, 2009, 8:16 AM |
ThePro | [quote author=Yegg link=topic=17995.msg183048#msg183048 date=1246954596] The < states you want the result in little-endian. Before you used either, the struct module returns a value based on whichever endian your native system uses. Just curious, but what is your processor brand and its specs? [/quote] I know, thats the weird thing. Processors: Notbook: Intel T2250 First Server: Intel Pentium 4 Second Server: Intel Xeon 3060 All are working with the LittleEndian method. Dont ask me why... | July 7, 2009, 10:55 AM |
Explicit[nK] | It's not unusual to handle the byte ordering as the packets go both out onto the wire and back. The idea is to forego any endianness issues that may arise since your code will then be designed to handle the ordering natively on any system running it. | July 7, 2009, 3:43 PM |
Myndfyr | [quote author=ThePro link=topic=17995.msg183049#msg183049 date=1246964132] All are working with the LittleEndian method. Dont ask me why... [/quote] More than likely because the protocol uses little-endian byte ordering. | August 3, 2009, 8:04 AM |