简体   繁体   中英

XOR'ing bitstrings

I am trying to implements an RC4 encrypt/decrypt of plaintext and I've been stuck on the XOR step for a while now.

My code looks like this:

def byteXOR(a, b):
    #TODO take two lists of byte string and return a list with the xor product of them
    u = a.split()
    v = b.split()
    print('Fst: \t {}'.format(u))
    print('Snd: \t {} \n'.format(v))

    '''Fill inn 0' if a bit is shorter than the other'''
    for x,y in zip(u, v):
        if len(x) != len(y):
            if len(x) < len(y):
                x.zfill(len(y) - len(x))
            elif len(x) > len(y):
                y.zfill(len(x) - len(y))

    xor = [ord(x) ^ ord(y) for (x, y) in zip(a, b)]
    print('Fst: \t {}'.format(u))
    print('Snd: \t {}'.format(v))
    print('XOR: \t {}'.format(xor))
    return xor

p = "1111 1010001 10111111 11111010 10101011"
q = "1101000 1100101 1101100 1101100 1101111"
byteXOR(p, q)

I get this output:

Fst:     ['1111', '1010001', '10111111', '11111010', '10101011']
Snd:     ['1101000', '1100101', '1101100', '1101100', '1101111'] 

Fst:     ['1111', '1010001', '10111111', '11111010', '10101011']
Snd:     ['1101000', '1100101', '1101100', '1101100', '1101111']
XOR:     [0, 0, 1, 0, 16, 1, 0, 17, 1, 1, 0, 1, 17, 1, 1, 17, 0, 0, 1, 0, 0, 16, 1, 17, 0, 0, 1, 1, 0, 0, 16, 17, 1, 0, 0, 0, 1, 0, 0]

The first problem I can't seem to solve is how to make sure the bit strings are of the same length, I used the builtin method zfill() but when I print the lists they are unchanged.

The second problem I have is how to get the desired output of the XOR product of the n'th elements from each list, like this:

Fst:      ['0001111', '1010001', '10111111', '11111010', '10101011']
Snd:      ['1101000', '1100101', '01101100', '01101100', '01101111']
XOR:      ['0110011', '0110100', '11010011', '10010110', '11000100']

Regarding XOR-ing you need convert str s to int s, use ^ , then convert to bin representation, to make it between according element of your list you might use map ie:

Fst = ['0001111', '1010001', '10111111', '11111010', '10101011']
Snd = ['1101000', '1100101', '01101100', '01101100', '01101111']
result = list(map(lambda x,y: bin(int(x,2)^int(y,2)), Fst, Snd))
print(result)

Output

['0b1100111', '0b110100', '0b11010011', '0b10010110', '0b11000100']

Note that bin returns representation starting with 0b and as few digit as possible, so if you want to have fixed width without 0b you need to jettison two first characters and then use zfill ie

final_result = [i[2:].zfill(8) for i in result]
print(final_result)

Output:

['01100111', '00110100', '11010011', '10010110', '11000100']

Note second optional argument for int function allowing specifing base of number, it can be any value from 2 to 36 . For example:

print(int('FFF',16))

outputs:

4095

and so on

Does this work for you?

def byteXOR(a, b):
    u = [int(bits, 2) for bits in a.split()]
    v = [int(bits, 2) for bits in b.split()]
    xor =  [f ^ s for (f, s) in zip(u, v)]
    print(" ".join([bin(bits).replace("0b", "") for bits in xor]))
    return xor

p = "1111 1010001 10111111 11111010 10101011"
q = "1101000 1100101 1101100 1101100 1101111"
byteXOR(p, q)

For your first problem, str.zfill() returns a new string, it doesn't modify the original string. As such, you have to reassign it to your variables:

for x,y in zip(u, v):
    if len(x) != len(y):
        if len(x) < len(y):
            x = x.zfill(len(y) - len(x))
        elif len(x) > len(y):
            y = y.zfill(len(x) - len(y))

For your second problem, you can cast the strings to int() and pass a base=2 parameter to say its in binary. You can then do the XOR '^' on the integers, then cast it back to a binary string using bin()

x = '0001111'
y = '1101000'

x = int(x, base=2)
y = int(y, base=2)

z = str(bin(x ^ y))[2:]  # remove the trailing 0b from the string
print(z)

Output:

1100111

That being said, since int(string, base=2) converts a string to integers, you really don't need to zfill() the bytes first, and you can use zip() to do this in one line as follows:

p = "1111 1010001 10111111 11111010 10101011"
q = "1101000 1100101 1101100 1101100 1101111"

r = [bin(int(x, base=2) ^ int(y, base=2))[2:].zfill(8) for x, y in zip(p.split(' '), q.split(' '))]

print(' '.join(r))

Output:

01100111 00110100 11010011 10010110 11000100

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM