1"""Test the binascii C module.""" 2 3import unittest 4import binascii 5import array 6import re 7from test import support 8 9# Note: "*_hex" functions are aliases for "(un)hexlify" 10b2a_functions = ['b2a_base64', 'b2a_hex', 'b2a_hqx', 'b2a_qp', 'b2a_uu', 11 'hexlify', 'rlecode_hqx'] 12a2b_functions = ['a2b_base64', 'a2b_hex', 'a2b_hqx', 'a2b_qp', 'a2b_uu', 13 'unhexlify', 'rledecode_hqx'] 14all_functions = a2b_functions + b2a_functions + ['crc32', 'crc_hqx'] 15 16 17class BinASCIITest(unittest.TestCase): 18 19 type2test = bytes 20 # Create binary test data 21 rawdata = b"The quick brown fox jumps over the lazy dog.\r\n" 22 # Be slow so we don't depend on other modules 23 rawdata += bytes(range(256)) 24 rawdata += b"\r\nHello world.\n" 25 26 def setUp(self): 27 self.data = self.type2test(self.rawdata) 28 29 def test_exceptions(self): 30 # Check module exceptions 31 self.assertTrue(issubclass(binascii.Error, Exception)) 32 self.assertTrue(issubclass(binascii.Incomplete, Exception)) 33 34 def test_functions(self): 35 # Check presence of all functions 36 for name in all_functions: 37 self.assertTrue(hasattr(getattr(binascii, name), '__call__')) 38 self.assertRaises(TypeError, getattr(binascii, name)) 39 40 @support.ignore_warnings(category=DeprecationWarning) 41 def test_returned_value(self): 42 # Limit to the minimum of all limits (b2a_uu) 43 MAX_ALL = 45 44 raw = self.rawdata[:MAX_ALL] 45 for fa, fb in zip(a2b_functions, b2a_functions): 46 a2b = getattr(binascii, fa) 47 b2a = getattr(binascii, fb) 48 try: 49 a = b2a(self.type2test(raw)) 50 res = a2b(self.type2test(a)) 51 except Exception as err: 52 self.fail("{}/{} conversion raises {!r}".format(fb, fa, err)) 53 if fb == 'b2a_hqx': 54 # b2a_hqx returns a tuple 55 res, _ = res 56 self.assertEqual(res, raw, "{}/{} conversion: " 57 "{!r} != {!r}".format(fb, fa, res, raw)) 58 self.assertIsInstance(res, bytes) 59 self.assertIsInstance(a, bytes) 60 self.assertLess(max(a), 128) 61 self.assertIsInstance(binascii.crc_hqx(raw, 0), int) 62 self.assertIsInstance(binascii.crc32(raw), int) 63 64 def test_base64valid(self): 65 # Test base64 with valid data 66 MAX_BASE64 = 57 67 lines = [] 68 for i in range(0, len(self.rawdata), MAX_BASE64): 69 b = self.type2test(self.rawdata[i:i+MAX_BASE64]) 70 a = binascii.b2a_base64(b) 71 lines.append(a) 72 res = bytes() 73 for line in lines: 74 a = self.type2test(line) 75 b = binascii.a2b_base64(a) 76 res += b 77 self.assertEqual(res, self.rawdata) 78 79 def test_base64invalid(self): 80 # Test base64 with random invalid characters sprinkled throughout 81 # (This requires a new version of binascii.) 82 MAX_BASE64 = 57 83 lines = [] 84 for i in range(0, len(self.data), MAX_BASE64): 85 b = self.type2test(self.rawdata[i:i+MAX_BASE64]) 86 a = binascii.b2a_base64(b) 87 lines.append(a) 88 89 fillers = bytearray() 90 valid = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/" 91 for i in range(256): 92 if i not in valid: 93 fillers.append(i) 94 def addnoise(line): 95 noise = fillers 96 ratio = len(line) // len(noise) 97 res = bytearray() 98 while line and noise: 99 if len(line) // len(noise) > ratio: 100 c, line = line[0], line[1:] 101 else: 102 c, noise = noise[0], noise[1:] 103 res.append(c) 104 return res + noise + line 105 res = bytearray() 106 for line in map(addnoise, lines): 107 a = self.type2test(line) 108 b = binascii.a2b_base64(a) 109 res += b 110 self.assertEqual(res, self.rawdata) 111 112 # Test base64 with just invalid characters, which should return 113 # empty strings. TBD: shouldn't it raise an exception instead ? 114 self.assertEqual(binascii.a2b_base64(self.type2test(fillers)), b'') 115 116 def test_base64errors(self): 117 # Test base64 with invalid padding 118 def assertIncorrectPadding(data): 119 with self.assertRaisesRegex(binascii.Error, r'(?i)Incorrect padding'): 120 binascii.a2b_base64(self.type2test(data)) 121 122 assertIncorrectPadding(b'ab') 123 assertIncorrectPadding(b'ab=') 124 assertIncorrectPadding(b'abc') 125 assertIncorrectPadding(b'abcdef') 126 assertIncorrectPadding(b'abcdef=') 127 assertIncorrectPadding(b'abcdefg') 128 assertIncorrectPadding(b'a=b=') 129 assertIncorrectPadding(b'a\nb=') 130 131 # Test base64 with invalid number of valid characters (1 mod 4) 132 def assertInvalidLength(data): 133 n_data_chars = len(re.sub(br'[^A-Za-z0-9/+]', br'', data)) 134 expected_errmsg_re = \ 135 r'(?i)Invalid.+number of data characters.+' + str(n_data_chars) 136 with self.assertRaisesRegex(binascii.Error, expected_errmsg_re): 137 binascii.a2b_base64(self.type2test(data)) 138 139 assertInvalidLength(b'a') 140 assertInvalidLength(b'a=') 141 assertInvalidLength(b'a==') 142 assertInvalidLength(b'a===') 143 assertInvalidLength(b'a' * 5) 144 assertInvalidLength(b'a' * (4 * 87 + 1)) 145 assertInvalidLength(b'A\tB\nC ??DE') # only 5 valid characters 146 147 def test_uu(self): 148 MAX_UU = 45 149 for backtick in (True, False): 150 lines = [] 151 for i in range(0, len(self.data), MAX_UU): 152 b = self.type2test(self.rawdata[i:i+MAX_UU]) 153 a = binascii.b2a_uu(b, backtick=backtick) 154 lines.append(a) 155 res = bytes() 156 for line in lines: 157 a = self.type2test(line) 158 b = binascii.a2b_uu(a) 159 res += b 160 self.assertEqual(res, self.rawdata) 161 162 self.assertEqual(binascii.a2b_uu(b"\x7f"), b"\x00"*31) 163 self.assertEqual(binascii.a2b_uu(b"\x80"), b"\x00"*32) 164 self.assertEqual(binascii.a2b_uu(b"\xff"), b"\x00"*31) 165 self.assertRaises(binascii.Error, binascii.a2b_uu, b"\xff\x00") 166 self.assertRaises(binascii.Error, binascii.a2b_uu, b"!!!!") 167 self.assertRaises(binascii.Error, binascii.b2a_uu, 46*b"!") 168 169 # Issue #7701 (crash on a pydebug build) 170 self.assertEqual(binascii.b2a_uu(b'x'), b'!> \n') 171 172 self.assertEqual(binascii.b2a_uu(b''), b' \n') 173 self.assertEqual(binascii.b2a_uu(b'', backtick=True), b'`\n') 174 self.assertEqual(binascii.a2b_uu(b' \n'), b'') 175 self.assertEqual(binascii.a2b_uu(b'`\n'), b'') 176 self.assertEqual(binascii.b2a_uu(b'\x00Cat'), b'$ $-A= \n') 177 self.assertEqual(binascii.b2a_uu(b'\x00Cat', backtick=True), 178 b'$`$-A=```\n') 179 self.assertEqual(binascii.a2b_uu(b'$`$-A=```\n'), 180 binascii.a2b_uu(b'$ $-A= \n')) 181 with self.assertRaises(TypeError): 182 binascii.b2a_uu(b"", True) 183 184 @support.ignore_warnings(category=DeprecationWarning) 185 def test_crc_hqx(self): 186 crc = binascii.crc_hqx(self.type2test(b"Test the CRC-32 of"), 0) 187 crc = binascii.crc_hqx(self.type2test(b" this string."), crc) 188 self.assertEqual(crc, 14290) 189 190 self.assertRaises(TypeError, binascii.crc_hqx) 191 self.assertRaises(TypeError, binascii.crc_hqx, self.type2test(b'')) 192 193 for crc in 0, 1, 0x1234, 0x12345, 0x12345678, -1: 194 self.assertEqual(binascii.crc_hqx(self.type2test(b''), crc), 195 crc & 0xffff) 196 197 def test_crc32(self): 198 crc = binascii.crc32(self.type2test(b"Test the CRC-32 of")) 199 crc = binascii.crc32(self.type2test(b" this string."), crc) 200 self.assertEqual(crc, 1571220330) 201 202 self.assertRaises(TypeError, binascii.crc32) 203 204 @support.ignore_warnings(category=DeprecationWarning) 205 def test_hqx(self): 206 # Perform binhex4 style RLE-compression 207 # Then calculate the hexbin4 binary-to-ASCII translation 208 rle = binascii.rlecode_hqx(self.data) 209 a = binascii.b2a_hqx(self.type2test(rle)) 210 211 b, _ = binascii.a2b_hqx(self.type2test(a)) 212 res = binascii.rledecode_hqx(b) 213 self.assertEqual(res, self.rawdata) 214 215 @support.ignore_warnings(category=DeprecationWarning) 216 def test_rle(self): 217 # test repetition with a repetition longer than the limit of 255 218 data = (b'a' * 100 + b'b' + b'c' * 300) 219 220 encoded = binascii.rlecode_hqx(data) 221 self.assertEqual(encoded, 222 (b'a\x90d' # 'a' * 100 223 b'b' # 'b' 224 b'c\x90\xff' # 'c' * 255 225 b'c\x90-')) # 'c' * 45 226 227 decoded = binascii.rledecode_hqx(encoded) 228 self.assertEqual(decoded, data) 229 230 def test_hex(self): 231 # test hexlification 232 s = b'{s\005\000\000\000worldi\002\000\000\000s\005\000\000\000helloi\001\000\000\0000' 233 t = binascii.b2a_hex(self.type2test(s)) 234 u = binascii.a2b_hex(self.type2test(t)) 235 self.assertEqual(s, u) 236 self.assertRaises(binascii.Error, binascii.a2b_hex, t[:-1]) 237 self.assertRaises(binascii.Error, binascii.a2b_hex, t[:-1] + b'q') 238 self.assertRaises(binascii.Error, binascii.a2b_hex, bytes([255, 255])) 239 self.assertRaises(binascii.Error, binascii.a2b_hex, b'0G') 240 self.assertRaises(binascii.Error, binascii.a2b_hex, b'0g') 241 self.assertRaises(binascii.Error, binascii.a2b_hex, b'G0') 242 self.assertRaises(binascii.Error, binascii.a2b_hex, b'g0') 243 244 # Confirm that b2a_hex == hexlify and a2b_hex == unhexlify 245 self.assertEqual(binascii.hexlify(self.type2test(s)), t) 246 self.assertEqual(binascii.unhexlify(self.type2test(t)), u) 247 248 def test_hex_separator(self): 249 """Test that hexlify and b2a_hex are binary versions of bytes.hex.""" 250 # Logic of separators is tested in test_bytes.py. This checks that 251 # arg parsing works and exercises the direct to bytes object code 252 # path within pystrhex.c. 253 s = b'{s\005\000\000\000worldi\002\000\000\000s\005\000\000\000helloi\001\000\000\0000' 254 self.assertEqual(binascii.hexlify(self.type2test(s)), s.hex().encode('ascii')) 255 expected8 = s.hex('.', 8).encode('ascii') 256 self.assertEqual(binascii.hexlify(self.type2test(s), '.', 8), expected8) 257 expected1 = s.hex(':').encode('ascii') 258 self.assertEqual(binascii.b2a_hex(self.type2test(s), ':'), expected1) 259 260 def test_qp(self): 261 type2test = self.type2test 262 a2b_qp = binascii.a2b_qp 263 b2a_qp = binascii.b2a_qp 264 265 a2b_qp(data=b"", header=False) # Keyword arguments allowed 266 267 # A test for SF bug 534347 (segfaults without the proper fix) 268 try: 269 a2b_qp(b"", **{1:1}) 270 except TypeError: 271 pass 272 else: 273 self.fail("binascii.a2b_qp(**{1:1}) didn't raise TypeError") 274 275 self.assertEqual(a2b_qp(type2test(b"=")), b"") 276 self.assertEqual(a2b_qp(type2test(b"= ")), b"= ") 277 self.assertEqual(a2b_qp(type2test(b"==")), b"=") 278 self.assertEqual(a2b_qp(type2test(b"=\nAB")), b"AB") 279 self.assertEqual(a2b_qp(type2test(b"=\r\nAB")), b"AB") 280 self.assertEqual(a2b_qp(type2test(b"=\rAB")), b"") # ? 281 self.assertEqual(a2b_qp(type2test(b"=\rAB\nCD")), b"CD") # ? 282 self.assertEqual(a2b_qp(type2test(b"=AB")), b"\xab") 283 self.assertEqual(a2b_qp(type2test(b"=ab")), b"\xab") 284 self.assertEqual(a2b_qp(type2test(b"=AX")), b"=AX") 285 self.assertEqual(a2b_qp(type2test(b"=XA")), b"=XA") 286 self.assertEqual(a2b_qp(type2test(b"=AB")[:-1]), b"=A") 287 288 self.assertEqual(a2b_qp(type2test(b'_')), b'_') 289 self.assertEqual(a2b_qp(type2test(b'_'), header=True), b' ') 290 291 self.assertRaises(TypeError, b2a_qp, foo="bar") 292 self.assertEqual(a2b_qp(type2test(b"=00\r\n=00")), b"\x00\r\n\x00") 293 self.assertEqual(b2a_qp(type2test(b"\xff\r\n\xff\n\xff")), 294 b"=FF\r\n=FF\r\n=FF") 295 self.assertEqual(b2a_qp(type2test(b"0"*75+b"\xff\r\n\xff\r\n\xff")), 296 b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF") 297 298 self.assertEqual(b2a_qp(type2test(b'\x7f')), b'=7F') 299 self.assertEqual(b2a_qp(type2test(b'=')), b'=3D') 300 301 self.assertEqual(b2a_qp(type2test(b'_')), b'_') 302 self.assertEqual(b2a_qp(type2test(b'_'), header=True), b'=5F') 303 self.assertEqual(b2a_qp(type2test(b'x y'), header=True), b'x_y') 304 self.assertEqual(b2a_qp(type2test(b'x '), header=True), b'x=20') 305 self.assertEqual(b2a_qp(type2test(b'x y'), header=True, quotetabs=True), 306 b'x=20y') 307 self.assertEqual(b2a_qp(type2test(b'x\ty'), header=True), b'x\ty') 308 309 self.assertEqual(b2a_qp(type2test(b' ')), b'=20') 310 self.assertEqual(b2a_qp(type2test(b'\t')), b'=09') 311 self.assertEqual(b2a_qp(type2test(b' x')), b' x') 312 self.assertEqual(b2a_qp(type2test(b'\tx')), b'\tx') 313 self.assertEqual(b2a_qp(type2test(b' x')[:-1]), b'=20') 314 self.assertEqual(b2a_qp(type2test(b'\tx')[:-1]), b'=09') 315 self.assertEqual(b2a_qp(type2test(b'\0')), b'=00') 316 317 self.assertEqual(b2a_qp(type2test(b'\0\n')), b'=00\n') 318 self.assertEqual(b2a_qp(type2test(b'\0\n'), quotetabs=True), b'=00\n') 319 320 self.assertEqual(b2a_qp(type2test(b'x y\tz')), b'x y\tz') 321 self.assertEqual(b2a_qp(type2test(b'x y\tz'), quotetabs=True), 322 b'x=20y=09z') 323 self.assertEqual(b2a_qp(type2test(b'x y\tz'), istext=False), 324 b'x y\tz') 325 self.assertEqual(b2a_qp(type2test(b'x \ny\t\n')), 326 b'x=20\ny=09\n') 327 self.assertEqual(b2a_qp(type2test(b'x \ny\t\n'), quotetabs=True), 328 b'x=20\ny=09\n') 329 self.assertEqual(b2a_qp(type2test(b'x \ny\t\n'), istext=False), 330 b'x =0Ay\t=0A') 331 self.assertEqual(b2a_qp(type2test(b'x \ry\t\r')), 332 b'x \ry\t\r') 333 self.assertEqual(b2a_qp(type2test(b'x \ry\t\r'), quotetabs=True), 334 b'x=20\ry=09\r') 335 self.assertEqual(b2a_qp(type2test(b'x \ry\t\r'), istext=False), 336 b'x =0Dy\t=0D') 337 self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n')), 338 b'x=20\r\ny=09\r\n') 339 self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n'), quotetabs=True), 340 b'x=20\r\ny=09\r\n') 341 self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n'), istext=False), 342 b'x =0D=0Ay\t=0D=0A') 343 344 self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1]), b'x \r') 345 self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1]), b'x\t\r') 346 self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1], quotetabs=True), 347 b'x=20\r') 348 self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1], quotetabs=True), 349 b'x=09\r') 350 self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1], istext=False), 351 b'x =0D') 352 self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1], istext=False), 353 b'x\t=0D') 354 355 self.assertEqual(b2a_qp(type2test(b'.')), b'=2E') 356 self.assertEqual(b2a_qp(type2test(b'.\n')), b'=2E\n') 357 self.assertEqual(b2a_qp(type2test(b'.\r')), b'=2E\r') 358 self.assertEqual(b2a_qp(type2test(b'.\0')), b'=2E=00') 359 self.assertEqual(b2a_qp(type2test(b'a.\n')), b'a.\n') 360 self.assertEqual(b2a_qp(type2test(b'.a')[:-1]), b'=2E') 361 362 @support.ignore_warnings(category=DeprecationWarning) 363 def test_empty_string(self): 364 # A test for SF bug #1022953. Make sure SystemError is not raised. 365 empty = self.type2test(b'') 366 for func in all_functions: 367 if func == 'crc_hqx': 368 # crc_hqx needs 2 arguments 369 binascii.crc_hqx(empty, 0) 370 continue 371 f = getattr(binascii, func) 372 try: 373 f(empty) 374 except Exception as err: 375 self.fail("{}({!r}) raises {!r}".format(func, empty, err)) 376 377 def test_unicode_b2a(self): 378 # Unicode strings are not accepted by b2a_* functions. 379 for func in set(all_functions) - set(a2b_functions) | {'rledecode_hqx'}: 380 try: 381 self.assertRaises(TypeError, getattr(binascii, func), "test") 382 except Exception as err: 383 self.fail('{}("test") raises {!r}'.format(func, err)) 384 # crc_hqx needs 2 arguments 385 self.assertRaises(TypeError, binascii.crc_hqx, "test", 0) 386 387 @support.ignore_warnings(category=DeprecationWarning) 388 def test_unicode_a2b(self): 389 # Unicode strings are accepted by a2b_* functions. 390 MAX_ALL = 45 391 raw = self.rawdata[:MAX_ALL] 392 for fa, fb in zip(a2b_functions, b2a_functions): 393 if fa == 'rledecode_hqx': 394 # Takes non-ASCII data 395 continue 396 a2b = getattr(binascii, fa) 397 b2a = getattr(binascii, fb) 398 try: 399 a = b2a(self.type2test(raw)) 400 binary_res = a2b(a) 401 a = a.decode('ascii') 402 res = a2b(a) 403 except Exception as err: 404 self.fail("{}/{} conversion raises {!r}".format(fb, fa, err)) 405 if fb == 'b2a_hqx': 406 # b2a_hqx returns a tuple 407 res, _ = res 408 binary_res, _ = binary_res 409 self.assertEqual(res, raw, "{}/{} conversion: " 410 "{!r} != {!r}".format(fb, fa, res, raw)) 411 self.assertEqual(res, binary_res) 412 self.assertIsInstance(res, bytes) 413 # non-ASCII string 414 self.assertRaises(ValueError, a2b, "\x80") 415 416 def test_b2a_base64_newline(self): 417 # Issue #25357: test newline parameter 418 b = self.type2test(b'hello') 419 self.assertEqual(binascii.b2a_base64(b), 420 b'aGVsbG8=\n') 421 self.assertEqual(binascii.b2a_base64(b, newline=True), 422 b'aGVsbG8=\n') 423 self.assertEqual(binascii.b2a_base64(b, newline=False), 424 b'aGVsbG8=') 425 426 def test_deprecated_warnings(self): 427 with self.assertWarns(DeprecationWarning): 428 self.assertEqual(binascii.b2a_hqx(b'abc'), b'B@*M') 429 with self.assertWarns(DeprecationWarning): 430 self.assertEqual(binascii.a2b_hqx(b'B@*M'), (b'abc', 0)) 431 432 with self.assertWarns(DeprecationWarning): 433 self.assertEqual(binascii.rlecode_hqx(b'a' * 10), b'a\x90\n') 434 435 with self.assertWarns(DeprecationWarning): 436 self.assertEqual(binascii.rledecode_hqx(b'a\x90\n'), b'a' * 10) 437 438 439class ArrayBinASCIITest(BinASCIITest): 440 def type2test(self, s): 441 return array.array('B', list(s)) 442 443 444class BytearrayBinASCIITest(BinASCIITest): 445 type2test = bytearray 446 447 448class MemoryviewBinASCIITest(BinASCIITest): 449 type2test = memoryview 450 451 452if __name__ == "__main__": 453 unittest.main() 454