Difference between revisions of "Sage Sandbox"

From YobiWiki
Jump to navigation Jump to search
 
(4 intermediate revisions by 2 users not shown)
Line 9: Line 9:
 
while i:
 
while i:
 
l[0:0]=chr(i&0xff)
 
l[0:0]=chr(i&0xff)
i=i>>8
+
i>>=8
 
return ''.join(l)
 
return ''.join(l)
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
Line 20: Line 20:
 
while i:
 
while i:
 
l.append(chr(i&0xff))
 
l.append(chr(i&0xff))
i=i>>8
+
i>>=8
 
return ''.join(l[::-1])
 
return ''.join(l[::-1])
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
Line 36: Line 36:
 
import math
 
import math
 
def long2string(i):
 
def long2string(i):
return binascii.unhexlify('%0*x' % (int(math.ceil(math.log(i,2)/8)*2) , i))
+
return ('%0*x' % (int(math.ceil(math.log(i,2)/8)*2) , i)).decode('hex')
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
 
#4.77 µs per loop
 
#4.77 µs per loop
Line 43: Line 43:
 
###################################################################################
 
###################################################################################
 
def long2string(i):
 
def long2string(i):
return binascii.unhexlify('%0*x' % (len(hex(i)) & -2 , i)).lstrip('\x00')
+
return ('%0*x' % (len(hex(i)) & -2 , i)).decode('hex').lstrip('\x00')
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
 
#3.64 µs per loop
 
#3.64 µs per loop
Line 49: Line 49:
 
#3.78 µs per loop
 
#3.78 µs per loop
 
###################################################################################
 
###################################################################################
binascii.unhexlify('%0*x' % (32 , 123456789012345678901234))
+
('%0*x' % (2 * 16 , 123456789012345678901234)).decode('hex')
 
#2.06 µs per loop
 
#2.06 µs per loop
binascii.unhexlify('%0*x' % (32 , 1234567890123456789012345))
+
('%0*x' % (2 * 16 , 1234567890123456789012345)).decode('hex')
 
#2.01 µs per loop
 
#2.01 µs per loop
#Ok if you want fixed length buffer, e.g. 32 bytes
+
#Ok if you want fixed length buffer, e.g. 16 bytes
 
###################################################################################
 
###################################################################################
 
def long2string(i):
 
def long2string(i):
Line 59: Line 59:
 
if len(s) % 2:
 
if len(s) % 2:
 
s='0'+s
 
s='0'+s
return binascii.unhexlify(s)
+
return s.decode('hex')
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
 
#2.88 µs per loop
 
#2.88 µs per loop
Line 67: Line 67:
 
def long2string(i):
 
def long2string(i):
 
s='0'+hex(long(i))[2:-1]
 
s='0'+hex(long(i))[2:-1]
return binascii.unhexlify(s[len(s) % 2:])
+
return s[len(s) % 2:].decode('hex')
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
 
#3.04 µs per loop
 
#3.04 µs per loop
Line 77: Line 77:
 
if len(s) % 2:
 
if len(s) % 2:
 
s='0'+s
 
s='0'+s
return binascii.unhexlify(s)
+
return s.decode('hex')
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
 
#2.75 µs per loop
 
#2.75 µs per loop
Line 86: Line 86:
 
def long2string(i):
 
def long2string(i):
 
s='0'+hex(i)[2:-1]
 
s='0'+hex(i)[2:-1]
return binascii.unhexlify(s[len(s) % 2:])
+
return s[len(s) % 2:].decode('hex')
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
 
#2.66 µs per loop
 
#2.66 µs per loop
Line 96: Line 96:
 
if len(s) % 2:
 
if len(s) % 2:
 
s='0'+s
 
s='0'+s
return binascii.unhexlify(s)
+
return s.decode('hex')
 
long2string(123456789012345678901234)
 
long2string(123456789012345678901234)
 
#2.42 µs per loop
 
#2.42 µs per loop
Line 103: Line 103:
 
###################################################################################
 
###################################################################################
 
# There is still room ;-)
 
# There is still room ;-)
  +
'1a249b1f10a06c96aff2'.decode('hex')
binascii.unhexlify('1a249b1f10a06c96aff2')
 
 
#990 ns per loop
 
#990 ns per loop
 
###################################################################################
 
###################################################################################
Line 116: Line 116:
 
###################################################################################
 
###################################################################################
 
int('\x01\x05n\x0f6\xa6D=\xe2\xdfy'.encode('hex'),16)
 
int('\x01\x05n\x0f6\xa6D=\xe2\xdfy'.encode('hex'),16)
#3.15 µs per loop
 
###################################################################################
 
int(binascii.hexlify('\x01\x05n\x0f6\xa6D=\xe2\xdfy'),16)
 
 
#1.64 µs per loop
 
#1.64 µs per loop
 
###################################################################################
 
###################################################################################
long(binascii.hexlify('\x01\x05n\x0f6\xa6D=\xe2\xdfy'),16)
+
long('\x01\x05n\x0f6\xa6D=\xe2\xdfy'.encode('hex'),16)
 
#1.38 µs per loop
 
#1.38 µs per loop
 
</source>
 
</source>
Line 149: Line 146:
 
{{#fileanchor: padding.py}}
 
{{#fileanchor: padding.py}}
 
<source lang=python>
 
<source lang=python>
#Als input al multiple van blocksize is, dan toch padden?
 
#bij ansi en iso zou ge de laatste byte verkeerd kunnen interpreteren als toch gepad ipv niet
 
 
from binascii import hexlify
 
 
import random
 
import random
  +
  +
import sys
  +
from optparse import OptionParser
   
 
try:
 
try:
Line 166: Line 162:
 
Only holds a variable blocksize. Class only exist for testing purpose.
 
Only holds a variable blocksize. Class only exist for testing purpose.
 
"""
 
"""
  +
 
def __init__(self, bs=8):
 
def __init__(self, bs=8):
 
self.blockSize = bs
 
self.blockSize = bs
Line 181: Line 178:
 
padded = padder.pad('test','bitpadding)
 
padded = padder.pad('test','bitpadding)
 
padder.unpad(padded,'bitpadding)
 
padder.unpad(padded,'bitpadding)
  +
"""
 
  +
After changing the source:
 
  +
del padder
#def __getattr__(self, name):
 
  +
del Padding
# #this is called when getattr() called on this object doesn't find the attribute wanted
 
  +
from padding import *
# #this will now try to see if it can find a private function
 
  +
"""
# return getattr(self,"_Padding__" + name)
 
 
   
 
def pad (self, toPad, algo):
 
def pad (self, toPad, algo):
return getattr(self,"_Padding__%s" % algo)(toPad) #private function has to be accessed by it's public name...
+
#private function has to be accessed by it's public name when using getattr...
  +
return getattr(self,"_%(classname)s__%(algo)s" % {'classname':self.__class__.__name__,'algo':algo})(toPad)
 
   
 
def unpad (self, padded, algo):
 
def unpad (self, padded, algo):
 
#there is no switch statement in python
 
#there is no switch statement in python
 
#discussion with alternatives: http://simonwillison.net/2004/May/7/switch/
 
#discussion with alternatives: http://simonwillison.net/2004/May/7/switch/
if algo == 'bitpadding':
+
if algo == 'bitPadding':
 
return self.__bitPadding_unpad(padded)
 
return self.__bitPadding_unpad(padded)
elif algo == 'zerospadding':
+
elif algo == 'zerosPadding':
 
return self.__zerosPadding_unpad(padded)
 
return self.__zerosPadding_unpad(padded)
 
elif algo == 'PKCS7':
 
elif algo == 'PKCS7':
Line 209: Line 205:
   
 
def __bitPadding (self, toPad ):
 
def __bitPadding (self, toPad ):
if 0 <> len(toPad)%self.blockSize:
+
padded = toPad + '\x80' + '\x00'*(self.blockSize - len(toPad)%self.blockSize -1)
padded = toPad + '\x80' + '\x00'*(self.blockSize - len(toPad)%self.blockSize -1)
 
else:
 
padded = toPad
 
 
return padded
 
return padded
   
 
def __bitPadding_unpad (self, padded ):
 
def __bitPadding_unpad (self, padded ):
return padded.rstrip('\x80' + '\x00')
+
if padded.rstrip('\x00')[-1] == '\x80':
  +
return padded.rstrip('\x00')[:-1]
  +
else:
  +
return padded
   
 
def __zerosPadding (self, toPad ):
 
def __zerosPadding (self, toPad ):
#if 0 <> len(toPad)%self.blockSize:
 
# pad = '\x00'*(self.blockSize - len(toPad)%self.blockSize)
 
#else:
 
# pad = ''
 
#return ''.join([toPad,pad])
 
 
totalLength = helper.roundUp(len(toPad),self.blockSize)
 
totalLength = helper.roundUp(len(toPad),self.blockSize)
 
return toPad.ljust(totalLength,'\x00')
 
return toPad.ljust(totalLength,'\x00')
Line 229: Line 220:
   
 
def __zerosPadding_unpad (self, padded ):
 
def __zerosPadding_unpad (self, padded ):
# info on rstrip and other string methods: http://docs.python.org/lib/string-methods.html
 
 
return padded.rstrip('\x00')
 
return padded.rstrip('\x00')
   
Line 241: Line 231:
 
return a binary string
 
return a binary string
 
"""
 
"""
  +
if 0 <> len(toPad)%self.blockSize:
 
pattern = self.blockSize - len(toPad)%self.blockSize
+
pattern = self.blockSize - len(toPad)%self.blockSize
  +
patternstring = chr(pattern)
# 0 -> zero padding for numeric values; 2 -> length modifier; x -> hex output
 
  +
amount = self.blockSize - len(toPad)%self.blockSize
# string formatting options: http://docs.python.org/lib/typesseq-strings.html
 
code = "%02x" % pattern
+
pad = patternstring * amount
patternstring = ('\\x' + code).decode('string_escape')
 
amount = self.blockSize - len(toPad)%self.blockSize
 
pad = ''.join(patternstring for x in range(0,amount))
 
#pad = patternstring*(self.blockSize - len(toPad)%self.blockSize)
 
else:
 
# no padding needed
 
pad = ''
 
 
return toPad + pad
 
return toPad + pad
   
 
def __PKCS7_unpad (self, padded ):
 
def __PKCS7_unpad (self, padded ):
 
pattern = padded[-1]
 
pattern = padded[-1]
length = int(hexlify(pattern),16)
+
length = ord(pattern)
  +
#check if the bytes to be removed are all the same pattern
flag = 0
 
for i in range(1,length + 1):
+
if padded.endswith(pattern*length):
if padded[-i] <> pattern:
+
return padded[:-length]
flag = 1
 
if flag == 0:
 
unpadded = padded[:-length]
 
 
else:
 
else:
unpadded = padded
+
return padded
 
print 'error: padding pattern not recognized'
 
print 'error: padding pattern not recognized'
return unpadded
 
   
 
def __ANSI_X923 (self, toPad ):
 
def __ANSI_X923 (self, toPad ):
 
bytesToPad = self.blockSize - len(toPad)%self.blockSize
 
bytesToPad = self.blockSize - len(toPad)%self.blockSize
trail = "%02x" % bytesToPad
+
pattern = '\x00'*(bytesToPad -1) + chr(bytesToPad)
pattern = '\x00'*(bytesToPad -1) + ('\\x' + trail).decode('string_escape')
 
 
return toPad + pattern
 
return toPad + pattern
   
 
def __ANSI_X923_unpad (self, padded ):
 
def __ANSI_X923_unpad (self, padded ):
length = int(hexlify(padded[-1]),16)
+
length = ord(padded[-1])
flag = 0
 
 
#check if the bytes to be removed are all zero
 
#check if the bytes to be removed are all zero
for x in padded[-length:-1]:
+
if padded.count('\x00',-length,-1) == length - 1:
  +
return padded[:-length]
if int(hexlify(x),16) <> 0:
 
flag = 1
 
if flag == 0:
 
unpadded = padded[:-length]
 
 
else:
 
else:
  +
print 'error: padding pattern not recognized %s' % padded.count('\x00',-length,-1)
unpadded = padded
 
  +
return unpadded
print 'error: padding pattern not recognized'
 
return unpadded
 
   
 
def __ISO_10126 (self, toPad):
 
def __ISO_10126 (self, toPad):
 
bytesToPad = self.blockSize - len(toPad)%self.blockSize
 
bytesToPad = self.blockSize - len(toPad)%self.blockSize
pattern1 = ''.join(("\\x%02x" % random.randint(0,255)).decode('string_escape') for x in range(0,bytesToPad-1))
+
pattern1 = ''.join(chr(random.randrange(256)) for x in range(bytesToPad-1))
pattern2 = ("\\x%02x" % bytesToPad).decode('string_escape')
+
pattern2 = chr(bytesToPad)
 
return toPad + pattern1 + pattern2
 
return toPad + pattern1 + pattern2
   
 
def __ISO_10126_unpad (self, padded):
 
def __ISO_10126_unpad (self, padded):
return padded[0:len(padded)-int(hexlify(padded[-1]),16)]
+
return padded[:-ord(padded[-1])]
  +
  +
def main():
  +
usage = "usage: %prog [options] [texttopad]"
  +
parser = OptionParser(usage=usage)
  +
parser.add_option("-b", "--blocksize", dest='blocksize', default=8, help="specify the bloksize", type='int')
  +
(options, args) = parser.parse_args()
  +
  +
if len(args) > 1:
  +
parser.error("Program takes maximum 1 argument")
  +
  +
try:
  +
testbench(args[0].decode('string_escape'),options.blocksize)
  +
except IndexError:
  +
testbench(blocksize=options.blocksize)
  +
  +
def testbench(toPad='test',blocksize=8):
  +
testbench = ('bitPadding','zerosPadding','PKCS7','ANSI_X923','ISO_10126')
  +
padder = Padding(blocksize)
  +
print "String to be padded: %r, with length %i\n" % (toPad,len(toPad))
  +
for test in testbench:
  +
padded = padder.pad(toPad,'%s'%test)
  +
print "padded: %r" % padded
  +
unpadded = padder.unpad(padded,'%s'%test)
  +
print "unpadded: %r" % unpadded
  +
if unpadded == toPad:
  +
print "%s test OK!\n" % test
  +
else:
  +
print "%s test not OK!\n" % test
   
   
 
if __name__ == "__main__":
 
if __name__ == "__main__":
  +
main()
#only executed when module is run by: "python padding.py"
 
from padding import *
 
padder = Padding()
 
toPad = 'test'
 
print "String to be padded: %r" % toPad
 
padded = padder.pad(toPad,'bitpadding')
 
print "padded: %r" % padded
 
unpadded = padder.unpad(padded,'bitpadding')
 
print "unpadded: %r" % unpadded
 
if unpadded == toPad:
 
print "Test OK!"
 
else:
 
print "Test not OK!"
 
 
</source>
 
</source>
 
Download code: [{{#filelink: padding.py}} padding.py]
 
Download code: [{{#filelink: padding.py}} padding.py]

Latest revision as of 13:18, 18 July 2011

Back to SAGE & cryptology

Helper functions

Conversion from int or long to raw string and back is discussed on the Python bug tracking list but since the problem is not yet solved, let's see how to make our own helper functions in a clean and fast way: (measures with %timeit of ipython)

###############################  long2string  ########################################
def long2string(i):
	l=[]
	while i:
		l[0:0]=chr(i&0xff)
		i>>=8
	return ''.join(l)
long2string(123456789012345678901234)
#46 µs per loop
long2string(1234567890123456789012345)
#51.8 µs per loop
###################################################################################
def long2string(i):
	l=[]
	while i:
		l.append(chr(i&0xff))
		i>>=8
	return ''.join(l[::-1])
long2string(123456789012345678901234)
#15.1 µs per loop
long2string(1234567890123456789012345)
#16.4 µs per loop
###################################################################################
def long2string(i): 
        return ''.join([chr(i>>j & 0xFF) for j in range(int(math.log(i,2)//8)<<3,-1,-8)])
long2string(123456789012345678901234)
#15.5 µs per loop
long2string(1234567890123456789012345)
#16.7 µs per loop
###################################################################################
import math
def long2string(i):
	return ('%0*x' % (int(math.ceil(math.log(i,2)/8)*2) , i)).decode('hex')
long2string(123456789012345678901234)
#4.77 µs per loop
long2string(1234567890123456789012345)
#4.83 µs per loop
###################################################################################
def long2string(i):
	return ('%0*x' % (len(hex(i)) & -2 , i)).decode('hex').lstrip('\x00')
long2string(123456789012345678901234)
#3.64 µs per loop
long2string(1234567890123456789012345)
#3.78 µs per loop
###################################################################################
('%0*x' % (2 * 16 , 123456789012345678901234)).decode('hex')
#2.06 µs per loop
('%0*x' % (2 * 16 , 1234567890123456789012345)).decode('hex')
#2.01 µs per loop
#Ok if you want fixed length buffer, e.g. 16 bytes
###################################################################################
def long2string(i):
    s=hex(i)[2:].rstrip('L')
    if len(s) % 2:
        s='0'+s
    return s.decode('hex')
long2string(123456789012345678901234)
#2.88 µs per loop
long2string(1234567890123456789012345)
#3.22 µs per loop
###################################################################################
def long2string(i):
    s='0'+hex(long(i))[2:-1]
    return s[len(s) % 2:].decode('hex')
long2string(123456789012345678901234)
#3.04 µs per loop
long2string(1234567890123456789012345)
#3.03 µs per loop
###################################################################################
def long2string(i):
    s=hex(long(i))[2:-1]
    if len(s) % 2:
        s='0'+s
    return s.decode('hex')
long2string(123456789012345678901234)
#2.75 µs per loop
long2string(1234567890123456789012345)
#3.06 µs per loop
###################################################################################
#If we are sure our argument is a long we can remove the cast in the 2 previous codes:
def long2string(i):
    s='0'+hex(i)[2:-1]
    return s[len(s) % 2:].decode('hex')
long2string(123456789012345678901234)
#2.66 µs per loop
long2string(1234567890123456789012345)
#2.62 µs per loop
###################################################################################
def long2string(i):
    s=hex(i)[2:-1]
    if len(s) % 2:
        s='0'+s
    return s.decode('hex')
long2string(123456789012345678901234)
#2.42 µs per loop
long2string(1234567890123456789012345)
#2.76 µs per loop
###################################################################################
# There is still room ;-)
'1a249b1f10a06c96aff2'.decode('hex')
#990 ns per loop
###################################################################################
###############################  string2long  ########################################
def string2long(s):
	i=0
	for c in s:
		i=(i<<8)+ord(c)
	return i
string2long('\x01\x05n\x0f6\xa6D=\xe2\xdfy')
#8.91 µs per loop
###################################################################################
int('\x01\x05n\x0f6\xa6D=\xe2\xdfy'.encode('hex'),16)
#1.64 µs per loop
###################################################################################
long('\x01\x05n\x0f6\xa6D=\xe2\xdfy'.encode('hex'),16)
#1.38 µs per loop

Learning Python

Some code to make some padding functions available. The code is used as a way to learn python, so it contains things that don't make sense or could be done better.

{{#fileanchor: helper.py}}

from __future__ import division #http://www.python.org/dev/peps/pep-0238/
import math

def roundUp (n, p):
	"""Round an integer up to the nearest multiple

	A given integer n will be round up to the nearest multiple of p

	Example:
	>>> roundUp(13,8)
	    16
	"""
	return int(math.ceil(n/p)*p)
	#return (n+p)/p*p

Download code: [{{#filelink: helper.py}} helper.py]

{{#fileanchor: padding.py}}

import random

import sys
from optparse import OptionParser

try:
	import helper
except ImportError:
	print 'ERROR: helper.py should be placed in the same location as padding.py'
	raise ImportError

class BlockOperator:
	"""Class for BlockOperator

	Only holds a variable blocksize. Class only exist for testing purpose.
	"""

	def __init__(self, bs=8):
		self.blockSize = bs

class Padding(BlockOperator):
	"""Class for padding functions

	Inherits from the BlockOperator class

	padding info here: http://en.wikipedia.org/wiki/Padding_(cryptography)

	Example:
		from padding import *
		padder = Padding()
		padded = padder.pad('test','bitpadding)
		padder.unpad(padded,'bitpadding)

	After changing the source:
		del padder
		del Padding
		from padding import *
	"""		

	def pad (self, toPad, algo):
		#private function has to be accessed by it's public name when using getattr...
		return getattr(self,"_%(classname)s__%(algo)s" % {'classname':self.__class__.__name__,'algo':algo})(toPad) 

	def unpad (self, padded, algo):
		#there is no switch statement in python
		#discussion with alternatives: http://simonwillison.net/2004/May/7/switch/
		if algo == 'bitPadding':
			return self.__bitPadding_unpad(padded)
		elif algo == 'zerosPadding':
			return self.__zerosPadding_unpad(padded)
		elif algo == 'PKCS7':
			return self.__PKCS7_unpad(padded)
		elif algo == 'ANSI_X923':
			return self.__ANSI_X923_unpad(padded)
		elif algo == 'ISO_10126':
			return self.__ISO_10126_unpad(padded)
		raise NotImplementedError()

	def __bitPadding (self, toPad ):
		padded = toPad + '\x80' + '\x00'*(self.blockSize - len(toPad)%self.blockSize -1)
		return padded

	def __bitPadding_unpad (self, padded ):
		if padded.rstrip('\x00')[-1] == '\x80':
			return padded.rstrip('\x00')[:-1]
		else:
			return padded

	def __zerosPadding (self, toPad ):
		totalLength = helper.roundUp(len(toPad),self.blockSize)
		return toPad.ljust(totalLength,'\x00')
		

	def __zerosPadding_unpad (self, padded ):
		return padded.rstrip('\x00')		

	def __PKCS7 (self, toPad ):
		"""Pad a binary string

		Input:
			toPad: binary string to be padded
			self.blockSize: the padded result will be a multiple of the self.blockSize(default=8)
		Output:
			return a binary string
		"""

		pattern = self.blockSize - len(toPad)%self.blockSize
		patternstring = chr(pattern)		
		amount = self.blockSize - len(toPad)%self.blockSize
		pad = patternstring * amount
		return toPad + pad

	def __PKCS7_unpad (self, padded ):
		pattern = padded[-1]
		length = ord(pattern)
		#check if the bytes to be removed are all the same pattern
		if padded.endswith(pattern*length):
			return padded[:-length]
		else:
			return padded
			print 'error: padding pattern not recognized'

	def __ANSI_X923 (self, toPad ):
		bytesToPad = self.blockSize - len(toPad)%self.blockSize
		pattern = '\x00'*(bytesToPad -1) + chr(bytesToPad)
		return toPad + pattern

	def __ANSI_X923_unpad (self, padded ):
		length = ord(padded[-1])
		#check if the bytes to be removed are all zero
		if padded.count('\x00',-length,-1) == length - 1:
			return padded[:-length]
		else:
			print 'error: padding pattern not recognized %s' % padded.count('\x00',-length,-1)
			return unpadded

	def __ISO_10126 (self, toPad):
		bytesToPad = self.blockSize - len(toPad)%self.blockSize
		pattern1 = ''.join(chr(random.randrange(256)) for x in range(bytesToPad-1))
		pattern2 = chr(bytesToPad)
		return toPad + pattern1 + pattern2

	def __ISO_10126_unpad (self, padded):
		return padded[:-ord(padded[-1])]

def main():
	usage = "usage: %prog [options] [texttopad]"
	parser = OptionParser(usage=usage)
	parser.add_option("-b", "--blocksize", dest='blocksize', default=8, help="specify the bloksize", type='int')
	(options, args) = parser.parse_args()
	
	if len(args) > 1:
		parser.error("Program takes maximum 1 argument")

	try:
		testbench(args[0].decode('string_escape'),options.blocksize)
	except IndexError:
		testbench(blocksize=options.blocksize)

def testbench(toPad='test',blocksize=8):
	testbench = ('bitPadding','zerosPadding','PKCS7','ANSI_X923','ISO_10126')
	padder = Padding(blocksize)
	print "String to be padded: %r, with length %i\n" % (toPad,len(toPad))
	for test in testbench:
		padded = padder.pad(toPad,'%s'%test)
		print "padded: %r" % padded
		unpadded = padder.unpad(padded,'%s'%test)
		print "unpadded: %r" % unpadded
		if unpadded == toPad:
			print "%s test OK!\n" % test
		else:
			print "%s test not OK!\n" % test


if __name__ == "__main__":
	main()

Download code: [{{#filelink: padding.py}} padding.py]