test_wycheproof.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. #!/usr/bin/env python
  2. import ctypes
  3. import json
  4. import os
  5. from binascii import hexlify, unhexlify
  6. import pytest
  7. from pyasn1.codec.ber.decoder import decode as ber_decode
  8. from pyasn1.codec.der.decoder import decode as der_decode
  9. from pyasn1.codec.der.encoder import encode as der_encode
  10. from pyasn1.type import namedtype, univ
  11. class EcSignature(univ.Sequence):
  12. componentType = namedtype.NamedTypes(
  13. namedtype.NamedType("r", univ.Integer()),
  14. namedtype.NamedType("s", univ.Integer()),
  15. )
  16. class EcKeyInfo(univ.Sequence):
  17. componentType = namedtype.NamedTypes(
  18. namedtype.NamedType("key_type", univ.ObjectIdentifier()),
  19. namedtype.NamedType("curve_name", univ.ObjectIdentifier()),
  20. )
  21. class EcPublicKey(univ.Sequence):
  22. componentType = namedtype.NamedTypes(
  23. namedtype.NamedType("key_info", EcKeyInfo()),
  24. namedtype.NamedType("public_key", univ.BitString()),
  25. )
  26. class EdKeyInfo(univ.Sequence):
  27. componentType = namedtype.NamedTypes(
  28. namedtype.NamedType("key_type", univ.ObjectIdentifier())
  29. )
  30. class EdPublicKey(univ.Sequence):
  31. componentType = namedtype.NamedTypes(
  32. namedtype.NamedType("key_info", EdKeyInfo()),
  33. namedtype.NamedType("public_key", univ.BitString()),
  34. )
  35. class ParseError(Exception):
  36. pass
  37. class NotSupported(Exception):
  38. pass
  39. class DataError(Exception):
  40. pass
  41. class curve_info(ctypes.Structure):
  42. _fields_ = [("bip32_name", ctypes.c_char_p), ("params", ctypes.c_void_p)]
  43. def keys_in_dict(dictionary, keys):
  44. return keys <= set(dictionary.keys())
  45. def parse_eddsa_signature(signature):
  46. if len(signature) != 64:
  47. raise ParseError("Not a valid EdDSA signature")
  48. return signature
  49. def parse_ecdh256_privkey(private_key):
  50. if private_key < 0 or private_key.bit_length() > 256:
  51. raise ParseError("Not a valid 256 bit ECDH private key")
  52. return private_key.to_bytes(32, byteorder="big")
  53. def parse_signed_hex(string):
  54. if len(string) % 2 == 1:
  55. string = "0" + string
  56. number = int(string, 16)
  57. if int(string[0], 16) & 8:
  58. return -number
  59. else:
  60. return number
  61. def parse_result(result):
  62. if result == "valid":
  63. return True
  64. elif result == "invalid":
  65. return False
  66. elif result == "acceptable":
  67. return None
  68. else:
  69. raise DataError()
  70. def is_valid_der(data):
  71. try:
  72. structure, _ = der_decode(data)
  73. return data == der_encode(structure)
  74. except Exception:
  75. return False
  76. def parse_ed_pubkey(public_key):
  77. try:
  78. public_key, _ = ber_decode(public_key, asn1Spec=EdPublicKey())
  79. except Exception:
  80. raise ParseError("Not a BER encoded Edwards curve public key")
  81. if not public_key["key_info"]["key_type"] == univ.ObjectIdentifier("1.3.101.112"):
  82. raise ParseError("Not a BER encoded Edwards curve public key")
  83. public_key = bytes(public_key["public_key"].asOctets())
  84. return public_key
  85. def parse_ec_pubkey(public_key):
  86. try:
  87. public_key, _ = ber_decode(public_key, asn1Spec=EcPublicKey())
  88. except Exception:
  89. raise ParseError("Not a BER encoded named elliptic curve public key")
  90. if not public_key["key_info"]["key_type"] == univ.ObjectIdentifier(
  91. "1.2.840.10045.2.1"
  92. ):
  93. raise ParseError("Not a BER encoded named elliptic curve public key")
  94. curve_identifier = public_key["key_info"]["curve_name"]
  95. curve_name = get_curve_name_by_identifier(curve_identifier)
  96. if curve_name is None:
  97. raise NotSupported(
  98. "Unsupported named elliptic curve: {}".format(curve_identifier)
  99. )
  100. try:
  101. public_key = bytes(public_key["public_key"].asOctets())
  102. except Exception:
  103. raise ParseError("Not a BER encoded named elliptic curve public key")
  104. return curve_name, public_key
  105. def parse_ecdsa256_signature(signature):
  106. s = signature
  107. if not is_valid_der(signature):
  108. raise ParseError("Not a valid DER")
  109. try:
  110. signature, _ = der_decode(signature, asn1Spec=EcSignature())
  111. except Exception:
  112. raise ParseError("Not a valid DER encoded ECDSA signature")
  113. try:
  114. r = int(signature["r"]).to_bytes(32, byteorder="big")
  115. s = int(signature["s"]).to_bytes(32, byteorder="big")
  116. signature = r + s
  117. except Exception:
  118. raise ParseError("Not a valid DER encoded 256 bit ECDSA signature")
  119. return signature
  120. def parse_digest(name):
  121. if name == "SHA-256":
  122. return 0
  123. else:
  124. raise NotSupported("Unsupported hash function: {}".format(name))
  125. def get_curve_by_name(name):
  126. lib.get_curve_by_name.restype = ctypes.c_void_p
  127. curve = lib.get_curve_by_name(bytes(name, "ascii"))
  128. if curve is None:
  129. return None
  130. curve = ctypes.cast(curve, ctypes.POINTER(curve_info))
  131. return ctypes.c_void_p(curve.contents.params)
  132. def parse_curve_name(name):
  133. if name == "secp256r1":
  134. return "nist256p1"
  135. elif name == "secp256k1":
  136. return "secp256k1"
  137. elif name == "curve25519":
  138. return "curve25519"
  139. else:
  140. return None
  141. def get_curve_name_by_identifier(identifier):
  142. if identifier == univ.ObjectIdentifier("1.3.132.0.10"):
  143. return "secp256k1"
  144. elif identifier == univ.ObjectIdentifier("1.2.840.10045.3.1.7"):
  145. return "nist256p1"
  146. else:
  147. return None
  148. def chacha_poly_encrypt(key, iv, associated_data, plaintext):
  149. context = bytes(context_structure_length)
  150. tag = bytes(16)
  151. ciphertext = bytes(len(plaintext))
  152. lib.rfc7539_init(context, key, iv)
  153. lib.rfc7539_auth(context, associated_data, len(associated_data))
  154. lib.chacha20poly1305_encrypt(context, plaintext, ciphertext, len(plaintext))
  155. lib.rfc7539_finish(context, len(associated_data), len(plaintext), tag)
  156. return ciphertext, tag
  157. def chacha_poly_decrypt(key, iv, associated_data, ciphertext, tag):
  158. context = bytes(context_structure_length)
  159. computed_tag = bytes(16)
  160. plaintext = bytes(len(ciphertext))
  161. lib.rfc7539_init(context, key, iv)
  162. lib.rfc7539_auth(context, associated_data, len(associated_data))
  163. lib.chacha20poly1305_decrypt(context, ciphertext, plaintext, len(ciphertext))
  164. lib.rfc7539_finish(context, len(associated_data), len(ciphertext), computed_tag)
  165. return plaintext if tag == computed_tag else False
  166. def add_pkcs_padding(data):
  167. padding_length = 16 - len(data) % 16
  168. return data + bytes([padding_length] * padding_length)
  169. def remove_pkcs_padding(data):
  170. padding_length = data[-1]
  171. if not (
  172. 0 < padding_length <= 16
  173. and data[-padding_length:] == bytes([padding_length] * padding_length)
  174. ):
  175. return False
  176. else:
  177. return data[:-padding_length]
  178. def aes_encrypt_initialise(key, context):
  179. if len(key) == (128 / 8):
  180. lib.aes_encrypt_key128(key, context)
  181. elif len(key) == (192 / 8):
  182. lib.aes_encrypt_key192(key, context)
  183. elif len(key) == (256 / 8):
  184. lib.aes_encrypt_key256(key, context)
  185. else:
  186. raise NotSupported("Unsupported key length: {}".format(len(key) * 8))
  187. def aes_cbc_encrypt(key, iv, plaintext):
  188. plaintext = add_pkcs_padding(plaintext)
  189. context = bytes(context_structure_length)
  190. ciphertext = bytes(len(plaintext))
  191. aes_encrypt_initialise(key, context)
  192. lib.aes_cbc_encrypt(
  193. plaintext, ciphertext, len(plaintext), bytes(bytearray(iv)), context
  194. )
  195. return ciphertext
  196. def aes_decrypt_initialise(key, context):
  197. if len(key) == (128 / 8):
  198. lib.aes_decrypt_key128(key, context)
  199. elif len(key) == (192 / 8):
  200. lib.aes_decrypt_key192(key, context)
  201. elif len(key) == (256 / 8):
  202. lib.aes_decrypt_key256(key, context)
  203. else:
  204. raise NotSupported("Unsupported AES key length: {}".format(len(key) * 8))
  205. def aes_cbc_decrypt(key, iv, ciphertext):
  206. context = bytes(context_structure_length)
  207. plaintext = bytes(len(ciphertext))
  208. aes_decrypt_initialise(key, context)
  209. lib.aes_cbc_decrypt(ciphertext, plaintext, len(ciphertext), iv, context)
  210. return remove_pkcs_padding(plaintext)
  211. def load_json_testvectors(filename):
  212. try:
  213. result = json.loads(open(os.path.join(testvectors_directory, filename)).read())
  214. except Exception:
  215. raise DataError()
  216. return result
  217. def generate_aes(filename):
  218. vectors = []
  219. data = load_json_testvectors(filename)
  220. if not keys_in_dict(data, {"algorithm", "testGroups"}):
  221. raise DataError()
  222. if data["algorithm"] != "AES-CBC-PKCS5":
  223. raise DataError()
  224. for test_group in data["testGroups"]:
  225. if not keys_in_dict(test_group, {"tests"}):
  226. raise DataError()
  227. for test in test_group["tests"]:
  228. if not keys_in_dict(test, {"key", "iv", "msg", "ct", "result"}):
  229. raise DataError()
  230. try:
  231. key = unhexlify(test["key"])
  232. iv = unhexlify(test["iv"])
  233. plaintext = unhexlify(test["msg"])
  234. ciphertext = unhexlify(test["ct"])
  235. result = parse_result(test["result"])
  236. except Exception:
  237. raise DataError()
  238. if len(key) not in [128 / 8, 192 / 8, 256 / 8]:
  239. continue
  240. if result is None:
  241. continue
  242. vectors.append(
  243. (
  244. hexlify(key),
  245. hexlify(iv),
  246. hexlify(plaintext),
  247. hexlify(ciphertext),
  248. result,
  249. )
  250. )
  251. return vectors
  252. def generate_chacha_poly(filename):
  253. vectors = []
  254. data = load_json_testvectors(filename)
  255. if not keys_in_dict(data, {"algorithm", "testGroups"}):
  256. raise DataError()
  257. if data["algorithm"] != "CHACHA20-POLY1305":
  258. raise DataError()
  259. for test_group in data["testGroups"]:
  260. if not keys_in_dict(test_group, {"tests"}):
  261. raise DataError()
  262. for test in test_group["tests"]:
  263. if not keys_in_dict(
  264. test, {"key", "iv", "aad", "msg", "ct", "tag", "result"}
  265. ):
  266. raise DataError()
  267. try:
  268. key = unhexlify(test["key"])
  269. iv = unhexlify(test["iv"])
  270. associated_data = unhexlify(test["aad"])
  271. plaintext = unhexlify(test["msg"])
  272. ciphertext = unhexlify(test["ct"])
  273. tag = unhexlify(test["tag"])
  274. result = parse_result(test["result"])
  275. except Exception:
  276. raise DataError()
  277. if result is None:
  278. continue
  279. vectors.append(
  280. (
  281. hexlify(key),
  282. hexlify(iv),
  283. hexlify(associated_data),
  284. hexlify(plaintext),
  285. hexlify(ciphertext),
  286. hexlify(tag),
  287. result,
  288. )
  289. )
  290. return vectors
  291. def generate_curve25519_dh(filename):
  292. vectors = []
  293. data = load_json_testvectors(filename)
  294. if not keys_in_dict(data, {"algorithm", "testGroups"}):
  295. raise DataError()
  296. if data["algorithm"] != "XDH":
  297. raise DataError()
  298. for test_group in data["testGroups"]:
  299. if not keys_in_dict(test_group, {"tests", "curve"}):
  300. raise DataError()
  301. try:
  302. curve_name = parse_curve_name(test_group["curve"])
  303. except Exception:
  304. raise DataError()
  305. for test in test_group["tests"]:
  306. if not keys_in_dict(test, {"public", "private", "shared", "result"}):
  307. raise DataError()
  308. try:
  309. public_key = unhexlify(test["public"])
  310. private_key = unhexlify(test["private"])
  311. shared = unhexlify(test["shared"])
  312. result = parse_result(test["result"])
  313. except Exception:
  314. raise DataError()
  315. if curve_name != "curve25519":
  316. continue
  317. if result is None:
  318. continue
  319. vectors.append(
  320. (hexlify(public_key), hexlify(private_key), hexlify(shared), result)
  321. )
  322. return vectors
  323. def generate_ecdh(filename):
  324. vectors = []
  325. data = load_json_testvectors(filename)
  326. if not keys_in_dict(data, {"algorithm", "testGroups"}):
  327. raise DataError()
  328. if data["algorithm"] != "ECDH":
  329. raise DataError()
  330. for test_group in data["testGroups"]:
  331. if not keys_in_dict(test_group, {"tests", "curve"}):
  332. raise DataError()
  333. try:
  334. curve_name = parse_curve_name(test_group["curve"])
  335. except Exception:
  336. raise DataError()
  337. for test in test_group["tests"]:
  338. if not keys_in_dict(test, {"public", "private", "shared", "result"}):
  339. raise DataError()
  340. try:
  341. public_key = unhexlify(test["public"])
  342. private_key = parse_signed_hex(test["private"])
  343. shared = unhexlify(test["shared"])
  344. result = parse_result(test["result"])
  345. except Exception:
  346. raise DataError()
  347. try:
  348. private_key = parse_ecdh256_privkey(private_key)
  349. except ParseError:
  350. continue
  351. try:
  352. key_curve_name, public_key = parse_ec_pubkey(public_key)
  353. except NotSupported:
  354. continue
  355. except ParseError:
  356. continue
  357. if key_curve_name != curve_name:
  358. continue
  359. if result is None:
  360. continue
  361. vectors.append(
  362. (
  363. curve_name,
  364. hexlify(public_key),
  365. hexlify(private_key),
  366. hexlify(shared),
  367. result,
  368. )
  369. )
  370. return vectors
  371. def generate_ecdsa(filename):
  372. vectors = []
  373. data = load_json_testvectors(filename)
  374. if not keys_in_dict(data, {"algorithm", "testGroups"}):
  375. raise DataError()
  376. if data["algorithm"] != "ECDSA":
  377. raise DataError()
  378. for test_group in data["testGroups"]:
  379. if not keys_in_dict(test_group, {"tests", "keyDer", "sha"}):
  380. raise DataError()
  381. try:
  382. public_key = unhexlify(test_group["keyDer"])
  383. except Exception:
  384. raise DataError()
  385. try:
  386. curve_name, public_key = parse_ec_pubkey(public_key)
  387. except NotSupported:
  388. continue
  389. except ParseError:
  390. continue
  391. try:
  392. hasher = parse_digest(test_group["sha"])
  393. except NotSupported:
  394. continue
  395. for test in test_group["tests"]:
  396. if not keys_in_dict(test, {"sig", "msg", "result"}):
  397. raise DataError()
  398. try:
  399. signature = unhexlify(test["sig"])
  400. message = unhexlify(test["msg"])
  401. result = parse_result(test["result"])
  402. except Exception:
  403. raise DataError()
  404. if result is None:
  405. continue
  406. try:
  407. signature = parse_ecdsa256_signature(signature)
  408. except ParseError:
  409. continue
  410. vectors.append(
  411. (
  412. curve_name,
  413. hexlify(public_key),
  414. hasher,
  415. hexlify(message),
  416. hexlify(signature),
  417. result,
  418. )
  419. )
  420. return vectors
  421. def generate_eddsa(filename):
  422. vectors = []
  423. data = load_json_testvectors(filename)
  424. if not keys_in_dict(data, {"algorithm", "testGroups"}):
  425. raise DataError()
  426. if data["algorithm"] != "EDDSA":
  427. raise DataError()
  428. for test_group in data["testGroups"]:
  429. if not keys_in_dict(test_group, {"tests", "keyDer"}):
  430. raise DataError()
  431. try:
  432. public_key = unhexlify(test_group["keyDer"])
  433. except Exception:
  434. raise DataError()
  435. try:
  436. public_key = parse_ed_pubkey(public_key)
  437. except ParseError:
  438. continue
  439. for test in test_group["tests"]:
  440. if not keys_in_dict(test, {"sig", "msg", "result"}):
  441. raise DataError()
  442. try:
  443. signature = unhexlify(test["sig"])
  444. message = unhexlify(test["msg"])
  445. result = parse_result(test["result"])
  446. except Exception:
  447. raise DataError()
  448. if result is None:
  449. continue
  450. try:
  451. signature = parse_eddsa_signature(signature)
  452. except ParseError:
  453. continue
  454. vectors.append(
  455. (hexlify(public_key), hexlify(message), hexlify(signature), result)
  456. )
  457. return vectors
  458. dir = os.path.abspath(os.path.dirname(__file__))
  459. lib = ctypes.cdll.LoadLibrary(os.path.join(dir, "libtrezor-crypto.so"))
  460. if not lib.zkp_context_is_initialized():
  461. assert lib.zkp_context_init() == 0
  462. testvectors_directory = os.path.join(dir, "wycheproof/testvectors")
  463. context_structure_length = 1024
  464. curve25519_dh_vectors = generate_curve25519_dh("x25519_test.json")
  465. eddsa_vectors = generate_eddsa("eddsa_test.json")
  466. ecdsa_vectors = (
  467. generate_ecdsa("ecdsa_test.json")
  468. + generate_ecdsa("ecdsa_secp256k1_sha256_test.json")
  469. + generate_ecdsa("ecdsa_secp256r1_sha256_test.json")
  470. )
  471. ecdh_vectors = (
  472. generate_ecdh("ecdh_test.json")
  473. + generate_ecdh("ecdh_secp256k1_test.json")
  474. + generate_ecdh("ecdh_secp256r1_test.json")
  475. )
  476. chacha_poly_vectors = generate_chacha_poly("chacha20_poly1305_test.json")
  477. aes_vectors = generate_aes("aes_cbc_pkcs5_test.json")
  478. @pytest.mark.parametrize("public_key, message, signature, result", eddsa_vectors)
  479. def test_eddsa(public_key, message, signature, result):
  480. public_key = unhexlify(public_key)
  481. signature = unhexlify(signature)
  482. message = unhexlify(message)
  483. computed_result = (
  484. lib.ed25519_sign_open(message, len(message), public_key, signature) == 0
  485. )
  486. assert result == computed_result
  487. @pytest.mark.parametrize(
  488. "curve_name, public_key, hasher, message, signature, result", ecdsa_vectors
  489. )
  490. def test_ecdsa(curve_name, public_key, hasher, message, signature, result):
  491. curve = get_curve_by_name(curve_name)
  492. if curve is None:
  493. raise NotSupported("Curve not supported: {}".format(curve_name))
  494. public_key = unhexlify(public_key)
  495. signature = unhexlify(signature)
  496. message = unhexlify(message)
  497. computed_result = (
  498. lib.ecdsa_verify(curve, hasher, public_key, signature, message, len(message))
  499. == 0
  500. )
  501. assert result == computed_result
  502. @pytest.mark.parametrize(
  503. "curve_name, public_key, hasher, message, signature, result",
  504. filter(lambda v: v[0] == "secp256k1", ecdsa_vectors),
  505. )
  506. def test_ecdsa_zkp(curve_name, public_key, hasher, message, signature, result):
  507. curve = get_curve_by_name(curve_name)
  508. if curve is None:
  509. raise NotSupported("Curve not supported: {}".format(curve_name))
  510. public_key = unhexlify(public_key)
  511. signature = unhexlify(signature)
  512. message = unhexlify(message)
  513. computed_result = (
  514. lib.zkp_ecdsa_verify(
  515. curve, hasher, public_key, signature, message, len(message)
  516. )
  517. == 0
  518. )
  519. assert result == computed_result
  520. @pytest.mark.parametrize(
  521. "public_key, private_key, shared, result", curve25519_dh_vectors
  522. )
  523. def test_curve25519_dh(public_key, private_key, shared, result):
  524. public_key = unhexlify(public_key)
  525. private_key = unhexlify(private_key)
  526. shared = unhexlify(shared)
  527. computed_shared = bytes([0] * 32)
  528. lib.curve25519_scalarmult(computed_shared, private_key, public_key)
  529. computed_result = shared == computed_shared
  530. assert result == computed_result
  531. @pytest.mark.parametrize(
  532. "curve_name, public_key, private_key, shared, result", ecdh_vectors
  533. )
  534. def test_ecdh(curve_name, public_key, private_key, shared, result):
  535. curve = get_curve_by_name(curve_name)
  536. if curve is None:
  537. raise NotSupported("Curve not supported: {}".format(curve_name))
  538. public_key = unhexlify(public_key)
  539. private_key = unhexlify(private_key)
  540. shared = unhexlify(shared)
  541. computed_shared = bytes([0] * 2 * 32)
  542. lib.ecdh_multiply(curve, private_key, public_key, computed_shared)
  543. computed_shared = computed_shared[1:33]
  544. computed_result = shared == computed_shared
  545. assert result == computed_result
  546. @pytest.mark.parametrize(
  547. "key, iv, associated_data, plaintext, ciphertext, tag, result", chacha_poly_vectors
  548. )
  549. def test_chacha_poly(key, iv, associated_data, plaintext, ciphertext, tag, result):
  550. key = unhexlify(key)
  551. iv = unhexlify(iv)
  552. associated_data = unhexlify(associated_data)
  553. plaintext = unhexlify(plaintext)
  554. ciphertext = unhexlify(ciphertext)
  555. tag = unhexlify(tag)
  556. computed_ciphertext, computed_tag = chacha_poly_encrypt(
  557. key, iv, associated_data, plaintext
  558. )
  559. computed_result = ciphertext == computed_ciphertext and tag == computed_tag
  560. assert result == computed_result
  561. computed_plaintext = chacha_poly_decrypt(key, iv, associated_data, ciphertext, tag)
  562. computed_result = plaintext == computed_plaintext
  563. assert result == computed_result
  564. @pytest.mark.parametrize("key, iv, plaintext, ciphertext, result", aes_vectors)
  565. def test_aes(key, iv, plaintext, ciphertext, result):
  566. key = unhexlify(key)
  567. iv = unhexlify(iv)
  568. plaintext = unhexlify(plaintext)
  569. ciphertext = unhexlify(ciphertext)
  570. computed_ciphertext = aes_cbc_encrypt(key, iv, plaintext)
  571. computed_result = ciphertext == computed_ciphertext
  572. assert result == computed_result
  573. computed_plaintext = aes_cbc_decrypt(key, bytes(iv), ciphertext)
  574. computed_result = plaintext == computed_plaintext
  575. assert result == computed_result