148 lines
4.7 KiB
Python
148 lines
4.7 KiB
Python
# /usr/bin/env python
|
|
# coding=utf-8
|
|
|
|
# Copyright 2010 Jonathan Bowman
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied. See the License for the specific language governing
|
|
# permissions and limitations under the License.
|
|
|
|
"""Pure Python implementation of the Threefish block cipher
|
|
|
|
The core of the Skein 512-bit hashing algorithm
|
|
|
|
"""
|
|
from util_numpy import add64, bigint, bytelist, bytes2words, imap, izip, sub64, \
|
|
SKEIN_KS_PARITY, words, words2bytes, words_format, xrange, zero_bytes, zero_words, RotL_64, RotR_64, xor
|
|
|
|
from itertools import cycle
|
|
|
|
ROT = bytelist((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))
|
|
|
|
PERM = bytelist(((0,1),(2,3),(4,5),(6,7),
|
|
(2,1),(4,7),(6,5),(0,3),
|
|
(4,1),(6,3),(0,5),(2,7),
|
|
(6,1),(0,7),(2,5),(4,3)))
|
|
|
|
class Threefish512(object):
|
|
"""The Threefish 512-bit block cipher.
|
|
|
|
The key and tweak may be set when initialized (as
|
|
bytestrings) or after initialization using the ``tweak`` or
|
|
``key`` properties. When choosing the latter, be sure to call
|
|
the ``prepare_key`` and ``prepare_tweak`` methods.
|
|
|
|
"""
|
|
def __init__(self, key=None, tweak=None):
|
|
"""Set key and tweak.
|
|
|
|
The key and the tweak will be lists of 8 64-bit words
|
|
converted from `key` and `tweak` bytestrings, or all
|
|
zeroes if not specified.
|
|
|
|
"""
|
|
if key:
|
|
self.key = bytes2words(key)
|
|
self.prepare_key()
|
|
else:
|
|
self.key = words(zero_words[:] + [0])
|
|
if tweak:
|
|
self.tweak = bytes2words(tweak, 2)
|
|
self.prepare_tweak()
|
|
else:
|
|
self.tweak = zero_words[:3]
|
|
|
|
def prepare_key(self):
|
|
"""Compute key."""
|
|
final = reduce(xor, self.key[:8]) ^ SKEIN_KS_PARITY
|
|
try:
|
|
self.key[8] = final
|
|
except IndexError:
|
|
#self.key.append(final)
|
|
self.key = words(list(self.key) + [final])
|
|
|
|
def prepare_tweak(self):
|
|
"""Compute tweak."""
|
|
final = self.tweak[0] ^ self.tweak[1]
|
|
try:
|
|
self.tweak[2] = final
|
|
except IndexError:
|
|
#self.tweak.append(final)
|
|
self.tweak = words(list(self.tweak) + [final])
|
|
|
|
def encrypt_block(self, plaintext):
|
|
"""Return 8-word ciphertext, encrypted from plaintext.
|
|
|
|
`plaintext` must be a list of 8 64-bit words.
|
|
|
|
"""
|
|
key = self.key
|
|
tweak = self.tweak
|
|
state = words(list(imap(add64, plaintext, key[:8])))
|
|
state[5] = add64(state[5], tweak[0])
|
|
state[6] = add64(state[6], tweak[1])
|
|
|
|
for r,s in izip(xrange(1,19),cycle((0,16))):
|
|
for i in xrange(16):
|
|
m,n = PERM[i]
|
|
state[m] = add64(state[m], state[n])
|
|
state[n] = RotL_64(state[n], ROT[i+s])
|
|
state[n] = state[n] ^ state[m]
|
|
for y in xrange(8):
|
|
state[y] = add64(state[y], key[(r+y) % 9])
|
|
state[5] = add64(state[5], tweak[r % 3])
|
|
state[6] = add64(state[6], tweak[(r+1) % 3])
|
|
state[7] = add64(state[7], r)
|
|
|
|
return state
|
|
|
|
def _feed_forward(self, state, plaintext):
|
|
"""Compute additional step required when hashing.
|
|
|
|
Primarily for internal use.
|
|
|
|
"""
|
|
state[:] = list(imap(xor, state, plaintext))
|
|
|
|
def decrypt_block(self, ciphertext):
|
|
"""Return 8-word plaintext, decrypted from plaintext.
|
|
|
|
`ciphertext` must be a list of 8 64-bit words.
|
|
|
|
"""
|
|
key = self.key
|
|
tweak = self.tweak
|
|
state = ciphertext[:]
|
|
|
|
for r,s in izip(xrange(18,0,-1),cycle((16,0))):
|
|
for y in xrange(8):
|
|
state[y] = sub64(state[y], key[(r+y) % 9])
|
|
state[5] = sub64(state[5], tweak[r % 3])
|
|
state[6] = sub64(state[6], tweak[(r+1) % 3])
|
|
state[7] = sub64(state[7], r)
|
|
|
|
for i in xrange(15,-1,-1):
|
|
m,n = PERM[i]
|
|
state[n] = RotR_64(state[m] ^ state[n], ROT[i+s])
|
|
state[m] = sub64(state[m], state[n])
|
|
|
|
result = list(imap(sub64, state, key))
|
|
result[5] = sub64(result[5], tweak[0])
|
|
result[6] = sub64(result[6], tweak[1])
|
|
return result
|