Scrambling by shifting bits across byte boundaries

TrustyTony 0 Tallied Votes 619 Views Share

There was question about encryption in Delphi/Pascal forum and a guy was suggested reversing the bits as additional step (I do not know if he in reality meant XOR 255, that is making 0's 1's and vice versa). As it keeps still same distribution of letters I suggested as better added scrambling to shift letters as whole right.

Shifting bits across byte boundary was interesting code, so I presented pseudo code and here is the implementation part. I added parameter for number of bits in letter, so this could be applicable for uni code also as the ord function works there and char function is selected according to bits per letter value. However as normal letters have tell tale 8 zero upper bits, the shift would immediately be obvious. If cracker suspects a shift, it is of course easy to reveal by proving all shifts like when breaking Caesar cipher. It does however mix up the letter frequencies, but probably not completely, as ascii values of normal ascii letters are in narrow band by which the bit shift could be revealed also.

from __future__ import print_function
from random import randint

def n_low_bits(n, value, bits=8):
    " returns n low bits of ord of c shifted as upper n bits) "
    return (value & ( (2 ** bits - 1) >> (bits - n) )) << (bits - n)

assert n_low_bits(5, ord('T')) == 0b10100000

def zbin(c, bits=8):
    try:
        return bin(ord(c))[2:].zfill(bits)
    except TypeError:
        return bin(c)[2:].zfill(bits)

                     
# shift bits n bits left and fil ends with random bits
def scramble(message, n, bits=8):
    # noice bits for ends not to leave tell tale zero tails
    # positive bits modulo number of bits
    n = n % bits
    values = 2**bits
    
    # for more than byte wide ord works but not chr
    char = unichr if bits > 8 else chr
    start, end = randint(1,2**bits-1), randint(1,2**bits-1)
    # take out need by dealing last letter separately, by adding end to message
    message = message + char(end)
    scrambleed = ""
    bits_in = n_low_bits(n, start, bits)
    for character in message:
        bitshifted = bits_in | (ord(character) >> n)
        scrambleed += char(bitshifted)
        bits_in = n_low_bits(n, ord(character), bits) 
    return scrambleed

def unscramble(message, n, bits=8):
    values = 2**bits
    return scramble(message, -n, bits)[1:-1]

if __name__ == '__main__':
    #making more interesting looking output from unicode text: bits = 16,
    #however the shift is then revealed by the shift of eight zero bits of normal letters
    #for unicode input should first do a compression of the message (uncompression after unscramble)
    message, n, bits = 'Xor encryption sucks!', 4, 8
    print(message)
    print('Length %i' % len(message))
    print(n * ' ' + ''.join(zbin(c) for c in message))

    encoded = scramble(message, n, bits)
    print(''.join(zbin(c) for c in encoded))
    print(encoded)
    print('\nLength %i' % len(encoded))

    decoded = unscramble(encoded, n, bits)
    print('\nLength %i' % len(decoded))
    print(decoded)