您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

603 行
18 KiB

  1. # compatibility with Python 2.6, for that we need unittest2 package,
  2. # which is not available on 3.3 or 3.4
  3. import warnings
  4. from binascii import hexlify
  5. try:
  6. import unittest2 as unittest
  7. except ImportError:
  8. import unittest
  9. import sys
  10. import hypothesis.strategies as st
  11. from hypothesis import given, settings
  12. import pytest
  13. from ._compat import str_idx_as_int
  14. from .curves import NIST256p, NIST224p
  15. from .der import (
  16. remove_integer,
  17. UnexpectedDER,
  18. read_length,
  19. encode_bitstring,
  20. remove_bitstring,
  21. remove_object,
  22. encode_oid,
  23. remove_constructed,
  24. remove_implicit,
  25. remove_octet_string,
  26. remove_sequence,
  27. encode_implicit,
  28. )
  29. class TestRemoveInteger(unittest.TestCase):
  30. # DER requires the integers to be 0-padded only if they would be
  31. # interpreted as negative, check if those errors are detected
  32. def test_non_minimal_encoding(self):
  33. with self.assertRaises(UnexpectedDER):
  34. remove_integer(b"\x02\x02\x00\x01")
  35. def test_negative_with_high_bit_set(self):
  36. with self.assertRaises(UnexpectedDER):
  37. remove_integer(b"\x02\x01\x80")
  38. def test_minimal_with_high_bit_set(self):
  39. val, rem = remove_integer(b"\x02\x02\x00\x80")
  40. self.assertEqual(val, 0x80)
  41. self.assertEqual(rem, b"")
  42. def test_two_zero_bytes_with_high_bit_set(self):
  43. with self.assertRaises(UnexpectedDER):
  44. remove_integer(b"\x02\x03\x00\x00\xff")
  45. def test_zero_length_integer(self):
  46. with self.assertRaises(UnexpectedDER):
  47. remove_integer(b"\x02\x00")
  48. def test_empty_string(self):
  49. with self.assertRaises(UnexpectedDER):
  50. remove_integer(b"")
  51. def test_encoding_of_zero(self):
  52. val, rem = remove_integer(b"\x02\x01\x00")
  53. self.assertEqual(val, 0)
  54. self.assertEqual(rem, b"")
  55. def test_encoding_of_127(self):
  56. val, rem = remove_integer(b"\x02\x01\x7f")
  57. self.assertEqual(val, 127)
  58. self.assertEqual(rem, b"")
  59. def test_encoding_of_128(self):
  60. val, rem = remove_integer(b"\x02\x02\x00\x80")
  61. self.assertEqual(val, 128)
  62. self.assertEqual(rem, b"")
  63. def test_wrong_tag(self):
  64. with self.assertRaises(UnexpectedDER) as e:
  65. remove_integer(b"\x01\x02\x00\x80")
  66. self.assertIn("wanted type 'integer'", str(e.exception))
  67. def test_wrong_length(self):
  68. with self.assertRaises(UnexpectedDER) as e:
  69. remove_integer(b"\x02\x03\x00\x80")
  70. self.assertIn("Length longer", str(e.exception))
  71. class TestReadLength(unittest.TestCase):
  72. # DER requires the lengths between 0 and 127 to be encoded using the short
  73. # form and lengths above that encoded with minimal number of bytes
  74. # necessary
  75. def test_zero_length(self):
  76. self.assertEqual((0, 1), read_length(b"\x00"))
  77. def test_two_byte_zero_length(self):
  78. with self.assertRaises(UnexpectedDER):
  79. read_length(b"\x81\x00")
  80. def test_two_byte_small_length(self):
  81. with self.assertRaises(UnexpectedDER):
  82. read_length(b"\x81\x7f")
  83. def test_long_form_with_zero_length(self):
  84. with self.assertRaises(UnexpectedDER):
  85. read_length(b"\x80")
  86. def test_smallest_two_byte_length(self):
  87. self.assertEqual((128, 2), read_length(b"\x81\x80"))
  88. def test_zero_padded_length(self):
  89. with self.assertRaises(UnexpectedDER):
  90. read_length(b"\x82\x00\x80")
  91. def test_two_three_byte_length(self):
  92. self.assertEqual((256, 3), read_length(b"\x82\x01\x00"))
  93. def test_empty_string(self):
  94. with self.assertRaises(UnexpectedDER):
  95. read_length(b"")
  96. def test_length_overflow(self):
  97. with self.assertRaises(UnexpectedDER):
  98. read_length(b"\x83\x01\x00")
  99. class TestEncodeBitstring(unittest.TestCase):
  100. # DER requires BIT STRINGS to include a number of padding bits in the
  101. # encoded byte string, that padding must be between 0 and 7
  102. def test_old_call_convention(self):
  103. """This is the old way to use the function."""
  104. warnings.simplefilter("always")
  105. with pytest.warns(DeprecationWarning) as warns:
  106. der = encode_bitstring(b"\x00\xff")
  107. self.assertEqual(len(warns), 1)
  108. self.assertIn(
  109. "unused= needs to be specified", warns[0].message.args[0]
  110. )
  111. self.assertEqual(der, b"\x03\x02\x00\xff")
  112. def test_new_call_convention(self):
  113. """This is how it should be called now."""
  114. # make sure no warnings are raised
  115. with warnings.catch_warnings():
  116. warnings.simplefilter("error")
  117. der = encode_bitstring(b"\xff", 0)
  118. self.assertEqual(der, b"\x03\x02\x00\xff")
  119. def test_implicit_unused_bits(self):
  120. """
  121. Writing bit string with already included the number of unused bits.
  122. """
  123. # make sure no warnings are raised
  124. with warnings.catch_warnings():
  125. warnings.simplefilter("error")
  126. der = encode_bitstring(b"\x00\xff", None)
  127. self.assertEqual(der, b"\x03\x02\x00\xff")
  128. def test_explicit_unused_bits(self):
  129. der = encode_bitstring(b"\xff\xf0", 4)
  130. self.assertEqual(der, b"\x03\x03\x04\xff\xf0")
  131. def test_empty_string(self):
  132. self.assertEqual(encode_bitstring(b"", 0), b"\x03\x01\x00")
  133. def test_invalid_unused_count(self):
  134. with self.assertRaises(ValueError):
  135. encode_bitstring(b"\xff\x00", 8)
  136. def test_invalid_unused_with_empty_string(self):
  137. with self.assertRaises(ValueError):
  138. encode_bitstring(b"", 1)
  139. def test_non_zero_padding_bits(self):
  140. with self.assertRaises(ValueError):
  141. encode_bitstring(b"\xff", 2)
  142. class TestRemoveBitstring(unittest.TestCase):
  143. def test_old_call_convention(self):
  144. """This is the old way to call the function."""
  145. warnings.simplefilter("always")
  146. with pytest.warns(DeprecationWarning) as warns:
  147. bits, rest = remove_bitstring(b"\x03\x02\x00\xff")
  148. self.assertEqual(len(warns), 1)
  149. self.assertIn(
  150. "expect_unused= needs to be specified", warns[0].message.args[0]
  151. )
  152. self.assertEqual(bits, b"\x00\xff")
  153. self.assertEqual(rest, b"")
  154. def test_new_call_convention(self):
  155. # make sure no warnings are raised
  156. with warnings.catch_warnings():
  157. warnings.simplefilter("error")
  158. bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0)
  159. self.assertEqual(bits, b"\xff")
  160. self.assertEqual(rest, b"")
  161. def test_implicit_unexpected_unused(self):
  162. # make sure no warnings are raised
  163. with warnings.catch_warnings():
  164. warnings.simplefilter("error")
  165. bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None)
  166. self.assertEqual(bits, (b"\xff", 0))
  167. self.assertEqual(rest, b"")
  168. def test_with_padding(self):
  169. ret, rest = remove_bitstring(b"\x03\x02\x04\xf0", None)
  170. self.assertEqual(ret, (b"\xf0", 4))
  171. self.assertEqual(rest, b"")
  172. def test_not_a_bitstring(self):
  173. with self.assertRaises(UnexpectedDER):
  174. remove_bitstring(b"\x02\x02\x00\xff", None)
  175. def test_empty_encoding(self):
  176. with self.assertRaises(UnexpectedDER):
  177. remove_bitstring(b"\x03\x00", None)
  178. def test_empty_string(self):
  179. with self.assertRaises(UnexpectedDER):
  180. remove_bitstring(b"", None)
  181. def test_no_length(self):
  182. with self.assertRaises(UnexpectedDER):
  183. remove_bitstring(b"\x03", None)
  184. def test_unexpected_number_of_unused_bits(self):
  185. with self.assertRaises(UnexpectedDER):
  186. remove_bitstring(b"\x03\x02\x00\xff", 1)
  187. def test_invalid_encoding_of_unused_bits(self):
  188. with self.assertRaises(UnexpectedDER):
  189. remove_bitstring(b"\x03\x03\x08\xff\x00", None)
  190. def test_invalid_encoding_of_empty_string(self):
  191. with self.assertRaises(UnexpectedDER):
  192. remove_bitstring(b"\x03\x01\x01", None)
  193. def test_invalid_padding_bits(self):
  194. with self.assertRaises(UnexpectedDER):
  195. remove_bitstring(b"\x03\x02\x01\xff", None)
  196. class TestStrIdxAsInt(unittest.TestCase):
  197. def test_str(self):
  198. self.assertEqual(115, str_idx_as_int("str", 0))
  199. def test_bytes(self):
  200. self.assertEqual(115, str_idx_as_int(b"str", 0))
  201. def test_bytearray(self):
  202. self.assertEqual(115, str_idx_as_int(bytearray(b"str"), 0))
  203. class TestEncodeOid(unittest.TestCase):
  204. def test_pub_key_oid(self):
  205. oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1)
  206. self.assertEqual(hexlify(oid_ecPublicKey), b"06072a8648ce3d0201")
  207. def test_nist224p_oid(self):
  208. self.assertEqual(hexlify(NIST224p.encoded_oid), b"06052b81040021")
  209. def test_nist256p_oid(self):
  210. self.assertEqual(
  211. hexlify(NIST256p.encoded_oid), b"06082a8648ce3d030107"
  212. )
  213. def test_large_second_subid(self):
  214. # from X.690, section 8.19.5
  215. oid = encode_oid(2, 999, 3)
  216. self.assertEqual(oid, b"\x06\x03\x88\x37\x03")
  217. def test_with_two_subids(self):
  218. oid = encode_oid(2, 999)
  219. self.assertEqual(oid, b"\x06\x02\x88\x37")
  220. def test_zero_zero(self):
  221. oid = encode_oid(0, 0)
  222. self.assertEqual(oid, b"\x06\x01\x00")
  223. def test_with_wrong_types(self):
  224. with self.assertRaises((TypeError, AssertionError)):
  225. encode_oid(0, None)
  226. def test_with_small_first_large_second(self):
  227. with self.assertRaises(AssertionError):
  228. encode_oid(1, 40)
  229. def test_small_first_max_second(self):
  230. oid = encode_oid(1, 39)
  231. self.assertEqual(oid, b"\x06\x01\x4f")
  232. def test_with_invalid_first(self):
  233. with self.assertRaises(AssertionError):
  234. encode_oid(3, 39)
  235. class TestRemoveObject(unittest.TestCase):
  236. @classmethod
  237. def setUpClass(cls):
  238. cls.oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1)
  239. def test_pub_key_oid(self):
  240. oid, rest = remove_object(self.oid_ecPublicKey)
  241. self.assertEqual(rest, b"")
  242. self.assertEqual(oid, (1, 2, 840, 10045, 2, 1))
  243. def test_with_extra_bytes(self):
  244. oid, rest = remove_object(self.oid_ecPublicKey + b"more")
  245. self.assertEqual(rest, b"more")
  246. self.assertEqual(oid, (1, 2, 840, 10045, 2, 1))
  247. def test_with_large_second_subid(self):
  248. # from X.690, section 8.19.5
  249. oid, rest = remove_object(b"\x06\x03\x88\x37\x03")
  250. self.assertEqual(rest, b"")
  251. self.assertEqual(oid, (2, 999, 3))
  252. def test_with_padded_first_subid(self):
  253. with self.assertRaises(UnexpectedDER):
  254. remove_object(b"\x06\x02\x80\x00")
  255. def test_with_padded_second_subid(self):
  256. with self.assertRaises(UnexpectedDER):
  257. remove_object(b"\x06\x04\x88\x37\x80\x01")
  258. def test_with_missing_last_byte_of_multi_byte(self):
  259. with self.assertRaises(UnexpectedDER):
  260. remove_object(b"\x06\x03\x88\x37\x83")
  261. def test_with_two_subids(self):
  262. oid, rest = remove_object(b"\x06\x02\x88\x37")
  263. self.assertEqual(rest, b"")
  264. self.assertEqual(oid, (2, 999))
  265. def test_zero_zero(self):
  266. oid, rest = remove_object(b"\x06\x01\x00")
  267. self.assertEqual(rest, b"")
  268. self.assertEqual(oid, (0, 0))
  269. def test_empty_string(self):
  270. with self.assertRaises(UnexpectedDER):
  271. remove_object(b"")
  272. def test_missing_length(self):
  273. with self.assertRaises(UnexpectedDER):
  274. remove_object(b"\x06")
  275. def test_empty_oid(self):
  276. with self.assertRaises(UnexpectedDER):
  277. remove_object(b"\x06\x00")
  278. def test_empty_oid_overflow(self):
  279. with self.assertRaises(UnexpectedDER):
  280. remove_object(b"\x06\x01")
  281. def test_with_wrong_type(self):
  282. with self.assertRaises(UnexpectedDER):
  283. remove_object(b"\x04\x02\x88\x37")
  284. def test_with_too_long_length(self):
  285. with self.assertRaises(UnexpectedDER):
  286. remove_object(b"\x06\x03\x88\x37")
  287. class TestRemoveConstructed(unittest.TestCase):
  288. def test_simple(self):
  289. data = b"\xa1\x02\xff\xaa"
  290. tag, body, rest = remove_constructed(data)
  291. self.assertEqual(tag, 0x01)
  292. self.assertEqual(body, b"\xff\xaa")
  293. self.assertEqual(rest, b"")
  294. def test_with_malformed_tag(self):
  295. data = b"\x01\x02\xff\xaa"
  296. with self.assertRaises(UnexpectedDER) as e:
  297. remove_constructed(data)
  298. self.assertIn("constructed tag", str(e.exception))
  299. class TestRemoveImplicit(unittest.TestCase):
  300. @classmethod
  301. def setUpClass(cls):
  302. cls.exp_tag = 6
  303. cls.exp_data = b"\x0a\x0b"
  304. # data with application tag class
  305. cls.data_application = b"\x46\x02\x0a\x0b"
  306. # data with context-specific tag class
  307. cls.data_context_specific = b"\x86\x02\x0a\x0b"
  308. # data with private tag class
  309. cls.data_private = b"\xc6\x02\x0a\x0b"
  310. def test_simple(self):
  311. tag, body, rest = remove_implicit(self.data_context_specific)
  312. self.assertEqual(tag, self.exp_tag)
  313. self.assertEqual(body, self.exp_data)
  314. self.assertEqual(rest, b"")
  315. def test_wrong_expected_class(self):
  316. with self.assertRaises(ValueError) as e:
  317. remove_implicit(self.data_context_specific, "foobar")
  318. self.assertIn("invalid `exp_class` value", str(e.exception))
  319. def test_with_wrong_class(self):
  320. with self.assertRaises(UnexpectedDER) as e:
  321. remove_implicit(self.data_application)
  322. self.assertIn(
  323. "wanted class context-specific, got 0x46 tag", str(e.exception)
  324. )
  325. def test_with_application_class(self):
  326. tag, body, rest = remove_implicit(self.data_application, "application")
  327. self.assertEqual(tag, self.exp_tag)
  328. self.assertEqual(body, self.exp_data)
  329. self.assertEqual(rest, b"")
  330. def test_with_private_class(self):
  331. tag, body, rest = remove_implicit(self.data_private, "private")
  332. self.assertEqual(tag, self.exp_tag)
  333. self.assertEqual(body, self.exp_data)
  334. self.assertEqual(rest, b"")
  335. def test_with_data_following(self):
  336. extra_data = b"\x00\x01"
  337. tag, body, rest = remove_implicit(
  338. self.data_context_specific + extra_data
  339. )
  340. self.assertEqual(tag, self.exp_tag)
  341. self.assertEqual(body, self.exp_data)
  342. self.assertEqual(rest, extra_data)
  343. def test_with_constructed(self):
  344. data = b"\xa6\x02\x0a\x0b"
  345. with self.assertRaises(UnexpectedDER) as e:
  346. remove_implicit(data)
  347. self.assertIn("wanted type primitive, got 0xa6 tag", str(e.exception))
  348. def test_encode_decode(self):
  349. data = b"some longish string"
  350. tag, body, rest = remove_implicit(
  351. encode_implicit(6, data, "application"), "application"
  352. )
  353. self.assertEqual(tag, 6)
  354. self.assertEqual(body, data)
  355. self.assertEqual(rest, b"")
  356. class TestEncodeImplicit(unittest.TestCase):
  357. @classmethod
  358. def setUpClass(cls):
  359. cls.data = b"\x0a\x0b"
  360. # data with application tag class
  361. cls.data_application = b"\x46\x02\x0a\x0b"
  362. # data with context-specific tag class
  363. cls.data_context_specific = b"\x86\x02\x0a\x0b"
  364. # data with private tag class
  365. cls.data_private = b"\xc6\x02\x0a\x0b"
  366. def test_encode_with_default_class(self):
  367. ret = encode_implicit(6, self.data)
  368. self.assertEqual(ret, self.data_context_specific)
  369. def test_encode_with_application_class(self):
  370. ret = encode_implicit(6, self.data, "application")
  371. self.assertEqual(ret, self.data_application)
  372. def test_encode_with_context_specific_class(self):
  373. ret = encode_implicit(6, self.data, "context-specific")
  374. self.assertEqual(ret, self.data_context_specific)
  375. def test_encode_with_private_class(self):
  376. ret = encode_implicit(6, self.data, "private")
  377. self.assertEqual(ret, self.data_private)
  378. def test_encode_with_invalid_class(self):
  379. with self.assertRaises(ValueError) as e:
  380. encode_implicit(6, self.data, "foobar")
  381. self.assertIn("invalid tag class", str(e.exception))
  382. def test_encode_with_too_large_tag(self):
  383. with self.assertRaises(ValueError) as e:
  384. encode_implicit(32, self.data)
  385. self.assertIn("Long tags not supported", str(e.exception))
  386. class TestRemoveOctetString(unittest.TestCase):
  387. def test_simple(self):
  388. data = b"\x04\x03\xaa\xbb\xcc"
  389. body, rest = remove_octet_string(data)
  390. self.assertEqual(body, b"\xaa\xbb\xcc")
  391. self.assertEqual(rest, b"")
  392. def test_with_malformed_tag(self):
  393. data = b"\x03\x03\xaa\xbb\xcc"
  394. with self.assertRaises(UnexpectedDER) as e:
  395. remove_octet_string(data)
  396. self.assertIn("octetstring", str(e.exception))
  397. class TestRemoveSequence(unittest.TestCase):
  398. def test_simple(self):
  399. data = b"\x30\x02\xff\xaa"
  400. body, rest = remove_sequence(data)
  401. self.assertEqual(body, b"\xff\xaa")
  402. self.assertEqual(rest, b"")
  403. def test_with_empty_string(self):
  404. with self.assertRaises(UnexpectedDER) as e:
  405. remove_sequence(b"")
  406. self.assertIn("Empty string", str(e.exception))
  407. def test_with_wrong_tag(self):
  408. data = b"\x20\x02\xff\xaa"
  409. with self.assertRaises(UnexpectedDER) as e:
  410. remove_sequence(data)
  411. self.assertIn("wanted type 'sequence'", str(e.exception))
  412. def test_with_wrong_length(self):
  413. data = b"\x30\x03\xff\xaa"
  414. with self.assertRaises(UnexpectedDER) as e:
  415. remove_sequence(data)
  416. self.assertIn("Length longer", str(e.exception))
  417. @st.composite
  418. def st_oid(draw, max_value=2**512, max_size=50):
  419. """
  420. Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples
  421. :param max_value: maximum value of any single sub-identifier
  422. :param max_size: maximum length of the generated OID
  423. """
  424. first = draw(st.integers(min_value=0, max_value=2))
  425. if first < 2:
  426. second = draw(st.integers(min_value=0, max_value=39))
  427. else:
  428. second = draw(st.integers(min_value=0, max_value=max_value))
  429. rest = draw(
  430. st.lists(
  431. st.integers(min_value=0, max_value=max_value), max_size=max_size
  432. )
  433. )
  434. return (first, second) + tuple(rest)
  435. HYP_SETTINGS = {}
  436. if "--fast" in sys.argv: # pragma: no cover
  437. HYP_SETTINGS["max_examples"] = 2
  438. @settings(**HYP_SETTINGS)
  439. @given(st_oid())
  440. def test_oids(ids):
  441. encoded_oid = encode_oid(*ids)
  442. decoded_oid, rest = remove_object(encoded_oid)
  443. assert rest == b""
  444. assert decoded_oid == ids