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 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 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 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 __init__(self, key): """ :param key: encryption/decryption key :type key: bytes, 32 bytes Key scheduling (roundkeys precomputation) is performed here. """ kr0 = bytearray(key[:16]) kr1 = bytearray(key[16:]) self.ks = [kr0, kr1] for i in range(4): for j in range(8): k = lp(bytearray(strxor(C[8 * i + j], kr0))) kr0, kr1 = [strxor(k, kr1), kr0] self.ks.append(kr0) self.ks.append(kr1)
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 range(12): x = _chi(x) x = strxor(x, m) x = _chi(x) x = strxor(hin, x) for _ in range(61): x = _chi(x) return x
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 :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) plaintext = [] data = iv + data for i in xrange(BLOCKSIZE, len(data), 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)[:size]
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 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 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 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 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 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 g(n, hsh, msg, rounds_amount=12): res = E(LPS(strxor(hsh[:8], pack("<Q", n)) + hsh[8:]), msg, rounds_amount) return strxor(strxor(res, hsh), msg)
def E(k, msg): for i in range(12): msg = LPS(strxor(k, msg)) k = LPS(strxor(k, C[i])) return strxor(k, msg)
def g(n, hsh, msg): res = E(LPS(strxor(hsh[:8], pack("<Q", n)) + hsh[8:]), msg) return strxor(strxor(res, hsh), msg)
def E(k, msg): for i in range(12): msg = LPS(strxor(k, msg)) k = LPS(strxor(k, C[i])) return strxor(k, msg)
def encrypt(self, blk): blk = bytearray(blk) for i in range(9): blk = lp(bytearray(strxor(self.ks[i], blk))) return bytes(strxor(self.ks[9], blk))
def A(x): x4, x3, x2, x1 = x[0:8], x[8:16], x[16:24], x[24:32] return b"".join((strxor(x1, x2), x4, x3, x2))
def encrypt(self, blk): blk = bytearray(blk) for i in range(9): blk = lp(bytearray(strxor(self.ks[i], blk))) return bytes(strxor(self.ks[9], blk))
def decrypt(self, blk): blk = bytearray(blk) for i in range(9, 0, -1): blk = [PIinv[v] for v in Linv(bytearray(strxor(self.ks[i], blk)))] return bytes(strxor(self.ks[0], blk))
def E(k, msg, rounds_amount): for i in range(rounds_amount): msg = LPS(strxor(k, msg)) k = LPS(strxor(k, C[i])) return strxor(k, msg)
def g(n, hsh, msg): res = E(LPS(strxor(hsh[:8], pack("<Q", n)) + hsh[8:]), msg) return strxor(strxor(res, hsh), msg)
def decrypt(self, blk): blk = bytearray(blk) for i in range(9, 0, -1): blk = [PIinv[v] for v in Linv(bytearray(strxor(self.ks[i], blk)))] return bytes(strxor(self.ks[0], blk))
def A(x): x4, x3, x2, x1 = x[0:8], x[8:16], x[16:24], x[24:32] return b''.join((strxor(x1, x2), x4, x3, x2))