**serpref.py**- Posted Dec 21, 1999
serpref.py

- MD5 |
`83de88b837341b18a045f9243cc1b8bb`

- Download | Favorite | Comments (0)

`serpref.py`

#!/usr/local/bin/python

# $RCSfile: SERPREF.PY,v $

# $Id: SERPREF.PY,v 1.15 1998/03/05 16:46:27 fms Exp fms $

# written by Frank Stajano, http://www.cl.cam.ac.uk/~fms27/

# Cambridge University Computer Laboratory

# Development started on 1998 02 12

#

# Serpent cipher invented by Eli Biham, Ross Anderson, Lars Knudsen.

# --------------------------------------------------------------

"""This is a reference implementation of the Serpent cipher invented by Eli

Biham, Ross Anderson, Lars Knudsen. It is written for the human reader more

than for the machine and, as such, it is optimised for clarity rather than

speed. ("Premature optimisation is the root of all evil.")

It can print out all the intermediate results (such as the subkeys) for a

given input and key so that implementers debugging erroneous code can

quickly verify which one of the building blocks is giving the wrong

answers."""

# --------------------------------------------------------------

# Requires python 1.5, freely available from http://www.python.org/

# --------------------------------------------------------------

import string

import sys

import getopt

import re

import whrandom

# --------------------------------------------------------------

# Functions used in the formal description of the cipher

def S(box, input):

"""Apply S-box number 'box' to 4-bit bitstring 'input' and return a

4-bit bitstring as the result."""

return SBoxBitstring[box][input]

def SInverse(box, output):

"""Apply S-box number 'box' in reverse to 4-bit bitstring 'output' and

return a 4-bit bitstring (the input) as the result."""

return SBoxBitstringInverse[box][output]

def SHat(box, input):

"""Apply a parallel array of 32 copies of S-box number 'box' to the

128-bit bitstring 'input' and return a 128-bit bitstring as the

result."""

result = ""

for i in range(32):

result = result + S(box, input[4*i:4*(i+1)])

return result

def SHatInverse(box, output):

"""Apply, in reverse, a parallel array of 32 copies of S-box number

'box' to the 128-bit bitstring 'output' and return a 128-bit bitstring

(the input) as the result."""

result = ""

for i in range(32):

result = result + SInverse(box, output[4*i:4*(i+1)])

return result

def SBitslice(box, words):

"""Take 'words', a list of 4 32-bit bitstrings, least significant word

first. Return a similar list of 4 32-bit bitstrings obtained as

follows. For each bit position from 0 to 31, apply S-box number 'box'

to the 4 input bits coming from the current position in each of the

items in 'words'; and put the 4 output bits in the corresponding

positions in the output words."""

result = ["", "", "", ""]

for i in range(32): # ideally in parallel

quad = S(box, words[0][i] + words[1][i] + words[2][i] + words[3][i])

for j in range(4):

result[j] = result[j] + quad[j]

return result

def SBitsliceInverse(box, words):

"""Take 'words', a list of 4 32-bit bitstrings, least significant word

first. Return a similar list of 4 32-bit bitstrings obtained as

follows. For each bit position from 0 to 31, apply S-box number 'box'

in reverse to the 4 output bits coming from the current position in

each of the items in the supplied 'words'; and put the 4 input bits in

the corresponding positions in the returned words."""

result = ["", "", "", ""]

for i in range(32): # ideally in parallel

quad = SInverse(

box, words[0][i] + words[1][i] + words[2][i] + words[3][i])

for j in range(4):

result[j] = result[j] + quad[j]

return result

def LT(input):

"""Apply the table-based version of the linear transformation to the

128-bit string 'input' and return a 128-bit string as the result."""

if len(input) != 128:

raise ValueError, "input to LT is not 128 bit long"

result = ""

for i in range(len(LTTable)):

outputBit = "0"

for j in LTTable[i]:

outputBit = xor(outputBit, input[j])

result = result + outputBit

return result

def LTInverse(output):

"""Apply the table-based version of the inverse of the linear

transformation to the 128-bit string 'output' and return a 128-bit

string (the input) as the result."""

if len(output) != 128:

raise ValueError, "input to inverse LT is not 128 bit long"

result = ""

for i in range(len(LTTableInverse)):

inputBit = "0"

for j in LTTableInverse[i]:

inputBit = xor(inputBit, output[j])

result = result + inputBit

return result

def LTBitslice(X):

"""Apply the equations-based version of the linear transformation to

'X', a list of 4 32-bit bitstrings, least significant bitstring first,

and return another list of 4 32-bit bitstrings as the result."""

X[0] = rotateLeft(X[0], 13)

X[2] = rotateLeft(X[2], 3)

X[1] = xor(X[1], X[0], X[2])

X[3] = xor(X[3], X[2], shiftLeft(X[0], 3))

X[1] = rotateLeft(X[1], 1)

X[3] = rotateLeft(X[3], 7)

X[0] = xor(X[0], X[1], X[3])

X[2] = xor(X[2], X[3], shiftLeft(X[1], 7))

X[0] = rotateLeft(X[0], 5)

X[2] = rotateLeft(X[2], 22)

return X

def LTBitsliceInverse(X):

"""Apply, in reverse, the equations-based version of the linear

transformation to 'X', a list of 4 32-bit bitstrings, least significant

bitstring first, and return another list of 4 32-bit bitstrings as the

result."""

X[2] = rotateRight(X[2], 22)

X[0] = rotateRight(X[0], 5)

X[2] = xor(X[2], X[3], shiftLeft(X[1], 7))

X[0] = xor(X[0], X[1], X[3])

X[3] = rotateRight(X[3], 7)

X[1] = rotateRight(X[1], 1)

X[3] = xor(X[3], X[2], shiftLeft(X[0], 3))

X[1] = xor(X[1], X[0], X[2])

X[2] = rotateRight(X[2], 3)

X[0] = rotateRight(X[0], 13)

return X

def IP(input):

"""Apply the Initial Permutation to the 128-bit bitstring 'input'

and return a 128-bit bitstring as the result."""

return applyPermutation(IPTable, input)

def FP(input):

"""Apply the Final Permutation to the 128-bit bitstring 'input'

and return a 128-bit bitstring as the result."""

return applyPermutation(FPTable, input)

def IPInverse(output):

"""Apply the Initial Permutation in reverse."""

return FP(output)

def FPInverse(output):

"""Apply the Final Permutation in reverse."""

return IP(output)

def applyPermutation(permutationTable, input):

"""Apply the permutation specified by the 128-element list

'permutationTable' to the 128-bit bitstring 'input' and return a

128-bit bitstring as the result."""

if len(input) != len(permutationTable):

raise ValueError, "input size (%d) does not match perm table size (%d)"\

% (len(input), len(permutationTable))

result = ""

for i in range(len(permutationTable)):

result = result + input[permutationTable[i]]

return result

def R(i, BHati, KHat):

"""Apply round 'i' to the 128-bit bitstring 'BHati', returning another

128-bit bitstring (conceptually BHatiPlus1). Do this using the

appropriately numbered subkey(s) from the 'KHat' list of 33 128-bit

bitstrings."""

O.show("BHati", BHati, "(i=%2d) BHati" % i)

xored = xor(BHati, KHat[i])

O.show("xored", xored, "(i=%2d) xored" % i)

SHati = SHat(i, xored)

O.show("SHati", SHati, "(i=%2d) SHati" % i)

if 0 <= i <= r-2:

BHatiPlus1 = LT(SHati)

elif i == r-1:

BHatiPlus1 = xor(SHati, KHat[r])

else:

raise ValueError, "round %d is out of 0..%d range" % (i, r-1)

O.show("BHatiPlus1", BHatiPlus1, "(i=%2d) BHatiPlus1" % i)

return BHatiPlus1

def RInverse(i, BHatiPlus1, KHat):

"""Apply round 'i' in reverse to the 128-bit bitstring 'BHatiPlus1',

returning another 128-bit bitstring (conceptually BHati). Do this using

the appropriately numbered subkey(s) from the 'KHat' list of 33 128-bit

bitstrings."""

O.show("BHatiPlus1", BHatiPlus1, "(i=%2d) BHatiPlus1" % i)

if 0 <= i <= r-2:

SHati = LTInverse(BHatiPlus1)

elif i == r-1:

SHati = xor(BHatiPlus1, KHat[r])

else:

raise ValueError, "round %d is out of 0..%d range" % (i, r-1)

O.show("SHati", SHati, "(i=%2d) SHati" % i)

xored = SHatInverse(i, SHati)

O.show("xored", xored, "(i=%2d) xored" % i)

BHati = xor(xored, KHat[i])

O.show("BHati", BHati, "(i=%2d) BHati" % i)

return BHati

def RBitslice(i, Bi, K):

"""Apply round 'i' (bitslice version) to the 128-bit bitstring 'Bi' and

return another 128-bit bitstring (conceptually B i+1). Use the

appropriately numbered subkey(s) from the 'K' list of 33 128-bit

bitstrings."""

O.show("Bi", Bi, "(i=%2d) Bi" % i)

# 1. Key mixing

xored = xor (Bi, K[i])

O.show("xored", xored, "(i=%2d) xored" % i)

# 2. S Boxes

Si = SBitslice(i, quadSplit(xored))

# Input and output to SBitslice are both lists of 4 32-bit bitstrings

O.show("Si", Si, "(i=%2d) Si" % i, "tlb")

# 3. Linear Transformation

if i == r-1:

# In the last round, replaced by an additional key mixing

BiPlus1 = xor(quadJoin(Si), K[r])

else:

BiPlus1 = quadJoin(LTBitslice(Si))

# BIPlus1 is a 128-bit bitstring

O.show("BiPlus1", BiPlus1, "(i=%2d) BiPlus1" % i)

return BiPlus1

def RBitsliceInverse(i, BiPlus1, K):

"""Apply the inverse of round 'i' (bitslice version) to the 128-bit

bitstring 'BiPlus1' and return another 128-bit bitstring (conceptually

B i). Use the appropriately numbered subkey(s) from the 'K' list of 33

128-bit bitstrings."""

O.show("BiPlus1", BiPlus1, "(i=%2d) BiPlus1" % i)

# 3. Linear Transformation

if i == r-1:

# In the last round, replaced by an additional key mixing

Si = quadSplit(xor(BiPlus1, K[r]))

else:

Si = LTBitsliceInverse(quadSplit(BiPlus1))

# SOutput (same as LTInput) is a list of 4 32-bit bitstrings

O.show("Si", Si, "(i=%2d) Si" % i, "tlb")

# 2. S Boxes

xored = SBitsliceInverse(i, Si)

# SInput and SOutput are both lists of 4 32-bit bitstrings

O.show("xored", xored, "(i=%2d) xored" % i)

# 1. Key mixing

Bi = xor (quadJoin(xored), K[i])

O.show("Bi", Bi, "(i=%2d) Bi" % i)

return Bi

def encrypt(plainText, userKey):

"""Encrypt the 128-bit bitstring 'plainText' with the 256-bit bitstring

'userKey', using the normal algorithm, and return a 128-bit ciphertext

bitstring."""

O.show("fnTitle", "encrypt", None, "tu")

O.show("plainText", plainText, "plainText")

O.show("userKey", userKey, "userKey")

K, KHat = makeSubkeys(userKey)

BHat = IP(plainText) # BHat_0 at this stage

for i in range(r):

BHat = R(i, BHat, KHat) # Produce BHat_i+1 from BHat_i

# BHat is now _32 i.e. _r

C = FP(BHat)

O.show("cipherText", C, "cipherText")

return C

def encryptBitslice(plainText, userKey):

"""Encrypt the 128-bit bitstring 'plainText' with the 256-bit bitstring

'userKey', using the bitslice algorithm, and return a 128-bit ciphertext

bitstring."""

O.show("fnTitle", "encryptBitslice", None, "tu")

O.show("plainText", plainText, "plainText")

O.show("userKey", userKey, "userKey")

K, KHat = makeSubkeys(userKey)

B = plainText # B_0 at this stage

for i in range(r):

B = RBitslice(i, B, K) # Produce B_i+1 from B_i

# B is now _r

O.show("cipherText", B, "cipherText")

return B

def decrypt(cipherText, userKey):

"""Decrypt the 128-bit bitstring 'cipherText' with the 256-bit

bitstring 'userKey', using the normal algorithm, and return a 128-bit

plaintext bitstring."""

O.show("fnTitle", "decrypt", None, "tu")

O.show("cipherText", cipherText, "cipherText")

O.show("userKey", userKey, "userKey")

K, KHat = makeSubkeys(userKey)

BHat = FPInverse(cipherText) # BHat_r at this stage

for i in range(r-1, -1, -1): # from r-1 down to 0 included

BHat = RInverse(i, BHat, KHat) # Produce BHat_i from BHat_i+1

# BHat is now _0

plainText = IPInverse(BHat)

O.show("plainText", plainText, "plainText")

return plainText

def decryptBitslice(cipherText, userKey):

"""Decrypt the 128-bit bitstring 'cipherText' with the 256-bit

bitstring 'userKey', using the bitslice algorithm, and return a 128-bit

plaintext bitstring."""

O.show("fnTitle", "decryptBitslice", None, "tu")

O.show("cipherText", cipherText, "cipherText")

O.show("userKey", userKey, "userKey")

K, KHat = makeSubkeys(userKey)

B = cipherText # B_r at this stage

for i in range(r-1, -1, -1): # from r-1 down to 0 included

B = RBitsliceInverse(i, B, K) # Produce B_i from B_i+1

# B is now _0

O.show("plainText", B, "plainText")

return B

def makeSubkeys(userKey):

"""Given the 256-bit bitstring 'userKey' (shown as K in the paper, but

we can't use that name because of a collision with K[i] used later for

something else), return two lists (conceptually K and KHat) of 33

128-bit bitstrings each."""

# Because in Python I can't index a list from anything other than 0,

# I use a dictionary instead to legibly represent the w_i that are

# indexed from -8.

# We write the key as 8 32-bit words w-8 ... w-1

# ENOTE: w-8 is the least significant word

w = {}

for i in range(-8, 0):

w[i] = userKey[(i+8)*32:(i+9)*32]

O.show("wi", w[i], "(i=%2d) wi" % i)

# We expand these to a prekey w0 ... w131 with the affine recurrence

for i in range(132):

w[i] = rotateLeft(

xor(w[i-8], w[i-5], w[i-3], w[i-1],

bitstring(phi, 32), bitstring(i,32)),

11)

O.show("wi", w[i], "(i=%2d) wi" % i)

# The round keys are now calculated from the prekeys using the S-boxes

# in bitslice mode. Each k[i] is a 32-bit bitstring.

k = {}

for i in range(r+1):

whichS = (r + 3 - i) % r

k[0+i] = ""

k[33+i] = ""

k[66+i] = ""

k[99+i] = ""

for j in range(32): # for every bit in the k and w words

# ENOTE: w0 and k0 are the least significant words, w99 and k99

# the most.

input = w[0+i][j] + w[33+i][j] + w[66+i][j] + w[99+i][j]

output = S(whichS, input)

k[0+i] = k[0+i] + output[0]

k[33+i] = k[33+i] + output[1]

k[66+i] = k[66+i] + output[2]

k[99+i] = k[99+i] + output[3]

# We then renumber the 32 bit values k_j as 128 bit subkeys K_i.

K = []

for i in range(33):

# ENOTE: k4i is the least significant word, k4i+3 the most.

K.append(k[4*i] + k[4*i+1] + k[4*i+2] + k[4*i+3])

# We now apply IP to the round key in order to place the key bits in

# the correct column

KHat = []

for i in range(33):

KHat.append(IP(K[i]))

O.show("Ki", K[i], "(i=%2d) Ki" % i)

O.show("KHati", KHat[i], "(i=%2d) KHati" % i)

return K, KHat

# --------------------------------------------------------------

# Generic bit-level primitives

# We represent the numbers manipulated by the cipher in a format that we

# call 'bitstring'. This is a string of "0" and "1" characters containing

# the binary representation of the number in little-endian format (so that

# subscripting with an index of i gives bit number i, corresponding to a

# weight of 2^i). This representation is only defined for nonnegative

# numbers (you can see why: think of the great unnecessary mess that would

# result from sign extension, two's complement and so on). Example: 10

# decimal is "0101" in bitstring format.

def bitstring(n, minlen=1):

"""Translate n from integer to bitstring, padding it with 0s as

necessary to reach the minimum length 'minlen'. 'n' must be >= 0 since

the bitstring format is undefined for negative integers. Note that,

while the bitstring format can represent arbitrarily large numbers,

this is not so for Python's normal integer type: on a 32-bit machine,

values of n >= 2^31 need to be expressed as python long integers or

they will "look" negative and won't work. E.g. 0x80000000 needs to be

passed in as 0x80000000L, or it will be taken as -2147483648 instead of

+2147483648L.

EXAMPLE: bitstring(10, 8) -> "01010000"

"""

if minlen < 1:

raise ValueError, "a bitstring must have at least 1 char"

if n < 0:

raise ValueError, "bitstring representation undefined for neg numbers"

result = ""

while n > 0:

if n & 1:

result = result + "1"

else:

result = result + "0"

n = n >> 1

if len(result) < minlen:

result = result + "0" * (minlen - len(result))

return result

def binaryXor(n1, n2):

"""Return the xor of two bitstrings of equal length as another

bitstring of the same length.

EXAMPLE: binaryXor("10010", "00011") -> "10001"

"""

if len(n1) != len(n2):

raise ValueError, "can't xor bitstrings of different " + \

"lengths (%d and %d)" % (len(n1), len(n2))

# We assume that they are genuine bitstrings instead of just random

# character strings.

result = ""

for i in range(len(n1)):

if n1[i] == n2[i]:

result = result + "0"

else:

result = result + "1"

return result

def xor(*args):

"""Return the xor of an arbitrary number of bitstrings of the same length as

another bitstring of the same length.

EXAMPLE: xor("01", "11", "10") -> "00"

"""

if args == []:

raise ValueError, "at least one argument needed"

result = args[0]

for arg in args[1:]:

result = binaryXor(result, arg)

return result

def rotateLeft(input, places):

"""Take a bitstring 'input' of arbitrary length. Rotate it left by

'places' places. Left means that the 'places' most significant bits are

taken out and reinserted as the least significant bits. Note that,

because the bitstring representation is little-endian, the visual

effect is actually that of rotating the string to the right.

EXAMPLE: rotateLeft("000111", 2) -> "110001"

"""

p = places % len(input)

return input[-p:] + input[:-p]

def rotateRight(input, places):

return rotateLeft(input, -places)

def shiftLeft(input, p):

"""Take a bitstring 'input' of arbitrary length. Shift it left by 'p'

places. Left means that the 'p' most significant bits are shifted out

and dropped, while 'p' 0s are inserted in the the least significant

bits. Note that, because the bitstring representation is little-endian,

the visual effect is actually that of shifting the string to the

right. Negative values for 'p' are allowed, with the effect of shifting

right instead (i.e. the 0s are inserted in the most significant bits).

EXAMPLE: shiftLeft("000111", 2) -> "000001"

shiftLeft("000111", -2) -> "011100"

"""

if abs(p) >= len(input):

# Everything gets shifted out anyway

return "0" * len(input)

if p < 0:

# Shift right instead

return input[-p:] + "0" * len(input[:-p])

elif p == 0:

return input

else: # p > 0, normal case

return "0" * len(input[-p:]) + input[:-p]

def shiftRight(input, p):

"""Take a bitstring 'input' and shift it right by 'p' places. See the

doc for shiftLeft for more details."""

return shiftLeft(input, -p)

# --------------------------------------------------------------

# Hex conversion functions

# A hexstring is little-endian and just like a bitstring except that it

# uses hex digits (considered as atomic). Example: the hexstring for ten is

# "a" (not "5" as some might perversely assume) and the hexstring for 160

# (= 10 * 16) is "0a". Similarly to what happens with the binstring, the

# hexstring has the property that the character at position i (indexing

# from 0) has weight 16^i.

bin2hex = {

# Given a 4-char bitstring, return the corresponding 1-char hexstring

"0000": "0", "1000": "1", "0100": "2", "1100": "3",

"0010": "4", "1010": "5", "0110": "6", "1110": "7",

"0001": "8", "1001": "9", "0101": "a", "1101": "b",

"0011": "c", "1011": "d", "0111": "e", "1111": "f",

}

# Make the reverse lookup table too

hex2bin = {}

for (bin, hex) in bin2hex.items():

hex2bin[hex] = bin

def bitstring2hexstring(b):

"""Take bitstring 'b' and return the corresponding hexstring."""

result = ""

l = len(b)

if l % 4:

b = b + "0" * (4-(l%4))

for i in range(0, len(b), 4):

result = result+bin2hex[b[i:i+4]]

return result

def hexstring2bitstring(h):

"""Take hexstring 'h' and return the corresponding bitstring."""

result = ""

for c in h:

result = result + hex2bin[c]

return result

# A hwseq is not a terribly pure representation but it may be easier for

# humans to read (and enter into C programs). It is a string containing as

# many 32-bit words as necessary to hold the number. The words are arranged

# in little-endian order and are separated by spaces. Each word is

# represented as 8 hex digits in big-endian order, prefixed by 0x (i.e. the

# format that you'd use in C source code). Example: decimal 10, extended to

# 64 bits, is represented as the following hwseq: "0x0000000a 0x00000000".

def bitstring2hwseq(b, minWords = 1):

"""Take bitstring 'b' and return the corresponding hwseq with at least

'minWords' words in it."""

result = ""

# Pad b with 0s if needed

l = len(b)

wordsNeeded = l / 32

if l % 32:

wordsNeeded = wordsNeeded + 1

if wordsNeeded < minWords:

wordsNeeded = minWords

bitsNeeded = wordsNeeded * 32

b = b + "0" * (bitsNeeded-l)

for word in range(bitsNeeded/32):

result = result + " 0x"

for i in range(8):

result = result + bin2hex[b[word*32+(7-i)*4:word*32+(8-i)*4]]

return result[1:]

def hwseq2bitstring(h):

"""Take hwseq 'h' and transform it into a bitstring. Keep the same

number of bits in the representation: don't chop any 0s off the end."""

result = ""

h = string.lower(h)

while h != "":

h = string.strip(h)

if len(h) < 8+2:

raise ValueError, "Can't find a complete word in this tail: " + h

if h[:2] != "0x":

raise ValueError, "Expecting 0x, found '%s'" % h[:2]

h = h[2:]

for i in range(8):

d = h[7-i]

if not d in hex2bin.keys():

raise ValueError, "Expecting 8 hex digits instead of '%s'"% h[:8]

result = result + hex2bin[h[7-i]]

h = h[8:]

return result

# --------------------------------------------------------------

# Format conversions

def quadSplit(b128):

"""Take a 128-bit bitstring and return it as a list of 4 32-bit

bitstrings, least significant bitstring first."""

if len(b128) != 128:

raise ValueError, "must be 128 bits long, not " + len(b128)

result = []

for i in range(4):

result.append(b128[(i*32):(i+1)*32])

return result

def quadJoin(l4x32):

"""Take a list of 4 32-bit bitstrings and return it as a single 128-bit

bitstring obtained by concatenating the internal ones."""

if len(l4x32) != 4:

raise ValueError, "need a list of 4 bitstrings, not " + len(l4x32)

return l4x32[0] + l4x32[1] + l4x32[2] + l4x32[3]

# --------------------------------------------------

# Self-testing

def test1(plainText, userKey):

"""Return true iff you can decrypt what you encrypted, using normal mode."""

O.show("testTitle", "Normal: can we decrypt what we encrypted?", None, "tu")

cipherText = encrypt(plainText, userKey)

decryptedText = decrypt(cipherText, userKey)

return (plainText == decryptedText)

def test2(plainText, userKey):

"""Return true iff you can decrypt what you encrypted, using bitslice."""

O.show("testTitle", "Bitslice: decrypting what we encrypted?", None, "tu")

cipherText = encryptBitslice(plainText, userKey)

decryptedText = decryptBitslice(cipherText, userKey)

return (plainText == decryptedText)

def test3(plainText, userKey):

"""Return true iff encrypting the same thing with normal and bitslice

modes gives the same result."""

O.show("testTitle", "Same results with normal and bitslice?", None, "tu")

cipherText = encrypt(plainText, userKey)

cipherTextBitslice = encryptBitslice(plainText, userKey)

return (cipherText == cipherTextBitslice)

def printTest(worked):

if worked:

print "---Success!---"

else:

print "***Failure***"

# --------------------------------------------------

# Seeing what happens inside

class Observer:

"""An object of this class can selectively display the values of the

variables you want to observe while the program is running. There are

tags that you can switch on or off. You sprinkle show() statements

throughout the program to show the value of a variable at a particular

point: show() will display the relevant variable only if the

corresponding tag is currently on. The special tag "ALL" forces all

show() statements to display their variable."""

typesOfVariable = {

"tu": "unknown", "tb": "bitstring", "tlb": "list of bitstrings",}

formats = {

"fb": "bitstring", "fh": "hexstring", "fhws": "hwseq",}

def __init__(self, tags=[], format="fhws"):

self.tags = {}

for tag in tags:

self.tags[tag] = 1

self.format = format

def addTag(self, *tags):

"""Add the supplied tag(s) to those that are currently active,

i.e. those that, if a corresponding "show()" is executed, will

print something."""

for t in tags:

self.tags[t] = 1

def removeTag(self, *tags):

"""Remove the supplied tag(s) from those currently active."""

for t in tags:

if t in self.tags.keys():

del self.tags[t]

def setFormat(self, f):

"""Set the output format to f, which must be one of Observer.formats."""

self.format = f

def show(self, tag, variable, label=None, type="tb"):

"""Conditionally print a message with the current value of

'variable'. The message will only be printed if the supplied 'tag'

is among the active ones (or if the 'ALL' tag is active). The

'label', if not null, is printed before the value of the

'variable'; if it is null, it is substituted with the 'tag'. The

'type' of the 'variable' (giving us a clue on how to print it) must

be one of Observer.typesOfVariable."""

if label == None:

label = tag

if "ALL" in self.tags.keys() or tag in self.tags.keys():

if type == "tu":

output = `variable`

elif type == "tb":

output = self._renderBitstring(variable)

elif type == "tlb":

output = ""

for item in variable:

output = output + " %s" % self._renderBitstring(item)

output = "[" + output[1:] + "]"

else:

raise ValueError, "unknown type: %s. Valid ones are %s" % (

type, self.typesOfVariable.keys())

print label,

if output:

print "=", output

else:

print

def _renderBitstring(self, b):

"""Internal helper function: take a bitstring 'b' and return its

string representation according to the currently active format."""

if self.format == "fb":

output = b

elif self.format == "fh":

output = bitstring2hexstring(b)

elif self.format == "fhws":

output = bitstring2hwseq(b)

else:

raise ValueError, "unknown format: %s. Valid ones are %s" % (

self.format, self.formats.keys())

return output

# We make one global observer object that is always available

O = Observer(["plainText", "userKey", "cipherText"])

# --------------------------------------------------------------

# Constants

phi = 0x9e3779b9L

r = 32

# --------------------------------------------------------------

# Data tables

# Each element of this list corresponds to one S-box. Each S-box in turn is

# a list of 16 integers in the range 0..15, without repetitions. Having the

# value v (say, 14) in position p (say, 0) means that if the input to that

# S-box is the pattern p (0, or 0x0) then the output will be the pattern v

# (14, or 0xe).

SBoxDecimalTable = [

[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],

[0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],

[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],

[15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],

[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],

[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],

[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],

[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],

[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],

[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],

[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],

[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],

[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],

[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],

[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],

[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],

[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],

[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],

[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],

[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],

[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],

[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],

[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],

[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],

[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],

[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],

[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],

[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],

[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],

[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],

[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],

[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],

]

# Make another version of this table as a list of dictionaries: one

# dictionary per S-box, where the value of the entry indexed by i tells you

# the output configuration when the input is i, with both the index and the

# value being bitstrings.

# Make also the inverse: another list of dictionaries, one per S-box, where each

# dictionary gets the output of the S-box as the key and gives you the

# input, with both values being 4-bit bitstrings.

SBoxBitstring = []

SBoxBitstringInverse = []

for line in SBoxDecimalTable:

dict = {}

inverseDict = {}

for i in range(len(line)):

index = bitstring(i, 4)

value = bitstring(line[i], 4)

dict[index] = value

inverseDict[value] = index

SBoxBitstring.append(dict)

SBoxBitstringInverse.append(inverseDict)

# The Initial and Final permutations are each represented by one list

# containing the integers in 0..127 without repetitions. Having value v

# (say, 32) at position p (say, 1) means that the output bit at position p

# (1) comes from the input bit at position v (32).

IPTable = [

0, 32, 64, 96, 1, 33, 65, 97, 2, 34, 66, 98, 3, 35, 67, 99,

4, 36, 68, 100, 5, 37, 69, 101, 6, 38, 70, 102, 7, 39, 71, 103,

8, 40, 72, 104, 9, 41, 73, 105, 10, 42, 74, 106, 11, 43, 75, 107,

12, 44, 76, 108, 13, 45, 77, 109, 14, 46, 78, 110, 15, 47, 79, 111,

16, 48, 80, 112, 17, 49, 81, 113, 18, 50, 82, 114, 19, 51, 83, 115,

20, 52, 84, 116, 21, 53, 85, 117, 22, 54, 86, 118, 23, 55, 87, 119,

24, 56, 88, 120, 25, 57, 89, 121, 26, 58, 90, 122, 27, 59, 91, 123,

28, 60, 92, 124, 29, 61, 93, 125, 30, 62, 94, 126, 31, 63, 95, 127,

]

FPTable = [

0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60,

64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124,

1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61,

65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125,

2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62,

66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126,

3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63,

67, 71, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123, 127,

]

# The Linear Transformation is represented as a list of 128 lists, one for

# each output bit. Each one of the 128 lists is composed of a variable

# number of integers in 0..127 specifying the positions of the input bits

# that must be XORed together (say, 72, 144 and 125) to yield the output

# bit corresponding to the position of that list (say, 1).

