Slight change in copyright notice
[python-cryptoplus.git] / src / CryptoPlus / Util / padding.py
blob14285b842c5a30f5fe208e0de325c16f6334bc7c
1 # =============================================================================
2 # Copyright (c) 2008 Christophe Oosterlynck <christophe.oosterlynck_AT_gmail.com>
3 # & NXP ( Philippe Teuwen <philippe.teuwen_AT_nxp.com> )
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:
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
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 # =============================================================================
24 """Module for padding functions
26 padding info here: http://en.wikipedia.org/wiki/Padding_(cryptography)
27 """
29 import random
31 PAD = 0
32 UNPAD = 1
34 def bitPadding (padData, direction, length=None):
35 """Pad a string using bitPadding
37 padData = raw string to pad/unpad
38 direction = PAD or UNPAD
39 length = amount of bytes the padded string should be a multiple of
40 (length variable is not used when unpadding)
42 returns: (un)padded raw string
44 A new block full of padding will be added when padding data that is
45 already a multiple of the length.
47 Example:
48 =========
49 >>> import padding
51 >>> padding.bitPadding('test', padding.PAD, 8)
52 'test\\x80\\x00\\x00\\x00'
53 >>> padding.bitPadding(_,padding.UNPAD)
54 'test'"""
55 if direction == PAD:
56 if length == None:
57 raise ValueError,"Supply a valid length"
58 return __bitPadding(padData, length)
59 elif direction == UNPAD:
60 return __bitPadding_unpad(padData)
61 else:
62 raise ValueError,"Supply a valid direction"
64 def __bitPadding (toPad,length):
65 padded = toPad + '\x80' + '\x00'*(length - len(toPad)%length -1)
66 return padded
68 def __bitPadding_unpad (padded):
69 if padded.rstrip('\x00')[-1] == '\x80':
70 return padded.rstrip('\x00')[:-1]
71 else:
72 return padded
74 def zerosPadding (padData, direction, length=None):
75 """Pad a string using zerosPadding
77 padData = raw string to pad/unpad
78 direction = PAD or UNPAD
79 beware: padding and unpadding a string ending in 0's
80 will remove those 0's too
81 length = amount of bytes the padded string should be a multiple of
82 (length variable is not used when unpadding)
84 returns: (un)padded raw string
86 No padding will be added when padding data that is already a
87 multiple of the given length.
89 Example:
90 =========
91 >>> import padding
93 >>> padding.zerosPadding('12345678',padding.PAD,16)
94 '12345678\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
95 >>> padding.zerosPadding(_,padding.UNPAD)
96 '12345678'"""
97 if direction == PAD:
98 if length == None:
99 raise ValueError,"Supply a valid length"
100 return __zerosPadding(padData, length)
101 elif direction == UNPAD:
102 return __zerosPadding_unpad(padData)
103 else:
104 raise ValueError,"Supply a valid direction"
106 def __zerosPadding (toPad, length):
107 padLength = (length - len(toPad))%length
108 return toPad + '\x00'*padLength
110 def __zerosPadding_unpad (padded ):
111 return padded.rstrip('\x00')
113 def PKCS7(padData, direction, length=None):
114 """Pad a string using PKCS7
116 padData = raw string to pad/unpad
117 direction = PAD or UNPAD
118 length = amount of bytes the padded string should be a multiple of
119 (length variable is not used when unpadding)
121 returns: (un)padded raw string
123 A new block full of padding will be added when padding data that is
124 already a multiple of the given length.
126 Example:
127 =========
128 >>> import padding
130 >>> padding.PKCS7('12345678',padding.PAD,16)
131 '12345678\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08'
132 >>> padding.PKCS7(_,padding.UNPAD)
133 '12345678'"""
134 if direction == PAD:
135 if length == None:
136 raise ValueError,"Supply a valid length"
137 return __PKCS7(padData, length)
138 elif direction == UNPAD:
139 return __PKCS7_unpad(padData)
140 else:
141 raise ValueError,"Supply a valid direction"
144 def __PKCS7 (toPad, length):
145 amount = length - len(toPad)%length
146 pattern = chr(amount)
147 pad = pattern*amount
148 return toPad + pad
150 def __PKCS7_unpad (padded):
151 pattern = padded[-1]
152 length = ord(pattern)
153 #check if the bytes to be removed are all the same pattern
154 if padded.endswith(pattern*length):
155 return padded[:-length]
156 else:
157 return padded
158 print 'error: padding pattern not recognized'
160 def ANSI_X923 (padData, direction, length=None):
161 """Pad a string using ANSI_X923
163 padData = raw string to pad/unpad
164 direction = PAD or UNPAD
165 length = amount of bytes the padded string should be a multiple of
166 (length variable is not used when unpadding)
168 returns: (un)padded raw string
170 A new block full of padding will be added when padding data that is
171 already a multiple of the given length.
173 Example:
174 =========
175 >>> import padding
177 >>> padding.ANSI_X923('12345678',padding.PAD,16)
178 '12345678\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08'
179 >>> padding.ANSI_X923(_,padding.UNPAD)
180 '12345678'"""
181 if direction == PAD:
182 if length == None:
183 raise ValueError,"Supply a valid length"
184 return __ANSI_X923(padData, length)
185 elif direction == UNPAD:
186 return __ANSI_X923_unpad(padData)
187 else:
188 raise ValueError,"Supply a valid direction"
190 def __ANSI_X923 (toPad, length):
191 bytesToPad = length - len(toPad)%length
192 trail = chr(bytesToPad)
193 pattern = '\x00'*(bytesToPad -1) + trail
194 return toPad + pattern
196 def __ANSI_X923_unpad (padded):
197 length =ord(padded[-1])
198 #check if the bytes to be removed are all zero
199 if padded.count('\x00',-length,-1) == length - 1:
200 return padded[:-length]
201 else:
202 print 'error: padding pattern not recognized %s' % padded.count('\x00',-length,-1)
203 return padded
205 def ISO_10126 (padData, direction, length=None):
206 """Pad a string using ISO_10126
208 padData = raw string to pad/unpad
209 direction = PAD or UNPAD
210 length = amount of bytes the padded string should be a multiple of
211 (length variable is not used when unpadding)
213 returns: (un)padded raw string
215 A new block full of padding will be added when padding data that is
216 already a multiple of the given length.
218 Example:
219 =========
220 >>> import padding
222 >>> padded = padding.ISO_10126('12345678',padding.PAD,16)
223 >>> padding.ISO_10126(padded,padding.UNPAD)
224 '12345678'"""
225 if direction == PAD:
226 if length == None:
227 raise ValueError,"Supply a valid length"
228 return __ISO_10126(padData, length)
229 elif direction == UNPAD:
230 return __ISO_10126_unpad(padData)
231 else:
232 raise ValueError,"Supply a valid direction"
234 def __ISO_10126 (toPad, length):
235 bytesToPad = length - len(toPad)%length
236 randomPattern = ''.join(chr(random.randint(0,255)) for x in range(0,bytesToPad-1))
237 return toPad + randomPattern + chr(bytesToPad)
239 def __ISO_10126_unpad (padded):
240 return padded[0:len(padded)-ord(padded[-1])]
242 def _test():
243 import doctest
244 doctest.testmod()
246 if __name__ == "__main__":
247 _test()