Sage Sandbox

From YobiWiki
Jump to: navigation, search

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.


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: helper.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: padding.py

Personal tools
Namespaces

Variants
Actions
Navigation
Tools