Difference between revisions of "Sage Sandbox"
Jump to navigation
Jump to search
(added padding.py) |
|||
Line 124: | Line 124: | ||
#1.38 µs per loop |
#1.38 µs per loop |
||
</source> |
</source> |
||
+ | |||
+ | ==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: padding.py}} |
||
+ | <source lang=python> |
||
+ | from binascii import hexlify |
||
+ | |||
+ | 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) |
||
+ | raise NotImplementedError() |
||
+ | |||
+ | def unpad (self, toPad, algo): |
||
+ | if algo == 'bitpadding': |
||
+ | return self.__bitPadding_unpad(toPad) |
||
+ | elif algo == 'zerospadding': |
||
+ | return self.__zerosPadding_unpad(toPad) |
||
+ | elif algo == 'PKCS7': |
||
+ | return self.__PKCS7_unpad(toPad) |
||
+ | elif algo == 'ANSI_X923': |
||
+ | return self.__ANSI_X923_unpad(toPad) |
||
+ | 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: |
||
+ | padded = toPad + '\x00'*(self.blockSize - len(toPad)%self.blockSize) |
||
+ | else: |
||
+ | padded = toPad |
||
+ | return 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') |
||
+ | |||
+ | 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') |
||
+ | padded = toPad + patternstring*(self.blockSize - len(toPad)%self.blockSize) |
||
+ | else: |
||
+ | # no padding needed |
||
+ | padded = toPad |
||
+ | return padded |
||
+ | |||
+ | 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') |
||
+ | padded = toPad + pattern |
||
+ | return padded |
||
+ | |||
+ | 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 i in range(1,length + 1): |
||
+ | if padded[-i] <> '\x00': |
||
+ | flag = 1 |
||
+ | if flag == 0: |
||
+ | unpadded = padded[:-length] |
||
+ | else: |
||
+ | unpadded = padded |
||
+ | print 'error: padding pattern not recognized' |
||
+ | return unpadded |
||
+ | |||
+ | 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!" |
||
+ | </source> |
||
+ | Download code: [{{#filelink: padding.py}} padding.py] |
Revision as of 15:06, 21 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: padding.py}}
from binascii import hexlify
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)
raise NotImplementedError()
def unpad (self, toPad, algo):
if algo == 'bitpadding':
return self.__bitPadding_unpad(toPad)
elif algo == 'zerospadding':
return self.__zerosPadding_unpad(toPad)
elif algo == 'PKCS7':
return self.__PKCS7_unpad(toPad)
elif algo == 'ANSI_X923':
return self.__ANSI_X923_unpad(toPad)
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:
padded = toPad + '\x00'*(self.blockSize - len(toPad)%self.blockSize)
else:
padded = toPad
return 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')
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')
padded = toPad + patternstring*(self.blockSize - len(toPad)%self.blockSize)
else:
# no padding needed
padded = toPad
return padded
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')
padded = toPad + pattern
return padded
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 i in range(1,length + 1):
if padded[-i] <> '\x00':
flag = 1
if flag == 0:
unpadded = padded[:-length]
else:
unpadded = padded
print 'error: padding pattern not recognized'
return unpadded
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]