Difference between revisions of "Sage Sandbox"

From YobiWiki
Jump to navigation Jump to search
(added padding.py)
Line 127: Line 127:
 
==Learning Python==
 
==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.
 
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}}
  +
<source lang=python>
  +
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
  +
</source>
  +
Download code: [{{#filelink: helper.py}} helper.py]
  +
 
{{#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
 
from binascii import hexlify
  +
import random
  +
  +
try:
  +
import helper
  +
except ImportError:
  +
print 'ERROR: helper.py should be placed in the same location as padding.py'
  +
raise ImportError
   
 
class BlockOperator:
 
class BlockOperator:
Line 164: Line 194:
 
elif algo == 'ANSI_X923':
 
elif algo == 'ANSI_X923':
 
return self.__ANSI_X923(toPad)
 
return self.__ANSI_X923(toPad)
  +
elif algo == 'ISO_10126':
  +
return self.__ISO_10126(toPad)
 
raise NotImplementedError()
 
raise NotImplementedError()
   
def unpad (self, toPad, algo):
+
def unpad (self, padded, algo):
 
if algo == 'bitpadding':
 
if algo == 'bitpadding':
return self.__bitPadding_unpad(toPad)
+
return self.__bitPadding_unpad(padded)
 
elif algo == 'zerospadding':
 
elif algo == 'zerospadding':
return self.__zerosPadding_unpad(toPad)
+
return self.__zerosPadding_unpad(padded)
 
elif algo == 'PKCS7':
 
elif algo == 'PKCS7':
return self.__PKCS7_unpad(toPad)
+
return self.__PKCS7_unpad(padded)
 
elif algo == 'ANSI_X923':
 
elif algo == 'ANSI_X923':
return self.__ANSI_X923_unpad(toPad)
+
return self.__ANSI_X923_unpad(padded)
  +
elif algo == 'ISO_10126':
  +
return self.__ISO_10126_unpad(padded)
 
raise NotImplementedError()
 
raise NotImplementedError()
   
Line 188: Line 222:
   
 
def __zerosPadding (self, toPad ):
 
def __zerosPadding (self, toPad ):
if 0 <> len(toPad)%self.blockSize:
+
#if 0 <> len(toPad)%self.blockSize:
padded = toPad + '\x00'*(self.blockSize - len(toPad)%self.blockSize)
+
# pad = '\x00'*(self.blockSize - len(toPad)%self.blockSize)
else:
+
#else:
padded = toPad
+
# pad = ''
return padded
+
#return ''.join([toPad,pad])
  +
totalLength = helper.roundUp(len(toPad),self.blockSize)
  +
return toPad.ljust(totalLength,'\x00')
  +
   
 
def __zerosPadding_unpad (self, padded ):
 
def __zerosPadding_unpad (self, padded ):
Line 212: Line 249:
 
# string formatting options: http://docs.python.org/lib/typesseq-strings.html
 
# string formatting options: http://docs.python.org/lib/typesseq-strings.html
 
code = "%02x" % pattern
 
code = "%02x" % pattern
patternstring = ('\\x' + code).decode('string_escape')
+
patternstring = ('\\x' + code).decode('string_escape')
padded = toPad + patternstring*(self.blockSize - len(toPad)%self.blockSize)
+
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:
 
else:
 
# no padding needed
 
# no padding needed
padded = toPad
+
pad = ''
return padded
+
return toPad + pad
   
 
def __PKCS7_unpad (self, padded ):
 
def __PKCS7_unpad (self, padded ):
Line 237: Line 276:
 
trail = "%02x" % bytesToPad
 
trail = "%02x" % bytesToPad
 
pattern = '\x00'*(bytesToPad -1) + ('\\x' + trail).decode('string_escape')
 
pattern = '\x00'*(bytesToPad -1) + ('\\x' + trail).decode('string_escape')
padded = toPad + pattern
+
return toPad + pattern
return padded
 
   
 
def __ANSI_X923_unpad (self, padded ):
 
def __ANSI_X923_unpad (self, padded ):
Line 244: Line 282:
 
flag = 0
 
flag = 0
 
#check if the bytes to be removed are all zero
 
#check if the bytes to be removed are all zero
for i in range(1,length + 1):
+
for x in padded[-length:-1]:
if padded[-i] <> '\x00':
+
if int(hexlify(x),16) <> 0:
 
flag = 1
 
flag = 1
 
if flag == 0:
 
if flag == 0:
Line 253: Line 291:
 
print 'error: padding pattern not recognized'
 
print 'error: padding pattern not recognized'
 
return unpadded
 
return unpadded
  +
  +
def __ISO_10126 (self, toPad):
  +
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))
  +
pattern2 = ("\\x%02x" % bytesToPad).decode('string_escape')
  +
return toPad + pattern1 + pattern2
  +
  +
def __ISO_10126_unpad (self, padded):
  +
return padded[0:len(padded)-int(hexlify(padded[-1]),16)]
  +
   
 
if __name__ == "__main__":
 
if __name__ == "__main__":
Line 267: Line 315:
 
print "Test OK!"
 
print "Test OK!"
 
else:
 
else:
print "Test not OK!"
+
print "Test not OK!"
 
</source>
 
</source>
 
Download code: [{{#filelink: padding.py}} padding.py]
 
Download code: [{{#filelink: padding.py}} padding.py]

Revision as of 19:19, 24 August 2008

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=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=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 binascii.unhexlify('%0*x' % (int(math.ceil(math.log(i,2)/8)*2) , i))
long2string(123456789012345678901234)
#4.77 µs per loop
long2string(1234567890123456789012345)
#4.83 µs per loop
###################################################################################
def long2string(i):
	return binascii.unhexlify('%0*x' % (len(hex(i)) & -2 , i)).lstrip('\x00')
long2string(123456789012345678901234)
#3.64 µs per loop
long2string(1234567890123456789012345)
#3.78 µs per loop
###################################################################################
binascii.unhexlify('%0*x' % (32 , 123456789012345678901234))
#2.06 µs per loop
binascii.unhexlify('%0*x' % (32 , 1234567890123456789012345))
#2.01 µs per loop
#Ok if you want fixed length buffer, e.g. 32 bytes
###################################################################################
def long2string(i):
    s=hex(i)[2:].rstrip('L')
    if len(s) % 2:
        s='0'+s
    return binascii.unhexlify(s)
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 binascii.unhexlify(s[len(s) % 2:])
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 binascii.unhexlify(s)
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 binascii.unhexlify(s[len(s) % 2:])
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 binascii.unhexlify(s)
long2string(123456789012345678901234)
#2.42 µs per loop
long2string(1234567890123456789012345)
#2.76 µs per loop
###################################################################################
# There is still room ;-)
binascii.unhexlify('1a249b1f10a06c96aff2')
#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)
#3.15 µs per loop
###################################################################################
int(binascii.hexlify('\x01\x05n\x0f6\xa6D=\xe2\xdfy'),16)
#1.64 µs per loop
###################################################################################
long(binascii.hexlify('\x01\x05n\x0f6\xa6D=\xe2\xdfy'),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}}

#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

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)
	"""

	def pad (self, toPad, algo):
		#there is no switch statement in python
		#discussion with alternatives: http://simonwillison.net/2004/May/7/switch/
		if algo == 'bitpadding':
			return self.__bitPadding(toPad)
		elif algo == 'zerospadding':
			return self.__zerosPadding(toPad)
		elif algo == 'PKCS7':
			return self.__PKCS7(toPad)
		elif algo == 'ANSI_X923':
			return self.__ANSI_X923(toPad)
		elif algo == 'ISO_10126':
			return self.__ISO_10126(toPad)
		raise NotImplementedError()

	def unpad (self, padded, algo):
		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 ):
		if 0 <> len(toPad)%self.blockSize:
			padded = toPad + '\x80' + '\x00'*(self.blockSize - len(toPad)%self.blockSize -1)
		else:
			padded = toPad
		return padded

	def __bitPadding_unpad (self, padded ):
		return padded.rstrip('\x80' + '\x00')

	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)
		return toPad.ljust(totalLength,'\x00')
		

	def __zerosPadding_unpad (self, padded ):
		# info on rstrip and other string methods: http://docs.python.org/lib/string-methods.html
		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
		"""
		if 0 <> len(toPad)%self.blockSize:
			pattern = self.blockSize - len(toPad)%self.blockSize
	  		# 0 -> zero padding for numeric values; 2 -> length modifier; x -> hex output
			# string formatting options: http://docs.python.org/lib/typesseq-strings.html
			code = "%02x" % pattern
			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

	def __PKCS7_unpad (self, padded ):
		pattern = padded[-1]
		length = int(hexlify(pattern),16)
		flag = 0
		for i in range(1,length + 1):
			if padded[-i] <> pattern:
				flag = 1
		if flag == 0:
			unpadded = padded[:-length]
		else:
			unpadded = padded
			print 'error: padding pattern not recognized'
		return unpadded

	def __ANSI_X923 (self, toPad ):
		bytesToPad = self.blockSize - len(toPad)%self.blockSize
		trail = "%02x" % bytesToPad
		pattern = '\x00'*(bytesToPad -1) + ('\\x' + trail).decode('string_escape')
		return toPad + pattern

	def __ANSI_X923_unpad (self, padded ):
		length = int(hexlify(padded[-1]),16)
		flag = 0
		#check if the bytes to be removed are all zero
		for x in padded[-length:-1]:
			if int(hexlify(x),16) <> 0:
				flag = 1
		if flag == 0:
			unpadded = padded[:-length]
		else:
			unpadded = padded
			print 'error: padding pattern not recognized'
		return unpadded

	def __ISO_10126 (self, toPad):
		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))
		pattern2 = ("\\x%02x" % bytesToPad).decode('string_escape')
		return toPad + pattern1 + pattern2

	def __ISO_10126_unpad (self, padded):
		return padded[0:len(padded)-int(hexlify(padded[-1]),16)]


if __name__ == "__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!"

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