passagemath-modules 10.6.31rc3__cp314-cp314-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-modules might be problematic. Click here for more details.

Files changed (807) hide show
  1. passagemath_modules-10.6.31rc3.dist-info/METADATA +281 -0
  2. passagemath_modules-10.6.31rc3.dist-info/RECORD +807 -0
  3. passagemath_modules-10.6.31rc3.dist-info/WHEEL +5 -0
  4. passagemath_modules-10.6.31rc3.dist-info/top_level.txt +2 -0
  5. passagemath_modules.libs/libgcc_s-2d945d6c.so.1 +0 -0
  6. passagemath_modules.libs/libgfortran-67378ab2.so.5.0.0 +0 -0
  7. passagemath_modules.libs/libgmp-28992bcb.so.10.5.0 +0 -0
  8. passagemath_modules.libs/libgsl-23768756.so.28.0.0 +0 -0
  9. passagemath_modules.libs/libmpc-7897025b.so.3.3.1 +0 -0
  10. passagemath_modules.libs/libmpfr-e34bb864.so.6.2.1 +0 -0
  11. passagemath_modules.libs/libopenblasp-r0-503f0c35.3.29.so +0 -0
  12. sage/algebras/all__sagemath_modules.py +20 -0
  13. sage/algebras/catalog.py +148 -0
  14. sage/algebras/clifford_algebra.py +3107 -0
  15. sage/algebras/clifford_algebra_element.cpython-314-aarch64-linux-musl.so +0 -0
  16. sage/algebras/clifford_algebra_element.pxd +16 -0
  17. sage/algebras/clifford_algebra_element.pyx +997 -0
  18. sage/algebras/commutative_dga.py +4252 -0
  19. sage/algebras/exterior_algebra_groebner.cpython-314-aarch64-linux-musl.so +0 -0
  20. sage/algebras/exterior_algebra_groebner.pxd +55 -0
  21. sage/algebras/exterior_algebra_groebner.pyx +727 -0
  22. sage/algebras/finite_dimensional_algebras/all.py +2 -0
  23. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +1029 -0
  24. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.cpython-314-aarch64-linux-musl.so +0 -0
  25. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pxd +12 -0
  26. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx +706 -0
  27. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_ideal.py +196 -0
  28. sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_morphism.py +255 -0
  29. sage/algebras/finite_gca.py +528 -0
  30. sage/algebras/group_algebra.py +232 -0
  31. sage/algebras/lie_algebras/abelian.py +197 -0
  32. sage/algebras/lie_algebras/affine_lie_algebra.py +1213 -0
  33. sage/algebras/lie_algebras/all.py +25 -0
  34. sage/algebras/lie_algebras/all__sagemath_modules.py +1 -0
  35. sage/algebras/lie_algebras/bch.py +177 -0
  36. sage/algebras/lie_algebras/bgg_dual_module.py +1184 -0
  37. sage/algebras/lie_algebras/bgg_resolution.py +232 -0
  38. sage/algebras/lie_algebras/center_uea.py +767 -0
  39. sage/algebras/lie_algebras/classical_lie_algebra.py +2516 -0
  40. sage/algebras/lie_algebras/examples.py +683 -0
  41. sage/algebras/lie_algebras/free_lie_algebra.py +973 -0
  42. sage/algebras/lie_algebras/heisenberg.py +820 -0
  43. sage/algebras/lie_algebras/lie_algebra.py +1562 -0
  44. sage/algebras/lie_algebras/lie_algebra_element.cpython-314-aarch64-linux-musl.so +0 -0
  45. sage/algebras/lie_algebras/lie_algebra_element.pxd +68 -0
  46. sage/algebras/lie_algebras/lie_algebra_element.pyx +2122 -0
  47. sage/algebras/lie_algebras/morphism.py +661 -0
  48. sage/algebras/lie_algebras/nilpotent_lie_algebra.py +457 -0
  49. sage/algebras/lie_algebras/onsager.py +1324 -0
  50. sage/algebras/lie_algebras/poincare_birkhoff_witt.py +816 -0
  51. sage/algebras/lie_algebras/quotient.py +462 -0
  52. sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py +355 -0
  53. sage/algebras/lie_algebras/representation.py +1040 -0
  54. sage/algebras/lie_algebras/structure_coefficients.py +459 -0
  55. sage/algebras/lie_algebras/subalgebra.py +967 -0
  56. sage/algebras/lie_algebras/symplectic_derivation.py +289 -0
  57. sage/algebras/lie_algebras/verma_module.py +1630 -0
  58. sage/algebras/lie_algebras/virasoro.py +1186 -0
  59. sage/algebras/octonion_algebra.cpython-314-aarch64-linux-musl.so +0 -0
  60. sage/algebras/octonion_algebra.pxd +20 -0
  61. sage/algebras/octonion_algebra.pyx +987 -0
  62. sage/algebras/orlik_solomon.py +907 -0
  63. sage/algebras/orlik_terao.py +779 -0
  64. sage/algebras/steenrod/all.py +7 -0
  65. sage/algebras/steenrod/steenrod_algebra.py +4258 -0
  66. sage/algebras/steenrod/steenrod_algebra_bases.py +1179 -0
  67. sage/algebras/steenrod/steenrod_algebra_misc.py +1167 -0
  68. sage/algebras/steenrod/steenrod_algebra_mult.py +954 -0
  69. sage/algebras/weyl_algebra.py +1126 -0
  70. sage/all__sagemath_modules.py +62 -0
  71. sage/calculus/all__sagemath_modules.py +19 -0
  72. sage/calculus/expr.py +205 -0
  73. sage/calculus/integration.cpython-314-aarch64-linux-musl.so +0 -0
  74. sage/calculus/integration.pyx +698 -0
  75. sage/calculus/interpolation.cpython-314-aarch64-linux-musl.so +0 -0
  76. sage/calculus/interpolation.pxd +13 -0
  77. sage/calculus/interpolation.pyx +387 -0
  78. sage/calculus/interpolators.cpython-314-aarch64-linux-musl.so +0 -0
  79. sage/calculus/interpolators.pyx +326 -0
  80. sage/calculus/ode.cpython-314-aarch64-linux-musl.so +0 -0
  81. sage/calculus/ode.pxd +5 -0
  82. sage/calculus/ode.pyx +610 -0
  83. sage/calculus/riemann.cpython-314-aarch64-linux-musl.so +0 -0
  84. sage/calculus/riemann.pyx +1521 -0
  85. sage/calculus/test_sympy.py +201 -0
  86. sage/calculus/transforms/all.py +7 -0
  87. sage/calculus/transforms/dft.py +844 -0
  88. sage/calculus/transforms/dwt.cpython-314-aarch64-linux-musl.so +0 -0
  89. sage/calculus/transforms/dwt.pxd +7 -0
  90. sage/calculus/transforms/dwt.pyx +160 -0
  91. sage/calculus/transforms/fft.cpython-314-aarch64-linux-musl.so +0 -0
  92. sage/calculus/transforms/fft.pxd +12 -0
  93. sage/calculus/transforms/fft.pyx +487 -0
  94. sage/calculus/wester.py +662 -0
  95. sage/coding/abstract_code.py +1108 -0
  96. sage/coding/ag_code.py +868 -0
  97. sage/coding/ag_code_decoders.cpython-314-aarch64-linux-musl.so +0 -0
  98. sage/coding/ag_code_decoders.pyx +2639 -0
  99. sage/coding/all.py +15 -0
  100. sage/coding/bch_code.py +494 -0
  101. sage/coding/binary_code.cpython-314-aarch64-linux-musl.so +0 -0
  102. sage/coding/binary_code.pxd +124 -0
  103. sage/coding/binary_code.pyx +4139 -0
  104. sage/coding/bounds_catalog.py +43 -0
  105. sage/coding/channel.py +819 -0
  106. sage/coding/channels_catalog.py +29 -0
  107. sage/coding/code_bounds.py +755 -0
  108. sage/coding/code_constructions.py +804 -0
  109. sage/coding/codes_catalog.py +111 -0
  110. sage/coding/cyclic_code.py +1329 -0
  111. sage/coding/databases.py +316 -0
  112. sage/coding/decoder.py +373 -0
  113. sage/coding/decoders_catalog.py +88 -0
  114. sage/coding/delsarte_bounds.py +709 -0
  115. sage/coding/encoder.py +390 -0
  116. sage/coding/encoders_catalog.py +64 -0
  117. sage/coding/extended_code.py +468 -0
  118. sage/coding/gabidulin_code.py +1058 -0
  119. sage/coding/golay_code.py +404 -0
  120. sage/coding/goppa_code.py +441 -0
  121. sage/coding/grs_code.py +2371 -0
  122. sage/coding/guava.py +107 -0
  123. sage/coding/guruswami_sudan/all.py +1 -0
  124. sage/coding/guruswami_sudan/gs_decoder.py +897 -0
  125. sage/coding/guruswami_sudan/interpolation.py +409 -0
  126. sage/coding/guruswami_sudan/utils.py +176 -0
  127. sage/coding/hamming_code.py +176 -0
  128. sage/coding/information_set_decoder.py +1032 -0
  129. sage/coding/kasami_codes.cpython-314-aarch64-linux-musl.so +0 -0
  130. sage/coding/kasami_codes.pyx +351 -0
  131. sage/coding/linear_code.py +3067 -0
  132. sage/coding/linear_code_no_metric.py +1354 -0
  133. sage/coding/linear_rank_metric.py +961 -0
  134. sage/coding/parity_check_code.py +353 -0
  135. sage/coding/punctured_code.py +719 -0
  136. sage/coding/reed_muller_code.py +999 -0
  137. sage/coding/self_dual_codes.py +942 -0
  138. sage/coding/source_coding/all.py +2 -0
  139. sage/coding/source_coding/huffman.py +553 -0
  140. sage/coding/subfield_subcode.py +423 -0
  141. sage/coding/two_weight_db.py +399 -0
  142. sage/combinat/all__sagemath_modules.py +7 -0
  143. sage/combinat/cartesian_product.py +347 -0
  144. sage/combinat/family.py +11 -0
  145. sage/combinat/free_module.py +1977 -0
  146. sage/combinat/root_system/all.py +147 -0
  147. sage/combinat/root_system/ambient_space.py +527 -0
  148. sage/combinat/root_system/associahedron.py +471 -0
  149. sage/combinat/root_system/braid_move_calculator.py +143 -0
  150. sage/combinat/root_system/braid_orbit.cpython-314-aarch64-linux-musl.so +0 -0
  151. sage/combinat/root_system/braid_orbit.pyx +144 -0
  152. sage/combinat/root_system/branching_rules.py +2301 -0
  153. sage/combinat/root_system/cartan_matrix.py +1245 -0
  154. sage/combinat/root_system/cartan_type.py +3069 -0
  155. sage/combinat/root_system/coxeter_group.py +162 -0
  156. sage/combinat/root_system/coxeter_matrix.py +1261 -0
  157. sage/combinat/root_system/coxeter_type.py +681 -0
  158. sage/combinat/root_system/dynkin_diagram.py +900 -0
  159. sage/combinat/root_system/extended_affine_weyl_group.py +2993 -0
  160. sage/combinat/root_system/fundamental_group.py +795 -0
  161. sage/combinat/root_system/hecke_algebra_representation.py +1203 -0
  162. sage/combinat/root_system/integrable_representations.py +1227 -0
  163. sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +1965 -0
  164. sage/combinat/root_system/pieri_factors.py +1147 -0
  165. sage/combinat/root_system/plot.py +1615 -0
  166. sage/combinat/root_system/root_lattice_realization_algebras.py +1214 -0
  167. sage/combinat/root_system/root_lattice_realizations.py +4628 -0
  168. sage/combinat/root_system/root_space.py +487 -0
  169. sage/combinat/root_system/root_system.py +882 -0
  170. sage/combinat/root_system/type_A.py +348 -0
  171. sage/combinat/root_system/type_A_affine.py +227 -0
  172. sage/combinat/root_system/type_A_infinity.py +241 -0
  173. sage/combinat/root_system/type_B.py +347 -0
  174. sage/combinat/root_system/type_BC_affine.py +287 -0
  175. sage/combinat/root_system/type_B_affine.py +216 -0
  176. sage/combinat/root_system/type_C.py +317 -0
  177. sage/combinat/root_system/type_C_affine.py +188 -0
  178. sage/combinat/root_system/type_D.py +357 -0
  179. sage/combinat/root_system/type_D_affine.py +208 -0
  180. sage/combinat/root_system/type_E.py +641 -0
  181. sage/combinat/root_system/type_E_affine.py +231 -0
  182. sage/combinat/root_system/type_F.py +387 -0
  183. sage/combinat/root_system/type_F_affine.py +137 -0
  184. sage/combinat/root_system/type_G.py +293 -0
  185. sage/combinat/root_system/type_G_affine.py +132 -0
  186. sage/combinat/root_system/type_H.py +105 -0
  187. sage/combinat/root_system/type_I.py +110 -0
  188. sage/combinat/root_system/type_Q.py +150 -0
  189. sage/combinat/root_system/type_affine.py +509 -0
  190. sage/combinat/root_system/type_dual.py +704 -0
  191. sage/combinat/root_system/type_folded.py +301 -0
  192. sage/combinat/root_system/type_marked.py +748 -0
  193. sage/combinat/root_system/type_reducible.py +601 -0
  194. sage/combinat/root_system/type_relabel.py +730 -0
  195. sage/combinat/root_system/type_super_A.py +837 -0
  196. sage/combinat/root_system/weight_lattice_realizations.py +1188 -0
  197. sage/combinat/root_system/weight_space.py +639 -0
  198. sage/combinat/root_system/weyl_characters.py +2238 -0
  199. sage/crypto/__init__.py +4 -0
  200. sage/crypto/all.py +28 -0
  201. sage/crypto/block_cipher/all.py +7 -0
  202. sage/crypto/block_cipher/des.py +1065 -0
  203. sage/crypto/block_cipher/miniaes.py +2171 -0
  204. sage/crypto/block_cipher/present.py +909 -0
  205. sage/crypto/block_cipher/sdes.py +1527 -0
  206. sage/crypto/boolean_function.cpython-314-aarch64-linux-musl.so +0 -0
  207. sage/crypto/boolean_function.pxd +10 -0
  208. sage/crypto/boolean_function.pyx +1487 -0
  209. sage/crypto/cipher.py +78 -0
  210. sage/crypto/classical.py +3668 -0
  211. sage/crypto/classical_cipher.py +569 -0
  212. sage/crypto/cryptosystem.py +387 -0
  213. sage/crypto/key_exchange/all.py +7 -0
  214. sage/crypto/key_exchange/catalog.py +24 -0
  215. sage/crypto/key_exchange/diffie_hellman.py +323 -0
  216. sage/crypto/key_exchange/key_exchange_scheme.py +107 -0
  217. sage/crypto/lattice.py +312 -0
  218. sage/crypto/lfsr.py +295 -0
  219. sage/crypto/lwe.py +840 -0
  220. sage/crypto/mq/__init__.py +4 -0
  221. sage/crypto/mq/mpolynomialsystemgenerator.py +204 -0
  222. sage/crypto/mq/rijndael_gf.py +2345 -0
  223. sage/crypto/mq/sbox.py +7 -0
  224. sage/crypto/mq/sr.py +3344 -0
  225. sage/crypto/public_key/all.py +5 -0
  226. sage/crypto/public_key/blum_goldwasser.py +776 -0
  227. sage/crypto/sbox.cpython-314-aarch64-linux-musl.so +0 -0
  228. sage/crypto/sbox.pyx +2090 -0
  229. sage/crypto/sboxes.py +2090 -0
  230. sage/crypto/stream.py +390 -0
  231. sage/crypto/stream_cipher.py +297 -0
  232. sage/crypto/util.py +519 -0
  233. sage/ext/all__sagemath_modules.py +1 -0
  234. sage/ext/interpreters/__init__.py +1 -0
  235. sage/ext/interpreters/all__sagemath_modules.py +2 -0
  236. sage/ext/interpreters/wrapper_cc.cpython-314-aarch64-linux-musl.so +0 -0
  237. sage/ext/interpreters/wrapper_cc.pxd +30 -0
  238. sage/ext/interpreters/wrapper_cc.pyx +252 -0
  239. sage/ext/interpreters/wrapper_cdf.cpython-314-aarch64-linux-musl.so +0 -0
  240. sage/ext/interpreters/wrapper_cdf.pxd +26 -0
  241. sage/ext/interpreters/wrapper_cdf.pyx +245 -0
  242. sage/ext/interpreters/wrapper_rdf.cpython-314-aarch64-linux-musl.so +0 -0
  243. sage/ext/interpreters/wrapper_rdf.pxd +23 -0
  244. sage/ext/interpreters/wrapper_rdf.pyx +221 -0
  245. sage/ext/interpreters/wrapper_rr.cpython-314-aarch64-linux-musl.so +0 -0
  246. sage/ext/interpreters/wrapper_rr.pxd +28 -0
  247. sage/ext/interpreters/wrapper_rr.pyx +335 -0
  248. sage/geometry/all__sagemath_modules.py +5 -0
  249. sage/geometry/toric_lattice.py +1745 -0
  250. sage/geometry/toric_lattice_element.cpython-314-aarch64-linux-musl.so +0 -0
  251. sage/geometry/toric_lattice_element.pyx +432 -0
  252. sage/groups/abelian_gps/abelian_group.py +1925 -0
  253. sage/groups/abelian_gps/abelian_group_element.py +164 -0
  254. sage/groups/abelian_gps/all__sagemath_modules.py +5 -0
  255. sage/groups/abelian_gps/dual_abelian_group.py +421 -0
  256. sage/groups/abelian_gps/dual_abelian_group_element.py +179 -0
  257. sage/groups/abelian_gps/element_base.py +341 -0
  258. sage/groups/abelian_gps/values.py +488 -0
  259. sage/groups/additive_abelian/additive_abelian_group.py +476 -0
  260. sage/groups/additive_abelian/additive_abelian_wrapper.py +857 -0
  261. sage/groups/additive_abelian/all.py +4 -0
  262. sage/groups/additive_abelian/qmodnz.py +231 -0
  263. sage/groups/additive_abelian/qmodnz_element.py +349 -0
  264. sage/groups/affine_gps/affine_group.py +535 -0
  265. sage/groups/affine_gps/all.py +1 -0
  266. sage/groups/affine_gps/catalog.py +17 -0
  267. sage/groups/affine_gps/euclidean_group.py +246 -0
  268. sage/groups/affine_gps/group_element.py +562 -0
  269. sage/groups/all__sagemath_modules.py +12 -0
  270. sage/groups/galois_group.py +479 -0
  271. sage/groups/matrix_gps/all.py +4 -0
  272. sage/groups/matrix_gps/all__sagemath_modules.py +13 -0
  273. sage/groups/matrix_gps/catalog.py +26 -0
  274. sage/groups/matrix_gps/coxeter_group.py +927 -0
  275. sage/groups/matrix_gps/finitely_generated.py +487 -0
  276. sage/groups/matrix_gps/group_element.cpython-314-aarch64-linux-musl.so +0 -0
  277. sage/groups/matrix_gps/group_element.pxd +11 -0
  278. sage/groups/matrix_gps/group_element.pyx +431 -0
  279. sage/groups/matrix_gps/linear.py +440 -0
  280. sage/groups/matrix_gps/matrix_group.py +617 -0
  281. sage/groups/matrix_gps/named_group.py +296 -0
  282. sage/groups/matrix_gps/orthogonal.py +544 -0
  283. sage/groups/matrix_gps/symplectic.py +251 -0
  284. sage/groups/matrix_gps/unitary.py +436 -0
  285. sage/groups/misc_gps/all__sagemath_modules.py +1 -0
  286. sage/groups/misc_gps/argument_groups.py +1905 -0
  287. sage/groups/misc_gps/imaginary_groups.py +479 -0
  288. sage/groups/perm_gps/all__sagemath_modules.py +1 -0
  289. sage/groups/perm_gps/partn_ref/all__sagemath_modules.py +1 -0
  290. sage/groups/perm_gps/partn_ref/refinement_binary.cpython-314-aarch64-linux-musl.so +0 -0
  291. sage/groups/perm_gps/partn_ref/refinement_binary.pxd +41 -0
  292. sage/groups/perm_gps/partn_ref/refinement_binary.pyx +1167 -0
  293. sage/groups/perm_gps/partn_ref/refinement_matrices.cpython-314-aarch64-linux-musl.so +0 -0
  294. sage/groups/perm_gps/partn_ref/refinement_matrices.pxd +31 -0
  295. sage/groups/perm_gps/partn_ref/refinement_matrices.pyx +385 -0
  296. sage/homology/algebraic_topological_model.py +595 -0
  297. sage/homology/all.py +2 -0
  298. sage/homology/all__sagemath_modules.py +8 -0
  299. sage/homology/chain_complex.py +2148 -0
  300. sage/homology/chain_complex_homspace.py +165 -0
  301. sage/homology/chain_complex_morphism.py +629 -0
  302. sage/homology/chain_homotopy.py +604 -0
  303. sage/homology/chains.py +653 -0
  304. sage/homology/free_resolution.py +923 -0
  305. sage/homology/graded_resolution.py +567 -0
  306. sage/homology/hochschild_complex.py +756 -0
  307. sage/homology/homology_group.py +188 -0
  308. sage/homology/homology_morphism.py +422 -0
  309. sage/homology/homology_vector_space_with_basis.py +1454 -0
  310. sage/homology/koszul_complex.py +169 -0
  311. sage/homology/matrix_utils.py +205 -0
  312. sage/libs/all__sagemath_modules.py +1 -0
  313. sage/libs/gsl/__init__.py +1 -0
  314. sage/libs/gsl/airy.pxd +56 -0
  315. sage/libs/gsl/all.pxd +66 -0
  316. sage/libs/gsl/array.cpython-314-aarch64-linux-musl.so +0 -0
  317. sage/libs/gsl/array.pxd +5 -0
  318. sage/libs/gsl/array.pyx +102 -0
  319. sage/libs/gsl/bessel.pxd +208 -0
  320. sage/libs/gsl/blas.pxd +116 -0
  321. sage/libs/gsl/blas_types.pxd +34 -0
  322. sage/libs/gsl/block.pxd +52 -0
  323. sage/libs/gsl/chebyshev.pxd +37 -0
  324. sage/libs/gsl/clausen.pxd +12 -0
  325. sage/libs/gsl/combination.pxd +47 -0
  326. sage/libs/gsl/complex.pxd +151 -0
  327. sage/libs/gsl/coulomb.pxd +30 -0
  328. sage/libs/gsl/coupling.pxd +21 -0
  329. sage/libs/gsl/dawson.pxd +12 -0
  330. sage/libs/gsl/debye.pxd +24 -0
  331. sage/libs/gsl/dilog.pxd +14 -0
  332. sage/libs/gsl/eigen.pxd +46 -0
  333. sage/libs/gsl/elementary.pxd +12 -0
  334. sage/libs/gsl/ellint.pxd +48 -0
  335. sage/libs/gsl/elljac.pxd +8 -0
  336. sage/libs/gsl/erf.pxd +32 -0
  337. sage/libs/gsl/errno.pxd +26 -0
  338. sage/libs/gsl/exp.pxd +44 -0
  339. sage/libs/gsl/expint.pxd +44 -0
  340. sage/libs/gsl/fermi_dirac.pxd +44 -0
  341. sage/libs/gsl/fft.pxd +121 -0
  342. sage/libs/gsl/fit.pxd +50 -0
  343. sage/libs/gsl/gamma.pxd +94 -0
  344. sage/libs/gsl/gegenbauer.pxd +26 -0
  345. sage/libs/gsl/histogram.pxd +176 -0
  346. sage/libs/gsl/hyperg.pxd +52 -0
  347. sage/libs/gsl/integration.pxd +69 -0
  348. sage/libs/gsl/interp.pxd +109 -0
  349. sage/libs/gsl/laguerre.pxd +24 -0
  350. sage/libs/gsl/lambert.pxd +16 -0
  351. sage/libs/gsl/legendre.pxd +90 -0
  352. sage/libs/gsl/linalg.pxd +185 -0
  353. sage/libs/gsl/log.pxd +26 -0
  354. sage/libs/gsl/math.pxd +43 -0
  355. sage/libs/gsl/matrix.pxd +143 -0
  356. sage/libs/gsl/matrix_complex.pxd +130 -0
  357. sage/libs/gsl/min.pxd +67 -0
  358. sage/libs/gsl/monte.pxd +56 -0
  359. sage/libs/gsl/ntuple.pxd +32 -0
  360. sage/libs/gsl/odeiv.pxd +70 -0
  361. sage/libs/gsl/permutation.pxd +78 -0
  362. sage/libs/gsl/poly.pxd +40 -0
  363. sage/libs/gsl/pow_int.pxd +12 -0
  364. sage/libs/gsl/psi.pxd +28 -0
  365. sage/libs/gsl/qrng.pxd +29 -0
  366. sage/libs/gsl/random.pxd +257 -0
  367. sage/libs/gsl/rng.pxd +100 -0
  368. sage/libs/gsl/roots.pxd +72 -0
  369. sage/libs/gsl/sort.pxd +36 -0
  370. sage/libs/gsl/statistics.pxd +59 -0
  371. sage/libs/gsl/sum.pxd +55 -0
  372. sage/libs/gsl/synchrotron.pxd +16 -0
  373. sage/libs/gsl/transport.pxd +24 -0
  374. sage/libs/gsl/trig.pxd +58 -0
  375. sage/libs/gsl/types.pxd +137 -0
  376. sage/libs/gsl/vector.pxd +101 -0
  377. sage/libs/gsl/vector_complex.pxd +83 -0
  378. sage/libs/gsl/wavelet.pxd +49 -0
  379. sage/libs/gsl/zeta.pxd +28 -0
  380. sage/libs/mpc/__init__.pxd +114 -0
  381. sage/libs/mpc/types.pxd +28 -0
  382. sage/libs/mpfr/__init__.pxd +299 -0
  383. sage/libs/mpfr/types.pxd +26 -0
  384. sage/libs/mpmath/__init__.py +1 -0
  385. sage/libs/mpmath/all.py +27 -0
  386. sage/libs/mpmath/all__sagemath_modules.py +1 -0
  387. sage/libs/mpmath/utils.cpython-314-aarch64-linux-musl.so +0 -0
  388. sage/libs/mpmath/utils.pxd +4 -0
  389. sage/libs/mpmath/utils.pyx +319 -0
  390. sage/matrix/action.cpython-314-aarch64-linux-musl.so +0 -0
  391. sage/matrix/action.pxd +26 -0
  392. sage/matrix/action.pyx +596 -0
  393. sage/matrix/all.py +9 -0
  394. sage/matrix/args.cpython-314-aarch64-linux-musl.so +0 -0
  395. sage/matrix/args.pxd +144 -0
  396. sage/matrix/args.pyx +1668 -0
  397. sage/matrix/benchmark.py +1258 -0
  398. sage/matrix/berlekamp_massey.py +95 -0
  399. sage/matrix/compute_J_ideal.py +926 -0
  400. sage/matrix/constructor.cpython-314-aarch64-linux-musl.so +0 -0
  401. sage/matrix/constructor.pyx +750 -0
  402. sage/matrix/docs.py +430 -0
  403. sage/matrix/echelon_matrix.cpython-314-aarch64-linux-musl.so +0 -0
  404. sage/matrix/echelon_matrix.pyx +155 -0
  405. sage/matrix/matrix.pxd +2 -0
  406. sage/matrix/matrix0.cpython-314-aarch64-linux-musl.so +0 -0
  407. sage/matrix/matrix0.pxd +68 -0
  408. sage/matrix/matrix0.pyx +6324 -0
  409. sage/matrix/matrix1.cpython-314-aarch64-linux-musl.so +0 -0
  410. sage/matrix/matrix1.pxd +8 -0
  411. sage/matrix/matrix1.pyx +2851 -0
  412. sage/matrix/matrix2.cpython-314-aarch64-linux-musl.so +0 -0
  413. sage/matrix/matrix2.pxd +25 -0
  414. sage/matrix/matrix2.pyx +20181 -0
  415. sage/matrix/matrix_cdv.cpython-314-aarch64-linux-musl.so +0 -0
  416. sage/matrix/matrix_cdv.pxd +4 -0
  417. sage/matrix/matrix_cdv.pyx +93 -0
  418. sage/matrix/matrix_complex_double_dense.cpython-314-aarch64-linux-musl.so +0 -0
  419. sage/matrix/matrix_complex_double_dense.pxd +5 -0
  420. sage/matrix/matrix_complex_double_dense.pyx +98 -0
  421. sage/matrix/matrix_dense.cpython-314-aarch64-linux-musl.so +0 -0
  422. sage/matrix/matrix_dense.pxd +5 -0
  423. sage/matrix/matrix_dense.pyx +343 -0
  424. sage/matrix/matrix_domain_dense.pxd +5 -0
  425. sage/matrix/matrix_domain_sparse.pxd +5 -0
  426. sage/matrix/matrix_double_dense.cpython-314-aarch64-linux-musl.so +0 -0
  427. sage/matrix/matrix_double_dense.pxd +7 -0
  428. sage/matrix/matrix_double_dense.pyx +3906 -0
  429. sage/matrix/matrix_double_sparse.cpython-314-aarch64-linux-musl.so +0 -0
  430. sage/matrix/matrix_double_sparse.pxd +6 -0
  431. sage/matrix/matrix_double_sparse.pyx +248 -0
  432. sage/matrix/matrix_generic_dense.cpython-314-aarch64-linux-musl.so +0 -0
  433. sage/matrix/matrix_generic_dense.pxd +7 -0
  434. sage/matrix/matrix_generic_dense.pyx +354 -0
  435. sage/matrix/matrix_generic_sparse.cpython-314-aarch64-linux-musl.so +0 -0
  436. sage/matrix/matrix_generic_sparse.pxd +7 -0
  437. sage/matrix/matrix_generic_sparse.pyx +461 -0
  438. sage/matrix/matrix_laurent_mpolynomial_dense.cpython-314-aarch64-linux-musl.so +0 -0
  439. sage/matrix/matrix_laurent_mpolynomial_dense.pxd +5 -0
  440. sage/matrix/matrix_laurent_mpolynomial_dense.pyx +115 -0
  441. sage/matrix/matrix_misc.py +313 -0
  442. sage/matrix/matrix_numpy_dense.cpython-314-aarch64-linux-musl.so +0 -0
  443. sage/matrix/matrix_numpy_dense.pxd +14 -0
  444. sage/matrix/matrix_numpy_dense.pyx +450 -0
  445. sage/matrix/matrix_numpy_integer_dense.cpython-314-aarch64-linux-musl.so +0 -0
  446. sage/matrix/matrix_numpy_integer_dense.pxd +7 -0
  447. sage/matrix/matrix_numpy_integer_dense.pyx +59 -0
  448. sage/matrix/matrix_polynomial_dense.cpython-314-aarch64-linux-musl.so +0 -0
  449. sage/matrix/matrix_polynomial_dense.pxd +5 -0
  450. sage/matrix/matrix_polynomial_dense.pyx +5341 -0
  451. sage/matrix/matrix_real_double_dense.cpython-314-aarch64-linux-musl.so +0 -0
  452. sage/matrix/matrix_real_double_dense.pxd +7 -0
  453. sage/matrix/matrix_real_double_dense.pyx +122 -0
  454. sage/matrix/matrix_space.py +2848 -0
  455. sage/matrix/matrix_sparse.cpython-314-aarch64-linux-musl.so +0 -0
  456. sage/matrix/matrix_sparse.pxd +5 -0
  457. sage/matrix/matrix_sparse.pyx +1222 -0
  458. sage/matrix/matrix_window.cpython-314-aarch64-linux-musl.so +0 -0
  459. sage/matrix/matrix_window.pxd +37 -0
  460. sage/matrix/matrix_window.pyx +242 -0
  461. sage/matrix/misc_mpfr.cpython-314-aarch64-linux-musl.so +0 -0
  462. sage/matrix/misc_mpfr.pyx +80 -0
  463. sage/matrix/operation_table.py +1182 -0
  464. sage/matrix/special.py +3666 -0
  465. sage/matrix/strassen.cpython-314-aarch64-linux-musl.so +0 -0
  466. sage/matrix/strassen.pyx +851 -0
  467. sage/matrix/symplectic_basis.py +541 -0
  468. sage/matrix/template.pxd +6 -0
  469. sage/matrix/tests.py +71 -0
  470. sage/matroids/advanced.py +77 -0
  471. sage/matroids/all.py +13 -0
  472. sage/matroids/basis_exchange_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  473. sage/matroids/basis_exchange_matroid.pxd +96 -0
  474. sage/matroids/basis_exchange_matroid.pyx +2344 -0
  475. sage/matroids/basis_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  476. sage/matroids/basis_matroid.pxd +45 -0
  477. sage/matroids/basis_matroid.pyx +1217 -0
  478. sage/matroids/catalog.py +44 -0
  479. sage/matroids/chow_ring.py +473 -0
  480. sage/matroids/chow_ring_ideal.py +849 -0
  481. sage/matroids/circuit_closures_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  482. sage/matroids/circuit_closures_matroid.pxd +16 -0
  483. sage/matroids/circuit_closures_matroid.pyx +559 -0
  484. sage/matroids/circuits_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  485. sage/matroids/circuits_matroid.pxd +38 -0
  486. sage/matroids/circuits_matroid.pyx +947 -0
  487. sage/matroids/constructor.py +1086 -0
  488. sage/matroids/database_collections.py +365 -0
  489. sage/matroids/database_matroids.py +5338 -0
  490. sage/matroids/dual_matroid.py +583 -0
  491. sage/matroids/extension.cpython-314-aarch64-linux-musl.so +0 -0
  492. sage/matroids/extension.pxd +34 -0
  493. sage/matroids/extension.pyx +519 -0
  494. sage/matroids/flats_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  495. sage/matroids/flats_matroid.pxd +28 -0
  496. sage/matroids/flats_matroid.pyx +715 -0
  497. sage/matroids/gammoid.py +600 -0
  498. sage/matroids/graphic_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  499. sage/matroids/graphic_matroid.pxd +39 -0
  500. sage/matroids/graphic_matroid.pyx +2024 -0
  501. sage/matroids/lean_matrix.cpython-314-aarch64-linux-musl.so +0 -0
  502. sage/matroids/lean_matrix.pxd +126 -0
  503. sage/matroids/lean_matrix.pyx +3667 -0
  504. sage/matroids/linear_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  505. sage/matroids/linear_matroid.pxd +180 -0
  506. sage/matroids/linear_matroid.pyx +6649 -0
  507. sage/matroids/matroid.cpython-314-aarch64-linux-musl.so +0 -0
  508. sage/matroids/matroid.pxd +243 -0
  509. sage/matroids/matroid.pyx +8759 -0
  510. sage/matroids/matroids_catalog.py +190 -0
  511. sage/matroids/matroids_plot_helpers.py +890 -0
  512. sage/matroids/minor_matroid.py +480 -0
  513. sage/matroids/minorfix.h +9 -0
  514. sage/matroids/named_matroids.py +5 -0
  515. sage/matroids/rank_matroid.py +268 -0
  516. sage/matroids/set_system.cpython-314-aarch64-linux-musl.so +0 -0
  517. sage/matroids/set_system.pxd +38 -0
  518. sage/matroids/set_system.pyx +800 -0
  519. sage/matroids/transversal_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  520. sage/matroids/transversal_matroid.pxd +14 -0
  521. sage/matroids/transversal_matroid.pyx +893 -0
  522. sage/matroids/union_matroid.cpython-314-aarch64-linux-musl.so +0 -0
  523. sage/matroids/union_matroid.pxd +20 -0
  524. sage/matroids/union_matroid.pyx +331 -0
  525. sage/matroids/unpickling.cpython-314-aarch64-linux-musl.so +0 -0
  526. sage/matroids/unpickling.pyx +843 -0
  527. sage/matroids/utilities.py +809 -0
  528. sage/misc/all__sagemath_modules.py +20 -0
  529. sage/misc/c3.cpython-314-aarch64-linux-musl.so +0 -0
  530. sage/misc/c3.pyx +238 -0
  531. sage/misc/compat.py +87 -0
  532. sage/misc/element_with_label.py +173 -0
  533. sage/misc/func_persist.py +79 -0
  534. sage/misc/pickle_old.cpython-314-aarch64-linux-musl.so +0 -0
  535. sage/misc/pickle_old.pyx +19 -0
  536. sage/misc/proof.py +7 -0
  537. sage/misc/replace_dot_all.py +472 -0
  538. sage/misc/sagedoc_conf.py +168 -0
  539. sage/misc/sphinxify.py +167 -0
  540. sage/misc/test_class_pickling.py +85 -0
  541. sage/modules/all.py +42 -0
  542. sage/modules/complex_double_vector.py +25 -0
  543. sage/modules/diamond_cutting.py +380 -0
  544. sage/modules/fg_pid/all.py +1 -0
  545. sage/modules/fg_pid/fgp_element.py +456 -0
  546. sage/modules/fg_pid/fgp_module.py +2091 -0
  547. sage/modules/fg_pid/fgp_morphism.py +550 -0
  548. sage/modules/filtered_vector_space.py +1271 -0
  549. sage/modules/finite_submodule_iter.cpython-314-aarch64-linux-musl.so +0 -0
  550. sage/modules/finite_submodule_iter.pxd +27 -0
  551. sage/modules/finite_submodule_iter.pyx +452 -0
  552. sage/modules/fp_graded/all.py +1 -0
  553. sage/modules/fp_graded/element.py +346 -0
  554. sage/modules/fp_graded/free_element.py +298 -0
  555. sage/modules/fp_graded/free_homspace.py +53 -0
  556. sage/modules/fp_graded/free_module.py +1060 -0
  557. sage/modules/fp_graded/free_morphism.py +217 -0
  558. sage/modules/fp_graded/homspace.py +563 -0
  559. sage/modules/fp_graded/module.py +1340 -0
  560. sage/modules/fp_graded/morphism.py +1990 -0
  561. sage/modules/fp_graded/steenrod/all.py +1 -0
  562. sage/modules/fp_graded/steenrod/homspace.py +65 -0
  563. sage/modules/fp_graded/steenrod/module.py +477 -0
  564. sage/modules/fp_graded/steenrod/morphism.py +404 -0
  565. sage/modules/fp_graded/steenrod/profile.py +241 -0
  566. sage/modules/free_module.py +8447 -0
  567. sage/modules/free_module_element.cpython-314-aarch64-linux-musl.so +0 -0
  568. sage/modules/free_module_element.pxd +22 -0
  569. sage/modules/free_module_element.pyx +5445 -0
  570. sage/modules/free_module_homspace.py +369 -0
  571. sage/modules/free_module_integer.py +896 -0
  572. sage/modules/free_module_morphism.py +823 -0
  573. sage/modules/free_module_pseudohomspace.py +352 -0
  574. sage/modules/free_module_pseudomorphism.py +578 -0
  575. sage/modules/free_quadratic_module.py +1706 -0
  576. sage/modules/free_quadratic_module_integer_symmetric.py +1790 -0
  577. sage/modules/matrix_morphism.py +1745 -0
  578. sage/modules/misc.py +103 -0
  579. sage/modules/module_functors.py +192 -0
  580. sage/modules/multi_filtered_vector_space.py +719 -0
  581. sage/modules/ore_module.py +2208 -0
  582. sage/modules/ore_module_element.py +178 -0
  583. sage/modules/ore_module_homspace.py +147 -0
  584. sage/modules/ore_module_morphism.py +968 -0
  585. sage/modules/quotient_module.py +699 -0
  586. sage/modules/real_double_vector.py +22 -0
  587. sage/modules/submodule.py +255 -0
  588. sage/modules/tensor_operations.py +567 -0
  589. sage/modules/torsion_quadratic_module.py +1352 -0
  590. sage/modules/tutorial_free_modules.py +248 -0
  591. sage/modules/vector_complex_double_dense.cpython-314-aarch64-linux-musl.so +0 -0
  592. sage/modules/vector_complex_double_dense.pxd +6 -0
  593. sage/modules/vector_complex_double_dense.pyx +117 -0
  594. sage/modules/vector_double_dense.cpython-314-aarch64-linux-musl.so +0 -0
  595. sage/modules/vector_double_dense.pxd +6 -0
  596. sage/modules/vector_double_dense.pyx +604 -0
  597. sage/modules/vector_integer_dense.cpython-314-aarch64-linux-musl.so +0 -0
  598. sage/modules/vector_integer_dense.pxd +15 -0
  599. sage/modules/vector_integer_dense.pyx +361 -0
  600. sage/modules/vector_integer_sparse.cpython-314-aarch64-linux-musl.so +0 -0
  601. sage/modules/vector_integer_sparse.pxd +29 -0
  602. sage/modules/vector_integer_sparse.pyx +406 -0
  603. sage/modules/vector_modn_dense.cpython-314-aarch64-linux-musl.so +0 -0
  604. sage/modules/vector_modn_dense.pxd +12 -0
  605. sage/modules/vector_modn_dense.pyx +394 -0
  606. sage/modules/vector_modn_sparse.cpython-314-aarch64-linux-musl.so +0 -0
  607. sage/modules/vector_modn_sparse.pxd +21 -0
  608. sage/modules/vector_modn_sparse.pyx +298 -0
  609. sage/modules/vector_numpy_dense.cpython-314-aarch64-linux-musl.so +0 -0
  610. sage/modules/vector_numpy_dense.pxd +15 -0
  611. sage/modules/vector_numpy_dense.pyx +304 -0
  612. sage/modules/vector_numpy_integer_dense.cpython-314-aarch64-linux-musl.so +0 -0
  613. sage/modules/vector_numpy_integer_dense.pxd +7 -0
  614. sage/modules/vector_numpy_integer_dense.pyx +54 -0
  615. sage/modules/vector_rational_dense.cpython-314-aarch64-linux-musl.so +0 -0
  616. sage/modules/vector_rational_dense.pxd +15 -0
  617. sage/modules/vector_rational_dense.pyx +387 -0
  618. sage/modules/vector_rational_sparse.cpython-314-aarch64-linux-musl.so +0 -0
  619. sage/modules/vector_rational_sparse.pxd +30 -0
  620. sage/modules/vector_rational_sparse.pyx +413 -0
  621. sage/modules/vector_real_double_dense.cpython-314-aarch64-linux-musl.so +0 -0
  622. sage/modules/vector_real_double_dense.pxd +6 -0
  623. sage/modules/vector_real_double_dense.pyx +126 -0
  624. sage/modules/vector_space_homspace.py +430 -0
  625. sage/modules/vector_space_morphism.py +989 -0
  626. sage/modules/with_basis/all.py +15 -0
  627. sage/modules/with_basis/cell_module.py +494 -0
  628. sage/modules/with_basis/indexed_element.cpython-314-aarch64-linux-musl.so +0 -0
  629. sage/modules/with_basis/indexed_element.pxd +13 -0
  630. sage/modules/with_basis/indexed_element.pyx +1058 -0
  631. sage/modules/with_basis/invariant.py +1075 -0
  632. sage/modules/with_basis/morphism.py +1636 -0
  633. sage/modules/with_basis/representation.py +2939 -0
  634. sage/modules/with_basis/subquotient.py +685 -0
  635. sage/numerical/all__sagemath_modules.py +6 -0
  636. sage/numerical/gauss_legendre.cpython-314-aarch64-linux-musl.so +0 -0
  637. sage/numerical/gauss_legendre.pyx +381 -0
  638. sage/numerical/optimize.py +910 -0
  639. sage/probability/all.py +10 -0
  640. sage/probability/probability_distribution.cpython-314-aarch64-linux-musl.so +0 -0
  641. sage/probability/probability_distribution.pyx +1242 -0
  642. sage/probability/random_variable.py +411 -0
  643. sage/quadratic_forms/all.py +4 -0
  644. sage/quadratic_forms/all__sagemath_modules.py +15 -0
  645. sage/quadratic_forms/binary_qf.py +2042 -0
  646. sage/quadratic_forms/bqf_class_group.py +748 -0
  647. sage/quadratic_forms/constructions.py +93 -0
  648. sage/quadratic_forms/count_local_2.cpython-314-aarch64-linux-musl.so +0 -0
  649. sage/quadratic_forms/count_local_2.pyx +365 -0
  650. sage/quadratic_forms/extras.py +195 -0
  651. sage/quadratic_forms/quadratic_form.py +1753 -0
  652. sage/quadratic_forms/quadratic_form__count_local_2.py +221 -0
  653. sage/quadratic_forms/quadratic_form__equivalence_testing.py +708 -0
  654. sage/quadratic_forms/quadratic_form__evaluate.cpython-314-aarch64-linux-musl.so +0 -0
  655. sage/quadratic_forms/quadratic_form__evaluate.pyx +139 -0
  656. sage/quadratic_forms/quadratic_form__local_density_congruence.py +977 -0
  657. sage/quadratic_forms/quadratic_form__local_field_invariants.py +1072 -0
  658. sage/quadratic_forms/quadratic_form__neighbors.py +424 -0
  659. sage/quadratic_forms/quadratic_form__reduction_theory.py +488 -0
  660. sage/quadratic_forms/quadratic_form__split_local_covering.py +416 -0
  661. sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +657 -0
  662. sage/quadratic_forms/quadratic_form__theta.py +352 -0
  663. sage/quadratic_forms/quadratic_form__variable_substitutions.py +370 -0
  664. sage/quadratic_forms/random_quadraticform.py +209 -0
  665. sage/quadratic_forms/ternary.cpython-314-aarch64-linux-musl.so +0 -0
  666. sage/quadratic_forms/ternary.pyx +1154 -0
  667. sage/quadratic_forms/ternary_qf.py +2027 -0
  668. sage/rings/all__sagemath_modules.py +28 -0
  669. sage/rings/asymptotic/all__sagemath_modules.py +1 -0
  670. sage/rings/asymptotic/misc.py +1252 -0
  671. sage/rings/cc.py +4 -0
  672. sage/rings/cfinite_sequence.py +1306 -0
  673. sage/rings/complex_conversion.cpython-314-aarch64-linux-musl.so +0 -0
  674. sage/rings/complex_conversion.pxd +8 -0
  675. sage/rings/complex_conversion.pyx +23 -0
  676. sage/rings/complex_double.cpython-314-aarch64-linux-musl.so +0 -0
  677. sage/rings/complex_double.pxd +21 -0
  678. sage/rings/complex_double.pyx +2654 -0
  679. sage/rings/complex_mpc.cpython-314-aarch64-linux-musl.so +0 -0
  680. sage/rings/complex_mpc.pxd +21 -0
  681. sage/rings/complex_mpc.pyx +2576 -0
  682. sage/rings/complex_mpfr.cpython-314-aarch64-linux-musl.so +0 -0
  683. sage/rings/complex_mpfr.pxd +18 -0
  684. sage/rings/complex_mpfr.pyx +3602 -0
  685. sage/rings/derivation.py +2334 -0
  686. sage/rings/finite_rings/all__sagemath_modules.py +1 -0
  687. sage/rings/finite_rings/maps_finite_field.py +191 -0
  688. sage/rings/function_field/all__sagemath_modules.py +8 -0
  689. sage/rings/function_field/derivations.py +102 -0
  690. sage/rings/function_field/derivations_rational.py +132 -0
  691. sage/rings/function_field/differential.py +853 -0
  692. sage/rings/function_field/divisor.py +1107 -0
  693. sage/rings/function_field/drinfeld_modules/action.py +199 -0
  694. sage/rings/function_field/drinfeld_modules/all.py +1 -0
  695. sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +673 -0
  696. sage/rings/function_field/drinfeld_modules/drinfeld_module.py +2087 -0
  697. sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +1131 -0
  698. sage/rings/function_field/drinfeld_modules/homset.py +420 -0
  699. sage/rings/function_field/drinfeld_modules/morphism.py +820 -0
  700. sage/rings/function_field/hermite_form_polynomial.cpython-314-aarch64-linux-musl.so +0 -0
  701. sage/rings/function_field/hermite_form_polynomial.pyx +188 -0
  702. sage/rings/function_field/khuri_makdisi.cpython-314-aarch64-linux-musl.so +0 -0
  703. sage/rings/function_field/khuri_makdisi.pyx +935 -0
  704. sage/rings/invariants/all.py +4 -0
  705. sage/rings/invariants/invariant_theory.py +4597 -0
  706. sage/rings/invariants/reconstruction.py +395 -0
  707. sage/rings/polynomial/all__sagemath_modules.py +17 -0
  708. sage/rings/polynomial/integer_valued_polynomials.py +1230 -0
  709. sage/rings/polynomial/laurent_polynomial_mpair.cpython-314-aarch64-linux-musl.so +0 -0
  710. sage/rings/polynomial/laurent_polynomial_mpair.pxd +15 -0
  711. sage/rings/polynomial/laurent_polynomial_mpair.pyx +2023 -0
  712. sage/rings/polynomial/ore_function_element.py +952 -0
  713. sage/rings/polynomial/ore_function_field.py +1028 -0
  714. sage/rings/polynomial/ore_polynomial_element.cpython-314-aarch64-linux-musl.so +0 -0
  715. sage/rings/polynomial/ore_polynomial_element.pxd +48 -0
  716. sage/rings/polynomial/ore_polynomial_element.pyx +3145 -0
  717. sage/rings/polynomial/ore_polynomial_ring.py +1334 -0
  718. sage/rings/polynomial/polynomial_real_mpfr_dense.cpython-314-aarch64-linux-musl.so +0 -0
  719. sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +788 -0
  720. sage/rings/polynomial/q_integer_valued_polynomials.py +1264 -0
  721. sage/rings/polynomial/skew_polynomial_element.cpython-314-aarch64-linux-musl.so +0 -0
  722. sage/rings/polynomial/skew_polynomial_element.pxd +9 -0
  723. sage/rings/polynomial/skew_polynomial_element.pyx +684 -0
  724. sage/rings/polynomial/skew_polynomial_finite_field.cpython-314-aarch64-linux-musl.so +0 -0
  725. sage/rings/polynomial/skew_polynomial_finite_field.pxd +19 -0
  726. sage/rings/polynomial/skew_polynomial_finite_field.pyx +1093 -0
  727. sage/rings/polynomial/skew_polynomial_finite_order.cpython-314-aarch64-linux-musl.so +0 -0
  728. sage/rings/polynomial/skew_polynomial_finite_order.pxd +10 -0
  729. sage/rings/polynomial/skew_polynomial_finite_order.pyx +567 -0
  730. sage/rings/polynomial/skew_polynomial_ring.py +908 -0
  731. sage/rings/real_double_element_gsl.cpython-314-aarch64-linux-musl.so +0 -0
  732. sage/rings/real_double_element_gsl.pxd +8 -0
  733. sage/rings/real_double_element_gsl.pyx +794 -0
  734. sage/rings/real_field.py +58 -0
  735. sage/rings/real_mpfr.cpython-314-aarch64-linux-musl.so +0 -0
  736. sage/rings/real_mpfr.pxd +29 -0
  737. sage/rings/real_mpfr.pyx +6122 -0
  738. sage/rings/ring_extension.cpython-314-aarch64-linux-musl.so +0 -0
  739. sage/rings/ring_extension.pxd +42 -0
  740. sage/rings/ring_extension.pyx +2779 -0
  741. sage/rings/ring_extension_conversion.cpython-314-aarch64-linux-musl.so +0 -0
  742. sage/rings/ring_extension_conversion.pxd +16 -0
  743. sage/rings/ring_extension_conversion.pyx +462 -0
  744. sage/rings/ring_extension_element.cpython-314-aarch64-linux-musl.so +0 -0
  745. sage/rings/ring_extension_element.pxd +21 -0
  746. sage/rings/ring_extension_element.pyx +1635 -0
  747. sage/rings/ring_extension_homset.py +64 -0
  748. sage/rings/ring_extension_morphism.cpython-314-aarch64-linux-musl.so +0 -0
  749. sage/rings/ring_extension_morphism.pxd +35 -0
  750. sage/rings/ring_extension_morphism.pyx +920 -0
  751. sage/schemes/all__sagemath_modules.py +1 -0
  752. sage/schemes/projective/all__sagemath_modules.py +1 -0
  753. sage/schemes/projective/coherent_sheaf.py +300 -0
  754. sage/schemes/projective/cohomology.py +510 -0
  755. sage/stats/all.py +15 -0
  756. sage/stats/basic_stats.py +489 -0
  757. sage/stats/distributions/all.py +7 -0
  758. sage/stats/distributions/catalog.py +34 -0
  759. sage/stats/distributions/dgs.h +50 -0
  760. sage/stats/distributions/dgs.pxd +111 -0
  761. sage/stats/distributions/dgs_bern.h +400 -0
  762. sage/stats/distributions/dgs_gauss.h +614 -0
  763. sage/stats/distributions/dgs_misc.h +104 -0
  764. sage/stats/distributions/discrete_gaussian_integer.cpython-314-aarch64-linux-musl.so +0 -0
  765. sage/stats/distributions/discrete_gaussian_integer.pxd +14 -0
  766. sage/stats/distributions/discrete_gaussian_integer.pyx +498 -0
  767. sage/stats/distributions/discrete_gaussian_lattice.py +908 -0
  768. sage/stats/distributions/discrete_gaussian_polynomial.py +141 -0
  769. sage/stats/hmm/all.py +15 -0
  770. sage/stats/hmm/chmm.cpython-314-aarch64-linux-musl.so +0 -0
  771. sage/stats/hmm/chmm.pyx +1595 -0
  772. sage/stats/hmm/distributions.cpython-314-aarch64-linux-musl.so +0 -0
  773. sage/stats/hmm/distributions.pxd +29 -0
  774. sage/stats/hmm/distributions.pyx +531 -0
  775. sage/stats/hmm/hmm.cpython-314-aarch64-linux-musl.so +0 -0
  776. sage/stats/hmm/hmm.pxd +17 -0
  777. sage/stats/hmm/hmm.pyx +1388 -0
  778. sage/stats/hmm/util.cpython-314-aarch64-linux-musl.so +0 -0
  779. sage/stats/hmm/util.pxd +7 -0
  780. sage/stats/hmm/util.pyx +165 -0
  781. sage/stats/intlist.cpython-314-aarch64-linux-musl.so +0 -0
  782. sage/stats/intlist.pxd +14 -0
  783. sage/stats/intlist.pyx +588 -0
  784. sage/stats/r.py +49 -0
  785. sage/stats/time_series.cpython-314-aarch64-linux-musl.so +0 -0
  786. sage/stats/time_series.pxd +6 -0
  787. sage/stats/time_series.pyx +2546 -0
  788. sage/tensor/all.py +2 -0
  789. sage/tensor/modules/all.py +8 -0
  790. sage/tensor/modules/alternating_contr_tensor.py +761 -0
  791. sage/tensor/modules/comp.py +5598 -0
  792. sage/tensor/modules/ext_pow_free_module.py +824 -0
  793. sage/tensor/modules/finite_rank_free_module.py +3589 -0
  794. sage/tensor/modules/format_utilities.py +333 -0
  795. sage/tensor/modules/free_module_alt_form.py +858 -0
  796. sage/tensor/modules/free_module_automorphism.py +1207 -0
  797. sage/tensor/modules/free_module_basis.py +1074 -0
  798. sage/tensor/modules/free_module_element.py +284 -0
  799. sage/tensor/modules/free_module_homset.py +652 -0
  800. sage/tensor/modules/free_module_linear_group.py +564 -0
  801. sage/tensor/modules/free_module_morphism.py +1581 -0
  802. sage/tensor/modules/free_module_tensor.py +3289 -0
  803. sage/tensor/modules/reflexive_module.py +386 -0
  804. sage/tensor/modules/tensor_free_module.py +780 -0
  805. sage/tensor/modules/tensor_free_submodule.py +538 -0
  806. sage/tensor/modules/tensor_free_submodule_basis.py +140 -0
  807. sage/tensor/modules/tensor_with_indices.py +1043 -0
@@ -0,0 +1,2345 @@
1
+ # sage_setup: distribution = sagemath-modules
2
+ # sage.doctest: needs sage.modules sage.rings.finite_rings
3
+ r"""
4
+ Rijndael-GF
5
+
6
+ Rijndael-GF is an algebraic implementation of the AES cipher which seeks to
7
+ provide a fully generalized algebraic representation of both the whole AES
8
+ cipher as well as its individual components.
9
+
10
+ This class is an algebraic implementation of the Rijndael-GF extension of the
11
+ AES cipher, as described in [DR2002]_. The AES cipher itself is defined to
12
+ operate on a state in `(\GF{2})^{8 n_t}` where
13
+ `n_t \in \{16, 20, 24, 28, 32\}`. Rijndael-GF is a generalization of AES which
14
+ allows for operations in `(\GF{2^8})^{n_t}`, enabling more algebraically
15
+ sophisticated study of AES and its variants. This implementation of
16
+ Rijndael-GF is suitable for learning purposes, for comparison to other
17
+ algebraic ciphers, and for studying various techniques of algebraic
18
+ cryptanalysis of AES. This cipher is different from
19
+ :mod:`Mini-AES <sage.crypto.block_cipher.miniaes>`, which is a
20
+ teaching tool for beginners to understand the basic structure of AES.
21
+
22
+ An algebraic implementation of Rijndael-GF is achieved by recognizing that
23
+ for each round component function `\phi` of AES (SubBytes, ShiftRows, etc.)
24
+ operating on state matrices, every entry of the output matrix `B = \phi(A)` is
25
+ representable as a polynomial with variables being the entries of the input
26
+ state matrix `A`. Correspondingly, this implementation of Rijndael-GF provides
27
+ a ``RijndaelGF.Round_Component_Poly_Constr`` class which allows for creation
28
+ of these such polynomials. For each round component function `\phi` of
29
+ Rijndael-GF there exists a ``Round_Component_Poly_Constr`` object with a
30
+ ``__call__`` method of the form ``__call__(i, j)`` which returns a polynomial
31
+ representing `\phi(A)_{i,j}` in terms of the entries of `A`.
32
+ There additionally are various methods provided which allow for easy polynomial
33
+ evaluation and for simple creation of ``Round_Component_Poly_Constr`` objects
34
+ representing more complex aspects of the cipher.
35
+
36
+ This approach to implementing Rijndael-GF bears some similarity to the
37
+ multivariate quadratic (MQ) systems utilized in :mod:`SR <sage.crypto.mq.sr>`,
38
+ in that the MQ systems also seek to describe the AES cipher as a system of
39
+ algebraic equations. Despite this initial similarity though, Rijndael-GF and
40
+ :mod:`SR <sage.crypto.mq.sr>` are quite different as this implementation
41
+ seeks to provide a fully generalized algebraic representation of both the
42
+ whole AES cipher as well as its individual components, while
43
+ :mod:`SR <sage.crypto.mq.sr>` is instead a family of parameterizable variants
44
+ of the AES suitable as a framework for comparing different cryptanalytic
45
+ techniques that can be brought to bear on the AES.
46
+
47
+ AUTHORS:
48
+
49
+ - Thomas Gagne (2015-06): initial version
50
+
51
+ EXAMPLES:
52
+
53
+ We build Rijndael-GF with a block length of 4 and a key length of 6::
54
+
55
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
56
+ sage: rgf = RijndaelGF(4, 6)
57
+
58
+ We can encrypt plaintexts and decrypt and ciphertexts by calling the
59
+ ``encrypt`` and ``decrypt`` methods or by calling the Rijndael-GF object
60
+ explicitly. Note that the default input format is a hex string. ::
61
+
62
+ sage: plaintext = '00112233445566778899aabbccddeeff'
63
+ sage: key = '000102030405060708090a0b0c0d0e0f1011121314151617'
64
+ sage: rgf.encrypt(plaintext, key)
65
+ 'dda97ca4864cdfe06eaf70a0ec0d7191'
66
+ sage: rgf.decrypt('dda97ca4864cdfe06eaf70a0ec0d7191', key)
67
+ '00112233445566778899aabbccddeeff'
68
+
69
+ We can also use binary strings as input and output. ::
70
+
71
+ sage: plain = '11101011100111110000000111001100' * 4
72
+ sage: key = '01100010111101101000110010111010' * 6
73
+ sage: ciphertext = rgf(plain, key, format='binary')
74
+ sage: ciphertext
75
+ '11010011000010011010110001000011101110110100110100110010011011111100011011100111110011100111010011001110110100011100000011111011'
76
+ sage: rgf(ciphertext, key, algorithm='decrypt', format='binary') == plain
77
+ True
78
+
79
+ [DR2002]_ demonstrates an example of encryption which takes the plaintext
80
+ '3243f6a8885a308d313198a2e0370734' and the key
81
+ '2b7e151628aed2a6abf7158809cf4f3c' and returns the ciphertext
82
+ '3902dc1925dc116a8409850b1dfb9732'. We can use this example to demonstrate
83
+ the correctness of this implementation::
84
+
85
+ sage: rgf = RijndaelGF(4, 4) # change dimensions for this example
86
+ sage: plain = '3243f6a8885a308d313198a2e0370734'
87
+ sage: key = '2b7e151628aed2a6abf7158809cf4f3c'
88
+ sage: expected_ciphertext = '3925841d02dc09fbdc118597196a0b32'
89
+ sage: rgf.encrypt(plain, key) == expected_ciphertext
90
+ True
91
+
92
+ ::
93
+
94
+ sage: rgf = RijndaelGF(4, 6) # revert to previous dimensions
95
+
96
+ To build polynomials representing entries of the output matrix `B = \phi(A)`
97
+ for any round component function `\phi`, each of the round component functions
98
+ (SubBytes, ShiftRows, and MixColumns) have a ``Round_Component_Poly_Constr``
99
+ object associated with it for building polynomials. These objects can be
100
+ accessed by calling their getter functions: ``rgf.sub_bytes_poly()``,
101
+ ``rgf.shift_rows_poly()``, and ``rgf.mix_columns_poly()``. Each returned
102
+ object has a ``__call__`` method which takes an index ``i,j`` and an
103
+ ``algorithm`` flag ('encrypt' or 'decrypt') and returns a polynomial
104
+ representing `\phi(A)_{i,j}` in terms of the entries of `A`, where `A` is an
105
+ arbitrary state matrix and `\phi` is the round component function associated
106
+ with that particular ``Round_Component_Poly_Constr`` object. Some of these
107
+ objects' ``__call__`` methods also have additional keywords to modify their
108
+ behavior, and so we describe the usage of each object below.
109
+
110
+ ``rgf.shift_rows_poly()`` and ``rgf.mix_columns_poly()`` do not have any
111
+ additional keywords for their ``__call__`` methods and we can call them as
112
+ such::
113
+
114
+ sage: sr_pc = rgf.shift_rows_poly_constr()
115
+ sage: sr_pc(1, 2)
116
+ a13
117
+ sage: sr_pc(2, 3, algorithm='decrypt')
118
+ a21
119
+
120
+ ::
121
+
122
+ sage: mc_pc = rgf.mix_columns_poly_constr()
123
+ sage: mc_pc(1, 2)
124
+ a02 + x*a12 + (x + 1)*a22 + a32
125
+ sage: mc_pc(2, 3, algorithm='decrypt')
126
+ (x^3 + x^2 + 1)*a03 + (x^3 + 1)*a13 + (x^3 + x^2 + x)*a23 + (x^3 + x + 1)*a33
127
+
128
+ ``rgf.sub_bytes_poly()`` has a single keyword ``no_inversion=False``, which
129
+ when set to ``True`` returns only the affine transformation step of SubBytes.
130
+ Below describes the usage of ``rgf.sub_bytes_poly()`` ::
131
+
132
+ sage: sb_pc = rgf.sub_bytes_poly_constr()
133
+ sage: sb_pc(1, 2)
134
+ (x^2 + 1)*a12^254 +
135
+ (x^3 + 1)*a12^253 +
136
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a12^251 +
137
+ (x^5 + x^2 + 1)*a12^247 +
138
+ (x^7 + x^6 + x^5 + x^4 + x^2)*a12^239 +
139
+ a12^223 +
140
+ (x^7 + x^5 + x^4 + x^2 + 1)*a12^191 +
141
+ (x^7 + x^3 + x^2 + x + 1)*a12^127 +
142
+ (x^6 + x^5 + x + 1)
143
+ sage: sb_pc(2, 3, no_inversion=True)
144
+ (x^7 + x^3 + x^2 + x + 1)*a23^128 +
145
+ (x^7 + x^5 + x^4 + x^2 + 1)*a23^64 +
146
+ a23^32 +
147
+ (x^7 + x^6 + x^5 + x^4 + x^2)*a23^16 +
148
+ (x^5 + x^2 + 1)*a23^8 +
149
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a23^4 +
150
+ (x^3 + 1)*a23^2 +
151
+ (x^2 + 1)*a23 +
152
+ (x^6 + x^5 + x + 1)
153
+
154
+ Because of the order of the affine transformation and the inversion step in
155
+ SubBytes, calling ``rgf.sub_bytes_poly()(i, j, algorithm='decrypt')`` results
156
+ in a polynomial with thousands of terms which takes a very long time to
157
+ compute. Hence, when using the decryption version of ``rgf.sub_bytes_poly()``
158
+ with the intention of evaluating the polynomials it constructs, it is
159
+ recommended to first call ``rgf.sub_bytes_poly()(i, j, algorithm='decrypt',
160
+ no_inversion=True)`` to get a polynomial representing only the inverse affine
161
+ transformation, evaluate this polynomial for a particular input block, then
162
+ finally perform the inversion step after the affine transformation polynomial
163
+ has been evaluated. ::
164
+
165
+ sage: inv_affine = sb_pc(1, 2, algorithm='decrypt', no_inversion=True)
166
+ sage: state = rgf._hex_to_GF('ff87968431d86a51645151fa773ad009')
167
+ sage: evaluated = inv_affine(state.list())
168
+ sage: result = evaluated * -1
169
+ sage: rgf._GF_to_hex(result)
170
+ '79'
171
+
172
+ We can see how the variables of these polynomials are organized in `A`::
173
+
174
+ sage: rgf.state_vrs
175
+ [a00 a01 a02 a03]
176
+ [a10 a11 a12 a13]
177
+ [a20 a21 a22 a23]
178
+ [a30 a31 a32 a33]
179
+
180
+ The final ``Round_Component_Poly_Constr`` object we have not discussed yet is
181
+ ``add_round_key_poly``, which corresponds to the AddRoundKey round component
182
+ function. This object differs from the other ``Round_Component_Poly_Constr``
183
+ objects in that it returns polynomials with variables being entries of an
184
+ input state `A` as well as entries of various subkeys. Since there are `N_r`
185
+ subkeys to choose from, ``add_round_key_poly`` has a keyword of ``round=0`` to
186
+ select which subkey to use variables from. ::
187
+
188
+ sage: ark_pc = rgf.add_round_key_poly_constr()
189
+ sage: ark_pc(1, 2)
190
+ a12 + k012
191
+ sage: ark_pc(1, 2, algorithm='decrypt')
192
+ a12 + k012
193
+ sage: ark_pc(2, 3, round=7)
194
+ a23 + k723
195
+
196
+ We can see how key variables are organized in the original key (the key used
197
+ to build the rest of the subkeys) below. Note that because key variables are
198
+ subkey entries, if the key length is longer than the block length we will have
199
+ entries from multiple subkeys in the original key matrix. ::
200
+
201
+ sage: rgf.key_vrs
202
+ [k000 k001 k002 k003 k100 k101]
203
+ [k010 k011 k012 k013 k110 k111]
204
+ [k020 k021 k022 k023 k120 k121]
205
+ [k030 k031 k032 k033 k130 k131]
206
+
207
+ We can evaluate any of these constructed polynomials for a particular input
208
+ state (in essence, calculate `\phi(A)_{i,j}`) as such::
209
+
210
+ sage: rgf = RijndaelGF(4, 6)
211
+ sage: state = rgf._hex_to_GF('fe7b5170fe7c8e93477f7e4bf6b98071')
212
+ sage: poly = mc_pc(3, 2, algorithm='decrypt')
213
+ sage: poly(state.list())
214
+ x^7 + x^6 + x^5 + x^2 + x
215
+
216
+ We can use the ``apply_poly`` method to build a matrix whose `i,j` th
217
+ entry equals the polynomial ``phi_poly(i, j)`` evaluated for a particular input
218
+ state, where ``phi_poly`` is the ``Round_Component_Poly_Constr`` object
219
+ associated with the round component function `\phi`. Essentially,
220
+ ``apply_poly`` calculates `\phi(A)`, where `A` is our input state.
221
+ Calling ``apply_poly`` is equivalent to applying the round component function
222
+ associated this ``Round_Component_Poly_Constr`` object to `A`. ::
223
+
224
+ sage: state = rgf._hex_to_GF('c4cedcabe694694e4b23bfdd6fb522fa')
225
+ sage: result = rgf.apply_poly(state, rgf.sub_bytes_poly_constr())
226
+ sage: rgf._GF_to_hex(result)
227
+ '1c8b86628e22f92fb32608c1a8d5932d'
228
+ sage: result == rgf.sub_bytes(state)
229
+ True
230
+
231
+ Alternatively, we can pass a matrix of polynomials as input to ``apply_poly``,
232
+ which will then return another matrix of polynomials. For example,
233
+ ``rgf.state_vrs`` can be used as input to make each ``i,j`` th entry of the
234
+ output matrix equal ``phi_poly_constr(i, j)``, where ``phi_poly_constr`` is
235
+ our inputted ``Round_Component_Poly_Constr`` object. This matrix can then be
236
+ passed through again and so on, demonstrating how one could potentially build
237
+ a matrix of polynomials representing the entire cipher. ::
238
+
239
+ sage: state = rgf.apply_poly(rgf.state_vrs, rgf.shift_rows_poly_constr())
240
+ sage: state
241
+ [a00 a01 a02 a03]
242
+ [a11 a12 a13 a10]
243
+ [a22 a23 a20 a21]
244
+ [a33 a30 a31 a32]
245
+ sage: rgf.apply_poly(state, rgf.add_round_key_poly_constr())
246
+ [a00 + k000 a01 + k001 a02 + k002 a03 + k003]
247
+ [a11 + k010 a12 + k011 a13 + k012 a10 + k013]
248
+ [a22 + k020 a23 + k021 a20 + k022 a21 + k023]
249
+ [a33 + k030 a30 + k031 a31 + k032 a32 + k033]
250
+
251
+ For any of these ``Round_Component_Poly_Constr`` objects, we can change the
252
+ keywords of its ``__call__`` method when ``apply_poly`` invokes it by passing
253
+ ``apply_poly`` a dictionary mapping keywords to their values. ::
254
+
255
+ sage: rgf.apply_poly(rgf.state_vrs, rgf.add_round_key_poly_constr(),
256
+ ....: poly_constr_attr={'round': 5})
257
+ [a00 + k500 a01 + k501 a02 + k502 a03 + k503]
258
+ [a10 + k510 a11 + k511 a12 + k512 a13 + k513]
259
+ [a20 + k520 a21 + k521 a22 + k522 a23 + k523]
260
+ [a30 + k530 a31 + k531 a32 + k532 a33 + k533]
261
+
262
+ We can build our own ``Round_Component_Poly_Constr`` objects which correspond
263
+ to the composition of multiple round component functions with the ``compose``
264
+ method. To do this, if we pass two ``Round_Component_Poly_Constr`` objects
265
+ to ``compose`` where the first object corresponds to the round component
266
+ function `f` and the second to the round component function `g`, ``compose``
267
+ will return a new ``Round_Component_Poly_Constr`` object corresponding to the
268
+ function `g \circ f`. This returned ``Round_Component_Poly_Constr`` object
269
+ will have the arguments of ``__call__(row, col, algorithm='encrypt')`` and
270
+ when passed an index ``i,j`` will return `g(f(A))_{i,j}` in terms of the
271
+ entries of `A`. ::
272
+
273
+ sage: # needs sage.libs.gap
274
+ sage: rcpc = rgf.compose(rgf.shift_rows_poly_constr(),
275
+ ....: rgf.mix_columns_poly_constr()); rcpc
276
+ A polynomial constructor of a round component of Rijndael-GF block cipher
277
+ with block length 4, key length 6, and 12 rounds.
278
+ sage: rcpc(2, 1)
279
+ a01 + a12 + x*a23 + (x + 1)*a30
280
+ <BLANKLINE>
281
+ sage: state = rgf._hex_to_GF('afb73eeb1cd1b85162280f27fb20d585')
282
+ sage: result = rgf.apply_poly(state, rcpc)
283
+ sage: new_state = rgf.shift_rows(state)
284
+ sage: new_state = rgf.mix_columns(new_state)
285
+ sage: result == new_state
286
+ True
287
+ <BLANKLINE>
288
+ sage: rcpc = rgf.compose(rgf.mix_columns_poly_constr(),
289
+ ....: rgf.shift_rows_poly_constr())
290
+ sage: result = rgf.apply_poly(state, rcpc, algorithm='decrypt')
291
+ sage: new_state = rgf.mix_columns(state, algorithm='decrypt')
292
+ sage: new_state = rgf.shift_rows(new_state, algorithm='decrypt')
293
+ sage: new_state == result
294
+ True
295
+
296
+ Alternatively, we can use ``compose`` to build the polynomial output of
297
+ a ``Round_Component_Poly_Constr`` object corresponding to the composition of
298
+ multiple round functions like above without having to explicitly build our
299
+ own ``Round_Component_Poly_Constr`` object. To do this, we simply make the
300
+ first input a ``Round_Component_Poly_Constr`` object corresponding to a
301
+ round component function `f` and make the second input a polynomial
302
+ representing `g(A)_{i,j}` for a round component function `g`. Given this,
303
+ ``compose`` will return a polynomial representing `g(f(A))_{i,j}` in terms
304
+ of the entries of `A`. ::
305
+
306
+ sage: poly = rgf.mix_columns_poly_constr()(0, 3); poly
307
+ x*a03 + (x + 1)*a13 + a23 + a33
308
+ sage: rgf.compose(rgf.sub_bytes_poly_constr(), poly)
309
+ (x^3 + x)*a03^254 +
310
+ (x^3 + x^2 + x + 1)*a13^254 +
311
+ (x^2 + 1)*a23^254 +
312
+ (x^2 + 1)*a33^254 +
313
+ (x^4 + x)*a03^253 +
314
+ (x^4 + x^3 + x + 1)*a13^253 +
315
+ (x^3 + 1)*a23^253 +
316
+ (x^3 + 1)*a33^253 +
317
+ (x^7 + x^6 + x^5 + x^3 + 1)*a03^251 +
318
+ (x^4)*a13^251 +
319
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a23^251 +
320
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a33^251 +
321
+ (x^6 + x^3 + x)*a03^247 +
322
+ (x^6 + x^5 + x^3 + x^2 + x + 1)*a13^247 +
323
+ (x^5 + x^2 + 1)*a23^247 +
324
+ (x^5 + x^2 + 1)*a33^247 +
325
+ (x^7 + x^6 + x^5 + x^4 + x + 1)*a03^239 +
326
+ (x^2 + x + 1)*a13^239 +
327
+ (x^7 + x^6 + x^5 + x^4 + x^2)*a23^239 +
328
+ (x^7 + x^6 + x^5 + x^4 + x^2)*a33^239 +
329
+ x*a03^223 +
330
+ (x + 1)*a13^223 +
331
+ a23^223 +
332
+ a33^223 +
333
+ (x^6 + x^5 + x^4 + 1)*a03^191 +
334
+ (x^7 + x^6 + x^2)*a13^191 +
335
+ (x^7 + x^5 + x^4 + x^2 + 1)*a23^191 +
336
+ (x^7 + x^5 + x^4 + x^2 + 1)*a33^191 +
337
+ (x^2 + 1)*a03^127 +
338
+ (x^7 + x^3 + x)*a13^127 +
339
+ (x^7 + x^3 + x^2 + x + 1)*a23^127 +
340
+ (x^7 + x^3 + x^2 + x + 1)*a33^127 +
341
+ (x^6 + x^5 + x + 1)
342
+
343
+ If we use ``algorithm='decrypt'`` as an argument to ``compose``, then the
344
+ value of ``algorithm`` will be passed directly to the first argument of
345
+ ``compose`` (a ``Round_Component_Poly_Constr`` object) when it is called,
346
+ provided the second argument is a polynomial. Setting this flag does nothing
347
+ if both arguments are ``Round_Component_Poly_Constr`` objects, since the
348
+ returned ``Round_Component_Poly_Constr`` object's ``__call__`` method must have
349
+ its own ``algorithm`` keyword defaulted to 'encrypt'. ::
350
+
351
+ sage: # needs sage.libs.gap
352
+ sage: poly = rgf.shift_rows_poly_constr()(2, 1)
353
+ sage: rgf.compose(rgf.mix_columns_poly_constr(), poly, algorithm='decrypt')
354
+ (x^3 + x^2 + 1)*a03 + (x^3 + 1)*a13 + (x^3 + x^2 + x)*a23 + (x^3 + x + 1)*a33
355
+ <BLANKLINE>
356
+ sage: state = rgf._hex_to_GF('80121e0776fd1d8a8d8c31bc965d1fee')
357
+ sage: with_decrypt = rgf.compose(rgf.sub_bytes_poly_constr(),
358
+ ....: rgf.shift_rows_poly_constr(),
359
+ ....: algorithm='decrypt')
360
+ sage: result_wd = rgf.apply_poly(state, with_decrypt)
361
+ sage: no_decrypt = rgf.compose(rgf.sub_bytes_poly_constr(),
362
+ ....: rgf.shift_rows_poly_constr())
363
+ sage: result_nd = rgf.apply_poly(state, no_decrypt)
364
+ sage: result_wd == result_nd
365
+ True
366
+
367
+ We can also pass keyword dictionaries of ``f_attr`` and ``g_attr`` to
368
+ ``compose`` to make ``f`` and ``g`` use those keywords during polynomial
369
+ creation. ::
370
+
371
+ sage: rcpc = rgf.compose(rgf.add_round_key_poly_constr(), # needs sage.libs.gap
372
+ ....: rgf.add_round_key_poly_constr(),
373
+ ....: f_attr={'round': 4}, g_attr={'round': 7})
374
+ sage: rcpc(1, 2) # needs sage.libs.gap
375
+ a12 + k412 + k712
376
+
377
+ In addition to building polynomial representations of state matrices, we can
378
+ also build polynomial representations of elements of the expanded key with the
379
+ ``expand_key_poly`` method. However, since the key schedule is defined
380
+ recursively, it is impossible to build polynomials for the key schedule in
381
+ the same manner as we do for the round component functions. Consequently,
382
+ ``expand_round_key_poly()`` is not a ``Round_Component_Poly_Constr`` object.
383
+ Instead, ``expand_key_poly`` is a method which takes an index ``i,j`` and a
384
+ round number ``round``, and returns a polynomial representing the `i,j` th
385
+ entry of the ``round`` th round key. This polynomial's variables are entries
386
+ of the original key we built above. ::
387
+
388
+ sage: rgf.expand_key_poly(1, 2, 0)
389
+ k012
390
+ sage: rgf.expand_key_poly(1, 1, 1)
391
+ k111
392
+ sage: rgf.expand_key_poly(1, 2, 1)
393
+ (x^2 + 1)*k121^254 +
394
+ (x^3 + 1)*k121^253 +
395
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*k121^251 +
396
+ (x^5 + x^2 + 1)*k121^247 +
397
+ (x^7 + x^6 + x^5 + x^4 + x^2)*k121^239 +
398
+ k121^223 +
399
+ (x^7 + x^5 + x^4 + x^2 + 1)*k121^191 +
400
+ (x^7 + x^3 + x^2 + x + 1)*k121^127 +
401
+ k010 +
402
+ (x^6 + x^5 + x)
403
+
404
+ Since ``expand_key_poly`` is not actually a
405
+ ``Round_Component_Poly_Constr`` object, we cannot use it as input to
406
+ ``apply_poly`` or ``compose``. ::
407
+
408
+ sage: rgf.apply_poly(state, rgf.expand_key_poly)
409
+ Traceback (most recent call last):
410
+ ...
411
+ TypeError: keyword 'poly_constr' must be a Round_Component_Poly_Constr
412
+ sage: rgf.compose(rgf.expand_key_poly, rgf.sub_bytes_poly_constr())
413
+ Traceback (most recent call last):
414
+ ...
415
+ TypeError: keyword 'f' must be a Round_Component_Poly_Constr
416
+ """
417
+
418
+ # ****************************************************************************
419
+ # Copyright (C) 2015 Thomas Gagne <thomasgagne100@gmail.com>
420
+ #
421
+ # This program is free software: you can redistribute it and/or modify
422
+ # it under the terms of the GNU General Public License as published by
423
+ # the Free Software Foundation, either version 2 of the License, or
424
+ # (at your option) any later version.
425
+ # https://www.gnu.org/licenses/
426
+ # ****************************************************************************
427
+
428
+ from sage.matrix.constructor import matrix
429
+ from sage.matrix.constructor import column_matrix
430
+ from sage.structure.element import Element, Matrix
431
+ from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_base
432
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField
433
+ from sage.structure.sage_object import SageObject
434
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
435
+ from sage.misc.sageinspect import sage_getargspec
436
+
437
+
438
+ class RijndaelGF(SageObject):
439
+
440
+ def __init__(self, Nb, Nk, state_chr='a', key_chr='k'):
441
+ r"""
442
+ An algebraically generalized version of the AES cipher.
443
+
444
+ INPUT:
445
+
446
+ - ``Nb`` -- the block length of this instantiation. Must be between 4
447
+ and 8
448
+
449
+ - ``Nk`` -- the key length of this instantiation. Must be between 4 and 8
450
+
451
+ - ``state_chr`` -- the variable name for polynomials representing
452
+ elements from state matrices
453
+
454
+ - ``key_chr`` -- the variable name for polynomials representing
455
+ elements of the key schedule
456
+
457
+ EXAMPLES::
458
+
459
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
460
+ sage: rgf = RijndaelGF(6, 8)
461
+ sage: rgf
462
+ Rijndael-GF block cipher with block length 6, key length 8, and 14 rounds.
463
+
464
+ By changing ``state_chr`` we can alter the names of variables in
465
+ polynomials representing elements from state matrices. ::
466
+
467
+ sage: rgf = RijndaelGF(4, 6, state_chr='myChr')
468
+ sage: rgf.mix_columns_poly_constr()(3, 2)
469
+ (x + 1)*myChr02 + myChr12 + myChr22 + x*myChr32
470
+
471
+ We can also alter the name of variables in polynomials representing
472
+ elements from round keys by changing ``key_chr``. ::
473
+
474
+ sage: rgf = RijndaelGF(4, 6, key_chr='myKeyChr')
475
+ sage: rgf.expand_key_poly(1, 2, 1)
476
+ (x^2 + 1)*myKeyChr121^254 +
477
+ (x^3 + 1)*myKeyChr121^253 +
478
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*myKeyChr121^251 +
479
+ (x^5 + x^2 + 1)*myKeyChr121^247 +
480
+ (x^7 + x^6 + x^5 + x^4 + x^2)*myKeyChr121^239 +
481
+ myKeyChr121^223 +
482
+ (x^7 + x^5 + x^4 + x^2 + 1)*myKeyChr121^191 +
483
+ (x^7 + x^3 + x^2 + x + 1)*myKeyChr121^127 +
484
+ myKeyChr010 +
485
+ (x^6 + x^5 + x)
486
+ """
487
+ if Nb not in range(4, 9):
488
+ msg = "Block length Nb must be in the range 4 - 8, not {0}"
489
+ raise ValueError(msg.format(Nb))
490
+ if Nk not in range(4, 9):
491
+ msg = "Key length Nk must be in the range 4 - 8, not {0}"
492
+ raise ValueError(msg.format(Nk))
493
+ if not isinstance(state_chr, str):
494
+ msg = "state_chr must be a string, not {0}"
495
+ raise TypeError(msg.format(state_chr))
496
+ if not isinstance(key_chr, str):
497
+ msg = "key_chr must be a string, not {0}"
498
+ raise TypeError(msg.format(key_chr))
499
+
500
+ self._Nb = Nb
501
+ self._Nk = Nk
502
+ round_num_table = matrix([[10,11,12,13,14], [11,11,12,13,14],
503
+ [12,12,12,13,14], [13,13,13,13,14],
504
+ [14,14,14,14,14]])
505
+ self._Nr = round_num_table[self._Nb - 4, self._Nk - 4]
506
+ from sage.rings.polynomial.polynomial_ring import polygen
507
+
508
+ # Build framework for polynomial creation.
509
+ from sage.rings.finite_rings.integer_mod_ring import Integers
510
+ pgen = polygen(Integers(2))
511
+ mod = pgen**8 + pgen**4 + pgen**3 + pgen + 1
512
+ self._F = FiniteField(2**8, 'x', modulus=mod)
513
+ state_names = [state_chr + str(i) + str(j)
514
+ for i in range(4) for j in range(self._Nb)]
515
+ subkey_names = [key_chr + str(r) + str(i) + str(j)
516
+ for r in range(self._Nr + 1) for i in range(4)
517
+ for j in range(self._Nb)]
518
+ self._state_PR = PolynomialRing(self._F, len(state_names), state_names)
519
+ self._all_PR = PolynomialRing(self._F, len(state_names + subkey_names),
520
+ state_names + subkey_names)
521
+ self.state_vrs = matrix(4, self._Nb, self._state_PR.gens())
522
+ fNb = 4 * self._Nb
523
+ self.subkey_vrs_list = list(self._all_PR.gens()[fNb:])
524
+ self.subkey_vrs = [matrix(4, self._Nb,
525
+ self.subkey_vrs_list[fNb * i: fNb * (i + 1)])
526
+ for i in range(self._Nr)]
527
+ self.key_vrs = column_matrix([
528
+ self.subkey_vrs[int(i / self._Nb)].column(i % 4)
529
+ for i in range(self._Nk)])
530
+ self._shiftrows_offsets_E = matrix([[0,1,2,3], [0,1,2,3], [0,1,2,3],
531
+ [0,1,2,4], [0,1,3,4]])
532
+ self._shiftrows_offsets_D = matrix([[0,-1,-2,-3], [0,-1,-2,-3],
533
+ [0,-1,-2,-3], [0,-1,-2,-4],
534
+ [0,-1,-3,-4]])
535
+ self._sb_E_coeffs = [self._F("x^2 + 1"),
536
+ self._F("x^3 + 1"),
537
+ self._F("x^7 + x^6 + x^5 + x^4 + x^3 + 1"),
538
+ self._F("x^5 + x^2 + 1"),
539
+ self._F("x^7 + x^6 + x^5 + x^4 + x^2"),
540
+ self._F("1"),
541
+ self._F("x^7 + x^5 + x^4 + x^2 + 1"),
542
+ self._F("x^7 + x^3 + x^2 + x + 1")]
543
+ self._sb_D_coeffs = [self._F("x^2 + 1"),
544
+ self._F("x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x"),
545
+ self._F("x^6 + x^5 + x^4 + x^3 + x^2 + x + 1"),
546
+ self._F("x^6 + x^4 + x^3 + x"),
547
+ self._F("x^6 + x^5 + x^4 + x^3"),
548
+ self._F("x^6 + x^4 + x^3 + 1"),
549
+ self._F("x^7 + x^6 + x^4 + x^3 + x + 1"),
550
+ self._F("x^6 + x^5 + x^3 + x^2 + x")]
551
+ mixcols_E_row = [self._F('x'), self._F('x+1'), self._F('1'),
552
+ self._F('1')]
553
+ self._mixcols_E = matrix([mixcols_E_row[-i:] + mixcols_E_row[:-i]
554
+ for i in range(4)])
555
+ mixcols_D_row = [self._F('x^3 + x^2 + x'), self._F('x^3 + x + 1'),
556
+ self._F('x^3 + x^2 + 1'), self._F('x^3 + 1')]
557
+ self._mixcols_D = matrix([mixcols_D_row[-i:] + mixcols_D_row[:-i]
558
+ for i in range(4)])
559
+ # Build the Round_Component_Poly_Constr objects
560
+ self._add_round_key_rcpc = \
561
+ RijndaelGF.Round_Component_Poly_Constr(self._add_round_key_pc, self,
562
+ "Add Round Key")
563
+ self._sub_bytes_rcpc = \
564
+ RijndaelGF.Round_Component_Poly_Constr(self._sub_bytes_pc, self,
565
+ "SubBytes")
566
+ self._mix_columns_rcpc = \
567
+ RijndaelGF.Round_Component_Poly_Constr(self._mix_columns_pc, self,
568
+ "Mix Columns")
569
+ self._shift_rows_rcpc = \
570
+ RijndaelGF.Round_Component_Poly_Constr(self._shift_rows_pc, self,
571
+ "Shift Rows")
572
+
573
+ def __call__(self, text, key, algorithm='encrypt', format='hex'):
574
+ r"""
575
+ Return the encryption/decryption of ``text`` with key ``key``.
576
+
577
+ INPUT:
578
+
579
+ - ``text`` -- a plaintext to encrypt or a ciphertext to decrypt
580
+
581
+ - ``key`` -- the key to encrypt/decrypt ``text`` with
582
+
583
+ - ``algorithm`` -- whether to encrypt or decrypt ``text``. Flag for
584
+ encryption is "encrypt", flag for decryption is "decrypt"
585
+
586
+ - ``format`` -- the format of ``text`` and ``key``, either "hex" or
587
+ "binary"
588
+
589
+ OUTPUT: the encrypted or decrypted message ``text`` with key ``key``
590
+
591
+ EXAMPLES::
592
+
593
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
594
+ sage: rgf = RijndaelGF(4, 4)
595
+ sage: text = 'ef053f7c8b3d32fd4d2a64ad3c93071a'
596
+ sage: key = '2d7e86a339d9393ee6570a1101904e16'
597
+ sage: rgf(text, key)
598
+ '84e75b142c8fd5a445312c0a9b2d6699'
599
+ sage: rgf(text, key, algorithm='decrypt')
600
+ '9bf83275406304f050c826ca72d035e6'
601
+
602
+ We can also use binary strings for ``text`` and ``key``. ::
603
+
604
+ sage: text = '11011100011010000011101111011011' * 4
605
+ sage: key = '01000000000011000101101011011110' * 4
606
+ sage: rgf(text, key, format='binary')
607
+ '00011000010110010011100100010111010101001000010010100110101010101111001001100000011111011100100011010001010100110011000111110011'
608
+ sage: rgf(text, key, algorithm='decrypt', format='binary')
609
+ '11000110011001001110000101011101001001010101110001110010000111110000010111111101000011010101101011111100100001010010111000011010'
610
+ """
611
+
612
+ if algorithm == 'encrypt':
613
+ return self.encrypt(text, key, format)
614
+ elif algorithm == 'decrypt':
615
+ return self.decrypt(text, key, format)
616
+ else:
617
+ raise ValueError("keyword 'algorithm' must be either 'encrypt' "
618
+ "or 'decrypt'")
619
+
620
+ def __repr__(self):
621
+ r"""
622
+ Return the string representation of ``self``.
623
+
624
+ EXAMPLES::
625
+
626
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
627
+ sage: rgf = RijndaelGF(5, 8)
628
+ sage: rgf
629
+ Rijndael-GF block cipher with block length 5, key length 8, and 14 rounds.
630
+ """
631
+
632
+ msg = ("Rijndael-GF block cipher with block length {0}, key length "
633
+ "{1}, and {2} rounds.")
634
+ return msg.format(self._Nb, self._Nk, self._Nr)
635
+
636
+ def block_length(self):
637
+ r"""
638
+ Return the block length of this instantiation of Rijndael-GF.
639
+
640
+ EXAMPLES::
641
+
642
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
643
+ sage: rgf = RijndaelGF(4, 6)
644
+ sage: rgf.block_length()
645
+ 4
646
+ """
647
+ return self._Nb
648
+
649
+ def key_length(self):
650
+ r"""
651
+ Return the key length of this instantiation of Rijndael-GF.
652
+
653
+ EXAMPLES::
654
+
655
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
656
+ sage: rgf = RijndaelGF(4, 8)
657
+ sage: rgf.key_length()
658
+ 8
659
+ """
660
+ return self._Nk
661
+
662
+ def number_rounds(self):
663
+ r"""
664
+ Return the number of rounds used in this instantiation of Rijndael-GF.
665
+
666
+ EXAMPLES::
667
+
668
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
669
+ sage: rgf = RijndaelGF(5, 4)
670
+ sage: rgf.number_rounds()
671
+ 11
672
+ """
673
+ return self._Nr
674
+
675
+ def _hex_to_GF(self, H, matrix=True):
676
+ r"""
677
+ Return a matrix/list of elements of `\GF{2^8}` corresponding to ``H``.
678
+
679
+ INPUT:
680
+
681
+ - ``H`` -- a hex string where every two hex characters correspond to a
682
+ single element in `\GF{2^8}`
683
+
684
+ - ``matrix`` -- boolean (default: ``True``); return a list if ``False``.
685
+ Return a state matrix if ``True``
686
+
687
+ OUTPUT:
688
+
689
+ - A list of or a state matrix of elements of `\GF{2^8}` where each
690
+ element corresponds to the appropriate hex value in ``H``. In
691
+ particular, every element `a_7x^7 + a_6x^6 + a_5x^5 + a_4x^4 +
692
+ a_3x^3 + a_2x^2 + a_1x^1 + a_0` in `\GF{2^8}` corresponds to the
693
+ 8-bit binary string '`a_7a_6a_5a_4a_3a_2a_1a_0`'.
694
+
695
+ EXAMPLES::
696
+
697
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
698
+ sage: rgf = RijndaelGF(4, 4)
699
+ sage: state = rgf._hex_to_GF('1147659047cf663b9b0ece8dfc0bf1f0')
700
+ sage: output = rgf.shift_rows(state)
701
+ sage: rgf._GF_to_hex(output)
702
+ '11cfcef0470ef1909b0b653bfc47668d'
703
+
704
+ We can output a list instead by setting ``matrix`` to ``False``. ::
705
+
706
+ sage: rgf._hex_to_GF('2f', matrix=False)
707
+ [x^5 + x^3 + x^2 + x + 1]
708
+ sage: rgf._hex_to_GF('1a2b0f', matrix=False)
709
+ [x^4 + x^3 + x, x^5 + x^3 + x + 1, x^3 + x^2 + x + 1]
710
+ """
711
+ if not isinstance(H, str) or \
712
+ any(c not in '0123456789abcdefABCDEF' for c in H):
713
+ raise TypeError("keyword 'H' must be a hex string")
714
+
715
+ def hx_to_gf(h):
716
+ return self._F([int(_) for _ in bin(int(h, 16))[2:].zfill(8)][::-1])
717
+ hexes = [H[2 * i] + H[2 * i + 1] for i in range(len(H) // 2)]
718
+ result = [hx_to_gf(h) for h in hexes]
719
+ if matrix:
720
+ return column_matrix(len(result) // 4, 4, result)
721
+ else:
722
+ return result
723
+
724
+ def _GF_to_hex(self, GF):
725
+ r"""
726
+ Return the hex string representation of ``GF``.
727
+
728
+ INPUT:
729
+
730
+ - ``GF`` -- either a state matrix over `\GF{2^8}`, a list of elements
731
+ from `\GF{2^8}`, or a single element from `\GF{2^8}`
732
+
733
+ OUTPUT:
734
+
735
+ - A hex string representation of ``GF``, where every two characters in
736
+ the string correspond to a single element in `\GF{2^8}`. In
737
+ particular, every element `a_7x^7 + a_6x^6 + a_5x^5 + a_4x^4 +
738
+ a_3x^3 + a_2x^2 + a_1x^1 + a_0` in `\GF{2^8}` corresponds to the
739
+ 8-bit binary string '`a_7a_6a_5a_4a_3a_2a_1a_0`'.
740
+
741
+ EXAMPLES::
742
+
743
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
744
+ sage: rgf = RijndaelGF(4,4)
745
+ sage: F.<a> = GF(2^8)
746
+ sage: els = [a^7 + a^5 + a + 1, a^7 + a^6 + a^4 + a^2]
747
+ sage: rgf._GF_to_hex(els)
748
+ 'a3d4'
749
+ <BLANKLINE>
750
+ sage: h = '8fb999c973b26839c7f9d89d85c68c72'
751
+ sage: h == rgf._GF_to_hex(rgf._hex_to_GF(h))
752
+ True
753
+
754
+ We can use this to get concise output from round functions. ::
755
+
756
+ sage: plain = rgf._hex_to_GF('72b86c7c0f0d52d3e0d0da104055036b')
757
+ sage: key = rgf._hex_to_GF('93faa123c2903f4743e4dd83431692de')
758
+ sage: output = rgf.add_round_key(plain, key)
759
+ sage: rgf._GF_to_hex(output)
760
+ 'e142cd5fcd9d6d94a3340793034391b5'
761
+ """
762
+ if not isinstance(GF, Matrix) and \
763
+ not isinstance(GF, list) and \
764
+ not (isinstance(GF, Element) and isinstance(GF.parent(), FiniteField_base)):
765
+ msg = ("keyword 'GF' must be a matrix over {0}, a list of "
766
+ "elements from {0}, or a single element from {0}")
767
+ raise TypeError(msg.format(self._F))
768
+
769
+ if isinstance(GF, Matrix):
770
+ if not GF.base_ring().is_field() or \
771
+ not GF.base_ring().is_finite() or \
772
+ not GF.base_ring().order() == 2**8:
773
+ msg = "The elements of keyword 'GF' must all be from {0}"
774
+ raise TypeError(msg.format(self._F))
775
+ return ''.join([self._GF_to_hex(el)
776
+ for col in GF.columns() for el in col])
777
+ elif isinstance(GF, list):
778
+ if not all(g.parent().is_field() and g.parent().is_finite() and
779
+ g.parent().order() == 2**8 for g in GF):
780
+ msg = "The elements of keyword 'GF' must all be from {0}"
781
+ raise TypeError(msg.format(self._F))
782
+ return ''.join([self._GF_to_hex(el) for el in GF])
783
+ else:
784
+ if not GF.parent().is_field() or \
785
+ not GF.parent().is_finite() or \
786
+ not GF.parent().order() == 2**8:
787
+ msg = "keyword 'GF' must be in"
788
+ raise TypeError(msg.format(self._F))
789
+ return hex(GF.to_integer())[2:].zfill(2)
790
+
791
+ def _bin_to_GF(self, B, matrix=True):
792
+ r"""
793
+ Return a matrix/list of elements of `\GF{2^8}` corresponding to ``B``.
794
+
795
+ INPUT:
796
+
797
+ - ``B`` -- a binary string where every eight bits correspond to a
798
+ single element in `\GF{2^8}`
799
+
800
+ - ``matrix`` -- boolean (default: ``True``); return a list if ``False``.
801
+ Return a state matrix over `\GF{2^8}` if ``True``
802
+
803
+ OUTPUT:
804
+
805
+ - A list of or a state matrix of elements of `\GF{2^8}` where each
806
+ element corresponds to the appropriate 8-bit binary string in ``B``.
807
+ In particular, every element `a_7x^7 + a_6x^6 + a_5x^5 + a_4x^4 +
808
+ a_3x^3 + a_2x^2 + a_1x^1 + a_0` in `\GF{2^8}` corresponds to the
809
+ 8-bit binary string '`a_7a_6a_5a_4a_3a_2a_1a_0`'.
810
+
811
+ EXAMPLES::
812
+
813
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
814
+ sage: rgf = RijndaelGF(4, 4)
815
+ sage: bs = '11101011100111110000000111001100' * 4
816
+ sage: len(bs)
817
+ 128
818
+ sage: state = rgf._bin_to_GF(bs)
819
+ sage: output = rgf.sub_bytes(state)
820
+ sage: rgf._GF_to_bin(output)
821
+ '11101001110110110111110001001011111010011101101101111100010010111110100111011011011111000100101111101001110110110111110001001011'
822
+
823
+ We can make this method output a list by setting ``matrix`` to
824
+ ``False``. ::
825
+
826
+ sage: bs = '01010011'
827
+ sage: rgf._bin_to_GF(bs, matrix=False)
828
+ [x^6 + x^4 + x + 1]
829
+ sage: bs = '000100000111001000110101110001101101011100110101'
830
+ sage: rgf._bin_to_GF(bs, matrix=False)
831
+ [x^4,
832
+ x^6 + x^5 + x^4 + x,
833
+ x^5 + x^4 + x^2 + 1,
834
+ x^7 + x^6 + x^2 + x,
835
+ x^7 + x^6 + x^4 + x^2 + x + 1,
836
+ x^5 + x^4 + x^2 + 1]
837
+ """
838
+ if not isinstance(B, str) or any(c not in '01' for c in B):
839
+ raise TypeError("keyword 'B' must be a binary string")
840
+
841
+ def bn_to_gf(b):
842
+ return self._F([int(_) for _ in b[::-1]])
843
+
844
+ bins = [B[8 * i : 8 * (i + 1)] for i in range(len(B) // 8)]
845
+ result = [bn_to_gf(b) for b in bins]
846
+ if matrix:
847
+ return column_matrix(len(result) // 4, 4, result)
848
+ else:
849
+ return result
850
+
851
+ def _GF_to_bin(self, GF):
852
+ r"""
853
+ Return the binary string representation of ``GF``.
854
+
855
+ INPUT:
856
+
857
+ - ``GF`` -- either a state matrix over `\GF{2^8}`, a list of elements
858
+ from `\GF{2^8}`, or a single element from `\GF{2^8}`
859
+
860
+ OUTPUT:
861
+
862
+ - A binary string representation of ``GF``, where every eight
863
+ characters in the string corresponds to a single element in
864
+ `\GF{2^8}`. In particular, every element `a_7x^7 + a_6x^6 + a_5x^5 +
865
+ a_4x^4 + a_3x^3 + a_2x^2 + a_1x^1 + a_0` in `\GF{2^8}` corresponds
866
+ to the binary string '`a_7a_6a_5a_4a_3a_2a_1a_0`'.
867
+
868
+ EXAMPLES::
869
+
870
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
871
+ sage: rgf = RijndaelGF(4, 4)
872
+ sage: F.<a> = GF(2^8)
873
+ sage: els = [a^7 + a^5 + a + 1, a^7 + a^6 + a^4 + a^2]
874
+ sage: rgf._GF_to_bin(els)
875
+ '1010001111010100'
876
+
877
+ We can use this to get clearer output from the round functions. ::
878
+
879
+ sage: plain = '11101011100111110000000111001100' * 4
880
+ sage: plain_state = rgf._bin_to_GF(plain)
881
+ sage: key = '00110011100000001111100111010111' * 4
882
+ sage: key_state = rgf._bin_to_GF(key)
883
+ sage: output = rgf.add_round_key(plain_state, key_state)
884
+ sage: rgf._GF_to_bin(output)
885
+ '11011000000111111111100000011011110110000001111111111000000110111101100000011111111110000001101111011000000111111111100000011011'
886
+ """
887
+ if not isinstance(GF, Matrix) and \
888
+ not isinstance(GF, list) and \
889
+ not (isinstance(GF, Element) and isinstance(GF.parent(), FiniteField_base)):
890
+ msg = ("keyword 'GF' must be a matrix over {0}, a list of "
891
+ "elements from {0}, or a single element from {0}")
892
+ raise TypeError(msg.format(self))
893
+
894
+ if isinstance(GF, Matrix):
895
+ if not GF.base_ring().is_field() or \
896
+ not GF.base_ring().is_finite() or \
897
+ not GF.base_ring().order() == 2**8:
898
+ msg = "The elements of keyword 'GF' must all be from {0}"
899
+ raise TypeError(msg.format(self._F))
900
+ return ''.join([self._GF_to_bin(el)
901
+ for col in GF.columns() for el in col])
902
+ elif isinstance(GF, list):
903
+ if not all(g.parent().is_field() and g.parent().is_finite() and
904
+ g.parent().order() == 2**8 for g in GF):
905
+ msg = "The elements of keyword 'GF' must all be from {0}"
906
+ raise TypeError(msg.format(self._F))
907
+ return ''.join([self._GF_to_bin(el) for el in GF])
908
+ else:
909
+ if not GF.parent().is_field() or \
910
+ not GF.parent().is_finite() or \
911
+ not GF.parent().order() == 2**8:
912
+ msg = "keyword 'GF' must be in"
913
+ raise TypeError(msg.format(self._F))
914
+ return bin(GF.to_integer())[2:].zfill(8)
915
+
916
+ def encrypt(self, plain, key, format='hex'):
917
+ r"""
918
+ Return the plaintext ``plain`` encrypted with the key ``key``.
919
+
920
+ INPUT:
921
+
922
+ - ``plain`` -- the plaintext to be encrypted
923
+
924
+ - ``key`` -- the key to encrypt ``plain`` with
925
+
926
+ - ``format`` -- (default: ``hex``) the string format of ``key`` and
927
+ ``plain``, either "hex" or "binary"
928
+
929
+ OUTPUT: string of the plaintext ``plain`` encrypted with the key ``key``
930
+
931
+ EXAMPLES::
932
+
933
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
934
+ sage: rgf = RijndaelGF(4, 4)
935
+ sage: key = 'c81677bc9b7ac93b25027992b0261996'
936
+ sage: plain = 'fde3bad205e5d0d73547964ef1fe37f1'
937
+ sage: expected_ciphertext = 'e767290ddfc6414e3c50a444bec081f0'
938
+ sage: rgf.encrypt(plain, key) == expected_ciphertext
939
+ True
940
+
941
+ We can encrypt binary strings as well. ::
942
+
943
+ sage: key = '10010111110000011111011011010001' * 4
944
+ sage: plain = '00000000101000000000000001111011' * 4
945
+ sage: expected_ciphertext = ('11010111100100001010001011110010111'
946
+ ....: '1110011000000011111100100011011100101000000001000111000010'
947
+ ....: '00100111011011001000111101111110100')
948
+ sage: result = rgf.encrypt(plain, key, format='binary')
949
+ sage: result == expected_ciphertext
950
+ True
951
+ """
952
+ if format == 'hex':
953
+ if not isinstance(plain, str) or \
954
+ any(c not in '0123456789abcdefABCDEF' for c in plain):
955
+ raise TypeError("'plain' keyword must be a hex string")
956
+ if len(plain) != 8 * self._Nb:
957
+ msg = "'plain' keyword\'s length must be {0}, not{1}"
958
+ raise ValueError(msg.format(8 * self._Nb, len(plain)))
959
+ if not isinstance(key, str) or \
960
+ any(c not in '0123456789abcdefABCDEF' for c in key):
961
+ raise TypeError("'key' keyword must be a hex string")
962
+ if len(key) != 8 * self._Nk:
963
+ msg = "'key' keyword's length must be {0}, not {1}"
964
+ raise ValueError(msg.format(8 * self._Nk, len(key)))
965
+ state = self._hex_to_GF(plain)
966
+ key_state = self._hex_to_GF(key)
967
+ roundKeys = self.expand_key(key_state)
968
+ elif format == 'binary':
969
+ if not isinstance(plain, str) or \
970
+ any(c not in '01' for c in plain):
971
+ raise TypeError("'plain' keyword must be a binary string")
972
+ if len(plain) != 32 * self._Nb:
973
+ msg = "'plain' keyword's length must be {0}, not {1}"
974
+ raise ValueError(msg.format(32 * self._Nb, len(plain)))
975
+ if not isinstance(key, str) or \
976
+ any(c not in '01' for c in key):
977
+ raise TypeError("'key' keyword must be a binary string")
978
+ if len(key) != 32 * self._Nk:
979
+ msg = "'key' keyword's length must be {0}, not {1}"
980
+ raise ValueError(msg.format(32 * self._Nk, len(key)))
981
+ state = self._bin_to_GF(plain)
982
+ key_state = self._bin_to_GF(key)
983
+ roundKeys = self.expand_key(key_state)
984
+ else:
985
+ raise ValueError("'format' keyword must be either 'hex' or "
986
+ "'binary'")
987
+
988
+ state = self.add_round_key(state, roundKeys[0])
989
+ for r in range(self._Nr-1):
990
+ state = self.sub_bytes(state, algorithm='encrypt')
991
+ state = self.shift_rows(state, algorithm='encrypt')
992
+ state = self.mix_columns(state, algorithm='encrypt')
993
+ state = self.add_round_key(state, roundKeys[r+1])
994
+ state = self.sub_bytes(state, algorithm='encrypt')
995
+ state = self.shift_rows(state, algorithm='encrypt')
996
+ state = self.add_round_key(state, roundKeys[self._Nr])
997
+
998
+ if format == 'hex':
999
+ return self._GF_to_hex(state)
1000
+ else:
1001
+ return self._GF_to_bin(state)
1002
+
1003
+ def decrypt(self, ciphertext, key, format='hex'):
1004
+ r"""
1005
+ Return the ciphertext ``ciphertext`` decrypted with the key ``key``.
1006
+
1007
+ INPUT:
1008
+
1009
+ - ``ciphertext`` -- the ciphertext to be decrypted
1010
+
1011
+ - ``key`` -- the key to decrypt ``ciphertext`` with
1012
+
1013
+ - ``format`` -- (default: ``hex``) the string format that both
1014
+ ``ciphertext`` and ``key`` must be in, either "hex" or "binary"
1015
+
1016
+ OUTPUT:
1017
+
1018
+ - A string in the format ``format`` of ``ciphertext`` decrypted with
1019
+ key ``key``.
1020
+
1021
+ EXAMPLES::
1022
+
1023
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1024
+ sage: rgf = RijndaelGF(4, 4)
1025
+ sage: key = '2dfb02343f6d12dd09337ec75b36e3f0'
1026
+ sage: ciphertext = '54d990a16ba09ab596bbf40ea111702f'
1027
+ sage: expected_plaintext = '1e1d913b7274ad9b5a4ab1a5f9133b93'
1028
+ sage: rgf.decrypt(ciphertext, key) == expected_plaintext
1029
+ True
1030
+
1031
+ We can also decrypt messages using binary strings. ::
1032
+
1033
+ sage: key = '00011010000011100011000000111101' * 4
1034
+ sage: ciphertext = '00110010001110000111110110000001' * 4
1035
+ sage: expected_plaintext = ('101111111010011100111100101010100111'
1036
+ ....: '1111010000101101100001101000000000000000010000000100111011'
1037
+ ....: '0100001111100011010001101101001011')
1038
+ sage: result = rgf.decrypt(ciphertext, key, format='binary')
1039
+ sage: result == expected_plaintext
1040
+ True
1041
+ """
1042
+ if format == 'hex':
1043
+ if not isinstance(ciphertext, str) or \
1044
+ any(c not in '0123456789abcdefABCDEF' for c in ciphertext):
1045
+ raise TypeError("'ciphertext' keyword must be a hex string")
1046
+ if len(ciphertext) != 8 * self._Nb:
1047
+ msg = "'ciphertext' keyword's length must be {0}, not{1}"
1048
+ raise ValueError(msg.format(8 * self._Nb, len(ciphertext)))
1049
+ if not isinstance(key, str) or \
1050
+ any(c not in '0123456789abcdefABCDEF' for c in key):
1051
+ raise TypeError("'key' keyword must be a hex string")
1052
+ if len(key) != 8 * self._Nk:
1053
+ msg = "'key' keyword's length must be {0}, not {1}"
1054
+ raise ValueError(msg.format(8 * self._Nk, len(key)))
1055
+ state = self._hex_to_GF(ciphertext)
1056
+ key_state = self._hex_to_GF(key)
1057
+ roundKeys = self.expand_key(key_state)
1058
+ elif format == 'binary':
1059
+ if not isinstance(ciphertext, str) or \
1060
+ any(c not in '01' for c in ciphertext):
1061
+ raise TypeError("'ciphertext' keyword must be a binary "
1062
+ "string")
1063
+ if len(ciphertext) != 32 * self._Nb:
1064
+ msg = "'ciphertext' keyword's length must be {0}, not {1}"
1065
+ raise ValueError(msg.format(32 * self._Nb, len(ciphertext)))
1066
+ if not isinstance(key, str) or \
1067
+ any(c not in '01' for c in key):
1068
+ raise TypeError("'key' keyword must be a binary string")
1069
+ if len(key) != 32 * self._Nk:
1070
+ msg = "'key' keyword\'s length must be {0}, not {1}"
1071
+ raise ValueError(msg.format(32 * self._Nk, len(key)))
1072
+ state = self._bin_to_GF(ciphertext)
1073
+ key_state = self._bin_to_GF(key)
1074
+ roundKeys = self.expand_key(key_state)
1075
+ else:
1076
+ raise ValueError("'format' keyword must be either \'hex\' or "
1077
+ "'binary'")
1078
+
1079
+ state = self.add_round_key(state, roundKeys[self._Nr])
1080
+ state = self.shift_rows(state, algorithm='decrypt')
1081
+ state = self.sub_bytes(state, algorithm='decrypt')
1082
+ for r in range(self._Nr-1):
1083
+ state = self.add_round_key(state, roundKeys[self._Nr - r - 1])
1084
+ state = self.mix_columns(state, algorithm='decrypt')
1085
+ state = self.shift_rows(state, algorithm='decrypt')
1086
+ state = self.sub_bytes(state, algorithm='decrypt')
1087
+ state = self.add_round_key(state, roundKeys[0])
1088
+
1089
+ if format == 'hex':
1090
+ return self._GF_to_hex(state)
1091
+ else:
1092
+ return self._GF_to_bin(state)
1093
+
1094
+ def _check_valid_PRmatrix(self, PRm, keyword):
1095
+ r"""
1096
+ Raises an error if ``PRm`` is not a valid input matrix over ``F``.
1097
+
1098
+ INPUT:
1099
+
1100
+ - ``PRm`` -- if ``PRm`` is a `4 \times Nb` matrix with entries from
1101
+ the multivariate PolynomialRing ``_all_PR``, this method does nothing
1102
+ `\GF{2^8}`, this method does nothing. Otherwise, this method raises
1103
+ an error. Note that a matrix of elements from `\GF{2^8}` is regarded
1104
+ as a matrix with entries from ``_all_PR`` and will pass this test.
1105
+
1106
+ - ``keyword`` -- the name of the keyword ``PRm`` from where this
1107
+ method was called, for the potential error message. For example, if
1108
+ called from ``sub_bytes``, ``keyword`` would be "state".
1109
+
1110
+ EXAMPLES::
1111
+
1112
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1113
+ sage: rgf = RijndaelGF(4, 4)
1114
+ sage: good_state = rgf._hex_to_GF('0'*32)
1115
+ sage: rgf._check_valid_PRmatrix(good_state, 'state')
1116
+ sage: rgf._check_valid_PRmatrix(rgf.state_vrs, 'state')
1117
+ sage: rgf._check_valid_PRmatrix(5, 'state')
1118
+ Traceback (most recent call last):
1119
+ ...
1120
+ TypeError: keyword 'state' must be a 4 x 4 matrix with entries from
1121
+ a multivariate PolynomialRing over Finite Field in x of size 2^8
1122
+ <BLANKLINE>
1123
+ sage: entries = [rgf._F.random_element() for i in range(24)]
1124
+ sage: wrong_dimensions = matrix(4, 6, entries)
1125
+ sage: rgf._check_valid_PRmatrix(wrong_dimensions, 'state')
1126
+ Traceback (most recent call last):
1127
+ ...
1128
+ TypeError: keyword 'state' must be a 4 x 4 matrix with entries from
1129
+ a multivariate PolynomialRing over Finite Field in x of size 2^8
1130
+ <BLANKLINE>
1131
+ sage: F.<a> = GF(3^4)
1132
+ sage: entries = [F.random_element() for i in range(16)]
1133
+ sage: wrong_base = matrix(4, 4, entries)
1134
+ sage: rgf._check_valid_PRmatrix(wrong_base, 'state')
1135
+ Traceback (most recent call last):
1136
+ ...
1137
+ TypeError: keyword 'state' must be a 4 x 4 matrix with entries from
1138
+ a multivariate PolynomialRing over Finite Field in x of size 2^8
1139
+ """
1140
+ from sage.rings.polynomial.multi_polynomial_ring_base import \
1141
+ MPolynomialRing_base
1142
+ msg = ("keyword '{0}' must be a {1} x {2} matrix with entries from a "
1143
+ "multivariate PolynomialRing over {3}")
1144
+ msg = msg.format(keyword, 4, self._Nb, self._F)
1145
+ if (not isinstance(PRm, Matrix) or
1146
+ not (PRm.base_ring().is_field() and
1147
+ PRm.base_ring().is_finite() and
1148
+ PRm.base_ring().order() == 256 and
1149
+ PRm.dimensions() == (4, self._Nb))) and \
1150
+ (not isinstance(PRm, Matrix) or
1151
+ not isinstance(PRm.base_ring(), MPolynomialRing_base) or
1152
+ not (PRm.base_ring().base_ring().is_field() and
1153
+ PRm.base_ring().base_ring().is_finite() and
1154
+ PRm.base_ring().base_ring().order() == 256) or
1155
+ not PRm.dimensions() == (4, self._Nb)):
1156
+ raise TypeError(msg)
1157
+
1158
+ def expand_key(self, key):
1159
+ r"""
1160
+ Return the expanded key schedule from ``key``.
1161
+
1162
+ INPUT:
1163
+
1164
+ - ``key`` -- the key to build a key schedule from. Must be a matrix
1165
+ over `\GF{2^8}` of dimensions `4 \times N_k`
1166
+
1167
+ OUTPUT:
1168
+
1169
+ - A length `Nr` list of `4 \times N_b` matrices corresponding to the
1170
+ expanded key. The `n` th entry of the list corresponds to the matrix
1171
+ used in the ``add_round_key`` step of the `n` th round.
1172
+
1173
+ EXAMPLES::
1174
+
1175
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1176
+ sage: rgf = RijndaelGF(4, 6)
1177
+ sage: key = '331D0084B176C3FB59CAA0EDA271B565BB5D9A2D1E4B2892'
1178
+ sage: key_state = rgf._hex_to_GF(key)
1179
+ sage: key_schedule = rgf.expand_key(key_state)
1180
+ sage: rgf._GF_to_hex(key_schedule[0])
1181
+ '331d0084b176c3fb59caa0eda271b565'
1182
+ sage: rgf._GF_to_hex(key_schedule[6])
1183
+ '5c5d51c4121f018d0f4f3e408ae9f78c'
1184
+ """
1185
+ msg = "keyword '{0}' must be a {1} x {2} matrix over GF({3})"
1186
+ msg = msg.format(key, 4, self._Nk, self._F.order())
1187
+ if not isinstance(key, Matrix) or \
1188
+ not (key.base_ring().is_field() and
1189
+ key.base_ring().is_finite() and
1190
+ key.base_ring().order() == self._F.order()) or \
1191
+ not key.dimensions() == (4, self._Nk):
1192
+ raise TypeError(msg)
1193
+
1194
+ def add_cols(col1, col2):
1195
+ return [x + y for x, y in zip(col1, col2)]
1196
+
1197
+ key_cols = []
1198
+ for i in range(self._Nb * (self._Nr + 1)):
1199
+ key_cols.append([])
1200
+
1201
+ # Copy columns from ``key``, then build the rest of the columns
1202
+ for j in range(self._Nk):
1203
+ key_cols[j] = list(key.columns()[j])
1204
+ for j in range(self._Nk, self._Nb * (self._Nr + 1)):
1205
+ if j % self._Nk == 0:
1206
+ # Apply non-linear function to k[j - 1]
1207
+ add_key = [self._srd(c) for c in key_cols[j - 1]]
1208
+ add_key = add_key[1:] + add_key[:1]
1209
+ add_key[0] += self._F.gen() ** (int(j / self._Nk) - 1)
1210
+ key_cols[j] = add_cols(key_cols[j - self._Nk], add_key)
1211
+ else:
1212
+ add_key = key_cols[j - 1]
1213
+ if self._Nk > 6 and j % self._Nk == 4:
1214
+ add_key = [self._srd(k) for k in add_key]
1215
+ key_cols[j] = add_cols(key_cols[j - self._Nk], add_key)
1216
+
1217
+ # Copy the expanded columns into 4xNb blocks
1218
+ round_keys = []
1219
+ for r in range(self._Nr + 1):
1220
+ rk = column_matrix([key_cols[r*self._Nb + i]
1221
+ for i in range(self._Nb)])
1222
+ round_keys.append(rk)
1223
+ return round_keys
1224
+
1225
+ def expand_key_poly(self, row, col, round):
1226
+ r"""
1227
+ Return a polynomial representing the ``row,col`` th entry of the
1228
+ ``round`` th round key.
1229
+
1230
+ INPUT:
1231
+
1232
+ - ``row`` -- the row position of the element represented by this
1233
+ polynomial
1234
+
1235
+ - ``col`` -- the column position of the element represented by this
1236
+ polynomial
1237
+
1238
+ OUTPUT:
1239
+
1240
+ - A polynomial representing the ``row,col`` th entry of the ``round``
1241
+ th round key in terms of entries of the input key.
1242
+
1243
+ EXAMPLES::
1244
+
1245
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1246
+ sage: rgf = RijndaelGF(4, 4)
1247
+ sage: rgf.expand_key_poly(1, 2, 0)
1248
+ k012
1249
+ sage: rgf.expand_key_poly(1, 2, 1)
1250
+ (x^2 + 1)*k023^254 +
1251
+ (x^3 + 1)*k023^253 +
1252
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*k023^251 +
1253
+ (x^5 + x^2 + 1)*k023^247 +
1254
+ (x^7 + x^6 + x^5 + x^4 + x^2)*k023^239 +
1255
+ k023^223 +
1256
+ (x^7 + x^5 + x^4 + x^2 + 1)*k023^191 +
1257
+ (x^7 + x^3 + x^2 + x + 1)*k023^127 +
1258
+ k010 +
1259
+ k011 +
1260
+ k012 +
1261
+ (x^6 + x^5 + x)
1262
+
1263
+ It should be noted that ``expand_key_poly`` cannot be used with
1264
+ ``apply_poly`` or ``compose``, since ``expand_key_poly`` is not a
1265
+ ``Round_Component_Poly_Constr`` object. ::
1266
+
1267
+ sage: rgf.compose(rgf.sub_bytes_poly_constr(), rgf.expand_key_poly)
1268
+ Traceback (most recent call last):
1269
+ ...
1270
+ TypeError: keyword 'g' must be a Round_Component_Poly_Constr or
1271
+ a polynomial over Finite Field in x of size 2^8
1272
+ <BLANKLINE>
1273
+ sage: state = rgf._hex_to_GF('00000000000000000000000000000000')
1274
+ sage: rgf.apply_poly(state, rgf.expand_key_poly)
1275
+ Traceback (most recent call last):
1276
+ ...
1277
+ TypeError: keyword 'poly_constr' must be a Round_Component_Poly_Constr
1278
+ """
1279
+ if row not in range(4):
1280
+ raise ValueError("keyword 'row' must be between 0 and 4")
1281
+ if col not in range(self._Nb):
1282
+ msg = "keyword 'col' must be between 0 and {0}"
1283
+ raise ValueError(msg.format(self._Nb))
1284
+ if round not in range(self._Nr + 1):
1285
+ msg = "keyword 'r' must be between 0 and {0}"
1286
+ raise ValueError(msg.format(self._Nr))
1287
+
1288
+ key_col = round * self._Nb + col
1289
+ if key_col < self._Nk:
1290
+ return self.key_vrs[row, key_col]
1291
+ else:
1292
+ if key_col % self._Nk == 0 or \
1293
+ (self._Nk > 6 and col % self._Nk == 4):
1294
+ # Apply non-linear transformation to key_col - 1
1295
+ recur_r = int((key_col - 1)/self._Nb)
1296
+ recur_j = (key_col - 1) - (recur_r * self._Nb)
1297
+ non_linear = self.expand_key_poly((row+1) % 4,
1298
+ recur_j, recur_r)
1299
+ non_linear = self._srd(non_linear)
1300
+ non_linear += self._F.gen() ** (int(key_col / self._Nk) - 1)
1301
+ # Identify key_col - Nk
1302
+ recur_r = int((key_col - self._Nk)/self._Nb)
1303
+ recur_j = (key_col - self._Nk) - (recur_r * self._Nb)
1304
+ return self.expand_key_poly(row, recur_j, recur_r) + non_linear
1305
+ else:
1306
+ # Identify key_col - Nk
1307
+ recur_r = int((key_col - self._Nk)/self._Nb)
1308
+ recur_j = (key_col - self._Nk) - (recur_r * self._Nb)
1309
+ result = self.expand_key_poly(row, recur_j, recur_r)
1310
+ # Identify key_col - 1
1311
+ recur_r = int((key_col - 1)/self._Nb)
1312
+ recur_j = (key_col - 1) - (recur_r * self._Nb)
1313
+ return result + \
1314
+ self.expand_key_poly(row, recur_j, recur_r)
1315
+
1316
+ def apply_poly(self, state, poly_constr, algorithm='encrypt', keys=None,
1317
+ poly_constr_attr=None):
1318
+ r"""
1319
+ Return a state matrix where ``poly_method`` is applied to each entry.
1320
+
1321
+ INPUT:
1322
+
1323
+ - ``state`` -- the state matrix over `\GF{2^8}` to which
1324
+ ``poly_method`` is applied to
1325
+
1326
+ - ``poly_constr`` -- the ``Round_Component_Poly_Constr`` object to
1327
+ build polynomials during evaluation
1328
+
1329
+ - ``algorithm`` -- (default: ``'encrypt'``) passed directly to
1330
+ ``rcpc`` to select encryption or decryption; the
1331
+ encryption flag is "encrypt" and the decrypt flag is "decrypt"
1332
+
1333
+ - ``keys`` -- (default: ``None``) an array of `N_r` subkey matrices to
1334
+ replace any key variables in any polynomials returned by
1335
+ ``poly_method``. Must be identical to the format returned by
1336
+ ``expand_key``. If any polynomials have key variables and ``keys``
1337
+ is not supplied, the key variables will remain as-is.
1338
+
1339
+ - ``poly_constr_attr`` -- (default: ``None``) a dictionary of keyword
1340
+ attributes to pass to ``rcpc`` when it is called
1341
+
1342
+ OUTPUT:
1343
+
1344
+ - A state matrix in `\GF{2^8}` whose `i,j` th entry equals the
1345
+ polynomial ``poly_constr(i, j, algorithm, **poly_constr_attr)``
1346
+ evaluated by setting its variables equal to the corresponding
1347
+ entries of ``state``.
1348
+
1349
+ EXAMPLES::
1350
+
1351
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1352
+ sage: rgf = RijndaelGF(4, 4)
1353
+ sage: state = rgf._hex_to_GF('3b59cb73fcd90ee05774222dc067fb68')
1354
+ sage: result = rgf.apply_poly(state, rgf.shift_rows_poly_constr())
1355
+ sage: rgf._GF_to_hex(result)
1356
+ '3bd92268fc74fb735767cbe0c0590e2d'
1357
+
1358
+ Calling ``apply_poly`` with the ``Round_Component_Poly_Constr`` object
1359
+ of a round component (e.g. ``sub_bytes_poly``) is identical to
1360
+ calling that round component function itself. ::
1361
+
1362
+ sage: state = rgf._hex_to_GF('4915598f55e5d7a0daca94fa1f0a63f7')
1363
+ sage: apply_poly_result = rgf.apply_poly(state,
1364
+ ....: rgf.sub_bytes_poly_constr())
1365
+ sage: direct_result = rgf.sub_bytes(state)
1366
+ sage: direct_result == apply_poly_result
1367
+ True
1368
+
1369
+ If the ``Round_Component_Poly_Constr`` object's ``__call__`` method
1370
+ returns a polynomial with state variables as well as key variables, we
1371
+ can supply a list of `N_r` round keys ``keys`` whose elements are
1372
+ evaluated as the key variables. If this is not provided, the key
1373
+ variables will remain as is.::
1374
+
1375
+ sage: state = rgf._hex_to_GF('14f9701ae35fe28c440adf4d4ea9c026')
1376
+ sage: key = rgf._hex_to_GF('54d990a16ba09ab596bbf40ea111702f')
1377
+ sage: keys = rgf.expand_key(key)
1378
+ sage: result = rgf.apply_poly(state,
1379
+ ....: rgf.add_round_key_poly_constr(),
1380
+ ....: keys=keys)
1381
+ sage: result == rgf.add_round_key(state, key)
1382
+ True
1383
+ <BLANKLINE>
1384
+ sage: rgf.apply_poly(state, rgf.add_round_key_poly_constr())[0,0]
1385
+ k000 + (x^4 + x^2)
1386
+
1387
+ We can change the value of the keywords of ``poly_constr`` 's
1388
+ ``__call__`` method when ``apply_poly`` calls it by passing in a
1389
+ dictionary ``poly_constr_attr`` mapping keywords to their values. ::
1390
+
1391
+ sage: rgf.apply_poly(rgf.state_vrs,
1392
+ ....: rgf.add_round_key_poly_constr(),
1393
+ ....: poly_constr_attr={'round': 5})
1394
+ [a00 + k500 a01 + k501 a02 + k502 a03 + k503]
1395
+ [a10 + k510 a11 + k511 a12 + k512 a13 + k513]
1396
+ [a20 + k520 a21 + k521 a22 + k522 a23 + k523]
1397
+ [a30 + k530 a31 + k531 a32 + k532 a33 + k533]
1398
+ """
1399
+ self._check_valid_PRmatrix(state, 'state')
1400
+ if not isinstance(poly_constr, RijndaelGF.Round_Component_Poly_Constr):
1401
+ msg = "keyword 'poly_constr' must be a Round_Component_Poly_Constr"
1402
+ raise TypeError(msg)
1403
+ if keys is not None and (not isinstance(keys, list) or
1404
+ len(keys) != self._Nr + 1 or
1405
+ not all(isinstance(k, Matrix) for k in keys) or
1406
+ not all(k.dimensions() == (4, self._Nb) for k in keys) or
1407
+ not all(k.base_ring().is_finite() and k.base_ring().is_field()
1408
+ and k.base_ring().order() == 256 for k in keys)):
1409
+ msg = ("keys must be a length {0} array of 4 by {1} matrices"
1410
+ " over {2}")
1411
+ raise TypeError(msg.format(self._Nr, self._Nb, self._F))
1412
+
1413
+ output = []
1414
+ if keys is not None:
1415
+ key_list = [el for inner in keys for el in inner.list()]
1416
+ for i in range(4):
1417
+ for j in range(self._Nb):
1418
+ # this is to combat a major performance issue caused by
1419
+ # subbytes' inversion transformation.
1420
+ if poly_constr == self.sub_bytes_poly_constr() and \
1421
+ algorithm == 'decrypt':
1422
+ p = poly_constr(i, j, algorithm, no_inversion=True)
1423
+ p = p(state.list()) ** 254
1424
+ else:
1425
+ if poly_constr_attr is None:
1426
+ p = poly_constr(i, j, algorithm)
1427
+ else:
1428
+ p = poly_constr(i, j, algorithm, **poly_constr_attr)
1429
+ # If there are key variables in the polynomial
1430
+ if len(p.args()) > 4 * self._Nb:
1431
+ if keys is not None:
1432
+ p = p(state.list() + key_list)
1433
+ else:
1434
+ p = p(state.list() + self.subkey_vrs_list)
1435
+ else:
1436
+ p = p(state.list())
1437
+ output.append(p)
1438
+ return matrix(4, 4, output)
1439
+
1440
+ def compose(self, f, g, algorithm='encrypt', f_attr=None, g_attr=None):
1441
+ r"""
1442
+ Return a ``Round_Component_Poly_Constr`` object corresponding to
1443
+ `g \circ f` or the polynomial output of this object's ``__call__``
1444
+ method.
1445
+
1446
+ INPUT:
1447
+
1448
+ - ``f`` -- a ``Round_Component_Poly_Constr`` object corresponding to
1449
+ a round component function `f`
1450
+
1451
+ - ``g`` -- a ``Round_Component_Poly_Constr`` object corresponding to
1452
+ a round component function `g` or a polynomial output of this
1453
+ object's ``__call__`` method.
1454
+
1455
+ - ``algorithm`` -- (default: ``'encrypt'``) whether ``f`` and ``g``
1456
+ should use their encryption transformations or their decryption
1457
+ transformations. Does nothing if ``g`` is a
1458
+ ``Round_Component_Poly_Constr`` object. The encryption flag is
1459
+ "encrypt" and the decryption flag is "decrypt".
1460
+
1461
+ - ``f_attr`` -- (default: ``None``) a dictionary of keyword attributes to
1462
+ pass to ``f`` when it is called
1463
+
1464
+ - ``g_attr`` -- (default: ``None``) a dictionary of keyword attributes to
1465
+ pass to ``g`` when it is called; does nothing if ``g`` is a
1466
+ polynomial
1467
+
1468
+ OUTPUT:
1469
+
1470
+ - If ``g`` is a ``Round_Component_Poly_Constr`` object corresponding
1471
+ to a round component function `g`, then ``compose`` returns a
1472
+ ``Round_Component_Poly_Constr`` corresponding to the round
1473
+ component function `g \circ f`, where `f` is the round component
1474
+ function corresponding to the first argument ``f``. On the other
1475
+ hand, if ``g`` `= g(A)_{i,j}` for a round component function `g`,
1476
+ then ``compose`` returns `g(f(A))_{i,j}`, where `A` is an
1477
+ arbitrary input state matrix.
1478
+
1479
+ EXAMPLES:
1480
+
1481
+ This function allows us to determine the polynomial representations
1482
+ of entries across multiple round functions. For example, if we
1483
+ wanted a polynomial representing the ``1,3`` entry of a matrix after
1484
+ we first apply ShiftRows and then MixColumns to that matrix, we do::
1485
+
1486
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1487
+ sage: rgf = RijndaelGF(4, 4)
1488
+ sage: mcp = rgf.mix_columns_poly_constr()(1, 3); mcp
1489
+ a03 + x*a13 + (x + 1)*a23 + a33
1490
+ sage: result = rgf.compose(rgf.shift_rows_poly_constr(), mcp)
1491
+ sage: result
1492
+ a03 + x*a10 + (x + 1)*a21 + a32
1493
+
1494
+ We can test the correctness of this::
1495
+
1496
+ sage: state = rgf._hex_to_GF('fa636a2825b339c940668a3157244d17')
1497
+ sage: new_state = rgf.shift_rows(state)
1498
+ sage: new_state = rgf.mix_columns(new_state)
1499
+ sage: result(state.list()) == new_state[1,3]
1500
+ True
1501
+
1502
+ We can also use ``compose`` to build a new
1503
+ ``Round_Component_Poly_Constr`` object corresponding to the composition
1504
+ of multiple round functions as such::
1505
+
1506
+ sage: fn = rgf.compose(rgf.shift_rows_poly_constr(), # needs sage.libs.gap
1507
+ ....: rgf.mix_columns_poly_constr())
1508
+ sage: fn(1, 3) # needs sage.libs.gap
1509
+ a03 + x*a10 + (x + 1)*a21 + a32
1510
+
1511
+ If we use ``compose`` to make a new ``Round_Component_Poly_Constr``
1512
+ object, we can use that object as input to ``apply_poly`` and
1513
+ ``compose``::
1514
+
1515
+ sage: state = rgf._hex_to_GF('36400926f9336d2d9fb59d23c42c3950')
1516
+ sage: result = rgf.apply_poly(state, fn) # needs sage.libs.gap
1517
+ sage: rgf._GF_to_hex(result)
1518
+ 'f4bcd45432e554d075f1d6c51dd03b3c'
1519
+ <BLANKLINE>
1520
+ sage: new_state = rgf.shift_rows(state)
1521
+ sage: new_state = rgf.mix_columns(new_state)
1522
+ sage: result == new_state
1523
+ True
1524
+
1525
+ ::
1526
+
1527
+ sage: fn2 = rgf.compose(rgf.sub_bytes_poly_constr(), fn) # needs sage.libs.gap
1528
+
1529
+ If the second argument is a polynomial, then the value of ``algorithm``
1530
+ is passed directly to the first argument `f` during evaluation.
1531
+ However, if the second argument is a ``Round_Component_Poly_Constr``
1532
+ object, changing ``algorithm`` does nothing since the returned object
1533
+ has its own ``algorithm='encrypt'`` keyword. ::
1534
+
1535
+ sage: f = rgf.compose(rgf.sub_bytes_poly_constr(), # needs sage.libs.gap
1536
+ ....: rgf.mix_columns_poly_constr(),
1537
+ ....: algorithm='decrypt')
1538
+ sage: g = rgf.compose(rgf.sub_bytes_poly_constr(), # needs sage.libs.gap
1539
+ ....: rgf.mix_columns_poly_constr())
1540
+ sage: all(f(i,j) == g(i,j) for i in range(4) for j in range(4)) # needs sage.libs.gap
1541
+ True
1542
+
1543
+ We can change the keyword attributes of the ``__call__`` methods of
1544
+ ``f`` and ``g`` by passing dictionaries ``f_attr`` and ``g_attr`` to
1545
+ ``compose``. ::
1546
+
1547
+ sage: fn = rgf.compose(rgf.add_round_key_poly_constr(), # needs sage.libs.gap
1548
+ ....: rgf.add_round_key_poly_constr(),
1549
+ ....: f_attr={'round': 4}, g_attr={'round': 7})
1550
+ sage: fn(1, 2) # needs sage.libs.gap
1551
+ a12 + k412 + k712
1552
+ """
1553
+ if not isinstance(f, RijndaelGF.Round_Component_Poly_Constr):
1554
+ msg = "keyword 'f' must be a Round_Component_Poly_Constr"
1555
+ raise TypeError(msg)
1556
+ from sage.rings.polynomial.multi_polynomial import MPolynomial
1557
+ if not isinstance(g, RijndaelGF.Round_Component_Poly_Constr) and \
1558
+ not isinstance(g, MPolynomial):
1559
+ msg = ("keyword 'g' must be a Round_Component_Poly_Constr or a "
1560
+ "polynomial over {0}")
1561
+ raise TypeError(msg.format(self._F))
1562
+ if f_attr is not None and not isinstance(f_attr, dict):
1563
+ raise TypeError("f_attr must be a dictionary of keywords for f")
1564
+ if g_attr is not None and not isinstance(g_attr, dict):
1565
+ raise TypeError("g_attr must be a dictionary of keywords for g")
1566
+
1567
+ if g in self._all_PR:
1568
+ if isinstance(f_attr, dict):
1569
+ f_vals = [f(i, j, algorithm, **f_attr)
1570
+ for i in range(4) for j in range(self._Nb)]
1571
+ else:
1572
+ f_vals = [f(i, j, algorithm)
1573
+ for i in range(4) for j in range(self._Nb)]
1574
+ if g in self._state_PR:
1575
+ return g(f_vals)
1576
+ else:
1577
+ return g(f_vals + self.subkey_vrs_list)
1578
+ else:
1579
+ if isinstance(g_attr, dict):
1580
+ lm = lambda i, j, alg='encrypt': \
1581
+ self.compose(f, g(i, j, alg, **g_attr), alg, f_attr, g_attr)
1582
+ else:
1583
+ lm = lambda i, j, alg='encrypt': \
1584
+ self.compose(f, g(i, j, alg), alg, f_attr, g_attr)
1585
+ return RijndaelGF.Round_Component_Poly_Constr(lm, self)
1586
+
1587
+ def add_round_key_poly_constr(self):
1588
+ r"""
1589
+ Return the ``Round_Component_Poly_Constr`` object corresponding to
1590
+ AddRoundKey.
1591
+
1592
+ EXAMPLES::
1593
+
1594
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1595
+ sage: rgf = RijndaelGF(4, 4)
1596
+ sage: ark_pc = rgf.add_round_key_poly_constr()
1597
+ sage: ark_pc
1598
+ A polynomial constructor for the function 'Add Round Key' of Rijndael-GF
1599
+ block cipher with block length 4, key length 4, and 10 rounds.
1600
+ sage: ark_pc(0, 1)
1601
+ a01 + k001
1602
+
1603
+ When invoking the returned object's ``__call__`` method, changing the
1604
+ value of ``algorithm='encrypt'`` does nothing, since the AddRoundKey
1605
+ round component function is its own inverse. ::
1606
+
1607
+ sage: with_encrypt = ark_pc(1, 1, algorithm='encrypt')
1608
+ sage: with_decrypt = ark_pc(1, 1, algorithm='decrypt')
1609
+ sage: with_encrypt == with_decrypt
1610
+ True
1611
+
1612
+ When invoking the returned object's ``__call__`` method, one can change
1613
+ the round subkey used in the returned polynomial by changing the
1614
+ ``round=0`` keyword. ::
1615
+
1616
+ sage: ark_pc(2, 1, round=7)
1617
+ a21 + k721
1618
+
1619
+ When passing the returned object to methods such as ``apply_poly`` and
1620
+ ``compose``, we can make these methods use a non-default value for
1621
+ ``round=0`` by passing in a dictionary mapping ``round`` to a different
1622
+ value. ::
1623
+
1624
+ sage: rgf.apply_poly(rgf.state_vrs, ark_pc,
1625
+ ....: poly_constr_attr={'round': 6})
1626
+ [a00 + k600 a01 + k601 a02 + k602 a03 + k603]
1627
+ [a10 + k610 a11 + k611 a12 + k612 a13 + k613]
1628
+ [a20 + k620 a21 + k621 a22 + k622 a23 + k623]
1629
+ [a30 + k630 a31 + k631 a32 + k632 a33 + k633]
1630
+
1631
+ ::
1632
+
1633
+ sage: rcpc = rgf.compose(ark_pc, ark_pc, # needs sage.libs.gap
1634
+ ....: f_attr={'round': 3}, g_attr={'round': 5})
1635
+ sage: rcpc(3, 1) # needs sage.libs.gap
1636
+ a31 + k331 + k531
1637
+ """
1638
+ return self._add_round_key_rcpc
1639
+
1640
+ def _add_round_key_pc(self, row, col, algorithm='encrypt', round=0):
1641
+ r"""
1642
+ Return a polynomial representing an element of a round-key addition.
1643
+
1644
+ INPUT:
1645
+
1646
+ - ``row`` -- the row number of the entry represented by this method's
1647
+ output
1648
+
1649
+ - ``col`` -- the column number of the entry represented by this
1650
+ method's output
1651
+
1652
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to return the
1653
+ polynomial as an encryption or as a decryption; the encryption flag
1654
+ is "encrypt" and the decryption flag is "decrypt"
1655
+
1656
+ - ``round`` -- (default: 0) the round number of the entry represented
1657
+ by this method's output
1658
+
1659
+ OUTPUT:
1660
+
1661
+ - A polynomial representing the ``row,col`` th entry of a state matrix
1662
+ after a round-key addition in terms of entries of the input state
1663
+ matrix and entries of the ``round`` th round key.
1664
+
1665
+ EXAMPLES::
1666
+
1667
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1668
+ sage: rgf = RijndaelGF(4, 4)
1669
+ sage: rgf._add_round_key_pc(1, 2, round=7)
1670
+ a12 + k712
1671
+
1672
+ As expected, since the encryption and decryption transformations are
1673
+ identical, changing ``algorithm`` has no effect.
1674
+
1675
+ sage: with_encrypt = rgf._add_round_key_pc(3, 2, 'encrypt')
1676
+ sage: with_decrypt = rgf._add_round_key_pc(3, 2, 'decrypt')
1677
+ sage: with_encrypt == with_decrypt
1678
+ True
1679
+ """
1680
+ if round not in range(self._Nr):
1681
+ msg = "keyword 'round' must be between 0 and {0}"
1682
+ raise ValueError(msg.format(self._Nr))
1683
+ state_var = self.state_vrs[row, col]
1684
+ key_var = self.subkey_vrs[round][row, col]
1685
+ return state_var + key_var
1686
+
1687
+ def add_round_key(self, state, round_key):
1688
+ r"""
1689
+ Return the round-key addition of matrices ``state`` and ``round_key``.
1690
+
1691
+ INPUT:
1692
+
1693
+ - ``state`` -- the state matrix to have ``round_key`` added to
1694
+
1695
+ - ``round_key`` -- the round key to add to ``state``
1696
+
1697
+ OUTPUT:
1698
+
1699
+ - A state matrix which is the round key addition of ``state`` and
1700
+ ``round_key``. This transformation is simply the entrywise addition
1701
+ of these two matrices.
1702
+
1703
+ EXAMPLES::
1704
+
1705
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1706
+ sage: rgf = RijndaelGF(4, 4)
1707
+ sage: state = rgf._hex_to_GF('36339d50f9b539269f2c092dc4406d23')
1708
+ sage: key = rgf._hex_to_GF('7CC78D0E22754E667E24573F454A6531')
1709
+ sage: key_schedule = rgf.expand_key(key)
1710
+ sage: result = rgf.add_round_key(state, key_schedule[0])
1711
+ sage: rgf._GF_to_hex(result)
1712
+ '4af4105edbc07740e1085e12810a0812'
1713
+ """
1714
+ self._check_valid_PRmatrix(state, 'state')
1715
+ self._check_valid_PRmatrix(round_key, 'round_key')
1716
+ # We don't use apply_poly here since that would require giving this
1717
+ # method an extra argument of a round number
1718
+ return state + round_key
1719
+
1720
+ def sub_bytes_poly_constr(self):
1721
+ r"""
1722
+ Return the ``Round_Component_Poly_Constr`` object corresponding to
1723
+ SubBytes.
1724
+
1725
+ EXAMPLES::
1726
+
1727
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1728
+ sage: rgf = RijndaelGF(4, 4)
1729
+ sage: sb_pc = rgf.sub_bytes_poly_constr(); sb_pc
1730
+ A polynomial constructor for the function 'SubBytes' of Rijndael-GF
1731
+ block cipher with block length 4, key length 4, and 10 rounds.
1732
+ sage: sb_pc(2, 3)
1733
+ (x^2 + 1)*a23^254 +
1734
+ (x^3 + 1)*a23^253 +
1735
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a23^251 +
1736
+ (x^5 + x^2 + 1)*a23^247 +
1737
+ (x^7 + x^6 + x^5 + x^4 + x^2)*a23^239 +
1738
+ a23^223 +
1739
+ (x^7 + x^5 + x^4 + x^2 + 1)*a23^191 +
1740
+ (x^7 + x^3 + x^2 + x + 1)*a23^127 +
1741
+ (x^6 + x^5 + x + 1)
1742
+
1743
+ The returned object's ``__call__`` method has an additional keyword
1744
+ of ``no_inversion=False``, which causes the returned polynomial to
1745
+ represent only the affine transformation step of SubBytes. ::
1746
+
1747
+ sage: sb_pc(1, 0, no_inversion=True)
1748
+ (x^7 + x^3 + x^2 + x + 1)*a10^128 +
1749
+ (x^7 + x^5 + x^4 + x^2 + 1)*a10^64 +
1750
+ a10^32 +
1751
+ (x^7 + x^6 + x^5 + x^4 + x^2)*a10^16 +
1752
+ (x^5 + x^2 + 1)*a10^8 +
1753
+ (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a10^4 +
1754
+ (x^3 + 1)*a10^2 +
1755
+ (x^2 + 1)*a10 +
1756
+ (x^6 + x^5 + x + 1)
1757
+
1758
+ We can build a polynomial representing the inverse transformation
1759
+ by setting the keyword ``algorithm='decrypt'``. However, the order of
1760
+ the affine transformation and the inversion step in SubBytes means that
1761
+ this polynomial has thousands of terms and is very slow to compute.
1762
+ Hence, if one wishes to build the decryption polynomial with the
1763
+ intention of evaluating that polynomial for a particular input, it is
1764
+ strongly recommended to first call
1765
+ ``sb_pc(i, j, algorithm='decrypt', no_inversion=True)`` to build a
1766
+ polynomial representing only the inverse affine transformation,
1767
+ evaluate this polynomial for your intended input, then finally
1768
+ calculate the inverse of the result. ::
1769
+
1770
+ sage: poly = sb_pc(1, 2, algorithm='decrypt', no_inversion=True)
1771
+ sage: state = rgf._hex_to_GF('39daee38f4f1a82aaf432410c36d45b9')
1772
+ sage: result = poly(state.list())
1773
+ sage: rgf._GF_to_hex(result * -1)
1774
+ '49'
1775
+
1776
+ When passing the returned object to ``apply_poly`` and ``compose``, we
1777
+ can make those methods change the keyword ``no_inversion`` of this
1778
+ object's ``__call__`` method by passing the dictionary
1779
+ ``{'no_inversion': True}`` to them. ::
1780
+
1781
+ sage: result = rgf.apply_poly(state, sb_pc,
1782
+ ....: poly_constr_attr={'no_inversion': True})
1783
+ sage: rgf._GF_to_hex(result)
1784
+ '961c72894526f746aa85fc920adcc719'
1785
+
1786
+ ::
1787
+
1788
+ sage: rcpc = rgf.compose(sb_pc, rgf.shift_rows_poly_constr(), # needs sage.libs.gap
1789
+ ....: f_attr={'no_inversion': True})
1790
+
1791
+ Note that if we set ``algorithm='decrypt'`` for ``apply_poly``, it
1792
+ will perform the necessary performance enhancement described above
1793
+ automatically. The structure of ``compose``, however, unfortunately
1794
+ does not allow this enhancement to be employed.
1795
+ """
1796
+ return self._sub_bytes_rcpc
1797
+
1798
+ def _sub_bytes_pc(self, row, col, algorithm='encrypt', no_inversion=False):
1799
+ r"""
1800
+ Return a polynomial representing `SubBytes(A)_{\textit{row, col}}`.
1801
+
1802
+ INPUT:
1803
+
1804
+ - ``row`` -- the row number of the entry represented by this method's
1805
+ output
1806
+
1807
+ - ``col`` -- the column number of the entry represented by this
1808
+ method's output
1809
+
1810
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to return the
1811
+ polynomial as an encryption or as a decryption. The encryption flag
1812
+ is "encrypt" and the decryption flag is "decrypt".
1813
+
1814
+ - ``no_inversion`` -- boolean (default: ``False``); don't perform the
1815
+ inversion step, only perform the affine transformation. Primarily
1816
+ intended to increase performance during decryption, as is shown in
1817
+ the below example.
1818
+
1819
+ OUTPUT:
1820
+
1821
+ - A polynomial representing the ``row,col`` th entry of a state matrix
1822
+ after the SubBytes method has been applied to it.
1823
+
1824
+ EXAMPLES::
1825
+
1826
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1827
+ sage: rgf = RijndaelGF(4, 4)
1828
+ sage: rgf._sub_bytes_pc(2, 3)
1829
+ (x^2 + 1)*a23^254 + (x^3 + 1)*a23^253 + (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a23^251 + (x^5 + x^2 + 1)*a23^247 + (x^7 + x^6 + x^5 + x^4 + x^2)*a23^239 + a23^223 + (x^7 + x^5 + x^4 + x^2 + 1)*a23^191 + (x^7 + x^3 + x^2 + x + 1)*a23^127 + (x^6 + x^5 + x + 1)
1830
+
1831
+ We can use this polynomial to calculate individual entries of the
1832
+ output matrix for any given state as such::
1833
+
1834
+ sage: state = rgf._hex_to_GF('6385b79ffc538df997be478e7547d691')
1835
+ sage: poly = rgf._sub_bytes_pc(2, 3)
1836
+ sage: poly(state.list())
1837
+ x^7 + x^6 + x^5 + x^4 + x^2 + x
1838
+
1839
+ We can set ``no_inversion`` to ``True`` to get a polynomial
1840
+ representation of solely the affine transformation. ::
1841
+
1842
+ sage: rgf._sub_bytes_pc(0, 2, no_inversion=True)
1843
+ (x^7 + x^3 + x^2 + x + 1)*a02^128 + (x^7 + x^5 + x^4 + x^2 + 1)*a02^64 + a02^32 + (x^7 + x^6 + x^5 + x^4 + x^2)*a02^16 + (x^5 + x^2 + 1)*a02^8 + (x^7 + x^6 + x^5 + x^4 + x^3 + 1)*a02^4 + (x^3 + 1)*a02^2 + (x^2 + 1)*a02 + (x^6 + x^5 + x + 1)
1844
+
1845
+ When generating a decryption polynomial, calculating the inverse of
1846
+ the polynomial representing the affine transformation can be a very
1847
+ slow process. In order to speed up decryption when applying
1848
+ ``sub_bytes_poly`` to a state matrix, it is recommended to calculate
1849
+ the decryption polynomial with ``no_inversion=True``, evaluate the
1850
+ arguments, then perform the inversion after this result has been
1851
+ calculated. ::
1852
+
1853
+ sage: poly = rgf._sub_bytes_pc(0, 0,
1854
+ ....: algorithm='decrypt', no_inversion=True)
1855
+ sage: state = rgf._hex_to_GF('b415f8016858552e4bb6124c5f998a4c')
1856
+ sage: poly(state.list()) ^ -1
1857
+ x^7 + x^6 + x^2 + x
1858
+ """
1859
+ if algorithm == 'encrypt':
1860
+ var = self.state_vrs[row, col]
1861
+ coeffs = self._sb_E_coeffs
1862
+ if no_inversion:
1863
+ return sum([coeffs[i] * (var**(2**i))
1864
+ for i in range(8)]) + self._F("x^6 + x^5 + x + 1")
1865
+ else:
1866
+ return sum([coeffs[i] * (var**(255 - 2**i))
1867
+ for i in range(8)]) + self._F("x^6 + x^5 + x + 1")
1868
+ elif algorithm == 'decrypt':
1869
+ var = self.state_vrs[row, col]
1870
+ coeffs = self._sb_D_coeffs
1871
+ result = (sum([coeffs[i] * var**(2**i) for i in range(8)]) +
1872
+ self._F("x^2 + 1"))
1873
+ if no_inversion:
1874
+ return result
1875
+ else:
1876
+ return result ** 254
1877
+ else:
1878
+ raise ValueError("keyword 'algorithm' must be either 'encrypt' "
1879
+ "or 'decrypt'")
1880
+
1881
+ def _srd(self, el, algorithm='encrypt'):
1882
+ r"""
1883
+ Return the application of SubBytes (`S_{RD}`) to ``el``.
1884
+
1885
+ INPUT:
1886
+
1887
+ - ``el`` -- an element of `\GF{2^8}`
1888
+
1889
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to perform the
1890
+ encryption transformation or the decryption transformation.
1891
+ The encryption flag is "encrypt" and the decryption flag is
1892
+ "decrypt".
1893
+
1894
+ OUTPUT: the result of the application of the non-linear transformation
1895
+ SubBytes to ``el``.
1896
+
1897
+ EXAMPLES::
1898
+
1899
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1900
+ sage: rgf = RijndaelGF(4, 4)
1901
+ sage: el = rgf._hex_to_GF('2A', matrix=False)[0]
1902
+ sage: rgf._srd(el)
1903
+ x^7 + x^6 + x^5 + x^2 + 1
1904
+ """
1905
+ if algorithm == 'encrypt':
1906
+ p = self._sub_bytes_rcpc(0, 0, algorithm)
1907
+ state = [el] + [self._F.zero()]*((4 * self._Nb)-1)
1908
+ return p(state)
1909
+ elif algorithm == 'decrypt':
1910
+ p = self._sub_bytes_rcpc(0, 0, algorithm, no_inversion=True)
1911
+ state = [el] + [self._F.zero()]*((4 * self._Nb)-1)
1912
+ return p(state) ** 254
1913
+ else:
1914
+ raise ValueError("keyword 'algorithm' must be either 'encrypt' "
1915
+ "or 'decrypt'")
1916
+
1917
+ def sub_bytes(self, state, algorithm='encrypt'):
1918
+ r"""
1919
+ Return the application of SubBytes to the state matrix ``state``.
1920
+
1921
+ INPUT:
1922
+
1923
+ - ``state`` -- the state matrix to apply SubBytes to
1924
+
1925
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to apply the
1926
+ encryption step of SubBytes or its decryption inverse. The encryption
1927
+ flag is "encrypt" and the decryption flag is "decrypt".
1928
+
1929
+ OUTPUT: the state matrix over `\GF{2^8}` where SubBytes has been
1930
+ applied to every entry of ``state``
1931
+
1932
+ EXAMPLES::
1933
+
1934
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1935
+ sage: rgf = RijndaelGF(4, 4)
1936
+ sage: state = rgf._hex_to_GF('d1c4941f7955f40fb46f6c0ad68730ad')
1937
+ sage: result = rgf.sub_bytes(state)
1938
+ sage: rgf._GF_to_hex(result)
1939
+ '3e1c22c0b6fcbf768da85067f6170495'
1940
+ sage: decryption = rgf.sub_bytes(result, algorithm='decrypt')
1941
+ sage: decryption == state
1942
+ True
1943
+ """
1944
+ self._check_valid_PRmatrix(state, 'state')
1945
+ return self.apply_poly(state, self._sub_bytes_rcpc, algorithm)
1946
+
1947
+ def mix_columns_poly_constr(self):
1948
+ r"""
1949
+ Return a ``Round_Component_Poly_Constr`` object corresponding to
1950
+ MixColumns.
1951
+
1952
+ EXAMPLES::
1953
+
1954
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1955
+ sage: rgf = RijndaelGF(4, 4)
1956
+ sage: mc_pc = rgf.mix_columns_poly_constr()
1957
+ sage: mc_pc
1958
+ A polynomial constructor for the function 'Mix Columns' of Rijndael-GF
1959
+ block cipher with block length 4, key length 4, and 10 rounds.
1960
+ sage: mc_pc(1, 2)
1961
+ a02 + x*a12 + (x + 1)*a22 + a32
1962
+ sage: mc_pc(1, 0, algorithm='decrypt')
1963
+ (x^3 + 1)*a00 + (x^3 + x^2 + x)*a10 + (x^3 + x + 1)*a20 + (x^3 + x^2 + 1)*a30
1964
+
1965
+ The returned object's ``__call__`` method has no additional keywords,
1966
+ unlike ``sub_bytes_poly_constr()`` and ``add_round_key_poly_constr()``.
1967
+ """
1968
+ return self._mix_columns_rcpc
1969
+
1970
+ def _mix_columns_pc(self, row, col, algorithm='encrypt'):
1971
+ r"""
1972
+ Return a polynomial representing `MixColumns(A)_{\textit{row, col}}`.
1973
+
1974
+ INPUT:
1975
+
1976
+ - ``row`` -- the row number of the entry represented by this method's
1977
+ output
1978
+
1979
+ - ``col`` -- the column number of the entry represented by this
1980
+ method's output
1981
+
1982
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to perform the
1983
+ encryption transformation or the decryption transformation; the
1984
+ encryption flag is "encrypt" and the decryption flag is "decrypt"
1985
+
1986
+ OUTPUT: a polynomial in terms of entries of the input state matrix
1987
+ which represents the ``row,col`` th entry of the output matrix after
1988
+ MixColumns has been applied to it
1989
+
1990
+ EXAMPLES::
1991
+
1992
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
1993
+ sage: rgf = RijndaelGF(4, 4)
1994
+ sage: rgf._mix_columns_pc(3, 1)
1995
+ (x + 1)*a01 + a11 + a21 + x*a31
1996
+
1997
+ We can use this to calculate individual entries of a state matrix after
1998
+ the decryption version of MixColumns has been applied to it as such::
1999
+
2000
+ sage: poly = rgf._mix_columns_pc(2, 2, algorithm='decrypt')
2001
+ sage: state = rgf._hex_to_GF('a761ca9b97be8b45d8ad1a611fc97369')
2002
+ sage: result = poly(state.list())
2003
+ sage: rgf._GF_to_hex(result)
2004
+ 'b7'
2005
+ sage: output = rgf.mix_columns(state, algorithm='decrypt')
2006
+ sage: output[2,2] == result
2007
+ True
2008
+ """
2009
+ if algorithm == 'encrypt':
2010
+ coeffs = self._mixcols_E
2011
+ elif algorithm == 'decrypt':
2012
+ coeffs = self._mixcols_D
2013
+ else:
2014
+ raise ValueError("keyword 'algorithm' must be either 'encrypt' "
2015
+ "or 'decrypt'")
2016
+ return sum([coeffs[row,k] * self.state_vrs[k,col] for k in range(4)])
2017
+
2018
+ def mix_columns(self, state, algorithm='encrypt'):
2019
+ r"""
2020
+ Return the application of MixColumns to the state matrix ``state``.
2021
+
2022
+ INPUT:
2023
+
2024
+ - ``state`` -- the state matrix to apply MixColumns to
2025
+
2026
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to perform the
2027
+ encryption version of MixColumns, or its decryption inverse; the
2028
+ encryption flag is "encrypt" and the decryption flag is "decrypt"
2029
+
2030
+ OUTPUT: the state matrix over `\GF{2^8}` which is the result of
2031
+ applying MixColumns to ``state``
2032
+
2033
+ EXAMPLES::
2034
+
2035
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
2036
+ sage: rgf = RijndaelGF(4, 4)
2037
+ sage: state = rgf._hex_to_GF('cd54c7283864c0c55d4c727e90c9a465')
2038
+ sage: result = rgf.mix_columns(state)
2039
+ sage: rgf._GF_to_hex(result)
2040
+ '921f748fd96e937d622d7725ba8ba50c'
2041
+ sage: decryption = rgf.mix_columns(result, algorithm='decrypt')
2042
+ sage: decryption == state
2043
+ True
2044
+ """
2045
+ self._check_valid_PRmatrix(state, 'state')
2046
+ return self.apply_poly(state, self._mix_columns_rcpc, algorithm)
2047
+
2048
+ def shift_rows_poly_constr(self):
2049
+ r"""
2050
+ Return a ``Round_Component_Poly_Constr`` object corresponding to
2051
+ ShiftRows.
2052
+
2053
+ EXAMPLES::
2054
+
2055
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
2056
+ sage: rgf = RijndaelGF(4, 4)
2057
+ sage: sr_pc = rgf.shift_rows_poly_constr()
2058
+ sage: sr_pc(3, 0)
2059
+ a33
2060
+ sage: sr_pc(2, 1, algorithm='decrypt')
2061
+ a23
2062
+
2063
+ The returned object's ``__call__`` method has no additional keywords,
2064
+ unlike ``sub_bytes_poly_constr()`` and ``add_round_key_poly_constr``.
2065
+ """
2066
+ return self._shift_rows_rcpc
2067
+
2068
+ def _shift_rows_pc(self, row, col, algorithm='encrypt'):
2069
+ r"""
2070
+ Return a polynomial representing `ShiftRows(A)_{\textit{row,col}}`.
2071
+
2072
+ INPUT:
2073
+
2074
+ - ``row`` -- the row number of the entry represented by this method's
2075
+ output
2076
+
2077
+ - ``col`` -- the column number of the entry represented by this
2078
+ method's output
2079
+
2080
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to perform ShiftRows'
2081
+ encryption step or its decryption inverse. The encryption flag is
2082
+ "encrypt" and the decryption flag is "decrypt".
2083
+
2084
+ OUTPUT: a polynomial in terms of entries of the input state matrix
2085
+ which represents the ``row,col`` th entry of the output matrix after
2086
+ ShiftRows has been applied to it
2087
+
2088
+ EXAMPLES::
2089
+
2090
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
2091
+ sage: rgf = RijndaelGF(4, 4)
2092
+ sage: rgf._shift_rows_pc(2, 3)
2093
+ a21
2094
+
2095
+ We can use this to calculate individual entries of a state matrix
2096
+ after the decryption version of ShiftRows has been applied to it as
2097
+ such::
2098
+
2099
+ sage: poly = rgf._shift_rows_pc(2, 3, algorithm='decrypt')
2100
+ sage: state = rgf._hex_to_GF('78c4f708318d3cd69655b701bfc093cf')
2101
+ sage: result = poly(state.list())
2102
+ sage: rgf._GF_to_hex(result)
2103
+ '3c'
2104
+ sage: output = rgf.shift_rows(state, algorithm='decrypt')
2105
+ sage: output[2,3] == result
2106
+ True
2107
+ """
2108
+ if algorithm == 'encrypt':
2109
+ offs = self._shiftrows_offsets_E
2110
+ elif algorithm == 'decrypt':
2111
+ offs = self._shiftrows_offsets_D
2112
+ else:
2113
+ raise ValueError("keyword 'algorithm' must be either 'encrypt' "
2114
+ "or 'decrypt'")
2115
+ return self.state_vrs[row, (col + offs[4 - self._Nb][row]) % 4]
2116
+
2117
+ def shift_rows(self, state, algorithm='encrypt'):
2118
+ r"""
2119
+ Return the application of ShiftRows to the state matrix ``state``.
2120
+
2121
+ INPUT:
2122
+
2123
+ - ``state`` -- a state matrix over `\GF{2^8}` to which ShiftRows is
2124
+ applied to
2125
+
2126
+ - ``algorithm`` -- (default: ``'encrypt'``) whether to perform the
2127
+ encryption version of ShiftRows or its decryption inverse; the
2128
+ encryption flag is "encrypt" and the decryption flag is "decrypt"
2129
+
2130
+ OUTPUT: a state matrix over `\GF{2^8}` which is the application of
2131
+ ShiftRows to ``state``
2132
+
2133
+ EXAMPLES::
2134
+
2135
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
2136
+ sage: rgf = RijndaelGF(4, 4)
2137
+ sage: state = rgf._hex_to_GF('adcb0f257e9c63e0bc557e951c15ef01')
2138
+ sage: result = rgf.shift_rows(state)
2139
+ sage: rgf._GF_to_hex(result)
2140
+ 'ad9c7e017e55ef25bc150fe01ccb6395'
2141
+ sage: decryption = rgf.shift_rows(result, algorithm='decrypt')
2142
+ sage: decryption == state
2143
+ True
2144
+ """
2145
+ self._check_valid_PRmatrix(state, 'state')
2146
+ return self.apply_poly(state, self._shift_rows_rcpc, algorithm)
2147
+
2148
+ class Round_Component_Poly_Constr(SageObject):
2149
+
2150
+ def __init__(self, polynomial_constr, rgf, round_component_name=None):
2151
+ r"""
2152
+ An object which constructs polynomials representing round
2153
+ component functions of a RijndaelGF object.
2154
+
2155
+ INPUT:
2156
+
2157
+ - ``polynomial_constr`` -- a function which takes an index
2158
+ ``row,col`` and returns a polynomial representing the ``row,col``
2159
+ th entry of a matrix after a specific round component function
2160
+ has been applied to it. This polynomial must be in terms of
2161
+ entries of the input matrix to that round component function and
2162
+ of entries of various subkeys. ``polynomial_constr`` must have
2163
+ arguments of the form ``polynomial_constr(row, col,
2164
+ algorithm='encrypt', **kwargs)`` and must be able to be called
2165
+ as ``polynomial_constr(row, col)``.
2166
+
2167
+ - ``rgf`` -- the RijndaelGF object whose state entries are
2168
+ represented by polynomials returned from ``polynomial_constr``
2169
+
2170
+ - ``round_component_name`` -- the name of the round component
2171
+ function this object corresponds to as a string; used solely
2172
+ for display purposes
2173
+
2174
+ EXAMPLES::
2175
+
2176
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
2177
+ sage: rgf = RijndaelGF(4, 4)
2178
+ sage: rcpc = RijndaelGF.Round_Component_Poly_Constr(
2179
+ ....: rgf._shift_rows_pc, rgf, "Shift Rows")
2180
+ sage: rcpc
2181
+ A polynomial constructor for the function 'Shift Rows' of
2182
+ Rijndael-GF block cipher with block length 4, key length 4,
2183
+ and 10 rounds.
2184
+
2185
+ If `\phi` is the round component function to which this object
2186
+ corresponds to, then ``__call__(i,j)`` `= \phi(A)_{i,j}`, where
2187
+ `A` is an arbitrary input matrix. Note that the polynomial returned
2188
+ by ``__call__(i,j)`` will be in terms of the entries of `A`. ::
2189
+
2190
+ sage: rcpc = RijndaelGF.Round_Component_Poly_Constr(
2191
+ ....: rgf._mix_columns_pc, rgf, "Mix Columns")
2192
+ sage: poly = rcpc(1, 2); poly
2193
+ a02 + x*a12 + (x + 1)*a22 + a32
2194
+ sage: state = rgf._hex_to_GF('d1876c0f79c4300ab45594add66ff41f')
2195
+ sage: result = rgf.mix_columns(state)
2196
+ sage: result[1,2] == poly(state.list())
2197
+ True
2198
+
2199
+ Invoking this objects ``__call__`` method passes its arguments
2200
+ directly to ``polynomial_constr`` and returns the result. In a
2201
+ sense, ``Round_Component_Poly_Constr`` acts as a wrapper for
2202
+ the ``polynomial_constr`` method and helps ensure that each
2203
+ ``Round_Component_Poly_Constr`` object will act similarly. ::
2204
+
2205
+ sage: all(rgf._mix_columns_pc(i, j) == rcpc(i, j)
2206
+ ....: for i in range(4) for j in range(4))
2207
+ True
2208
+
2209
+ Since all keyword arguments of ``polynomial_constr`` must have a
2210
+ default value except for ``row`` and ``col``, we can always call
2211
+ a ``Round_Component_Poly_Constr`` object by ``__call__(row, col)``.
2212
+ Because of this, methods such as ``apply_poly`` and ``compose``
2213
+ will only call ``__call__(row, col)`` when passed a
2214
+ ``Round_Component_Poly_Constr`` object. In order to change this
2215
+ object's behavior and force methods such as ``apply_poly`` to use
2216
+ non-default values for keywords we can pass dictionaries mapping
2217
+ keywords to non-default values as input to ``apply_poly`` and
2218
+ ``compose``. ::
2219
+
2220
+ sage: rgf.apply_poly(rgf.state_vrs,
2221
+ ....: rgf.add_round_key_poly_constr(),
2222
+ ....: poly_constr_attr={'round': 9})
2223
+ [a00 + k900 a01 + k901 a02 + k902 a03 + k903]
2224
+ [a10 + k910 a11 + k911 a12 + k912 a13 + k913]
2225
+ [a20 + k920 a21 + k921 a22 + k922 a23 + k923]
2226
+ [a30 + k930 a31 + k931 a32 + k932 a33 + k933]
2227
+
2228
+ ::
2229
+
2230
+ sage: fn = rgf.compose(rgf.add_round_key_poly_constr(), # needs sage.libs.gap
2231
+ ....: rgf.add_round_key_poly_constr(),
2232
+ ....: f_attr={'round': 3}, g_attr={'round': 7})
2233
+ sage: fn(2, 3) # needs sage.libs.gap
2234
+ a23 + k323 + k723
2235
+
2236
+ Because all ``Round_Component_Poly_Constr`` objects are callable
2237
+ as ``__call__(row, col, algorithm)``, ``__call__`` will check
2238
+ the validity of these three arguments automatically. Any other
2239
+ keywords, however, must be checked in ``polynomial_constr``. ::
2240
+
2241
+ sage: def my_poly_constr(row, col, algorithm='encrypt'):
2242
+ ....: return x * rgf._F.one() # example body with no checks
2243
+ sage: rcpc = RijndaelGF.Round_Component_Poly_Constr(
2244
+ ....: my_poly_constr, rgf, "My Poly Constr")
2245
+ sage: rcpc(-1, 2)
2246
+ Traceback (most recent call last):
2247
+ ...
2248
+ ValueError: keyword 'row' must be in range 0 - 3
2249
+ sage: rcpc(1, 2, algorithm=5)
2250
+ Traceback (most recent call last):
2251
+ ...
2252
+ ValueError: keyword 'algorithm' must be either 'encrypt' or 'decrypt'
2253
+ """
2254
+ pc_args = sage_getargspec(polynomial_constr)
2255
+ if pc_args[0][0] == 'self':
2256
+ # Check number of defaulted arguments
2257
+ if len(pc_args[3]) != len(pc_args[0]) - 3:
2258
+ msg = ("keyword 'polynomial_constr' must be callable as: "
2259
+ "polynomial_constr(row, col, algorithm='encrypt')")
2260
+ raise TypeError(msg)
2261
+ else:
2262
+ if len(pc_args[3]) != len(pc_args[0]) - 2:
2263
+ msg = ("keyword 'polynomial_constr' must be callable as: "
2264
+ "polynomial_constr(row, col, algorithm='encrypt')")
2265
+ raise TypeError(msg)
2266
+ self._polynomial_constr = polynomial_constr
2267
+ self._Nb = rgf.block_length()
2268
+ self._rgf_name = rgf.__repr__()
2269
+ if round_component_name is not None and \
2270
+ not isinstance(round_component_name, str):
2271
+ msg = "round_component_name must be None or a string"
2272
+ raise TypeError(msg)
2273
+ self._rc_name = round_component_name
2274
+
2275
+ def __call__(self, row, col, algorithm='encrypt', **kwargs):
2276
+ r"""
2277
+ Return ``polynomial_constr(row, col, algorithm, **attr_dict)``.
2278
+
2279
+ INPUT:
2280
+
2281
+ - ``row`` -- the row number to pass to ``polynomial_constr``
2282
+
2283
+ - ``col`` -- the column number to pass to ``polynomial_constr``
2284
+
2285
+ - ``algorithm`` -- (default: ``'encrypt'``) the algorithm keyword
2286
+ to pass to ``polynomial_constr``
2287
+
2288
+ - ``**kwargs`` -- keyword arguments to pass to
2289
+ ``polynomial_constr``. Keyword arguments will vary depending
2290
+ on ``polynomial_constr``
2291
+
2292
+ OUTPUT:
2293
+
2294
+ - The output of ``polynomial_constr(row, col, algorithm,
2295
+ ** attr_dict)``. This is required to be a polynomial over
2296
+ `\GF{2^8}`.
2297
+
2298
+ EXAMPLES::
2299
+
2300
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
2301
+ sage: rgf = RijndaelGF(4, 4)
2302
+ sage: rcpc = RijndaelGF.Round_Component_Poly_Constr(
2303
+ ....: rgf._shift_rows_pc, rgf, "Shift Rows")
2304
+ sage: rcpc(1, 2)
2305
+ a13
2306
+ sage: all(rcpc(i, j) == rgf._shift_rows_pc(i, j)
2307
+ ....: for i in range(4) for j in range(4))
2308
+ True
2309
+ """
2310
+ if row not in range(4):
2311
+ raise ValueError("keyword 'row' must be in range 0 - 3")
2312
+ if col not in range(self._Nb):
2313
+ msg = "keyword 'col' must be in range 0 - {0}"
2314
+ raise ValueError(msg.format(self._Nb - 1))
2315
+ if algorithm not in ['encrypt', 'decrypt']:
2316
+ msg = ("keyword 'algorithm' must be either 'encrypt' or "
2317
+ "'decrypt'")
2318
+ print(algorithm)
2319
+ raise ValueError(msg)
2320
+ return self._polynomial_constr(row, col, algorithm, **kwargs)
2321
+
2322
+ def __repr__(self):
2323
+ r"""
2324
+ Return a string representation of this object.
2325
+
2326
+ EXAMPLES::
2327
+
2328
+ sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
2329
+ sage: rgf = RijndaelGF(4, 4)
2330
+ sage: RijndaelGF.Round_Component_Poly_Constr(
2331
+ ....: rgf._shift_rows_pc, rgf, "Shift Rows")
2332
+ A polynomial constructor for the function 'Shift Rows' of
2333
+ Rijndael-GF block cipher with block length 4, key length 4,
2334
+ and 10 rounds.
2335
+ sage: RijndaelGF.Round_Component_Poly_Constr(
2336
+ ....: rgf._shift_rows_pc, rgf)
2337
+ A polynomial constructor of a round component of Rijndael-GF
2338
+ block cipher with block length 4, key length 4, and 10 rounds.
2339
+ """
2340
+ if self._rc_name is None:
2341
+ msg = "A polynomial constructor of a round component of {0}"
2342
+ return msg.format(self._rgf_name)
2343
+ else:
2344
+ msg = "A polynomial constructor for the function '{0}' of {1}"
2345
+ return msg.format(self._rc_name, self._rgf_name)