Skip to content

Instantly share code, notes, and snippets.

@amintos
Created September 6, 2013 01:54
Show Gist options
  • Save amintos/6458623 to your computer and use it in GitHub Desktop.
Save amintos/6458623 to your computer and use it in GitHub Desktop.
Simple string encryption and decryption in pure Python using Threefish-512. No guarantee this implementation is 100% correct!
#
# Simple Pure-Python Threefish-512 Encryption.
# (No guarantee that this implementation is 100% correct!)
#
# Use encrypt(text, key) and decrypt(text, key) for string encryption.
#
# The cipher operates in CBC mode with a random tweak value.
#
from StringIO import StringIO
from os import urandom
# Constants
ROUNDS = 72
R = ((46, 36, 19, 37),
(33, 27, 14, 42),
(17, 49, 36, 39),
(44, 9 , 54, 56),
(39, 30, 34, 24),
(13, 50, 10, 17),
(25, 29, 39, 43),
(8 , 35, 56, 22))
# Rotation primitives
rol = lambda x, n: ((x << n) & 0xFFFFFFFFFFFFFFFF) | (x >> (64 - n))
ror = lambda x, n: (x >> n) | ((x << (64 - n)) & 0xFFFFFFFFFFFFFFFF)
# Key Schedule
def expand(key, tweak):
assert len(key) == 8
assert len(tweak) == 2
result = []
key = key + [reduce(lambda a, b: a ^ b, key, 0) ^ 0x1BD11BDAA9FC1A22]
tweak = tweak + [tweak[0] ^ tweak[1]]
for i in xrange(8):
for s in xrange(ROUNDS / 4):
if i < 5:
result.append((key[(s + i) % 9]) & 0xFFFFFFFFFFFFFFFF)
elif i == 5:
result.append((key[(s + i) % 9] + tweak[s % 3]) & 0xFFFFFFFFFFFFFFFF)
elif i == 6:
result.append((key[(s + i) % 9] + tweak[(s + 1) % 3]) & 0xFFFFFFFFFFFFFFFF)
elif i == 7:
result.append((key[(s + i) % 9] + s) & 0xFFFFFFFFFFFFFFFF)
return result
# Block Encryption
def enc(block, key = [1]):
assert len(block) == 8
assert len(key) == ROUNDS * 2
k = len(key)
b0 = block[0]
b1 = block[1]
b2 = block[2]
b3 = block[3]
b4 = block[4]
b5 = block[5]
b6 = block[6]
b7 = block[7]
for i in xrange(ROUNDS):
if i % 4 == 0:
i8 = i * 2
b0 ^= key[i8 + 0]
b1 ^= key[i8 + 1]
b2 ^= key[i8 + 2]
b3 ^= key[i8 + 3]
b0 ^= key[i8 + 4]
b1 ^= key[i8 + 5]
b2 ^= key[i8 + 6]
b3 ^= key[i8 + 7]
b0 = (b0 + b1) & 0xFFFFFFFFFFFFFFFF
b2 = (b2 + b3) & 0xFFFFFFFFFFFFFFFF
b4 = (b4 + b5) & 0xFFFFFFFFFFFFFFFF
b6 = (b6 + b7) & 0xFFFFFFFFFFFFFFFF
r = R[i % 8]
b1 = rol(b1, r[0]) ^ b0
b3 = rol(b3, r[1]) ^ b2
b5 = rol(b5, r[2]) ^ b4
b7 = rol(b7, r[3]) ^ b6
b0, b1, b2, b3, b4, b5, b6, b7 = b2, b1, b4, b7, b6, b5, b0, b3
return [b0, b1, b2, b3, b4, b5, b6, b7]
# Block Decryption
def dec(block, key = [1]):
assert len(block) == 8
assert len(key) == ROUNDS * 2
k = len(key)
b0 = block[0]
b1 = block[1]
b2 = block[2]
b3 = block[3]
b4 = block[4]
b5 = block[5]
b6 = block[6]
b7 = block[7]
for i in xrange(ROUNDS - 1, -1, -1):
b2, b1, b4, b7, b6, b5, b0, b3 = b0, b1, b2, b3, b4, b5, b6, b7
r = R[i % 8]
b1 = ror(b1 ^ b0, r[0])
b3 = ror(b3 ^ b2, r[1])
b5 = ror(b5 ^ b4, r[2])
b7 = ror(b7 ^ b6, r[3])
b0 = (b0 - b1) & 0xFFFFFFFFFFFFFFFF
b2 = (b2 - b3) & 0xFFFFFFFFFFFFFFFF
b4 = (b4 - b5) & 0xFFFFFFFFFFFFFFFF
b6 = (b6 - b7) & 0xFFFFFFFFFFFFFFFF
if i % 4 == 0:
i8 = i * 2
b0 ^= key[i8 + 0]
b1 ^= key[i8 + 1]
b2 ^= key[i8 + 2]
b3 ^= key[i8 + 3]
b0 ^= key[i8 + 4]
b1 ^= key[i8 + 5]
b2 ^= key[i8 + 6]
b3 ^= key[i8 + 7]
return [b0, b1, b2, b3, b4, b5, b6, b7]
# Padding
def pad(b):
original_len = len(b)
if original_len % 64 != 63:
b += '\0' * (63 - (original_len % 64))
b += chr(original_len % 64)
return b
def pad_key(k):
assert len(k) <= 64
if len(k) < 64:
k += '\0' * (64 - len(k))
return k
def unpad(b):
last_block = ord(b[-1])
original_len = 64 * (len(b) / 64 - 1) + last_block
return b[:original_len]
# String to Int64 encoding
def iter_blocks(b):
for i in xrange(0, len(b), 8):
yield (ord(b[i])
| (ord(b[i + 1]) << 8)
| (ord(b[i + 2]) << 16)
| (ord(b[i + 3]) << 24)
| (ord(b[i + 4]) << 32)
| (ord(b[i + 5]) << 40)
| (ord(b[i + 6]) << 48)
| (ord(b[i + 7]) << 56)
)
def to_bytes(blocks):
buf = StringIO()
for n in blocks:
for i in xrange(8):
buf.write(chr(n & 0xff))
n >>= 8
return buf.getvalue()
# Simple encryption with CBC
def encrypt(text, key):
if isinstance(key, str):
key = list(iter_blocks(pad_key(key)))
blocks = list(iter_blocks(pad(text)))
tweak = list(iter_blocks(urandom(16)))
for i in xrange(0, len(blocks), 8):
e_key = expand(key, tweak)
blocks[i : i + 8] = enc(blocks[i : i + 8], e_key)
# Cipher Block Chaining (CBC)
key[0] ^= blocks[i]
key[1] ^= blocks[i + 1]
key[2] ^= blocks[i + 2]
key[3] ^= blocks[i + 3]
key[4] ^= blocks[i + 4]
key[5] ^= blocks[i + 5]
key[6] ^= blocks[i + 6]
key[7] ^= blocks[i + 7]
return to_bytes(tweak + blocks)
def decrypt(text, key):
if isinstance(key, str):
key = list(iter_blocks(pad_key(key)))
blocks = list(iter_blocks(text))
tweak = blocks[:2]
blocks = blocks[2:]
for i in xrange(0, len(blocks), 8):
next_key = key[:]
# Cipher Block Chaining (CBC)
next_key[0] ^= blocks[i]
next_key[1] ^= blocks[i + 1]
next_key[2] ^= blocks[i + 2]
next_key[3] ^= blocks[i + 3]
next_key[4] ^= blocks[i + 4]
next_key[5] ^= blocks[i + 5]
next_key[6] ^= blocks[i + 6]
next_key[7] ^= blocks[i + 7]
e_key = expand(key, tweak)
blocks[i : i + 8] = dec(blocks[i : i + 8], e_key)
key = next_key
return unpad(to_bytes(blocks))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment