views:

137

answers:

4

I found a simple pure python blowfish implementation that meets my needs for a particular project.

There's just one part of it that bothers me:

def initialize(key):
    """
    Use key to setup subkeys -- requires 521 encryptions
    to set p and s boxes.  key is a hex number corresponding
    to a string of 32 up to 448 1s and 0s -- keylen says
    how long
    """    

    # Note that parray and sboxes are globals that have been pre-initialized.

    hexkey = hex(key)[2:]
    if hexkey[-1]=='L':
       hexkey = hexkey[:-1]

    if len(hexkey)%2==1:
        hexkey = '0'+hexkey

    lenkey = len(hexkey)/8    
    if lenkey==0:
        pos=0

    # XOR key segments with P-boxes

    for i in range(18):
        if lenkey>0:
            pos = (i%lenkey)*8  # offset into key gives subkey

        subkey = eval('0x'+hexkey[pos:pos+8]+'L')
        parray[i] ^= subkey  # immediate XOR -- Python 2.0+ syntax


    # encrypt 0-data, then keep re-encrypting and reassigning P-boxes

    output = 0L
    for i in range(0,17,2):
        output = bfencrypt(output)
        parray[i], parray[i+1] = output>>32, output & 0xFFFFFFFFL

    # re-encrypt and reassign through all the S-boxes        

    for i in range(4):
        for j in range(0,255,2):
            output = bfencrypt(output)
            sbox[i][j],sbox[i][j+1] = output>>32, output & 0xFFFFFFFFL

    # print "Initialization complete"

subkey = eval('0x'+hexkey[pos:pos+8]+'L')? Please tell me there's a better way to do this.

Isn't there a way to refactor this to use an actual integer type rather than hex values in a string?

+5  A: 

Yes. Use int() with a base of 16.

>>> int('ffffffff',16)
4294967295L

so:

subkey = int(hexkey[pos:pos+8], 16)

should do the same thing without needing eval.

[Edit] In fact, there's generally no reason why you'd need to convert to a string representation at all, given an integer - you can simply extract out each 32 bit value by ANDing with 0xffffffff and shifting the key right by 32 bits in a loop. eg:

subkeys = []
while key:
    subkeys.append(key & 0xffffffff)
    key >>= 32

if not subkeys: subkeys = [0] # Handle 0 case
subkeys.reverse() # Use same order as before (BUT SEE BELOW)

However, this initialization process seems a bit odd - it's using the hex digits starting from the left, with no zero padding to round to a multiple of 8 hex digits (so the number 0x123456789 would be split into 0x12345678 and 0x9, rather than the more customary 0x00000001 and 0x23456789. It also repeats these numbers, rather than treating it as a single large number. You should check that this code is actually performing the correct algorithm.

Brian
it performs correctly against schneier's test vectors: http://www.schneier.com/code/vectors.txt. That said, I'm going to rewrite it against schneier's c code (which isn't very complicated) rather than mess with this string slice silliness.
ʞɔıu
Could it be the particular key that is used? If it lacks a leading zero nybble (94% likely), there won't actually be a difference, since the string will be a multiple of 8 hex digits. Otherwise it will split at different boundaries to the normal internal representation.
Brian
+1  A: 

An alternative is "int('0x111', 0)". int's second argument is the base. "0" means "use the usual rules: no prefix is decimal, 0 prefix is octal and 0x is hexa -- just like eval".

This is the preferred way to "emulate" the eval operation for intifying strings.

moshez
A: 

You can do this with the long function:

subkey = long(hexkey[pos:pos+8], 16)

From help(long):

class long(object)
| long(x[, base]) -> integer
|
| Convert a string or number to a long integer, if possible. A floating
| point argument will be truncated towards zero (this does not include a
| string representation of a floating point number!) When converting a
| string, use the optional base. It is an error to supply a base when
| converting a non-string.

Tom Viner
+2  A: 

Don't use this code, much less try to improve it.

Using crypto code found on the internet is likely to cause serious security failures in your software. See Jeff Atwood's little series on the topic.

It is much better to use a proven crypto library at the highest possible level of abstraction. Ideally one that implements all the key handling in C and takes care of destroying key material after use.

One problem of doing crypto in Python is that you have no control over proliferation of key material in memory due to the nature of Python strings and the garbage collection process.

Ber