Más rápido xor bit a bit entre dos variables de datos binarios multibyte

Cuál es la forma más rápida de implementar la siguiente lógica:

def xor(data, key):
    l = len(key)

    buff = ""
    for i in range(0, len(data)):
        buff += chr(ord(data[i]) ^ ord(key[i % l]))
    return buff

En mi casollav es un resumen de 20 bytes sha1 ydato son algunos datos binarios entre 20 bytes y pocos (1, 2, 3) megabytes de longitud

ACTUALIZAR

Ok muchachos. Aquí hay una implementación 3.5 veces más rápida, que divide datos y clave por fragmentos de 4, 2 o 1 bytes (en mi caso, la mayoría de las veces es un entero largo de 4 bytes):

def xor(data, key):
    index = len(data) % 4
    size = (4, 1, 2, 1)[index]
    type = ('L', 'B', 'H', 'B')[index]
    key_len = len(key)/size
    data_len = len(data)/size
    key_fmt = "<" + str(key_len) + type;
    data_fmt = "<" + str(data_len) + type;

    key_list = struct.unpack(key_fmt, key)
    data_list = struct.unpack(data_fmt, data)

    result = []
    for i in range(data_len):
        result.append (key_list[i % key_len] ^ data_list[i])

    return struct.pack(data_fmt, *result)

Utiliza mucha memoria, pero en mi caso no es gran cosa.

¿Alguna idea de cómo aumentar la velocidad unas pocas veces más? : -)

ACTUALIZACIÓN FINAL:

OK, ok ... numpy hizo el trabajo. Eso es increíblemente rápido:

def xor(data, key):
    import numpy, math

    # key multiplication in order to match the data length
    key = (key*int(math.ceil(float(len(data))/float(len(key)))))[:len(data)]

    # Select the type size in bytes       
    for i in (8,4,2,1):
        if not len(data) % i: break

    if i == 8: dt = numpy.dtype('<Q8');
    elif i == 4: dt = numpy.dtype('<L4');
    elif i == 2: dt = numpy.dtype('<H2');
    else: dt = numpy.dtype('B');

    return numpy.bitwise_xor(numpy.fromstring(key, dtype=dt), numpy.fromstring(data, dtype=dt)).tostring()

La implementación inicial necesitó 8 minutos y 50 segundos para procesar un gigabyte, el segundo, alrededor de 2 minutos y 30 segundos y el último solo ... 0 minutos y 10 segundos.

Gracias a cualquiera que contribuyó con ideas y código. ¡Son geniales chicos!

Respuestas a la pregunta(6)

Su respuesta a la pregunta