LTTable = [

[16, 52, 56, 70, 83, 94, 105],

[72, 114, 125],

[2, 9, 15, 30, 76, 84, 126],

[36, 90, 103],

[20, 56, 60, 74, 87, 98, 109],

[1, 76, 118],

[2, 6, 13, 19, 34, 80, 88],

[40, 94, 107],

[24, 60, 64, 78, 91, 102, 113],

[5, 80, 122],

[6, 10, 17, 23, 38, 84, 92],

[44, 98, 111],

[28, 64, 68, 82, 95, 106, 117],

[9, 84, 126],

[10, 14, 21, 27, 42, 88, 96],

[48, 102, 115],

[32, 68, 72, 86, 99, 110, 121],

[2, 13, 88],

[14, 18, 25, 31, 46, 92, 100],

[52, 106, 119],

[36, 72, 76, 90, 103, 114, 125],

[6, 17, 92],

[18, 22, 29, 35, 50, 96, 104],

[56, 110, 123],

[1, 40, 76, 80, 94, 107, 118],

[10, 21, 96],

[22, 26, 33, 39, 54, 100, 108],

[60, 114, 127],

[5, 44, 80, 84, 98, 111, 122],

[14, 25, 100],

[26, 30, 37, 43, 58, 104, 112],

[3, 118],

[9, 48, 84, 88, 102, 115, 126],

[18, 29, 104],

[30, 34, 41, 47, 62, 108, 116],

[7, 122],

[2, 13, 52, 88, 92, 106, 119],

[22, 33, 108],

[34, 38, 45, 51, 66, 112, 120],

[11, 126],

[6, 17, 56, 92, 96, 110, 123],

[26, 37, 112],

[38, 42, 49, 55, 70, 116, 124],

[2, 15, 76],

[10, 21, 60, 96, 100, 114, 127],

[30, 41, 116],

[0, 42, 46, 53, 59, 74, 120],

[6, 19, 80],

[3, 14, 25, 100, 104, 118],

[34, 45, 120],

[4, 46, 50, 57, 63, 78, 124],

[10, 23, 84],

[7, 18, 29, 104, 108, 122],

[38, 49, 124],

[0, 8, 50, 54, 61, 67, 82],

[14, 27, 88],

[11, 22, 33, 108, 112, 126],

[0, 42, 53],

[4, 12, 54, 58, 65, 71, 86],

[18, 31, 92],

[2, 15, 26, 37, 76, 112, 116],

[4, 46, 57],

[8, 16, 58, 62, 69, 75, 90],

[22, 35, 96],

[6, 19, 30, 41, 80, 116, 120],

[8, 50, 61],

[12, 20, 62, 66, 73, 79, 94],

[26, 39, 100],

[10, 23, 34, 45, 84, 120, 124],

[12, 54, 65],

[16, 24, 66, 70, 77, 83, 98],

[30, 43, 104],

[0, 14, 27, 38, 49, 88, 124],

[16, 58, 69],

[20, 28, 70, 74, 81, 87, 102],

[34, 47, 108],

[0, 4, 18, 31, 42, 53, 92],

[20, 62, 73],

[24, 32, 74, 78, 85, 91, 106],

[38, 51, 112],

[4, 8, 22, 35, 46, 57, 96],

[24, 66, 77],

[28, 36, 78, 82, 89, 95, 110],

[42, 55, 116],

[8, 12, 26, 39, 50, 61, 100],

[28, 70, 81],

[32, 40, 82, 86, 93, 99, 114],

[46, 59, 120],

[12, 16, 30, 43, 54, 65, 104],

[32, 74, 85],

[36, 90, 103, 118],

[50, 63, 124],

[16, 20, 34, 47, 58, 69, 108],

[36, 78, 89],

[40, 94, 107, 122],

[0, 54, 67],

[20, 24, 38, 51, 62, 73, 112],

[40, 82, 93],

[44, 98, 111, 126],

[4, 58, 71],

[24, 28, 42, 55, 66, 77, 116],

[44, 86, 97],

[2, 48, 102, 115],

[8, 62, 75],

[28, 32, 46, 59, 70, 81, 120],

[48, 90, 101],

[6, 52, 106, 119],

[12, 66, 79],

[32, 36, 50, 63, 74, 85, 124],

[52, 94, 105],

[10, 56, 110, 123],

[16, 70, 83],

[0, 36, 40, 54, 67, 78, 89],

[56, 98, 109],

[14, 60, 114, 127],

[20, 74, 87],

[4, 40, 44, 58, 71, 82, 93],

[60, 102, 113],

[3, 18, 72, 114, 118, 125],

[24, 78, 91],

[8, 44, 48, 62, 75, 86, 97],

[64, 106, 117],

[1, 7, 22, 76, 118, 122],

[28, 82, 95],

[12, 48, 52, 66, 79, 90, 101],

[68, 110, 121],

[5, 11, 26, 80, 122, 126],

[32, 86, 99],

]

# The following table, necessary for the non-bitslice decryption, doesn't

# come from the paper (although it would make a good addition to it). I

# derived it with a separate program that basically took a vector of [0, 1,

# ..., 127] and applied to it the three transformations defined by FP, the

# equations-based inverse LT, and IP.

LTTableInverse = [

[53, 55, 72],

[1, 5, 20, 90],

[15, 102],

[3, 31, 90],

[57, 59, 76],

[5, 9, 24, 94],

[19, 106],

[7, 35, 94],

[61, 63, 80],

[9, 13, 28, 98],

[23, 110],

[11, 39, 98],

[65, 67, 84],

[13, 17, 32, 102],

[27, 114],

[1, 3, 15, 20, 43, 102],

[69, 71, 88],

[17, 21, 36, 106],

[1, 31, 118],

[5, 7, 19, 24, 47, 106],

[73, 75, 92],

[21, 25, 40, 110],

[5, 35, 122],

[9, 11, 23, 28, 51, 110],

[77, 79, 96],

[25, 29, 44, 114],

[9, 39, 126],

[13, 15, 27, 32, 55, 114],

[81, 83, 100],

[1, 29, 33, 48, 118],

[2, 13, 43],

[1, 17, 19, 31, 36, 59, 118],

[85, 87, 104],

[5, 33, 37, 52, 122],

[6, 17, 47],

[5, 21, 23, 35, 40, 63, 122],

[89, 91, 108],

[9, 37, 41, 56, 126],

[10, 21, 51],

[9, 25, 27, 39, 44, 67, 126],

[93, 95, 112],

[2, 13, 41, 45, 60],

[14, 25, 55],

[2, 13, 29, 31, 43, 48, 71],

[97, 99, 116],

[6, 17, 45, 49, 64],

[18, 29, 59],

[6, 17, 33, 35, 47, 52, 75],

[101, 103, 120],

[10, 21, 49, 53, 68],

[22, 33, 63],

[10, 21, 37, 39, 51, 56, 79],

[105, 107, 124],

[14, 25, 53, 57, 72],

[26, 37, 67],

[14, 25, 41, 43, 55, 60, 83],

[0, 109, 111],

[18, 29, 57, 61, 76],

[30, 41, 71],

[18, 29, 45, 47, 59, 64, 87],

[4, 113, 115],

[22, 33, 61, 65, 80],

[34, 45, 75],

[22, 33, 49, 51, 63, 68, 91],

[8, 117, 119],

[26, 37, 65, 69, 84],

[38, 49, 79],

[26, 37, 53, 55, 67, 72, 95],

[12, 121, 123],

[30, 41, 69, 73, 88],

[42, 53, 83],

[30, 41, 57, 59, 71, 76, 99],

[16, 125, 127],

[34, 45, 73, 77, 92],

[46, 57, 87],

[34, 45, 61, 63, 75, 80, 103],

[1, 3, 20],

[38, 49, 77, 81, 96],

[50, 61, 91],

[38, 49, 65, 67, 79, 84, 107],

[5, 7, 24],

[42, 53, 81, 85, 100],

[54, 65, 95],

[42, 53, 69, 71, 83, 88, 111],

[9, 11, 28],

[46, 57, 85, 89, 104],

[58, 69, 99],

[46, 57, 73, 75, 87, 92, 115],

[13, 15, 32],

[50, 61, 89, 93, 108],

[62, 73, 103],

[50, 61, 77, 79, 91, 96, 119],

[17, 19, 36],

[54, 65, 93, 97, 112],

[66, 77, 107],

[54, 65, 81, 83, 95, 100, 123],

[21, 23, 40],

[58, 69, 97, 101, 116],

[70, 81, 111],

[58, 69, 85, 87, 99, 104, 127],

[25, 27, 44],

[62, 73, 101, 105, 120],

[74, 85, 115],

[3, 62, 73, 89, 91, 103, 108],

[29, 31, 48],

[66, 77, 105, 109, 124],

[78, 89, 119],

[7, 66, 77, 93, 95, 107, 112],

[33, 35, 52],

[0, 70, 81, 109, 113],

[82, 93, 123],

[11, 70, 81, 97, 99, 111, 116],

[37, 39, 56],

[4, 74, 85, 113, 117],

[86, 97, 127],

[15, 74, 85, 101, 103, 115, 120],

[41, 43, 60],

[8, 78, 89, 117, 121],

[3, 90],

[19, 78, 89, 105, 107, 119, 124],

[45, 47, 64],

[12, 82, 93, 121, 125],

[7, 94],

[0, 23, 82, 93, 109, 111, 123],

[49, 51, 68],

[1, 16, 86, 97, 125],

[11, 98],

[4, 27, 86, 97, 113, 115, 127],

]

