def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False): """ CBC decryption mode of operation :param bytes key: encryption key :param bytes data: ciphertext :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :param bool mesh: enable key meshing :returns: plaintext :rtype: bytes """ validate_key(key) validate_sbox(sbox) if not data or len(data) % BLOCKSIZE != 0: raise ValueError("Data is not blocksize aligned") if len(data) < 2 * BLOCKSIZE: raise ValueError("There is no either data, or IV in ciphertext") iv = data[:BLOCKSIZE] plaintext = [] for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE): if ( mesh and (i - BLOCKSIZE) >= MESH_MAX_DATA and (i - BLOCKSIZE) % MESH_MAX_DATA == 0 ): key, _ = meshing(key, iv, sbox=sbox) plaintext.append(strxor( ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))), data[i - BLOCKSIZE:i], )) if pad: plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE) return b"".join(plaintext)
def cnt(key, data, iv=8 * b'\x00', sbox=DEFAULT_SBOX): """ Counter mode of operation :param bytes key: encryption key :param bytes data: plaintext :param iv: initialization vector :type iv: bytes, BLOCKSIZE length :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :return: ciphertext :rtype: bytes For decryption you use the same function again. """ validate_key(key) validate_iv(iv) validate_sbox(sbox) if not data: raise ValueError("No data supplied") n2, n1 = encrypt(sbox, key, block2ns(iv)) size, data = _pad(data) gamma = [] for _ in xrange(0, len(data), BLOCKSIZE): n1 = addmod(n1, C2, 2 ** 32) n2 = addmod(n2, C1, 2 ** 32 - 1) gamma.append(ns2block(encrypt(sbox, key, (n1, n2)))) return strxor(b''.join(gamma), data[:size])
def digest(self): """ Get hash of the provided data """ hsh = BLOCKSIZE * (b'\x01' if self.digest_size == 256 else b'\x00') chk = BLOCKSIZE * b'\x00' n = 0 data = self.data for i in xrange(0, len(data) // BLOCKSIZE * BLOCKSIZE, BLOCKSIZE): block = data[i:i + BLOCKSIZE] hsh = g(n, hsh, block) chk = add512bit(chk, block) n += 512 # Padding padblock_size = len(data) * 8 - n data += b'\x01' padlen = BLOCKSIZE - len(data) % BLOCKSIZE if padlen != BLOCKSIZE: data += b'\x00' * padlen hsh = g(n, hsh, data[-BLOCKSIZE:]) n += padblock_size chk = add512bit(chk, data[-BLOCKSIZE:]) hsh = g(0, hsh, pack("<Q", n) + 56 * b'\x00') hsh = g(0, hsh, chk) if self.digest_size == 256: return hsh[32:] return hsh
def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False): """ CBC encryption mode of operation :param bytes key: encryption key :param bytes data: plaintext :param iv: initialization vector :type iv: bytes, BLOCKSIZE length :type bool pad: perform ISO/IEC 7816-4 padding :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :param bool mesh: enable key meshing :returns: ciphertext :rtype: bytes 34.13-2015 padding method 2 is used. """ validate_key(key) validate_iv(iv) validate_sbox(sbox) if not data: raise ValueError("No data supplied") if pad: data = pad2(data, BLOCKSIZE) if len(data) % BLOCKSIZE != 0: raise ValueError("Data is not blocksize aligned") ciphertext = [iv] for i in xrange(0, len(data), BLOCKSIZE): if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0: key, _ = meshing(key, iv, sbox=sbox) ciphertext.append(ns2block(encrypt(sbox, key, block2ns( strxor(ciphertext[-1], data[i:i + BLOCKSIZE]) )))) return b"".join(ciphertext)
def cbc_encrypt(key, data, iv=8 * b'\x00', pad=True, sbox=DEFAULT_SBOX): """ CBC encryption mode of operation :param bytes key: encryption key :param bytes data: plaintext :param iv: initialization vector :type iv: bytes, BLOCKSIZE length :type bool pad: perform ISO/IEC 7816-4 padding :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :return: ciphertext :rtype: bytes Padding is following: append 0x80 and then necessary number of zeros. """ validate_key(key) validate_iv(iv) validate_sbox(sbox) if not data: raise ValueError("No data supplied") if pad: _, data = _pad(data + b'\x80') if len(data) % BLOCKSIZE != 0: raise ValueError("Data is not blocksize aligned") ciphertext = [iv] for i in xrange(0, len(data), BLOCKSIZE): ciphertext.append(ns2block(encrypt(sbox, key, block2ns( strxor(ciphertext[-1], data[i:i + BLOCKSIZE]) )))) return b''.join(ciphertext)
def S(self, A): Pi = ( \ 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, \ 218, 35, 197, 4, 77, 233, 119, 240, 219, 147, 46, \ 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, 249, \ 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, \ 139, 1, 142, 79, 5, 132, 2, 174, 227, 106, 143, \ 160, 6, 11, 237, 152, 127, 212, 211, 31, 235, 52, \ 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, \ 58, 206, 204, 181, 112, 14, 86, 8, 12, 118, 18, \ 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, 150, \ 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, \ 178, 177, 50, 117, 25, 61, 255, 53, 138, 126, 109, \ 84, 198, 128, 195, 189, 13, 87, 223, 245, 36, 169, \ 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, \ 3, 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, \ 40, 80, 78, 51, 10, 74, 167, 151, 96, 115, 30, \ 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, \ 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, \ 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172, \ 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, \ 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, \ 202, 216, 133, 97, 32, 113, 103, 164, 45, 43, 9, \ 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, \ 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, \ 75, 99, 182) result = [0] * 64 for i in xrange(0, 64): result[i] = Pi[A[i]] return result
def pbkdf2(hasher, password, salt, iterations, dklen): """PBKDF2 implementation suitable for GOST R 34.11-94/34.11-2012 """ inner = hasher() outer = hasher() password = password + b"\x00" * (inner.block_size - len(password)) inner.update(strxor(password, len(password) * b"\x36")) outer.update(strxor(password, len(password) * b"\x5C")) def prf(msg): icpy = inner.copy() ocpy = outer.copy() icpy.update(msg) ocpy.update(icpy.digest()) return ocpy.digest() dkey = b'' loop = 1 while len(dkey) < dklen: prev = prf(salt + long2bytes(loop, 4)) rkey = bytes2long(prev) for _ in xrange(iterations - 1): prev = prf(prev) rkey ^= bytes2long(prev) loop += 1 dkey += long2bytes(rkey, inner.digest_size) return dkey[:dklen]
def digest(self): """ Get hash of the provided data """ hsh = BLOCKSIZE * (b"\x01" if self.digest_size == 32 else b"\x00") chk = bytearray(BLOCKSIZE * b"\x00") n = 0 data = self.data for i in xrange(0, len(data) // BLOCKSIZE * BLOCKSIZE, BLOCKSIZE): block = data[i:i + BLOCKSIZE] hsh = g(n, hsh, block) chk = add512bit(chk, block) n += 512 # Padding padblock_size = len(data) * 8 - n data += b"\x01" padlen = BLOCKSIZE - len(data) % BLOCKSIZE if padlen != BLOCKSIZE: data += b"\x00" * padlen hsh = g(n, hsh, data[-BLOCKSIZE:]) n += padblock_size chk = add512bit(chk, data[-BLOCKSIZE:]) hsh = g(0, hsh, pack("<Q", n) + 56 * b"\x00") hsh = g(0, hsh, chk) return hsh[-self._digest_size:]
def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX): """ CBC decryption mode of operation :param bytes key: encryption key :param bytes data: ciphertext :param iv: initialization vector :type iv: bytes, BLOCKSIZE length :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :return: plaintext :rtype: bytes """ validate_key(key) validate_sbox(sbox) if not data or len(data) % BLOCKSIZE != 0: raise ValueError("Data is not blocksize aligned") if len(data) < 2 * BLOCKSIZE: raise ValueError("There is no either data, or IV in ciphertext") plaintext = [] for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE): plaintext.append(strxor( ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))), data[i - BLOCKSIZE:i], )) if pad: last_block = bytearray(plaintext[-1]) pad_index = last_block.rfind(b'\x80') if pad_index == -1: raise ValueError("Invalid padding") for c in last_block[pad_index + 1:]: if c != 0: raise ValueError("Invalid padding") plaintext[-1] = bytes(last_block[:pad_index]) return b''.join(plaintext)
def cfb_encrypt(key, data, iv=8 * b'\x00', sbox=DEFAULT_SBOX, mesh=False): """ CFB encryption mode of operation :param bytes key: encryption key :param bytes data: plaintext :param iv: initialization vector :type iv: bytes, BLOCKSIZE length :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :param bool mesh: enable key meshing :return: ciphertext :rtype: bytes """ validate_key(key) validate_iv(iv) validate_sbox(sbox) if not data: raise ValueError("No data supplied") size, data = _pad(data) ciphertext = [iv] for i in xrange(0, len(data), BLOCKSIZE): if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0: key, iv = meshing(key, ciphertext[-1], sbox=sbox) ciphertext.append(strxor( data[i:i + BLOCKSIZE], ns2block(encrypt(sbox, key, block2ns(iv))), )) continue ciphertext.append(strxor( data[i:i + BLOCKSIZE], ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))), )) return b''.join(ciphertext[1:])[:size]
def _step(hin, m, sbox): """ Step function H_out = f(H_in, m) """ # Generate keys u = hin v = m w = strxor(hin, m) k1 = P(w) u = strxor(A(u), C2) v = A(A(v)) w = strxor(u, v) k2 = P(w) u = strxor(A(u), C3) v = A(A(v)) w = strxor(u, v) k3 = P(w) u = strxor(A(u), C4) v = A(A(v)) w = strxor(u, v) k4 = P(w) # Encipher h4, h3, h2, h1 = hin[0:8], hin[8:16], hin[16:24], hin[24:32] s1 = ns2block(encrypt(sbox, k1[::-1], block2ns(h1[::-1])))[::-1] s2 = ns2block(encrypt(sbox, k2[::-1], block2ns(h2[::-1])))[::-1] s3 = ns2block(encrypt(sbox, k3[::-1], block2ns(h3[::-1])))[::-1] s4 = ns2block(encrypt(sbox, k4[::-1], block2ns(h4[::-1])))[::-1] s = b"".join((s4, s3, s2, s1)) # Permute # H_out = chi^61(H_in XOR chi(m XOR chi^12(S))) x = s for _ in xrange(12): x = _chi(x) x = strxor(x, m) x = _chi(x) x = strxor(hin, x) for _ in xrange(61): x = _chi(x) return x
def FromString(self, string, hashtype): h = [1 if hashtype == 256 else 0] * 64 e = [0] * 64 N = [0] * 64 Z = [0] * 64 m = [0] * 64 start = 0 while start + 64 <= len(string): for i in xrange(0, 64): m[63 - i] = ord(string[start + i]) h = self.g(N, h, m) e[62] = (512 >> 8) e[63] = (512 & 0xFF) N = self.plus(N, e) Z = self.plus(Z, m) start += 64 sz = len(string) - start m = [0] * 64 for i in xrange(0, sz): m[63 - i] = ord(string[start + i]) m[64 - sz - 1] = 1 h = self.g(N, h, m) e[62] = (sz * 8) >> 8 e[63] = (sz * 8) & 0xFF N = self.plus(N, e) Z = self.plus(Z, m) e[62] = 0 e[63] = 0 h = self.g(e, h, N) h = self.g(e, h, Z) return ''.join( reversed([('%0.2X' % a) for a in h][:(32 if hashtype == 256 else 64)]))
def ecb_encrypt(encrypter, bs, pt): """ECB encryption mode of operation :param encrypter: Encrypting function, that takes block as an input :param int bs: cipher's blocksize :param bytes pt: already padded plaintext """ if not pt or len(pt) % bs != 0: raise ValueError("Plaintext is not blocksize aligned") ct = [] for i in xrange(0, len(pt), bs): ct.append(encrypter(pt[i:i + bs])) return b"".join(ct)
def ecb_decrypt(decrypter, bs, ct): """ECB decryption mode of operation :param decrypter: Decrypting function, that takes block as an input :param int bs: cipher's blocksize :param bytes ct: ciphertext """ if not ct or len(ct) % bs != 0: raise ValueError("Ciphertext is not blocksize aligned") pt = [] for i in xrange(0, len(ct), bs): pt.append(decrypter(ct[i:i + bs])) return b"".join(pt)
def cfb_encrypt(encrypter, bs, pt, iv): """CFB encryption mode of operation :param encrypter: Encrypting function, that takes block as an input :param int bs: cipher's blocksize :param bytes pt: plaintext :param bytes iv: blocksize-sized initialization vector """ if len(iv) < bs or len(iv) % bs != 0: raise ValueError("Invalid IV size") r = [iv[i:i + bs] for i in range(0, len(iv), bs)] ct = [] for i in xrange(0, len(pt) + pad_size(len(pt), bs), bs): ct.append(strxor(encrypter(r[0]), pt[i:i + bs])) r = r[1:] + [ct[-1]] return b"".join(ct)
def P(self, A): tau = ( \ 0, 8, 16, 24, 32, 40, 48, 56, \ 1, 9, 17, 25, 33, 41, 49, 57, \ 2, 10, 18, 26, 34, 42, 50, 58, \ 3, 11, 19, 27, 35, 43, 51, 59, \ 4, 12, 20, 28, 36, 44, 52, 60, \ 5, 13, 21, 29, 37, 45, 53, 61, \ 6, 14, 22, 30, 38, 46, 54, 62, \ 7, 15, 23, 31, 39, 47, 55, 63) result = [0] * 64 for i in xrange(0, 64): result[i] = A[tau[i]] return result
def ofb(encrypter, bs, data, iv): """OFB mode of operation :param encrypter: Encrypting function, that takes block as an input :param int bs: cipher's blocksize :param bytes data: plaintext/ciphertext :param bytes iv: blocksize-sized initialization vector For decryption you use the same function again. """ if len(iv) < bs or len(iv) % bs != 0: raise ValueError("Invalid IV size") r = [iv[i:i + bs] for i in range(0, len(iv), bs)] result = [] for i in xrange(0, len(data) + pad_size(len(data), bs), bs): r = r[1:] + [encrypter(r[0])] result.append(strxor(r[-1], data[i:i + bs])) return b"".join(result)
def ctr(encrypter, bs, data, iv): """Counter mode of operation :param encrypter: Encrypting function, that takes block as an input :param int bs: cipher's blocksize :param bytes data: plaintext/ciphertext :param bytes iv: half blocksize-sized initialization vector For decryption you use the same function again. """ if len(iv) != bs // 2: raise ValueError("Invalid IV size") stream = [] ctr_value = 0 for _ in xrange(0, len(data) + pad_size(len(data), bs), bs): stream.append(encrypter(iv + long2bytes(ctr_value, bs // 2))) ctr_value += 1 return strxor(b"".join(stream), data)
def cbc_decrypt(decrypter, bs, ct, iv): """CBC decryption mode of operation :param decrypter: Decrypting function, that takes block as an input :param int bs: cipher's blocksize :param bytes ct: ciphertext :param bytes iv: blocksize-sized initialization vector """ if not ct or len(ct) % bs != 0: raise ValueError("Ciphertext is not blocksize aligned") if len(iv) < bs or len(iv) % bs != 0: raise ValueError("Invalid IV size") r = [iv[i:i + bs] for i in range(0, len(iv), bs)] pt = [] for i in xrange(0, len(ct), bs): blk = ct[i:i + bs] pt.append(strxor(r[0], decrypter(blk))) r = r[1:] + [blk] return b"".join(pt)
def digest(self): """ Get MAC tag of supplied data You have to provide at least single byte of data. If you want to produce tag length of 3 bytes, then ``digest()[:3]``. """ if not self.data: raise ValueError("No data processed") _, data = _pad(self.data) prev = block2ns(self.iv)[::-1] for i in xrange(0, len(data), BLOCKSIZE): prev = xcrypt( SEQ_MAC, self.sbox, self.key, block2ns(strxor( data[i:i + BLOCKSIZE], ns2block(prev), )), )[::-1] return ns2block(prev)
def ecb(key, data, action, sbox=DEFAULT_SBOX): """ ECB mode of operation :param bytes key: encryption key :param data: plaintext :type data: bytes, multiple of BLOCKSIZE :param func action: encrypt/decrypt :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :return: ciphertext :rtype: bytes """ validate_key(key) validate_sbox(sbox) if not data or len(data) % BLOCKSIZE != 0: raise ValueError("Data is not blocksize aligned") result = [] for i in xrange(0, len(data), BLOCKSIZE): result.append( ns2block(action(sbox, key, block2ns(data[i:i + BLOCKSIZE])))) return b''.join(result)
def ecb(key, data, action, sbox=DEFAULT_SBOX): """ ECB mode of operation :param bytes key: encryption key :param data: plaintext :type data: bytes, multiple of BLOCKSIZE :param func action: encrypt/decrypt :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :return: ciphertext :rtype: bytes """ validate_key(key) validate_sbox(sbox) if not data or len(data) % BLOCKSIZE != 0: raise ValueError("Data is not blocksize aligned") result = [] for i in xrange(0, len(data), BLOCKSIZE): result.append(ns2block(action( sbox, key, block2ns(data[i:i + BLOCKSIZE]) ))) return b''.join(result)
def mac(encrypter, bs, data): """MAC (known here as CMAC, OMAC1) mode of operation :param encrypter: Encrypting function, that takes block as an input :param int bs: cipher's blocksize :param bytes data: data to authenticate Implementation is based on PyCrypto's CMAC one, that is in public domain. """ k1, k2 = _mac_ks(encrypter, bs) if len(data) % bs == 0: tail_offset = len(data) - bs else: tail_offset = len(data) - (len(data) % bs) prev = bs * b'\x00' for i in xrange(0, tail_offset, bs): prev = encrypter(strxor(data[i:i + bs], prev)) tail = data[tail_offset:] return encrypter(strxor( strxor(pad3(tail, bs), prev), k1 if len(tail) == bs else k2, ))
def digest(self): """ Get hash of the provided data """ l = 0 checksum = 0 h = 32 * b'\x00' m = self.data for i in xrange(0, len(m), BLOCKSIZE): part = m[i:i + BLOCKSIZE][::-1] l += len(part) * 8 checksum = addmod(checksum, int(hexenc(part), 16), 2 ** 256) if len(part) < BLOCKSIZE: part = b'\x00' * (BLOCKSIZE - len(part)) + part h = _step(h, part, self.sbox) h = _step(h, 24 * b'\x00' + pack(">Q", l), self.sbox) checksum = hex(checksum)[2:].rstrip("L") if len(checksum) % 2 != 0: checksum = "0" + checksum checksum = hexdec(checksum) checksum = b'\x00' * (BLOCKSIZE - len(checksum)) + checksum h = _step(h, checksum, self.sbox) return h
def digest(self): """ Get hash of the provided data """ _len = 0 checksum = 0 h = 32 * b"\x00" m = self.data for i in xrange(0, len(m), BLOCKSIZE): part = m[i:i + BLOCKSIZE][::-1] _len += len(part) * 8 checksum = addmod(checksum, int(hexenc(part), 16), 2 ** 256) if len(part) < BLOCKSIZE: part = b"\x00" * (BLOCKSIZE - len(part)) + part h = _step(h, part, self.sbox) h = _step(h, 24 * b"\x00" + pack(">Q", _len), self.sbox) checksum = hex(checksum)[2:].rstrip("L") if len(checksum) % 2 != 0: checksum = "0" + checksum checksum = hexdec(checksum) checksum = b"\x00" * (BLOCKSIZE - len(checksum)) + checksum h = _step(h, checksum, self.sbox) return h[::-1]
def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): """ CFB decryption mode of operation :param bytes key: encryption key :param bytes data: plaintext :param iv: initialization vector :type iv: bytes, BLOCKSIZE length :param sbox: S-box parameters to use :type sbox: str, SBOXES'es key :param bool mesh: enable key meshing :returns: ciphertext :rtype: bytes """ validate_key(key) validate_iv(iv) validate_sbox(sbox) if not data: raise ValueError("No data supplied") plaintext = [] data = iv + data for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): if ( mesh and (i - BLOCKSIZE) >= MESH_MAX_DATA and (i - BLOCKSIZE) % MESH_MAX_DATA == 0 ): key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox) plaintext.append(strxor( data[i:i + BLOCKSIZE], ns2block(encrypt(sbox, key, block2ns(iv))), )) continue plaintext.append(strxor( data[i:i + BLOCKSIZE], ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))), )) return b"".join(plaintext)
36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74, 167, 151, 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172, 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97, 32, 113, 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182, )) ######################################################################## # Precalculate inverted PI value as a performance optimization. # Actually it can be computed only once and saved on the disk. ######################################################################## PIinv = bytearray(256) for x in xrange(256): PIinv[PI[x]] = x def gf(a, b): c = 0 while b: if b & 1: c ^= a if a & 0x80: a = (a << 1) ^ 0x1C3 else: a <<= 1 b >>= 1 return c
209, 102, 175, 194, 57, 75, 99, 182, )) ######################################################################## # Precalculate inverted PI value as a performance optimization. # Actually it can be computed only once and saved on the disk. ######################################################################## PIinv = bytearray(256) for x in xrange(256): PIinv[PI[x]] = x def gf(a, b): c = 0 while b: if b & 1: c ^= a if a & 0x80: a = (a << 1) ^ 0x1C3 else: a <<= 1 b >>= 1 return c