padding.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. # =============================================================================
  2. # Copyright (c) 2008 Christophe Oosterlynck <christophe.oosterlynck_AT_gmail.com>
  3. # & NXP ( Philippe Teuwen <philippe.teuwen_AT_nxp.com> )
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. # THE SOFTWARE.
  22. # =============================================================================
  23. """Module for padding functions
  24. padding info here: http://en.wikipedia.org/wiki/Padding_(cryptography)
  25. """
  26. import random
  27. PAD = 0
  28. UNPAD = 1
  29. def bitPadding (padData, direction, length=None):
  30. """Pad a string using bitPadding
  31. padData = raw string to pad/unpad
  32. direction = PAD or UNPAD
  33. length = amount of bytes the padded string should be a multiple of
  34. (length variable is not used when unpadding)
  35. returns: (un)padded raw string
  36. A new block full of padding will be added when padding data that is
  37. already a multiple of the length.
  38. Example:
  39. =========
  40. >>> import padding
  41. >>> padding.bitPadding('test', padding.PAD, 8)
  42. 'test\\x80\\x00\\x00\\x00'
  43. >>> padding.bitPadding(_,padding.UNPAD)
  44. 'test'"""
  45. if direction == PAD:
  46. if length == None:
  47. raise ValueError("Supply a valid length")
  48. return __bitPadding(padData, length)
  49. elif direction == UNPAD:
  50. return __bitPadding_unpad(padData)
  51. else:
  52. raise ValueError("Supply a valid direction")
  53. def __bitPadding (toPad,length):
  54. padded = toPad + '\x80' + '\x00'*(length - len(toPad)%length -1)
  55. return padded
  56. def __bitPadding_unpad (padded):
  57. if padded.rstrip('\x00')[-1] == '\x80':
  58. return padded.rstrip('\x00')[:-1]
  59. else:
  60. return padded
  61. def zerosPadding (padData, direction, length=None):
  62. """Pad a string using zerosPadding
  63. padData = raw string to pad/unpad
  64. direction = PAD or UNPAD
  65. beware: padding and unpadding a string ending in 0's
  66. will remove those 0's too
  67. length = amount of bytes the padded string should be a multiple of
  68. (length variable is not used when unpadding)
  69. returns: (un)padded raw string
  70. No padding will be added when padding data that is already a
  71. multiple of the given length.
  72. Example:
  73. =========
  74. >>> import padding
  75. >>> padding.zerosPadding('12345678',padding.PAD,16)
  76. '12345678\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
  77. >>> padding.zerosPadding(_,padding.UNPAD)
  78. '12345678'"""
  79. if direction == PAD:
  80. if length == None:
  81. raise ValueError("Supply a valid length")
  82. return __zerosPadding(padData, length)
  83. elif direction == UNPAD:
  84. return __zerosPadding_unpad(padData)
  85. else:
  86. raise ValueError("Supply a valid direction")
  87. def __zerosPadding (toPad, length):
  88. padLength = (length - len(toPad))%length
  89. return toPad + '\x00'*padLength
  90. def __zerosPadding_unpad (padded ):
  91. return padded.rstrip('\x00')
  92. def PKCS7(padData, direction, length=None):
  93. """Pad a string using PKCS7
  94. padData = raw string to pad/unpad
  95. direction = PAD or UNPAD
  96. length = amount of bytes the padded string should be a multiple of
  97. (length variable is not used when unpadding)
  98. returns: (un)padded raw string
  99. A new block full of padding will be added when padding data that is
  100. already a multiple of the given length.
  101. Example:
  102. =========
  103. >>> import padding
  104. >>> padding.PKCS7('12345678',padding.PAD,16)
  105. '12345678\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08'
  106. >>> padding.PKCS7(_,padding.UNPAD)
  107. '12345678'"""
  108. if direction == PAD:
  109. if length == None:
  110. raise ValueError("Supply a valid length")
  111. return __PKCS7(padData, length)
  112. elif direction == UNPAD:
  113. return __PKCS7_unpad(padData)
  114. else:
  115. raise ValueError("Supply a valid direction")
  116. def __PKCS7 (toPad, length):
  117. amount = length - len(toPad)%length
  118. pattern = chr(amount)
  119. pad = pattern*amount
  120. return toPad + pad
  121. def __PKCS7_unpad (padded):
  122. pattern = padded[-1]
  123. length = ord(pattern)
  124. #check if the bytes to be removed are all the same pattern
  125. if padded.endswith(pattern*length):
  126. return padded[:-length]
  127. else:
  128. return padded
  129. print('error: padding pattern not recognized')
  130. def ANSI_X923 (padData, direction, length=None):
  131. """Pad a string using ANSI_X923
  132. padData = raw string to pad/unpad
  133. direction = PAD or UNPAD
  134. length = amount of bytes the padded string should be a multiple of
  135. (length variable is not used when unpadding)
  136. returns: (un)padded raw string
  137. A new block full of padding will be added when padding data that is
  138. already a multiple of the given length.
  139. Example:
  140. =========
  141. >>> import padding
  142. >>> padding.ANSI_X923('12345678',padding.PAD,16)
  143. '12345678\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08'
  144. >>> padding.ANSI_X923(_,padding.UNPAD)
  145. '12345678'"""
  146. if direction == PAD:
  147. if length == None:
  148. raise ValueError("Supply a valid length")
  149. return __ANSI_X923(padData, length)
  150. elif direction == UNPAD:
  151. return __ANSI_X923_unpad(padData)
  152. else:
  153. raise ValueError("Supply a valid direction")
  154. def __ANSI_X923 (toPad, length):
  155. bytesToPad = length - len(toPad)%length
  156. trail = chr(bytesToPad)
  157. pattern = '\x00'*(bytesToPad -1) + trail
  158. return toPad + pattern
  159. def __ANSI_X923_unpad (padded):
  160. length =ord(padded[-1])
  161. #check if the bytes to be removed are all zero
  162. if padded.count('\x00',-length,-1) == length - 1:
  163. return padded[:-length]
  164. else:
  165. print('error: padding pattern not recognized %s' % padded.count('\x00',-length,-1))
  166. return padded
  167. def ISO_10126 (padData, direction, length=None):
  168. """Pad a string using ISO_10126
  169. padData = raw string to pad/unpad
  170. direction = PAD or UNPAD
  171. length = amount of bytes the padded string should be a multiple of
  172. (length variable is not used when unpadding)
  173. returns: (un)padded raw string
  174. A new block full of padding will be added when padding data that is
  175. already a multiple of the given length.
  176. Example:
  177. =========
  178. >>> import padding
  179. >>> padded = padding.ISO_10126('12345678',padding.PAD,16)
  180. >>> padding.ISO_10126(padded,padding.UNPAD)
  181. '12345678'"""
  182. if direction == PAD:
  183. if length == None:
  184. raise ValueError("Supply a valid length")
  185. return __ISO_10126(padData, length)
  186. elif direction == UNPAD:
  187. return __ISO_10126_unpad(padData)
  188. else:
  189. raise ValueError("Supply a valid direction")
  190. def __ISO_10126 (toPad, length):
  191. bytesToPad = length - len(toPad)%length
  192. randomPattern = ''.join(chr(random.randint(0,255)) for x in range(0,bytesToPad-1))
  193. return toPad + randomPattern + chr(bytesToPad)
  194. def __ISO_10126_unpad (padded):
  195. return padded[0:len(padded)-ord(padded[-1])]
  196. def _test():
  197. import doctest
  198. doctest.testmod()
  199. if __name__ == "__main__":
  200. _test()