# --------------------------------------------------

# Handling command line arguments and stuff

help = """

Serpent Reference Implementation

written by Frank Stajano http://www.cl.cam.ac.uk/~fms27/

Cambridge University Computer Laboratory

$Id: SERPREF.PY,v 1.15 1998/03/05 16:46:27 fms Exp fms $

Serpent cipher by Eli Biham, Ross Anderson, Lars Knudsen

Encrypts or decrypts one block of data using the Serpent cipher and

optionally showing you what's going on inside at the various stages of

the computation.

SYNTAX: serpref mode [options]

MODE is one of the following:

-e -> encrypt

-d -> decrypt

-s -> self-test

-h -> help (the text you're reading right now)

OPTIONS are:

-f format -> The format in which the long numbers are expressed

(both for input and for output).

There are three formats: "b" for bitstring (little-

endian sequence of 0 and 1); "h" for hexstring

(little-endian sequence of hex digits); "hws" for hex

word sequence (little-endian sequence of C-style 32-bit

words, each printed in big-endian format as "0x" followed

by 8 hex digits, with one space between words). The

default is "hws". Optional.

-p plainText -> The 128-bit value to be encrypted, expressed in the

format specified by -f. Required in mode -e, optional

in mode -s. Ignored otherwise.

-c cipherText -> The 128-bit value to be decrypted, expressed in the

format specified by -f. Required in mode -d. Ignored

otherwise.

-k key -> The 256-bit value of the key, expressed in the format

specified by -f. Required in modes -e and -d, optional

in mode -s.

-t tagName -> Turn on the observer tag with that name. This means that

any observer messages associated with this tag will

now be displayed. This option may be specified several

times to add multiple tags.

The special tag ALL turns on all the messages.

-b -> Use the bitslice version instead of the traditional

version, which is otherwise used by default. Optional in

modes -e and -d. Ignored otherwise.

NOTE: when using the (default) "hws" format, where values contain spaces,

be sure to use quoting so that this program receives the entire value as

one argument.

TAGS:

These are the tags of the quantities you can currently observe with

-t. Names are modelled on the paper's notation.

For the non-bitslice: BHati xored SHati BHatiPlus1 wi KHati

For the bitslice: Bi xored Si BiPlus1 wi Ki

Generic: plainText userKey cipherText testTitle fnTitle

I/O FORMAT:

All the I/O formats used by serpref (-f b for bitstring, -f h for

hexstring, -f hws for hex word sequence) are little-endian: whatever

the basic chunk is (respectively: a bit, a 4-bit nibble or a 32-bit

word), if it occurs in position 'i' then its weight is N^i (with N

being respectively 2, 2^4, 2^32).

As an example, the number ten extended to 64 bits is expressed like this:

-f b

"0101000000000000000000000000000000000000000000000000000000000000"

-f h

"a00000000000000"

-f hws

"0x0000000a 0x00000000"

Note that the hws format always requires you to use 8 hex digits per

word even if C would allow you to write the third example as "0xa

0x0". Individual words in the hws format are in fact big-endian, for

compatibility with the C I/O library.

USAGE EXAMPLES:

serpref -s

Runs a self-test (made of three sub-tests) on a randomly chosen

plaintext and key. The tests compare encryption/decription and

traditional/bitslice.

serpref -s -k 0x0000000d -p 0x00000011

Runs the self-test on the key "13" and on the plaintext "17".

serpref -s -k 1011 -p 10001 -f b

Runs the self-test on the key "13" and on the plaintext "17", but

using bitstrings as the input/output format.

serpref -s -k 0x0000000d -p "0x00000011 0x00000001"

Runs the self-test on the key "13" and on the plaintext "17+2^32"

(however much that is).

serpref -e -k "0x32158636 0x0da0aa51 0x97db1144 0x44cf2c28 0x7c3fb76d

0x987257da 0xdafd0f29 0x7bf6334d" -p "0x650cba82 0xffd10f30 0xf645ba29

0x6f195106"

A realistic example (imagine it all on one line) in which we supply

a full 256 bit key and a full 128 bit plaintext. The -e requests an

encryption.

serpref -e -k "0x32158636 0x0da0aa51 0x97db1144 0x44cf2c28 0x7c3fb76d

0x987257da 0xdafd0f29 0x7bf6334d" -p "0x650cba82 0xffd10f30 0xf645ba29

0x6f195106" -b

Same as above, but the extra -b requests bitslice operation. As

things are, we won't notice the difference, but see below...

serpref -e -k "0x32158636 0x0da0aa51 0x97db1144 0x44cf2c28 0x7c3fb76d

0x987257da 0xdafd0f29 0x7bf6334d" -p "0x650cba82 0xffd10f30 0xf645ba29

0x6f195106" -b -t Bi

Same as above, but the "-t Bi" prints out all the intermediate

results with a tag of Bi, allowing you to see what happens inside

the rounds. Compare this with the following...

serpref -e -k "0x32158636 0x0da0aa51 0x97db1144 0x44cf2c28 0x7c3fb76d

0x987257da 0xdafd0f29 0x7bf6334d" -p "0x650cba82 0xffd10f30 0xf645ba29

0x6f195106" -t BHati

Same as above except that we are back to the non-bitslice version

(there is no -b) and we are printing the items with the BHati tag

(which is the equivalent of Bi for the non-bitslice version).

serpref -e -k "0x32158636 0x0da0aa51 0x97db1144 0x44cf2c28 0x7c3fb76d

0x987257da 0xdafd0f29 0x7bf6334d" -p "0x650cba82 0xffd10f30 0xf645ba29

0x6f195106" -t xored -t SHati -t BHati

Same as above but we are requesting even more details, basically

looking at all the intermediate results of each round as well. (You

could use the single magic tag -t ALL if you didn't want to have to

find out the names of the individual tags.)

"""

def helpExit(message = None):

print help

if message:

print "ERROR:", message

sys.exit()

def convertToBitstring(input, inputFormat, numBits):

"""Take a string 'input', theoretically in the format described by

'inputFormat' (one of Observer.formats) but in practice liable to

contain any sort of crap since it's user supplied, and return its

bitstring representation, normalised to numBits bits. Raise the

appropriate variant of ValueError (with explanatory message) if

anything can't be done (this includes the case where the 'input', while

otherwise syntactically correct, can't be represented in 'numBits'

bits)."""

if inputFormat == "fb":

if re.match("^[01]+$", input):

bitstring = input

else:

raise ValueError, "%s is not a valid bitstring" % input

elif inputFormat == "fh":

if re.match("^[0-9a-f]+$", input):

bitstring = hexstring2bitstring(input)

else:

raise ValueError, "%s is not a valid hexstring" % input

elif inputFormat == "fhws":

re_word = "(0x" + "[0-9a-f]"*8 + ")"

if re.match("^" + re_word + "( " + re_word + ")*$", input):

bitstring = hwseq2bitstring(input)

else:

raise ValueError, "%s is not a valid hwseq" % input

else:

raise ValueError, "invalid input format: %s (must be one of %s)" % (

inputFormat, Observer.formats)

# assert: bitstring now contains the bitstring version of the input

if len(bitstring) > numBits:

# Last chance: maybe it's got some useless 0s...

if re.match("^0+$", bitstring[numBits:]):

bitstring = bitstring[:numBits]

else:

raise ValueError, "input too large to fit in %d bits" % numBits

else:

bitstring = bitstring + "0" * (numBits-len(bitstring))

return bitstring

def randomBitstring(numBits):

"""Return a random bitstring of length 'numBits'."""

result = ""

# I don't do it in 32-bit chunks or I'd have problems with the sign bit

# in randint.

for i in range(numBits/30 + 1):

result = result + bitstring(whrandom.randint(0, 0x3fffffff), 30)

return result[:numBits]

def main():

optList, rest = getopt.getopt(sys.argv[1:], "edshbt:f:k:p:c:")

if rest:

helpExit("Sorry, can't make sense of this: '%s'" % rest)

# Transform the list of options into a more comfortable

# dictionary. This only works with non-repeated options, though, so

# tags (which are repeated) must be dealt with separately.

options = {}

for key, value in optList:

if key == "-t":

O.addTag(value)

else:

if key in options.keys():

helpExit("Multiple occurrences of " + key)

else:

options[string.strip(key)] = string.strip(value)

# Not more than one mode

mode = None

for k in options.keys():

if k in ["-e", "-d", "-s", "-h"]:

if mode:

helpExit("you can only specify one mode")

else:

mode = k

if not mode:

helpExit("No mode specified")

# Determine the number format

if options.has_key("-f"):

if options["-f"] in ["b", "h", "hws"]:

format = "f" + options["-f"]

else:

helpExit("-f (format) can only be one of b, h or hws")

else:

format = "fhws"

O.setFormat(format)

# Put plainText, userKey, cipherText in bitstring format.

plainText = userKey = cipherText = None

if options.has_key("-k"):

userKey = convertToBitstring(options["-k"], format, 256)

if options.has_key("-p"):

plainText = convertToBitstring(options["-p"], format, 128)

if options.has_key("-c"):

cipherText = convertToBitstring(options["-c"], format, 128)

if mode == "-e" or mode == "-d":

if not userKey:

helpExit("-k (key) required when doing -e (encrypt) or -d (decrypt)")

if mode == "-e":

if not plainText:

helpExit("-p (plaintext) is required when doing -e (encrypt)")

if mode == "-d":

if not cipherText:

helpExit("-c (ciphertext) is required when doing -d (decrypt)")

# Make up random key and plaintext if missing for self test

if mode == "-s":

if not plainText:

plainText = randomBitstring(128)

if not userKey:

userKey = randomBitstring(256)

# Perform the action specified by the mode

# NOTE that the observer will automatically print the basic stuff such

# as plainText, userKey and cipherText (in the right format too), so we

# only need to perform the action, without adding any extra print

# statements here.

if mode == "-e":

if options.has_key("-b"):

cipherText = encryptBitslice(plainText, userKey)

else:

cipherText = encrypt(plainText, userKey)

elif mode == "-d":

if options.has_key("-b"):

plainText = decryptBitslice(cipherText, userKey)

else:

plainText = decrypt(cipherText, userKey)

elif mode == "-s":

O.addTag("testTitle", "fnTitle")

printTest(test1(plainText, userKey))

printTest(test2(plainText, userKey))

printTest(test3(plainText, userKey))

else:

helpExit()

if __name__ == "__main__":

main()

- ActiveX (927)
- Advisory (69,350)
- Arbitrary (13,317)
- BBS (2,859)
- Bypass (1,190)
- CGI (967)
- Code Execution (5,062)
- Conference (618)
- Cracker (771)
- CSRF (2,939)
- DoS (19,154)
- Encryption (2,253)
- Exploit (42,306)
- File Inclusion (3,926)
- File Upload (803)
- Firewall (813)
- Info Disclosure (2,124)
- Intrusion Detection (755)
- Java (2,284)
- JavaScript (669)
- Kernel (4,868)
- Local (13,036)
- Magazine (570)
- Overflow (10,623)
- Perl (1,360)
- PHP (4,733)
- Proof of Concept (2,018)
- Protocol (2,723)
- Python (1,090)
- Remote (26,277)
- Root (3,115)
- Scanner (1,527)
- Security Tool (6,993)
- Shell (2,663)
- Shellcode (1,035)
- Sniffer (841)
- Spoof (1,908)
- SQL Injection (14,816)
- TCP (2,234)
- Trojan (554)
- UDP (849)
- Virus (615)
- Vulnerability (27,188)
- Web (7,674)
- Whitepaper (3,403)
- x86 (826)
- XSS (15,702)
- Other

- AIX (411)
- Apple (1,600)
- BSD (341)
- Cisco (1,830)
- Debian (5,635)
- Fedora (1,683)
- FreeBSD (1,201)
- Gentoo (3,613)
- HPUX (867)
- iOS (195)
- iPhone (104)
- IRIX (219)
- Juniper (66)
- Linux (34,383)
- Mac OS X (654)
- Mandriva (3,105)
- NetBSD (255)
- OpenBSD (458)
- RedHat (6,925)
- Slackware (862)
- Solaris (1,575)
- SUSE (1,444)
- Ubuntu (5,892)
- UNIX (8,363)
- UnixWare (174)
- Windows (5,401)
- Other

- Site Links
- News by Month
- News Tags
- Files by Month
- File Tags
- File Directory

- Services
- Security Services
- Hosting By
- Rokasec

## Comments

Subscribe to this comment feedNo comments yet, be the first!