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,3668 @@
1
+ # sage_setup: distribution = sagemath-modules
2
+ # sage.doctest: needs sage.combinat
3
+ r"""
4
+ Classical Cryptosystems
5
+
6
+ A convenient user interface to various classical ciphers. These include:
7
+
8
+ - affine cipher; see :class:`AffineCryptosystem`
9
+ - Hill or matrix cipher; see :class:`HillCryptosystem`
10
+ - shift cipher; see :class:`ShiftCryptosystem`
11
+ - substitution cipher; see :class:`SubstitutionCryptosystem`
12
+ - transposition cipher; see :class:`TranspositionCryptosystem`
13
+ - Vigenere cipher; see :class:`VigenereCryptosystem`
14
+
15
+ These classical cryptosystems support alphabets such as:
16
+
17
+ - the capital letters of the English alphabet; see
18
+ :func:`AlphabeticStrings() <sage.monoids.string_monoid.AlphabeticStrings>`
19
+ - the hexadecimal number system; see
20
+ :func:`HexadecimalStrings() <sage.monoids.string_monoid.HexadecimalStrings>`
21
+ - the binary number system; see
22
+ :func:`BinaryStrings() <sage.monoids.string_monoid.BinaryStrings>`
23
+ - the octal number system; see
24
+ :func:`OctalStrings() <sage.monoids.string_monoid.OctalStrings>`
25
+ - the radix-64 number system; see
26
+ :func:`Radix64Strings() <sage.monoids.string_monoid.Radix64Strings>`
27
+
28
+ AUTHORS:
29
+
30
+ - David Kohel (2007): initial version with the Hill, substitution,
31
+ transposition, and Vigenere cryptosystems.
32
+
33
+ - Minh Van Nguyen (2009-08): shift cipher, affine cipher
34
+ """
35
+
36
+ #*****************************************************************************
37
+ # Copyright (C) 2007 David Kohel <kohel@maths.usyd.edu.au>
38
+ #
39
+ # This program is free software: you can redistribute it and/or modify
40
+ # it under the terms of the GNU General Public License as published by
41
+ # the Free Software Foundation, either version 2 of the License, or
42
+ # (at your option) any later version.
43
+ # http://www.gnu.org/licenses/
44
+ #*****************************************************************************
45
+
46
+ # TODO: check off this todo list:
47
+ # - methods to cryptanalyze the Hill, substitution, transposition, and
48
+ # Vigenere ciphers
49
+
50
+ from random import randint
51
+
52
+ from sage.arith.misc import inverse_mod, xgcd
53
+ from sage.misc.lazy_import import lazy_import
54
+ from sage.monoids.string_monoid import (
55
+ StringMonoid_class,
56
+ AlphabeticStringMonoid)
57
+ from sage.monoids.string_monoid_element import StringMonoidElement
58
+ from sage.monoids.string_ops import strip_encoding
59
+ from sage.rings.integer import Integer
60
+ from sage.rings.integer_ring import ZZ
61
+ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
62
+
63
+ lazy_import('sage.groups.perm_gps.permgroup_named', 'SymmetricGroup')
64
+ lazy_import('sage.groups.perm_gps.permgroup_element', 'PermutationGroupElement')
65
+ lazy_import('sage.matrix.matrix_space', 'MatrixSpace')
66
+
67
+ from .cryptosystem import SymmetricKeyCryptosystem
68
+ from .classical_cipher import (
69
+ AffineCipher,
70
+ HillCipher,
71
+ ShiftCipher,
72
+ SubstitutionCipher,
73
+ TranspositionCipher,
74
+ VigenereCipher)
75
+
76
+
77
+ class AffineCryptosystem(SymmetricKeyCryptosystem):
78
+ r"""
79
+ Create an affine cryptosystem.
80
+
81
+ Let `A = \{ a_0, a_1, a_2, \dots, a_{n-1} \}` be a non-empty alphabet
82
+ consisting of `n` unique elements. Define a mapping
83
+ `f : A \longrightarrow \ZZ / n\ZZ` from the alphabet `A` to
84
+ the set `\ZZ / n\ZZ` of integers modulo `n`, given by
85
+ `f(a_i) = i`. Thus we can identify each element of the alphabet `A`
86
+ with a unique integer `0 \leq i < n`. A key of the affine cipher is an
87
+ ordered pair of integers `(a, b) \in \ZZ / n\ZZ \times \ZZ / n\ZZ` such
88
+ that `\gcd(a, n) = 1`. Therefore the key space is
89
+ `\ZZ / n\ZZ \times \ZZ / n\ZZ`. Since we assume that `A` does not have
90
+ repeated elements, the mapping `f : A \longrightarrow \ZZ/ n\ZZ` is
91
+ bijective. Encryption and decryption functions are both affine functions.
92
+ Let `(a,b)` be a secret key, i.e. an element of the key space, and let
93
+ `p` be a plaintext character and consequently `p \in \ZZ / n\ZZ`. Then
94
+ the ciphertext character `c` corresponding to `p` is given by
95
+
96
+ .. MATH::
97
+
98
+ c \equiv ap + b \pmod{n}
99
+
100
+ Similarly, given a ciphertext character `c \in \ZZ / n\ZZ` and a secret
101
+ key `(a,b)`, we can recover the corresponding plaintext character as
102
+ follows:
103
+
104
+ .. MATH::
105
+
106
+ p \equiv a^{-1} (c - b) \pmod{n}
107
+
108
+ where `a^{-1}` is the inverse of `a` modulo `n`. Use the bijection
109
+ `f : A \longrightarrow \ZZ / n\ZZ` to convert `c` and `p` back to
110
+ elements of the alphabet `A`. Currently, only the following alphabet is
111
+ supported for the affine cipher:
112
+
113
+ - capital letters of the English alphabet as implemented in
114
+ :func:`AlphabeticStrings()
115
+ <sage.monoids.string_monoid.AlphabeticStrings>`
116
+
117
+ EXAMPLES:
118
+
119
+ Encryption and decryption over the capital letters of the English
120
+ alphabet::
121
+
122
+ sage: A = AffineCryptosystem(AlphabeticStrings()); A
123
+ Affine cryptosystem on Free alphabetic string monoid on A-Z
124
+ sage: P = A.encoding("The affine cryptosystem generalizes the shift cipher.")
125
+ sage: P
126
+ THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
127
+ sage: a, b = (9, 13)
128
+ sage: C = A.enciphering(a, b, P); C
129
+ CYXNGGHAXFKVSCJTVTCXRPXAXKNIHEXTCYXTYHGCFHSYXK
130
+ sage: A.deciphering(a, b, C)
131
+ THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
132
+ sage: A.deciphering(a, b, C) == P
133
+ True
134
+
135
+ We can also use functional notation to work through the previous
136
+ example::
137
+
138
+ sage: A = AffineCryptosystem(AlphabeticStrings()); A
139
+ Affine cryptosystem on Free alphabetic string monoid on A-Z
140
+ sage: P = A.encoding("The affine cryptosystem generalizes the shift cipher.")
141
+ sage: P
142
+ THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
143
+ sage: a, b = (9, 13)
144
+ sage: E = A(a, b); E
145
+ Affine cipher on Free alphabetic string monoid on A-Z
146
+ sage: C = E(P); C
147
+ CYXNGGHAXFKVSCJTVTCXRPXAXKNIHEXTCYXTYHGCFHSYXK
148
+ sage: aInv, bInv = A.inverse_key(a, b)
149
+ sage: D = A(aInv, bInv); D
150
+ Affine cipher on Free alphabetic string monoid on A-Z
151
+ sage: D(C)
152
+ THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
153
+ sage: D(C) == P
154
+ True
155
+ sage: D(C) == P == D(E(P))
156
+ True
157
+
158
+ Encrypting the ciphertext with the inverse key also produces the
159
+ plaintext::
160
+
161
+ sage: A = AffineCryptosystem(AlphabeticStrings())
162
+ sage: P = A.encoding("Encrypt with inverse key.")
163
+ sage: a, b = (11, 8)
164
+ sage: C = A.enciphering(a, b, P)
165
+ sage: P; C
166
+ ENCRYPTWITHINVERSEKEY
167
+ AVENMRJQSJHSVFANYAOAM
168
+ sage: aInv, bInv = A.inverse_key(a, b)
169
+ sage: A.enciphering(aInv, bInv, C)
170
+ ENCRYPTWITHINVERSEKEY
171
+ sage: A.enciphering(aInv, bInv, C) == P
172
+ True
173
+
174
+ For a secret key `(a,b) \in \ZZ/n\ZZ \times \ZZ/n\ZZ`, if `a = 1` then
175
+ any affine cryptosystem with key `(1, b)` for any `b \in \ZZ/n\ZZ` is
176
+ a shift cryptosystem. Here is how we can create a Caesar cipher using
177
+ an affine cipher::
178
+
179
+ sage: caesar = AffineCryptosystem(AlphabeticStrings())
180
+ sage: a, b = (1, 3)
181
+ sage: P = caesar.encoding("abcdef"); P
182
+ ABCDEF
183
+ sage: C = caesar.enciphering(a, b, P); C
184
+ DEFGHI
185
+ sage: caesar.deciphering(a, b, C) == P
186
+ True
187
+
188
+ Any affine cipher with keys of the form
189
+ `(a,0) \in \ZZ/n\ZZ \times \ZZ/n\ZZ` is called a decimation cipher on
190
+ the Roman alphabet, or decimation cipher for short::
191
+
192
+ sage: A = AffineCryptosystem(AlphabeticStrings())
193
+ sage: P = A.encoding("A decimation cipher is a specialized affine cipher.")
194
+ sage: a, b = (17, 0)
195
+ sage: C = A.enciphering(a, b, P)
196
+ sage: P; C
197
+ ADECIMATIONCIPHERISASPECIALIZEDAFFINECIPHER
198
+ AZQIGWALGENIGVPQDGUAUVQIGAFGJQZAHHGNQIGVPQD
199
+ sage: A.deciphering(a, b, C) == P
200
+ True
201
+
202
+ Generate a random key for encryption and decryption::
203
+
204
+ sage: A = AffineCryptosystem(AlphabeticStrings())
205
+ sage: P = A.encoding("An affine cipher with a random key.")
206
+ sage: a, b = A.random_key()
207
+ sage: C = A.enciphering(a, b, P)
208
+ sage: A.deciphering(a, b, C) == P
209
+ True
210
+
211
+ TESTS:
212
+
213
+ The binary number system is currently not a supported alphabet of
214
+ this affine cryptosystem::
215
+
216
+ sage: AffineCryptosystem(BinaryStrings())
217
+ Traceback (most recent call last):
218
+ ...
219
+ TypeError: A (= Free binary string monoid) is not supported as a cipher domain of this affine cryptosystem.
220
+
221
+ Nor are the octal, hexadecimal, and radix-64 number systems supported::
222
+
223
+ sage: AffineCryptosystem(OctalStrings())
224
+ Traceback (most recent call last):
225
+ ...
226
+ TypeError: A (= Free octal string monoid) is not supported as a cipher domain of this affine cryptosystem.
227
+ sage: AffineCryptosystem(HexadecimalStrings())
228
+ Traceback (most recent call last):
229
+ ...
230
+ TypeError: A (= Free hexadecimal string monoid) is not supported as a cipher domain of this affine cryptosystem.
231
+ sage: AffineCryptosystem(Radix64Strings())
232
+ Traceback (most recent call last):
233
+ ...
234
+ TypeError: A (= Free radix 64 string monoid) is not supported as a cipher domain of this affine cryptosystem.
235
+
236
+ A secret key `(a,b)` must be an element of `\ZZ/n\ZZ \times \ZZ/n\ZZ` with
237
+ `\gcd(a,n) = 1`. This rules out the case `a = 0` irrespective of the
238
+ value of `b`. For the upper-case letters of the English alphabet, where
239
+ the alphabet size is `n = 26`, `a` cannot take on any even value::
240
+
241
+ sage: A = AffineCryptosystem(AlphabeticStrings())
242
+ sage: A(0, 1)
243
+ Traceback (most recent call last):
244
+ ...
245
+ ValueError: (a, b) = (0, 1) is outside the range of acceptable values for a key of this affine cryptosystem.
246
+ sage: A(2, 1)
247
+ Traceback (most recent call last):
248
+ ...
249
+ ValueError: (a, b) = (2, 1) is outside the range of acceptable values for a key of this affine cryptosystem.
250
+
251
+ REFERENCES:
252
+
253
+ - [Sti2006]_
254
+ """
255
+
256
+ def __init__(self, A):
257
+ r"""
258
+ See ``AffineCryptosystem`` for full documentation.
259
+
260
+ INPUT:
261
+
262
+ - ``A`` -- string monoid over some alphabet; this is the non-empty
263
+ alphabet over which the plaintext and ciphertext spaces
264
+ are defined
265
+
266
+ OUTPUT: an affine cryptosystem over the alphabet ``A``
267
+
268
+ EXAMPLES:
269
+
270
+ Testing of dumping and loading objects::
271
+
272
+ sage: A = AffineCryptosystem(AlphabeticStrings())
273
+ sage: A == loads(dumps(A))
274
+ True
275
+ """
276
+ # sanity check
277
+ if not isinstance(A, AlphabeticStringMonoid):
278
+ raise TypeError("A (= %s) is not supported as a cipher domain of this affine cryptosystem." % A)
279
+ # List L of invertible linear coefficients modulo n, where n is the
280
+ # alphabet size. Each e in L satisfies gcd(e, n) = 1.
281
+ n = Integer(A.ngens())
282
+ self._invertible_A = n.coprime_integers(n)
283
+ # Initialize the affine cryptosystem with the plaintext, ciphertext,
284
+ # and key spaces.
285
+ SymmetricKeyCryptosystem.__init__(
286
+ self, A, A,
287
+ key_space=(IntegerModRing(A.ngens()), IntegerModRing(A.ngens())))
288
+
289
+ def __call__(self, a, b):
290
+ r"""
291
+ Create an affine cipher with secret key ``(a,b)``.
292
+
293
+ INPUT:
294
+
295
+ - ``(a, b)`` -- a secret key; this key is used for both encryption and
296
+ decryption. For the affine cryptosystem whose plaintext and
297
+ ciphertext spaces are `A`, a key is an ordered pair
298
+ `(a,b) \in \ZZ / n\ZZ \times \ZZ / n\ZZ` where `n` is the size or
299
+ cardinality of the set `A` and `\gcd(a,n) = 1`.
300
+
301
+ OUTPUT:
302
+
303
+ - An affine cipher with secret key ``(a,b)``.
304
+
305
+ EXAMPLES::
306
+
307
+ sage: A = AffineCryptosystem(AlphabeticStrings())
308
+ sage: P = A.encoding("Fine here, fine there."); P
309
+ FINEHEREFINETHERE
310
+ sage: a, b = (17, 3)
311
+ sage: E = A(a, b); E
312
+ Affine cipher on Free alphabetic string monoid on A-Z
313
+ sage: E(P)
314
+ KJQTSTGTKJQTOSTGT
315
+ sage: C = E(P)
316
+ sage: C
317
+ KJQTSTGTKJQTOSTGT
318
+ sage: aInv, bInv = A.inverse_key(a, b)
319
+ sage: D = A(aInv, bInv); D
320
+ Affine cipher on Free alphabetic string monoid on A-Z
321
+ sage: P == D(C)
322
+ True
323
+ sage: D(E(P))
324
+ FINEHEREFINETHERE
325
+
326
+ TESTS:
327
+
328
+ The key must be an ordered pair
329
+ `(a,b) \in \ZZ/n\ZZ \times \ZZ/n\ZZ` with `n` being the size of the
330
+ plaintext and ciphertext spaces. Furthermore, `a` must be
331
+ relatively prime to `n`, i.e. `\gcd(a,n) = 1`::
332
+
333
+ sage: A = AffineCryptosystem(AlphabeticStrings())
334
+ sage: A(2, 3)
335
+ Traceback (most recent call last):
336
+ ...
337
+ ValueError: (a, b) = (2, 3) is outside the range of acceptable values for a key of this affine cryptosystem.
338
+ """
339
+ # Sanity check: the key K = (a,b) must be an element of
340
+ # ZZ/nZZ x ZZ/nZZ where n is the size of the plaintext and ciphertext
341
+ # spaces. For the affine cryptosystem, these two spaces are the
342
+ # same alphabet.
343
+ try:
344
+ n = self.alphabet_size()
345
+ # If a is an element of the multiplicative group G of ZZ/nZZ, then
346
+ # gcd(a,n) = 1. So here we don't need to explicitly test that
347
+ # a is coprime to n since we assume that the list
348
+ # self._invertible_A contains all the elements of G.
349
+ if (a in self._invertible_A) and (0 <= b < n):
350
+ return AffineCipher(self, key=(a,b))
351
+ else:
352
+ raise ValueError
353
+ except Exception:
354
+ raise ValueError("(a, b) = (%s, %s) is outside the range of acceptable values for a key of this affine cryptosystem." % (a, b))
355
+
356
+ def _repr_(self):
357
+ r"""
358
+ Return the string representation of ``self``.
359
+
360
+ EXAMPLES::
361
+
362
+ sage: A = AffineCryptosystem(AlphabeticStrings()); A
363
+ Affine cryptosystem on Free alphabetic string monoid on A-Z
364
+ """
365
+ # The affine cipher has the plaintext and ciphertext spaces defined
366
+ # over the same non-empty alphabet. The cipher domain is the same
367
+ # as the alphabet used for the plaintext and ciphertext spaces.
368
+ return "Affine cryptosystem on %s" % self.cipher_domain()
369
+
370
+ def rank_by_chi_square(self, C, pdict):
371
+ r"""
372
+ Use the chi-square statistic to rank all possible keys. Currently,
373
+ this method only applies to the capital letters of the English
374
+ alphabet.
375
+
376
+ ALGORITHM:
377
+
378
+ Consider a non-empty alphabet `A` consisting of `n`
379
+ elements, and let `C` be a ciphertext encoded using elements of
380
+ `A`. The plaintext `P` corresponding to `C` is also encoded using
381
+ elements of `A`. Let `M` be a candidate decipherment of `C`,
382
+ i.e. `M` is the result of attempting to decrypt `C` using a key
383
+ `(a,b)` which is not necessarily the same key used to encrypt `P`.
384
+ Suppose `F_A(e)` is the characteristic frequency probability of
385
+ `e \in A` and let `F_M(e)` be the message frequency probability with
386
+ respect to `M`. The characteristic frequency probability
387
+ distribution of an alphabet is the expected frequency probability
388
+ distribution for that alphabet. The message frequency probability
389
+ distribution of `M` provides a distribution of the ratio of character
390
+ occurrences over message length. One can interpret the
391
+ characteristic frequency probability `F_A(e)` as the expected
392
+ probability, while the message frequency probability `F_M(e)` is
393
+ the observed probability. If `M` is of length `L`, then the observed
394
+ frequency of `e \in A` is
395
+
396
+ .. MATH::
397
+
398
+ O_M(e)
399
+ =
400
+ F_M(e) \cdot L
401
+
402
+ and the expected frequency of `e \in A` is
403
+
404
+ .. MATH::
405
+
406
+ E_A(e)
407
+ =
408
+ F_A(e) \cdot L
409
+
410
+ The chi-square rank `R_{\chi^2}(M)` of `M` corresponding to a key
411
+ `(a,b) \in \ZZ/n\ZZ \times \ZZ/n\ZZ` is given by
412
+
413
+ .. MATH::
414
+
415
+ R_{\chi^2}(M)
416
+ =
417
+ \sum_{e \in A} \frac {\big( O_M(e) - E_A(e) \big)^2}
418
+ {E_A(e)}
419
+
420
+ Cryptanalysis by exhaustive key search produces a candidate
421
+ decipherment `M_{a,b}` for each possible key `(a,b)`. For a set
422
+ `D = \big\{M_{a_1,b_1}, M_{a_2,b_2}, \dots, M_{a_k,b_k} \big\}`
423
+ of all candidate decipherments corresponding to a ciphertext `C`,
424
+ the smaller is the rank `R_{\chi^2}(M_{a_i,b_i})` the more likely
425
+ that `(a_i,b_i)` is the secret key. This key ranking method is
426
+ based on the Pearson chi-square test [PearsonTest]_.
427
+
428
+ INPUT:
429
+
430
+ - ``C`` -- the ciphertext, a non-empty string. The ciphertext
431
+ must be encoded using the upper-case letters of the English
432
+ alphabet.
433
+
434
+ - ``pdict`` -- dictionary of key, possible plaintext
435
+ pairs. This should be the output of :func:`brute_force` with
436
+ ``ranking="none"``.
437
+
438
+ OUTPUT:
439
+
440
+ - A list ranking the most likely keys first. Each element of the
441
+ list is a tuple of key, possible plaintext pairs.
442
+
443
+ EXAMPLES:
444
+
445
+ Use the chi-square statistic to rank all possible keys and their
446
+ corresponding decipherment::
447
+
448
+ sage: A = AffineCryptosystem(AlphabeticStrings())
449
+ sage: a, b = (3, 7)
450
+ sage: P = A.encoding("Line.")
451
+ sage: C = A.enciphering(a, b, P)
452
+ sage: Plist = A.brute_force(C)
453
+ sage: Rank = A.rank_by_chi_square(C, Plist)
454
+ sage: Rank[:10] # display only the top 10 candidate keys
455
+ <BLANKLINE>
456
+ [((1, 1), NETS),
457
+ ((3, 7), LINE),
458
+ ((17, 20), STAD),
459
+ ((5, 2), SLOT),
460
+ ((5, 5), HADI),
461
+ ((9, 25), TSLI),
462
+ ((17, 15), DELO),
463
+ ((15, 6), ETUN),
464
+ ((21, 8), ELID),
465
+ ((7, 17), HCTE)]
466
+
467
+ As more ciphertext is available, the reliability of the chi-square
468
+ ranking function increases::
469
+
470
+ sage: A = AffineCryptosystem(AlphabeticStrings())
471
+ sage: a, b = (11, 24)
472
+ sage: P = A.encoding("Longer message is more information for cryptanalysis.")
473
+ sage: C = A.enciphering(a, b, P)
474
+ sage: Plist = A.brute_force(C)
475
+ sage: Rank = A.rank_by_chi_square(C, Plist)
476
+ sage: Rank[:10] # display only the top 10 candidate keys
477
+ <BLANKLINE>
478
+ [((11, 24), LONGERMESSAGEISMOREINFORMATIONFORCRYPTANALYSIS),
479
+ ((17, 9), INURFSBFLLHRFDLBNSFDUYNSBHEDNUYNSTSVGEHUHIVLDL),
480
+ ((9, 18), RMFIUHYUOOSIUWOYMHUWFBMHYSVWMFBMHGHETVSFSREOWO),
481
+ ((15, 12), VSTACPUCOOGACYOUSPCYTBSPUGNYSTBSPEPIRNGTGVIOYO),
482
+ ((3, 22), PAFOYLKYGGSOYEGKALYEFTALKSBEAFTALILCVBSFSPCGEG),
483
+ ((25, 3), OHSRNADNPPFRNVPDHANVSCHADFEVHSCHAJABWEFSFOBPVP),
484
+ ((7, 25), GHYNVIPVRRLNVFRPHIVFYEHIPLAFHYEHIDITQALYLGTRFR),
485
+ ((5, 2), NEHCIVKISSUCIWSKEVIWHFEVKUPWEHFEVOVABPUHUNASWS),
486
+ ((15, 25), IFGNPCHPBBTNPLBHFCPLGOFCHTALFGOFCRCVEATGTIVBLB),
487
+ ((9, 6), BWPSERIEYYCSEGYIWREGPLWRICFGWPLWRQRODFCPCBOYGY)]
488
+
489
+ TESTS:
490
+
491
+ The ciphertext cannot be an empty string::
492
+
493
+ sage: A.rank_by_chi_square("", Plist)
494
+ Traceback (most recent call last):
495
+ ...
496
+ AttributeError: 'str' object has no attribute 'parent'...
497
+ sage: A.rank_by_chi_square(A.encoding(""), Plist)
498
+ Traceback (most recent call last):
499
+ ...
500
+ ValueError: The ciphertext must be a non-empty string.
501
+ sage: A.rank_by_chi_square(A.encoding(" "), Plist)
502
+ Traceback (most recent call last):
503
+ ...
504
+ ValueError: The ciphertext must be a non-empty string.
505
+
506
+ The ciphertext must be encoded using the capital letters of the
507
+ English alphabet as implemented in
508
+ :func:`AlphabeticStrings()
509
+ <sage.monoids.string_monoid.AlphabeticStrings>`::
510
+
511
+ sage: H = HexadecimalStrings()
512
+ sage: A.rank_by_chi_square(H.encoding("shift"), Plist)
513
+ Traceback (most recent call last):
514
+ ...
515
+ TypeError: The ciphertext must be capital letters of the English alphabet.
516
+ sage: B = BinaryStrings()
517
+ sage: A.rank_by_chi_square(B.encoding("shift"), Plist)
518
+ Traceback (most recent call last):
519
+ ...
520
+ TypeError: The ciphertext must be capital letters of the English alphabet.
521
+
522
+ The dictionary ``pdict`` cannot be empty::
523
+
524
+ sage: A.rank_by_chi_square(C, {})
525
+ Traceback (most recent call last):
526
+ ...
527
+ KeyError: (1, 0)
528
+ """
529
+ # NOTE: the code here is very similar to that in the method
530
+ # rank_by_chi_square() of the class ShiftCryptosystem. The most
531
+ # significant change in the code below is in how the secret key (a,b)
532
+ # is processed.
533
+
534
+ # sanity check
535
+ from sage.monoids.string_monoid import AlphabeticStrings
536
+ if not isinstance(C.parent(), AlphabeticStringMonoid):
537
+ raise TypeError("The ciphertext must be capital letters of the English alphabet.")
538
+ if str(C) == "":
539
+ raise ValueError("The ciphertext must be a non-empty string.")
540
+
541
+ # compute the rank of each key
542
+ AS = AlphabeticStrings()
543
+ # the alphabet in question
544
+ Alph = self.encoding("".join([str(e) for e in AS.gens()]))
545
+ StrAlph = str(Alph)
546
+ # message length
547
+ L = len(C)
548
+ # expected frequency tally
549
+ EA = AS.characteristic_frequency()
550
+ for e in EA:
551
+ EA[e] *= L
552
+ # Compute the rank R_{chi^2}(M) of M with secret key (a,b).
553
+ Rank = []
554
+ for a in self._invertible_A:
555
+ for b in range(self.alphabet_size()):
556
+ # observed frequency tally
557
+ OM = pdict[(a, b)].frequency_distribution().function()
558
+ for e in Alph:
559
+ if e in OM:
560
+ OM[e] *= L
561
+ else:
562
+ OM.setdefault(e, 0.0)
563
+ # the rank R_{chi^2}(M) of M with secret key (a,b)
564
+ RMab = [(OM[AS(e)] - EA[e])**2 / EA[e] for e in StrAlph]
565
+ Rank.append((sum(RMab), (a, b)))
566
+ # Sort in non-decreasing order of chi-square statistic. It's
567
+ # possible that two different keys share the same chi-square
568
+ # statistic.
569
+ Rank = sorted(Rank)
570
+ RankedList = []
571
+ # NOTE: each secret key is a tuple (a,b). So key[0] indexes a,
572
+ # and key[1] indexes b. The value of val is not used at all, making
573
+ # it redundant to access val in the first place. The following line
574
+ # of code is written with readability in mind.
575
+ [RankedList.append((key, pdict[(key[0], key[1])]))
576
+ for val, key in Rank]
577
+ return RankedList
578
+
579
+ def rank_by_squared_differences(self, C, pdict):
580
+ r"""
581
+ Use the squared-differences measure to rank all possible keys.
582
+ Currently, this method only applies to the capital letters of
583
+ the English alphabet.
584
+
585
+ ALGORITHM:
586
+
587
+ Consider a non-empty alphabet `A` consisting of `n`
588
+ elements, and let `C` be a ciphertext encoded using elements of
589
+ `A`. The plaintext `P` corresponding to `C` is also encoded using
590
+ elements of `A`. Let `M` be a candidate decipherment of `C`,
591
+ i.e. `M` is the result of attempting to decrypt `C` using a key
592
+ `(a,b)` which is not necessarily the same key used to encrypt `P`.
593
+ Suppose `F_A(e)` is the characteristic frequency probability of
594
+ `e \in A` and let `F_M(e)` be the message frequency probability with
595
+ respect to `M`. The characteristic frequency probability
596
+ distribution of an alphabet is the expected frequency probability
597
+ distribution for that alphabet. The message frequency probability
598
+ distribution of `M` provides a distribution of the ratio of character
599
+ occurrences over message length. One can interpret the
600
+ characteristic frequency probability `F_A(e)` as the expected
601
+ probability, while the message frequency probability `F_M(e)` is
602
+ the observed probability. If `M` is of length `L`, then the observed
603
+ frequency of `e \in A` is
604
+
605
+ .. MATH::
606
+
607
+ O_M(e)
608
+ =
609
+ F_M(e) \cdot L
610
+
611
+ and the expected frequency of `e \in A` is
612
+
613
+ .. MATH::
614
+
615
+ E_A(e)
616
+ =
617
+ F_A(e) \cdot L
618
+
619
+ The squared-differences, or residual sum of squares, rank
620
+ `R_{RSS}(M)` of `M` corresponding to a key
621
+ `(a,b) \in \ZZ/n\ZZ \times \ZZ/n\ZZ` is given by
622
+
623
+ .. MATH::
624
+
625
+ R_{RSS}(M)
626
+ =
627
+ \sum_{e \in A} \big( O_M(e) - E_A(e) \big)^2
628
+
629
+ Cryptanalysis by exhaustive key search produces a candidate
630
+ decipherment `M_{a,b}` for each possible key `(a,b)`. For a set
631
+ `D = \big\{M_{a_1,b_1}, M_{a_2,b_2}, \dots, M_{a_k,b_k} \big\}`
632
+ of all candidate decipherments corresponding to a ciphertext `C`,
633
+ the smaller is the rank `R_{RSS}(M_{a_i,b_i})` the more likely
634
+ that `(a_i,b_i)` is the secret key. This key ranking method is
635
+ based on the residual sum of squares measure [RSS]_.
636
+
637
+ INPUT:
638
+
639
+ - ``C`` -- the ciphertext, a non-empty string. The ciphertext
640
+ must be encoded using the upper-case letters of the English
641
+ alphabet.
642
+
643
+ - ``pdict`` -- dictionary of key, possible plaintext
644
+ pairs. This should be the output of :func:`brute_force` with
645
+ ``ranking="none"``.
646
+
647
+ OUTPUT:
648
+
649
+ - A list ranking the most likely keys first. Each element of the
650
+ list is a tuple of key, possible plaintext pairs.
651
+
652
+ EXAMPLES:
653
+
654
+ Use the method of squared differences to rank all possible keys
655
+ and their corresponding decipherment::
656
+
657
+ sage: A = AffineCryptosystem(AlphabeticStrings())
658
+ sage: a, b = (3, 7)
659
+ sage: P = A.encoding("Line.")
660
+ sage: C = A.enciphering(a, b, P)
661
+ sage: Plist = A.brute_force(C)
662
+ sage: Rank = A.rank_by_squared_differences(C, Plist)
663
+ sage: Rank[:10] # display only the top 10 candidate keys
664
+ <BLANKLINE>
665
+ [((1, 1), NETS),
666
+ ((15, 6), ETUN),
667
+ ((7, 17), HCTE),
668
+ ((3, 7), LINE),
669
+ ((17, 15), DELO),
670
+ ((9, 4), EDWT),
671
+ ((9, 9), POHE),
672
+ ((21, 8), ELID),
673
+ ((17, 20), STAD),
674
+ ((7, 18), SNEP)]
675
+
676
+ As more ciphertext is available, the reliability of the
677
+ squared-differences ranking function increases::
678
+
679
+ sage: A = AffineCryptosystem(AlphabeticStrings())
680
+ sage: a, b = (11, 24)
681
+ sage: P = A.encoding("Longer message is more information for cryptanalysis.")
682
+ sage: C = A.enciphering(a, b, P)
683
+ sage: Plist = A.brute_force(C)
684
+ sage: Rank = A.rank_by_squared_differences(C, Plist)
685
+ sage: Rank[:10] # display only the top 10 candidate keys
686
+ <BLANKLINE>
687
+ [((11, 24), LONGERMESSAGEISMOREINFORMATIONFORCRYPTANALYSIS),
688
+ ((9, 14), DYRUGTKGAAEUGIAKYTGIRNYTKEHIYRNYTSTQFHEREDQAIA),
689
+ ((23, 24), DSNEUHIUMMAEUOMISHUONZSHIAROSNZSHKHQXRANADQMOM),
690
+ ((23, 1), ETOFVIJVNNBFVPNJTIVPOATIJBSPTOATILIRYSBOBERNPN),
691
+ ((21, 16), VEBGANYAQQOGAMQYENAMBDENYOTMEBDENUNIHTOBOVIQMQ),
692
+ ((7, 12), TULAIVCIEEYAISECUVISLRUVCYNSULRUVQVGDNYLYTGESE),
693
+ ((5, 20), ZQTOUHWUEEGOUIEWQHUITRQHWGBIQTRQHAHMNBGTGZMEIE),
694
+ ((21, 8), JSPUOBMOEECUOAEMSBOAPRSBMCHASPRSBIBWVHCPCJWEAE),
695
+ ((25, 7), SLWVREHRTTJVRZTHLERZWGLEHJIZLWGLENEFAIJWJSFTZT),
696
+ ((25, 15), ATEDZMPZBBRDZHBPTMZHEOTMPRQHTEOTMVMNIQRERANBHB)]
697
+
698
+ TESTS:
699
+
700
+ The ciphertext cannot be an empty string::
701
+
702
+ sage: A.rank_by_squared_differences("", Plist)
703
+ Traceback (most recent call last):
704
+ ...
705
+ AttributeError: 'str' object has no attribute 'parent'...
706
+ sage: A.rank_by_squared_differences(A.encoding(""), Plist)
707
+ Traceback (most recent call last):
708
+ ...
709
+ ValueError: The ciphertext must be a non-empty string.
710
+ sage: A.rank_by_squared_differences(A.encoding(" "), Plist)
711
+ Traceback (most recent call last):
712
+ ...
713
+ ValueError: The ciphertext must be a non-empty string.
714
+
715
+ The ciphertext must be encoded using the capital letters of the
716
+ English alphabet as implemented in
717
+ :func:`AlphabeticStrings()
718
+ <sage.monoids.string_monoid.AlphabeticStrings>`::
719
+
720
+ sage: H = HexadecimalStrings()
721
+ sage: A.rank_by_squared_differences(H.encoding("line"), Plist)
722
+ Traceback (most recent call last):
723
+ ...
724
+ TypeError: The ciphertext must be capital letters of the English alphabet.
725
+ sage: B = BinaryStrings()
726
+ sage: A.rank_by_squared_differences(B.encoding("line"), Plist)
727
+ Traceback (most recent call last):
728
+ ...
729
+ TypeError: The ciphertext must be capital letters of the English alphabet.
730
+
731
+ The dictionary ``pdict`` cannot be empty::
732
+
733
+ sage: A.rank_by_squared_differences(C, {})
734
+ Traceback (most recent call last):
735
+ ...
736
+ KeyError: (1, 0)
737
+ """
738
+ # NOTE: the code here is very similar to that in the method
739
+ # rank_by_squared_differences() of the class ShiftCryptosystem.
740
+ # The most significant change in the code below is in how the
741
+ # secret key (a,b) is processed.
742
+
743
+ # sanity check
744
+ from sage.monoids.string_monoid import AlphabeticStrings
745
+ if not isinstance(C.parent(), AlphabeticStringMonoid):
746
+ raise TypeError("The ciphertext must be capital letters of the English alphabet.")
747
+ if str(C) == "":
748
+ raise ValueError("The ciphertext must be a non-empty string.")
749
+
750
+ # compute the rank of each key
751
+ AS = AlphabeticStrings()
752
+ # the alphabet in question
753
+ Alph = self.encoding("".join([str(e) for e in AS.gens()]))
754
+ StrAlph = str(Alph)
755
+ # message length
756
+ L = len(C)
757
+ # expected frequency tally
758
+ EA = AS.characteristic_frequency()
759
+ for e in EA:
760
+ EA[e] *= L
761
+ # Compute the rank R_{RSS}(M) of M with secret key (a,b).
762
+ Rank = []
763
+ for a in self._invertible_A:
764
+ for b in range(self.alphabet_size()):
765
+ # observed frequency tally
766
+ OM = pdict[(a, b)].frequency_distribution().function()
767
+ for e in Alph:
768
+ if e in OM:
769
+ OM[e] *= L
770
+ else:
771
+ OM.setdefault(e, 0.0)
772
+ # the rank R_{RSS}(M) of M with secret key (a,b)
773
+ RMab = [(OM[AS(e)] - EA[e])**2 for e in StrAlph]
774
+ Rank.append((sum(RMab), (a, b)))
775
+ # Sort in non-decreasing order of squared-differences statistic. It's
776
+ # possible that two different keys share the same squared-differences
777
+ # statistic.
778
+ Rank = sorted(Rank)
779
+ RankedList = []
780
+ # NOTE: each secret key is a tuple (a,b). So key[0] indexes a,
781
+ # and key[1] indexes b. The value of val is not used at all, making
782
+ # it redundant to access val in the first place. The following line
783
+ # of code is written with readability in mind.
784
+ [RankedList.append((key, pdict[(key[0], key[1])]))
785
+ for val, key in Rank]
786
+ return RankedList
787
+
788
+ def brute_force(self, C, ranking='none'):
789
+ r"""
790
+ Attempt a brute force cryptanalysis of the ciphertext ``C``.
791
+
792
+ INPUT:
793
+
794
+ - ``C`` -- a ciphertext over one of the supported alphabets of this
795
+ affine cryptosystem. See the class :class:`AffineCryptosystem` for
796
+ documentation on the supported alphabets.
797
+
798
+ - ``ranking`` -- (default: ``'none'``) the method to use for
799
+ ranking all possible keys. If ``ranking="none"``, then do not
800
+ use any ranking function. The following ranking functions are
801
+ supported:
802
+
803
+ - ``'chi_square'`` -- the chi-square ranking function
804
+ as implemented in the method :func:`rank_by_chi_square`
805
+
806
+ - ``'squared_differences'`` -- the squared differences ranking
807
+ function as implemented in the method
808
+ :func:`rank_by_squared_differences`.
809
+
810
+ OUTPUT:
811
+
812
+ - All the possible plaintext sequences corresponding to the
813
+ ciphertext ``C``. This method effectively uses all the possible
814
+ keys in this affine cryptosystem to decrypt ``C``. The method is
815
+ also referred to as exhaustive key search. The output is a
816
+ dictionary of key, candidate decipherment pairs.
817
+
818
+ EXAMPLES:
819
+
820
+ Cryptanalyze using all possible keys with the option
821
+ ``ranking="none"``::
822
+
823
+ sage: A = AffineCryptosystem(AlphabeticStrings())
824
+ sage: a, b = (3, 7)
825
+ sage: P = A.encoding("Linear"); P
826
+ LINEAR
827
+ sage: C = A.enciphering(a, b, P)
828
+ sage: L = A.brute_force(C)
829
+ sage: sorted(L.items())[:26] # display 26 candidate decipherments
830
+ <BLANKLINE>
831
+ [((1, 0), OFUTHG),
832
+ ((1, 1), NETSGF),
833
+ ((1, 2), MDSRFE),
834
+ ((1, 3), LCRQED),
835
+ ((1, 4), KBQPDC),
836
+ ((1, 5), JAPOCB),
837
+ ((1, 6), IZONBA),
838
+ ((1, 7), HYNMAZ),
839
+ ((1, 8), GXMLZY),
840
+ ((1, 9), FWLKYX),
841
+ ((1, 10), EVKJXW),
842
+ ((1, 11), DUJIWV),
843
+ ((1, 12), CTIHVU),
844
+ ((1, 13), BSHGUT),
845
+ ((1, 14), ARGFTS),
846
+ ((1, 15), ZQFESR),
847
+ ((1, 16), YPEDRQ),
848
+ ((1, 17), XODCQP),
849
+ ((1, 18), WNCBPO),
850
+ ((1, 19), VMBAON),
851
+ ((1, 20), ULAZNM),
852
+ ((1, 21), TKZYML),
853
+ ((1, 22), SJYXLK),
854
+ ((1, 23), RIXWKJ),
855
+ ((1, 24), QHWVJI),
856
+ ((1, 25), PGVUIH)]
857
+
858
+ Use the chi-square ranking function, i.e. ``ranking="chisquare"``::
859
+
860
+ sage: A = AffineCryptosystem(AlphabeticStrings())
861
+ sage: a, b = (3, 7)
862
+ sage: P = A.encoding("Linear functions for encrypting and decrypting."); P
863
+ LINEARFUNCTIONSFORENCRYPTINGANDDECRYPTING
864
+ sage: C = A.enciphering(a, b, P)
865
+ sage: Rank = A.brute_force(C, ranking='chisquare')
866
+ sage: Rank[:10] # display only the top 10 candidate keys
867
+ <BLANKLINE>
868
+ [((3, 7), LINEARFUNCTIONSFORENCRYPTINGANDDECRYPTING),
869
+ ((23, 25), VYTCGPBMTENYSTOBSPCTEPIRNYTAGTDDCEPIRNYTA),
870
+ ((1, 12), CTIHVUKDIBATLIXKLUHIBUPOATINVIEEHBUPOATIN),
871
+ ((11, 15), HSRYELDAROVSWRQDWLYROLUBVSRIERTTYOLUBVSRI),
872
+ ((25, 1), NWHIUVFMHOPWEHSFEVIHOVABPWHCUHLLIOVABPWHC),
873
+ ((25, 7), TCNOABLSNUVCKNYLKBONUBGHVCNIANRROUBGHVCNI),
874
+ ((15, 4), SHIBVOWZILEHDIJWDOBILOFYEHIRVIGGBLOFYEHIR),
875
+ ((15, 23), PEFYSLTWFIBEAFGTALYFILCVBEFOSFDDYILCVBEFO),
876
+ ((7, 10), IDUFHSYXUTEDNULYNSFUTSVGEDURHUMMFTSVGEDUR),
877
+ ((19, 22), QVETRGABEFUVLENALGTEFGDSUVEHREMMTFGDSUVEH)]
878
+
879
+ Use the squared differences ranking function, i.e.
880
+ ``ranking="squared_differences"``::
881
+
882
+ sage: Rank = A.brute_force(C, ranking='squared_differences')
883
+ sage: Rank[:10] # display only the top 10 candidate keys
884
+ <BLANKLINE>
885
+ [((3, 7), LINEARFUNCTIONSFORENCRYPTINGANDDECRYPTING),
886
+ ((23, 6), GJENRAMXEPYJDEZMDANEPATCYJELREOONPATCYJEL),
887
+ ((23, 25), VYTCGPBMTENYSTOBSPCTEPIRNYTAGTDDCEPIRNYTA),
888
+ ((19, 22), QVETRGABEFUVLENALGTEFGDSUVEHREMMTFGDSUVEH),
889
+ ((19, 9), DIRGETNORSHIYRANYTGRSTQFHIRUERZZGSTQFHIRU),
890
+ ((23, 18), KNIRVEQBITCNHIDQHERITEXGCNIPVISSRTEXGCNIP),
891
+ ((17, 16), GHORBEIDOJMHFOVIFEROJETWMHOZBOAARJETWMHOZ),
892
+ ((21, 14), AHEZRMOFEVQHTEBOTMZEVMNIQHEDREKKZVMNIQHED),
893
+ ((1, 12), CTIHVUKDIBATLIXKLUHIBUPOATINVIEEHBUPOATIN),
894
+ ((7, 18), SNEPRCIHEDONXEVIXCPEDCFQONEBREWWPDCFQONEB)]
895
+
896
+ TESTS:
897
+
898
+ Currently, the binary number system is not supported as an
899
+ alphabet of this affine cryptosystem::
900
+
901
+ sage: A = AffineCryptosystem(AlphabeticStrings())
902
+ sage: BinStr = BinaryStrings()
903
+ sage: C = BinStr.encoding("abc")
904
+ sage: A.brute_force(C)
905
+ Traceback (most recent call last):
906
+ ...
907
+ TypeError: Ciphertext must be encoded using one of the supported cipher domains of this affine cryptosystem.
908
+
909
+ Nor are the octal, hexadecimal, and radix-64 number systems
910
+ supported::
911
+
912
+ sage: OctStr = OctalStrings()
913
+ sage: C = OctStr([1, 2, 3])
914
+ sage: A.brute_force(C)
915
+ Traceback (most recent call last):
916
+ ...
917
+ TypeError: Ciphertext must be encoded using one of the supported cipher domains of this affine cryptosystem.
918
+ sage: HexStr = HexadecimalStrings()
919
+ sage: C = HexStr.encoding("abc")
920
+ sage: A.brute_force(C)
921
+ Traceback (most recent call last):
922
+ ...
923
+ TypeError: Ciphertext must be encoded using one of the supported cipher domains of this affine cryptosystem.
924
+ sage: RadStr = Radix64Strings()
925
+ sage: C = RadStr([1, 2, 3])
926
+ sage: A.brute_force(C)
927
+ Traceback (most recent call last):
928
+ ...
929
+ TypeError: Ciphertext must be encoded using one of the supported cipher domains of this affine cryptosystem.
930
+
931
+ Only the chi-square and squared-differences ranking functions are
932
+ currently supported. The keyword ``ranking`` must take on either
933
+ of the values ``'none'``, ``'chisquare'`` or
934
+ ``'squared_differences'``::
935
+
936
+ sage: A = AffineCryptosystem(AlphabeticStrings())
937
+ sage: a, b = (3, 7)
938
+ sage: P = A.encoding("Linear")
939
+ sage: C = A.enciphering(a, b, P)
940
+ sage: A.brute_force(C, ranking='chi')
941
+ Traceback (most recent call last):
942
+ ...
943
+ ValueError: Keyword 'ranking' must be either 'none', 'chisquare', or 'squared_differences'.
944
+ sage: A.brute_force(C, ranking="")
945
+ Traceback (most recent call last):
946
+ ...
947
+ ValueError: Keyword 'ranking' must be either 'none', 'chisquare', or 'squared_differences'.
948
+ """
949
+ # Sanity check: ensure that C is encoded using one of the
950
+ # supported alphabets of this affine cryptosystem.
951
+ if not isinstance(C.parent(), AlphabeticStringMonoid):
952
+ raise TypeError("Ciphertext must be encoded using one of the supported cipher domains of this affine cryptosystem.")
953
+ ranking_functions = ["none", "chisquare", "squared_differences"]
954
+ if ranking not in ranking_functions:
955
+ raise ValueError("Keyword 'ranking' must be either 'none', 'chisquare', or 'squared_differences'.")
956
+
957
+ # Now do the actual task of cryptanalysis by means of exhaustive
958
+ # key search, also known as the brute force method. Let D be a
959
+ # dictionary of key/plaintext pairs.
960
+ D = {}
961
+
962
+ # NOTE: This is a good candidate for loop unrolling and
963
+ # further optimization. Unless we can justify that this block of
964
+ # code is a bottleneck on the runtime of the method, we should
965
+ # leave it as is.
966
+ [D.setdefault((a, b), self.deciphering(a, b, C))
967
+ for a in self._invertible_A
968
+ for b in range(self.alphabet_size())]
969
+
970
+ if ranking == "none":
971
+ return D
972
+ if ranking == "chisquare":
973
+ return self.rank_by_chi_square(C, D)
974
+ if ranking == "squared_differences":
975
+ return self.rank_by_squared_differences(C, D)
976
+
977
+ def deciphering(self, a, b, C):
978
+ r"""
979
+ Decrypt the ciphertext ``C`` with the key ``(a, b)`` using affine
980
+ cipher decryption.
981
+
982
+ INPUT:
983
+
984
+ - ``a``, ``b`` -- a secret key belonging to the key space of this affine
985
+ cipher. This key must be an element of
986
+ `\ZZ/n\ZZ \times \ZZ/n\ZZ` such that `\gcd(a,n) = 1` with `n`
987
+ being the size of the ciphertext and plaintext spaces.
988
+
989
+ - ``C`` -- string of ciphertext; possibly an empty string.
990
+ Characters in this string must be encoded using one of the
991
+ supported alphabets. See the method :func:`encoding()` for more
992
+ information.
993
+
994
+ OUTPUT: the plaintext corresponding to the ciphertext ``C``
995
+
996
+ EXAMPLES:
997
+
998
+ Decryption over the capital letters of the English alphabet::
999
+
1000
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1001
+ sage: a, b = (5, 2)
1002
+ sage: P = A.encoding("Affine functions are linear functions.")
1003
+ sage: C = A.enciphering(a, b, P); C
1004
+ CBBQPWBYPMTQUPOCJWFQPWCJBYPMTQUPO
1005
+ sage: P == A.deciphering(a, b, C)
1006
+ True
1007
+
1008
+ The previous example can also be worked through using functional
1009
+ notation::
1010
+
1011
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1012
+ sage: a, b = (5, 2)
1013
+ sage: P = A.encoding("Affine functions are linear functions.")
1014
+ sage: E = A(a, b); E
1015
+ Affine cipher on Free alphabetic string monoid on A-Z
1016
+ sage: C = E(P); C
1017
+ CBBQPWBYPMTQUPOCJWFQPWCJBYPMTQUPO
1018
+ sage: aInv, bInv = A.inverse_key(a, b)
1019
+ sage: D = A(aInv, bInv); D
1020
+ Affine cipher on Free alphabetic string monoid on A-Z
1021
+ sage: D(C) == P
1022
+ True
1023
+
1024
+ If the ciphertext is an empty string, then the plaintext is also
1025
+ an empty string regardless of the value of the secret key::
1026
+
1027
+ sage: a, b = A.random_key()
1028
+ sage: A.deciphering(a, b, A.encoding(""))
1029
+ <BLANKLINE>
1030
+ sage: A.deciphering(a, b, A.encoding(" "))
1031
+ <BLANKLINE>
1032
+
1033
+ TESTS:
1034
+
1035
+ The key must be an ordered pair
1036
+ `(a,b) \in \ZZ/n\ZZ \times \ZZ/n\ZZ` with `n` being the size of the
1037
+ plaintext and ciphertext spaces. Furthermore, `a` must be
1038
+ relatively prime to `n`, i.e. `\gcd(a,n) = 1`::
1039
+
1040
+ sage: A.deciphering(2, 6, P)
1041
+ Traceback (most recent call last):
1042
+ ...
1043
+ ValueError: (a, b) = (2, 6) is outside the range of acceptable values for a key of this affine cipher.
1044
+ """
1045
+ aInv, bInv = self.inverse_key(a, b)
1046
+ D = self(aInv, bInv)
1047
+ return D(C)
1048
+
1049
+ def enciphering(self, a, b, P):
1050
+ r"""
1051
+ Encrypt the plaintext ``P`` with the key ``(a, b)`` using affine cipher
1052
+ encryption.
1053
+
1054
+ INPUT:
1055
+
1056
+ - ``a``, ``b`` -- a secret key belonging to the key space of this affine
1057
+ cipher. This key must be an element of
1058
+ `\ZZ/n\ZZ \times \ZZ/n\ZZ` such that `\gcd(a,n) = 1` with `n`
1059
+ being the size of the ciphertext and plaintext spaces.
1060
+
1061
+ - ``P`` -- string of plaintext; possibly an empty string.
1062
+ Characters in this string must be encoded using one of the
1063
+ supported alphabets. See the method :func:`encoding()` for more
1064
+ information.
1065
+
1066
+ OUTPUT: the ciphertext corresponding to the plaintext ``P``
1067
+
1068
+ EXAMPLES:
1069
+
1070
+ Encryption over the capital letters of the English alphabet::
1071
+
1072
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1073
+ sage: a, b = (3, 6)
1074
+ sage: P = A.encoding("Affine ciphers work with linear functions.")
1075
+ sage: A.enciphering(a, b, P)
1076
+ GVVETSMEZBSFIUWFKUELBNETSGFVOTMLEWTI
1077
+
1078
+ Now work through the previous example using functional notation::
1079
+
1080
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1081
+ sage: a, b = (3, 6)
1082
+ sage: P = A.encoding("Affine ciphers work with linear functions.")
1083
+ sage: E = A(a, b); E
1084
+ Affine cipher on Free alphabetic string monoid on A-Z
1085
+ sage: E(P)
1086
+ GVVETSMEZBSFIUWFKUELBNETSGFVOTMLEWTI
1087
+
1088
+ If the plaintext is an empty string, then the ciphertext is also
1089
+ an empty string regardless of the value of the secret key::
1090
+
1091
+ sage: a, b = A.random_key()
1092
+ sage: A.enciphering(a, b, A.encoding(""))
1093
+ <BLANKLINE>
1094
+ sage: A.enciphering(a, b, A.encoding(" "))
1095
+ <BLANKLINE>
1096
+
1097
+ TESTS:
1098
+
1099
+ The key must be an ordered pair
1100
+ `(a,b) \in \ZZ/n\ZZ \times \ZZ/n\ZZ` with `n` being the size of the
1101
+ plaintext and ciphertext spaces. Furthermore, `a` must be
1102
+ relatively prime to `n`, i.e. `\gcd(a,n) = 1`::
1103
+
1104
+ sage: A.enciphering(2, 6, P)
1105
+ Traceback (most recent call last):
1106
+ ...
1107
+ ValueError: (a, b) = (2, 6) is outside the range of acceptable values for a key of this affine cryptosystem.
1108
+ """
1109
+ E = self(a, b)
1110
+ return E(P)
1111
+
1112
+ def encoding(self, S):
1113
+ r"""
1114
+ The encoding of the string ``S`` over the string monoid of this
1115
+ affine cipher. For example, if the string monoid of this cryptosystem
1116
+ is
1117
+ :class:`AlphabeticStringMonoid <sage.monoids.string_monoid.AlphabeticStringMonoid>`,
1118
+ then the encoding of ``S`` would be its upper-case equivalent
1119
+ stripped of all non-alphabetic characters. Only the following alphabet
1120
+ is supported for the affine cipher:
1121
+
1122
+ - capital letters of the English alphabet as implemented in
1123
+ :func:`AlphabeticStrings() <sage.monoids.string_monoid.AlphabeticStrings>`
1124
+
1125
+ INPUT:
1126
+
1127
+ - ``S`` -- string, possibly empty
1128
+
1129
+ OUTPUT: the encoding of ``S`` over the string monoid of this
1130
+ cryptosystem; if ``S`` is an empty string, return an empty string
1131
+
1132
+ EXAMPLES:
1133
+
1134
+ Encoding over the upper-case letters of the English alphabet::
1135
+
1136
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1137
+ sage: A.encoding("Affine cipher over capital letters of the English alphabet.")
1138
+ AFFINECIPHEROVERCAPITALLETTERSOFTHEENGLISHALPHABET
1139
+
1140
+ The argument ``S`` can be an empty string, in which case an empty
1141
+ string is returned::
1142
+
1143
+ sage: AffineCryptosystem(AlphabeticStrings()).encoding("")
1144
+ <BLANKLINE>
1145
+ """
1146
+ D = self.cipher_domain()
1147
+ if isinstance(D, AlphabeticStringMonoid):
1148
+ return D(strip_encoding(S))
1149
+ try:
1150
+ return D.encoding(S)
1151
+ except Exception:
1152
+ raise TypeError("Argument S = %s does not encode in the cipher domain" % S)
1153
+
1154
+ def inverse_key(self, a, b):
1155
+ r"""
1156
+ The inverse key corresponding to the secret key `(a,b)`. If `p` is
1157
+ a plaintext character so that `p \in \ZZ/n\ZZ` and `n` is the
1158
+ alphabet size, then the ciphertext `c` corresponding to `p` is
1159
+
1160
+ .. MATH::
1161
+
1162
+ c \equiv ap + b \pmod{n}
1163
+
1164
+ As `(a,b)` is a key, then the multiplicative inverse `a^{-1}`
1165
+ exists and the original plaintext can be recovered as follows
1166
+
1167
+ .. MATH::
1168
+
1169
+ p \equiv a^{-1} (c - b) \pmod{n}
1170
+ \equiv a^{-1}c + a^{-1}(-b) \pmod{n}
1171
+
1172
+ Therefore the ordered pair `(a^{-1}, -ba^{-1})` is the inverse key
1173
+ corresponding to `(a,b)`.
1174
+
1175
+ INPUT:
1176
+
1177
+ - ``a``, ``b`` -- a secret key for this affine cipher. The ordered pair
1178
+ `(a,b)` must be an element of `\ZZ/n\ZZ \times \ZZ/n\ZZ` such that
1179
+ `\gcd(a,n) = 1`.
1180
+
1181
+ OUTPUT:
1182
+
1183
+ - The inverse key `(a^{-1}, -ba^{-1})` corresponding to `(a,b)`.
1184
+
1185
+ EXAMPLES::
1186
+
1187
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1188
+ sage: a, b = (1, 2)
1189
+ sage: A.inverse_key(a, b)
1190
+ (1, 24)
1191
+ sage: A.inverse_key(3, 2)
1192
+ (9, 8)
1193
+
1194
+ Suppose that the plaintext and ciphertext spaces are the capital
1195
+ letters of the English alphabet so that `n = 26`. If `\varphi(n)`
1196
+ is the Euler phi function of `n`, then there are `\varphi(n)`
1197
+ integers `0 \leq a < n` that are relatively prime to `n`. For the
1198
+ capital letters of the English alphabet, there are 12 such integers
1199
+ relatively prime to `n`::
1200
+
1201
+ sage: euler_phi(A.alphabet_size()) # needs sage.libs.pari
1202
+ 12
1203
+
1204
+ And here is a list of those integers::
1205
+
1206
+ sage: n = A.alphabet_size()
1207
+ sage: L = [i for i in range(n) if gcd(i, n) == 1]; L
1208
+ [1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25]
1209
+
1210
+ Then a secret key `(a,b)` of this shift cryptosystem is
1211
+ such that `a` is an element of the list ``L`` in the last example.
1212
+ Any inverse key `(A, B)` corresponding to `(a,b)` is such that
1213
+ `A` is also in the list ``L`` above::
1214
+
1215
+ sage: a, b = (3, 9)
1216
+ sage: a in L
1217
+ True
1218
+ sage: aInv, bInv = A.inverse_key(a, b)
1219
+ sage: aInv, bInv
1220
+ (9, 23)
1221
+ sage: aInv in L
1222
+ True
1223
+
1224
+ TESTS:
1225
+
1226
+ Any ordered pair of the form `(0, b)` for any integer `b` cannot be
1227
+ a secret key of this affine cipher. Hence `(0, b)` does not have
1228
+ a corresponding inverse key::
1229
+
1230
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1231
+ sage: A.inverse_key(0, 1)
1232
+ Traceback (most recent call last):
1233
+ ...
1234
+ ValueError: (a, b) = (0, 1) is outside the range of acceptable values for a key of this affine cipher.
1235
+ """
1236
+ try:
1237
+ from sage.rings.finite_rings.integer_mod import Mod
1238
+ n = self.alphabet_size()
1239
+ aInv = inverse_mod(a, n)
1240
+ bInv = Mod(-b * aInv, n).lift()
1241
+ return (aInv, bInv)
1242
+ except Exception:
1243
+ raise ValueError("(a, b) = (%s, %s) is outside the range of acceptable values for a key of this affine cipher." % (a, b))
1244
+
1245
+ def random_key(self):
1246
+ r"""
1247
+ Generate a random key within the key space of this affine cipher.
1248
+ The generated secret key is an ordered pair
1249
+ `(a, b) \in \ZZ/n\ZZ \times \ZZ/n\ZZ` with `n` being the size of
1250
+ the cipher domain and `\gcd(a, n) = 1`. Let `\varphi(n)` denote
1251
+ the Euler phi function of `n`. Then the affine cipher has
1252
+ `n \cdot \varphi(n)` possible keys (see page 10 of [Sti2006]_).
1253
+
1254
+ OUTPUT:
1255
+
1256
+ - A random key within the key space of this affine cryptosystem.
1257
+ The output key is an ordered pair `(a,b)`.
1258
+
1259
+ EXAMPLES::
1260
+
1261
+ sage: A = AffineCryptosystem(AlphabeticStrings())
1262
+ sage: A.random_key() # random
1263
+ (17, 25)
1264
+
1265
+ If `(a,b)` is a secret key and `n` is the size of the plaintext and
1266
+ ciphertext alphabets, then `\gcd(a, n) = 1`::
1267
+
1268
+ sage: a, b = A.random_key()
1269
+ sage: n = A.alphabet_size()
1270
+ sage: gcd(a, n)
1271
+ 1
1272
+ """
1273
+ # Return a random element in ZZ/nZZ x ZZ/nZZ where n is the number
1274
+ # of elements in the plaintext/ciphertext alphabet.
1275
+ from sage.misc.prandom import randint
1276
+ n = self.alphabet_size()
1277
+ L = len(self._invertible_A)
1278
+ a = Integer(self._invertible_A[randint(0, L - 1)])
1279
+ b = Integer(randint(0, n - 1))
1280
+ return (a, b)
1281
+
1282
+
1283
+ class HillCryptosystem(SymmetricKeyCryptosystem):
1284
+ r"""
1285
+ Create a Hill cryptosystem defined by the `m \times m` matrix space
1286
+ over `\ZZ / N \ZZ`, where `N` is the alphabet size of
1287
+ the string monoid ``S``.
1288
+
1289
+ INPUT:
1290
+
1291
+ - ``S`` -- string monoid over some alphabet
1292
+
1293
+ - ``m`` -- integer `> 0`; the block length of matrices that specify
1294
+ block permutations
1295
+
1296
+ OUTPUT: a Hill cryptosystem of block length ``m`` over the alphabet ``S``
1297
+
1298
+ EXAMPLES::
1299
+
1300
+ sage: # needs sage.modules
1301
+ sage: S = AlphabeticStrings()
1302
+ sage: E = HillCryptosystem(S, 3); E
1303
+ Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3
1304
+ sage: R = IntegerModRing(26)
1305
+ sage: M = MatrixSpace(R,3,3)
1306
+ sage: A = M([[1,0,1],[0,1,1],[2,2,3]]); A
1307
+ [1 0 1]
1308
+ [0 1 1]
1309
+ [2 2 3]
1310
+ sage: e = E(A); e
1311
+ Hill cipher on Free alphabetic string monoid on A-Z of block length 3
1312
+ sage: e(S("LAMAISONBLANCHE"))
1313
+ JYVKSKQPELAYKPV
1314
+
1315
+ TESTS::
1316
+
1317
+ sage: S = AlphabeticStrings()
1318
+ sage: E = HillCryptosystem(S, 3) # needs sage.modules
1319
+ sage: E == loads(dumps(E)) # needs sage.modules
1320
+ True
1321
+ """
1322
+
1323
+ def __init__(self, S, m):
1324
+ r"""
1325
+ See ``HillCryptosystem`` for full documentation.
1326
+
1327
+ Create a Hill cryptosystem defined by the `m \times m` matrix space
1328
+ over `\ZZ / N \ZZ`, where `N` is the alphabet size of
1329
+ the string monoid ``S``.
1330
+
1331
+ INPUT:
1332
+
1333
+ - ``S`` -- string monoid over some alphabet
1334
+
1335
+ - ``m`` -- integer `> 0`; the block length of matrices that specify
1336
+ block permutations
1337
+
1338
+ OUTPUT: a Hill cryptosystem of block length ``m`` over the alphabet ``S``
1339
+
1340
+ EXAMPLES::
1341
+
1342
+ sage: S = AlphabeticStrings()
1343
+ sage: E = HillCryptosystem(S, 3); E # needs sage.modules
1344
+ Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3
1345
+ """
1346
+ if not isinstance(S, StringMonoid_class):
1347
+ raise TypeError("S (= %s) must be a string monoid." % S)
1348
+ R = IntegerModRing(S.ngens())
1349
+ M = MatrixSpace(R, m, m)
1350
+ SymmetricKeyCryptosystem.__init__(self, S, S, M, block_length=m)
1351
+
1352
+ def __call__(self, A):
1353
+ """
1354
+ Create a Hill cipher.
1355
+
1356
+ INPUT:
1357
+
1358
+ - ``A`` -- a matrix which specifies a block permutation
1359
+
1360
+ EXAMPLES::
1361
+
1362
+ sage: # needs sage.modules
1363
+ sage: S = AlphabeticStrings()
1364
+ sage: E = HillCryptosystem(S,3); E
1365
+ Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3
1366
+ sage: M = E.key_space()
1367
+ sage: A = M([[1,0,1],[0,1,1],[2,2,3]]); A
1368
+ [1 0 1]
1369
+ [0 1 1]
1370
+ [2 2 3]
1371
+ sage: e = E(A); e
1372
+ Hill cipher on Free alphabetic string monoid on A-Z of block length 3
1373
+ sage: m = S("LAMAISONBLANCHE")
1374
+ sage: e(m)
1375
+ JYVKSKQPELAYKPV
1376
+ sage: c = e.inverse()
1377
+ sage: c(e(m))
1378
+ LAMAISONBLANCHE
1379
+ """
1380
+ M = self.key_space()
1381
+ m = self.block_length()
1382
+ if isinstance(A, list):
1383
+ try:
1384
+ A = M(A)
1385
+ except Exception:
1386
+ raise TypeError("A (= %s) must specify a square matrix of degree %s." % (A, m))
1387
+ return HillCipher(self, A)
1388
+
1389
+ def _repr_(self):
1390
+ """
1391
+ Return a string representation of ``self``.
1392
+
1393
+ EXAMPLES::
1394
+
1395
+ sage: A = AlphabeticStrings()
1396
+ sage: H = HillCryptosystem(A, 3); H # needs sage.modules
1397
+ Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3
1398
+ sage: H._repr_() # needs sage.modules
1399
+ 'Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3'
1400
+ """
1401
+ return "Hill cryptosystem on %s of block length %s" % (
1402
+ self.cipher_domain(), self.block_length())
1403
+
1404
+ def block_length(self):
1405
+ """
1406
+ The row or column dimension of a matrix specifying a block
1407
+ permutation. Encryption and decryption keys of a Hill cipher are
1408
+ square matrices, i.e. the row and column dimensions of an encryption
1409
+ or decryption key are the same. This row/column dimension is referred
1410
+ to as the *block length*.
1411
+
1412
+ OUTPUT: the block length of an encryption/decryption key
1413
+
1414
+ EXAMPLES::
1415
+
1416
+ sage: A = AlphabeticStrings()
1417
+ sage: n = randint(1, A.ngens() - 1)
1418
+ sage: H = HillCryptosystem(A, n) # needs sage.modules
1419
+ sage: H.block_length() == n # needs sage.modules
1420
+ True
1421
+ """
1422
+ return self.key_space().nrows()
1423
+
1424
+ def random_key(self):
1425
+ r"""
1426
+ A random key within the key space of this Hill cipher. That is,
1427
+ generate a random `m \times m` matrix to be used as a block
1428
+ permutation, where `m` is the block length of this Hill cipher. If
1429
+ `n` is the size of the cryptosystem alphabet, then there are
1430
+ `n^{m^2}` possible keys. However the number of valid keys,
1431
+ i.e. invertible `m \times m` square matrices, is smaller than
1432
+ `n^{m^2}`.
1433
+
1434
+ OUTPUT: a random key within the key space of this Hill cipher
1435
+
1436
+ EXAMPLES::
1437
+
1438
+ sage: # needs sage.modules
1439
+ sage: A = AlphabeticStrings()
1440
+ sage: n = 3
1441
+ sage: H = HillCryptosystem(A, n)
1442
+ sage: K = H.random_key()
1443
+ sage: Ki = H.inverse_key(K)
1444
+ sage: M = "LAMAISONBLANCHE"
1445
+ sage: e = H(K)
1446
+ sage: d = H(Ki)
1447
+ sage: d(e(A(M))) == A(M)
1448
+ True
1449
+ """
1450
+ M = self.key_space()
1451
+ m = M.nrows()
1452
+ N = Integer(self.cipher_domain().ngens())
1453
+ while True:
1454
+ A = M([randint(0, N-1) for i in range(m**2)])
1455
+ if N.gcd(A.det().lift()) == 1:
1456
+ break
1457
+ return A
1458
+
1459
+ def inverse_key(self, A):
1460
+ """
1461
+ The inverse key corresponding to the key ``A``.
1462
+
1463
+ INPUT:
1464
+
1465
+ - ``A`` -- an invertible matrix of the key space of this Hill cipher
1466
+
1467
+ OUTPUT: the inverse matrix of ``A``
1468
+
1469
+ EXAMPLES::
1470
+
1471
+ sage: # needs sage.modules
1472
+ sage: S = AlphabeticStrings()
1473
+ sage: E = HillCryptosystem(S, 3)
1474
+ sage: A = E.random_key()
1475
+ sage: B = E.inverse_key(A)
1476
+ sage: M = S("LAMAISONBLANCHE")
1477
+ sage: e = E(A)
1478
+ sage: c = E(B)
1479
+ sage: c(e(M))
1480
+ LAMAISONBLANCHE
1481
+ """
1482
+ S = self.plaintext_space()
1483
+ M = self.key_space()
1484
+ if A not in M:
1485
+ raise TypeError("A (= %s) must be a matrix in the key space of %s." % (A, self))
1486
+ m = self.block_length()
1487
+ MatZZ = MatrixSpace(ZZ, m)
1488
+ AZ = MatZZ([[A[i, j].lift() for j in range(m)] for i in range(m)])
1489
+ AZ_adj = AZ.adjugate()
1490
+ u, r, s = xgcd(A.det().lift(), S.ngens())
1491
+ if u != 1:
1492
+ raise ValueError("Argument:\n\n%s\n\nis not invertible." % (A))
1493
+ return r * A.parent()(AZ_adj)
1494
+
1495
+ def encoding(self, M):
1496
+ """
1497
+ The encoding of the string ``M`` over the string monoid of this
1498
+ Hill cipher. For example, if the string monoid of this Hill cipher
1499
+ is :class:`AlphabeticStringMonoid`, then the encoding of ``M`` would
1500
+ be its upper-case equivalent stripped of all non-alphabetic
1501
+ characters.
1502
+
1503
+ INPUT:
1504
+
1505
+ - ``M`` -- string, possibly empty
1506
+
1507
+ OUTPUT: the encoding of ``M`` over the string monoid of this Hill
1508
+ cipher
1509
+
1510
+ EXAMPLES::
1511
+
1512
+ sage: M = "The matrix cipher by Lester S. Hill."
1513
+ sage: A = AlphabeticStrings()
1514
+ sage: H = HillCryptosystem(A, 7) # needs sage.modules
1515
+ sage: H.encoding(M) == A.encoding(M) # needs sage.modules
1516
+ True
1517
+ """
1518
+ S = self.cipher_domain()
1519
+ if isinstance(S, AlphabeticStringMonoid):
1520
+ return S(strip_encoding(M))
1521
+ try:
1522
+ return S.encoding(M)
1523
+ except Exception:
1524
+ raise TypeError("Argument M = %s does not encode in the cipher domain" % M)
1525
+
1526
+ def deciphering(self, A, C):
1527
+ """
1528
+ Decrypt the ciphertext ``C`` using the key ``A``.
1529
+
1530
+ INPUT:
1531
+
1532
+ - ``A`` -- a key within the key space of this Hill cipher
1533
+
1534
+ - ``C`` -- string (possibly empty) over the string monoid of this
1535
+ Hill cipher
1536
+
1537
+ OUTPUT: the plaintext corresponding to the ciphertext ``C``
1538
+
1539
+ EXAMPLES::
1540
+
1541
+ sage: # needs sage.modules
1542
+ sage: H = HillCryptosystem(AlphabeticStrings(), 3)
1543
+ sage: K = H.random_key()
1544
+ sage: M = H.encoding("Good day, mate! How ya going?")
1545
+ sage: H.deciphering(K, H.enciphering(K, M)) == M
1546
+ True
1547
+ """
1548
+ # TODO: some type checking that A is invertible hence a valid key
1549
+ i = self(self.inverse_key(A))
1550
+ return i(C)
1551
+
1552
+ def enciphering(self, A, M):
1553
+ """
1554
+ Encrypt the plaintext ``M`` using the key ``A``.
1555
+
1556
+ INPUT:
1557
+
1558
+ - ``A`` -- a key within the key space of this Hill cipher
1559
+
1560
+ - ``M`` -- string (possibly empty) over the string monoid of this
1561
+ Hill cipher
1562
+
1563
+ OUTPUT: the ciphertext corresponding to the plaintext ``M``
1564
+
1565
+ EXAMPLES::
1566
+
1567
+ sage: # needs sage.modules
1568
+ sage: H = HillCryptosystem(AlphabeticStrings(), 3)
1569
+ sage: K = H.random_key()
1570
+ sage: M = H.encoding("Good day, mate! How ya going?")
1571
+ sage: H.deciphering(K, H.enciphering(K, M)) == M
1572
+ True
1573
+ """
1574
+ # TODO: some type checking that A is invertible hence a valid key
1575
+ e = self(A)
1576
+ return e(M)
1577
+
1578
+
1579
+ class ShiftCryptosystem(SymmetricKeyCryptosystem):
1580
+ r"""
1581
+ Create a shift cryptosystem.
1582
+
1583
+ Let `A = \{ a_0, a_1, a_2, \dots, a_{n-1} \}` be a non-empty alphabet
1584
+ consisting of `n` unique elements. Define a mapping
1585
+ `f : A \longrightarrow \ZZ/ n\ZZ` from the alphabet `A` to
1586
+ the set `\ZZ / n\ZZ` of integers modulo `n`, given by
1587
+ `f(a_i) = i`. Thus we can identify each element of the alphabet `A`
1588
+ with a unique integer `0 \leq i < n`. A key of the shift cipher is an
1589
+ integer `0 \leq k < n`. Therefore the key space is `\ZZ / n\ZZ`. Since
1590
+ we assume that `A` does not have repeated elements, the mapping
1591
+ `f : A \longrightarrow \ZZ/ n\ZZ` is bijective.
1592
+ Encryption works by moving along the alphabet by `k` positions, with
1593
+ wrap around. Decryption reverses the process by moving backwards by
1594
+ `k` positions, with wrap around. More generally, let `k` be a secret key,
1595
+ i.e. an element of the key space, and let `p` be a plaintext
1596
+ character and consequently `p \in \ZZ / n\ZZ`. Then the ciphertext
1597
+ character `c` corresponding to `p` is given by
1598
+
1599
+ .. MATH::
1600
+
1601
+ c \equiv p + k \pmod{n}
1602
+
1603
+ Similarly, given a ciphertext character `c \in \ZZ / n\ZZ` and a secret
1604
+ key `k`, we can recover the corresponding plaintext character as follows:
1605
+
1606
+ .. MATH::
1607
+
1608
+ p \equiv c - k \pmod{n}
1609
+
1610
+ Use the bijection `f : A \longrightarrow \ZZ/ n\ZZ` to convert `c`
1611
+ and `p` back to elements of the alphabet `A`. Currently, the following
1612
+ alphabets are supported for the shift cipher:
1613
+
1614
+ - capital letters of the English alphabet as implemented in
1615
+ :func:`AlphabeticStrings()
1616
+ <sage.monoids.string_monoid.AlphabeticStrings>`
1617
+
1618
+ - the alphabet consisting of the hexadecimal number system as
1619
+ implemented in
1620
+ :func:`HexadecimalStrings()
1621
+ <sage.monoids.string_monoid.HexadecimalStrings>`
1622
+
1623
+ - the alphabet consisting of the binary number system as implemented in
1624
+ :func:`BinaryStrings() <sage.monoids.string_monoid.BinaryStrings>`
1625
+
1626
+ EXAMPLES:
1627
+
1628
+ Some examples illustrating encryption and decryption over various
1629
+ alphabets. Here is an example over the upper-case letters of the English
1630
+ alphabet::
1631
+
1632
+ sage: S = ShiftCryptosystem(AlphabeticStrings()); S
1633
+ Shift cryptosystem on Free alphabetic string monoid on A-Z
1634
+ sage: P = S.encoding("The shift cryptosystem generalizes the Caesar cipher.")
1635
+ sage: P
1636
+ THESHIFTCRYPTOSYSTEMGENERALIZESTHECAESARCIPHER
1637
+ sage: K = 7
1638
+ sage: C = S.enciphering(K, P); C
1639
+ AOLZOPMAJYFWAVZFZALTNLULYHSPGLZAOLJHLZHYJPWOLY
1640
+ sage: S.deciphering(K, C)
1641
+ THESHIFTCRYPTOSYSTEMGENERALIZESTHECAESARCIPHER
1642
+ sage: S.deciphering(K, C) == P
1643
+ True
1644
+
1645
+ The previous example can also be done as follows::
1646
+
1647
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
1648
+ sage: P = S.encoding("The shift cryptosystem generalizes the Caesar cipher.")
1649
+ sage: K = 7
1650
+ sage: E = S(K); E
1651
+ Shift cipher on Free alphabetic string monoid on A-Z
1652
+ sage: C = E(P); C
1653
+ AOLZOPMAJYFWAVZFZALTNLULYHSPGLZAOLJHLZHYJPWOLY
1654
+ sage: D = S(S.inverse_key(K)); D
1655
+ Shift cipher on Free alphabetic string monoid on A-Z
1656
+ sage: D(C) == P
1657
+ True
1658
+ sage: D(C) == P == D(E(P))
1659
+ True
1660
+
1661
+ Over the hexadecimal number system::
1662
+
1663
+ sage: S = ShiftCryptosystem(HexadecimalStrings()); S
1664
+ Shift cryptosystem on Free hexadecimal string monoid
1665
+ sage: P = S.encoding("Encryption & decryption shifts along the alphabet."); P
1666
+ 456e6372797074696f6e20262064656372797074696f6e2073686966747320616c6f6e672074686520616c7068616265742e
1667
+ sage: K = 5
1668
+ sage: C = S.enciphering(K, P); C
1669
+ 9ab3b8c7cec5c9beb4b3757b75b9bab8c7cec5c9beb4b375c8bdbebbc9c875b6b1b4b3bc75c9bdba75b6b1c5bdb6b7bac973
1670
+ sage: S.deciphering(K, C)
1671
+ 456e6372797074696f6e20262064656372797074696f6e2073686966747320616c6f6e672074686520616c7068616265742e
1672
+ sage: S.deciphering(K, C) == P
1673
+ True
1674
+
1675
+ And over the binary number system::
1676
+
1677
+ sage: S = ShiftCryptosystem(BinaryStrings()); S
1678
+ Shift cryptosystem on Free binary string monoid
1679
+ sage: P = S.encoding("The binary alphabet is very insecure."); P
1680
+ 01010100011010000110010100100000011000100110100101101110011000010111001001111001001000000110000101101100011100000110100001100001011000100110010101110100001000000110100101110011001000000111011001100101011100100111100100100000011010010110111001110011011001010110001101110101011100100110010100101110
1681
+ sage: K = 1
1682
+ sage: C = S.enciphering(K, P); C
1683
+ 10101011100101111001101011011111100111011001011010010001100111101000110110000110110111111001111010010011100011111001011110011110100111011001101010001011110111111001011010001100110111111000100110011010100011011000011011011111100101101001000110001100100110101001110010001010100011011001101011010001
1684
+ sage: S.deciphering(K, C)
1685
+ 01010100011010000110010100100000011000100110100101101110011000010111001001111001001000000110000101101100011100000110100001100001011000100110010101110100001000000110100101110011001000000111011001100101011100100111100100100000011010010110111001110011011001010110001101110101011100100110010100101110
1686
+ sage: S.deciphering(K, C) == P
1687
+ True
1688
+
1689
+ A shift cryptosystem with key `k = 3` is commonly referred to as the
1690
+ Caesar cipher. Create a Caesar cipher over the upper-case letters of the
1691
+ English alphabet::
1692
+
1693
+ sage: caesar = ShiftCryptosystem(AlphabeticStrings())
1694
+ sage: K = 3
1695
+ sage: P = caesar.encoding("abcdef"); P
1696
+ ABCDEF
1697
+ sage: C = caesar.enciphering(K, P); C
1698
+ DEFGHI
1699
+ sage: caesar.deciphering(K, C) == P
1700
+ True
1701
+
1702
+ Generate a random key for encryption and decryption::
1703
+
1704
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
1705
+ sage: P = S.encoding("Shift cipher with a random key.")
1706
+ sage: K = S.random_key()
1707
+ sage: C = S.enciphering(K, P)
1708
+ sage: S.deciphering(K, C) == P
1709
+ True
1710
+
1711
+ Decrypting with the key ``K`` is equivalent to encrypting with its
1712
+ corresponding inverse key::
1713
+
1714
+ sage: S.enciphering(S.inverse_key(K), C) == P
1715
+ True
1716
+
1717
+ TESTS:
1718
+
1719
+ Currently, the octal number system is not supported as an alphabet for
1720
+ this shift cryptosystem::
1721
+
1722
+ sage: ShiftCryptosystem(OctalStrings())
1723
+ Traceback (most recent call last):
1724
+ ...
1725
+ TypeError: A (= Free octal string monoid) is not supported as a cipher domain of this shift cryptosystem.
1726
+
1727
+ Nor is the radix-64 number system supported::
1728
+
1729
+ sage: ShiftCryptosystem(Radix64Strings())
1730
+ Traceback (most recent call last):
1731
+ ...
1732
+ TypeError: A (= Free radix 64 string monoid) is not supported as a cipher domain of this shift cryptosystem.
1733
+
1734
+ Testing of dumping and loading objects::
1735
+
1736
+ sage: SA = ShiftCryptosystem(AlphabeticStrings())
1737
+ sage: SA == loads(dumps(SA))
1738
+ True
1739
+ sage: SH = ShiftCryptosystem(HexadecimalStrings())
1740
+ sage: SH == loads(dumps(SH))
1741
+ True
1742
+ sage: SB = ShiftCryptosystem(BinaryStrings())
1743
+ sage: SB == loads(dumps(SB))
1744
+ True
1745
+
1746
+ The key ``K`` must satisfy the inequality `0 \leq K < n` with `n`
1747
+ being the size of the plaintext, ciphertext, and key spaces. For the
1748
+ shift cryptosystem, all these spaces are the same alphabet. This
1749
+ inequality must be satisfied for each of the supported alphabets.
1750
+ The capital letters of the English alphabet::
1751
+
1752
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
1753
+ sage: S(2 + S.alphabet_size())
1754
+ Traceback (most recent call last):
1755
+ ...
1756
+ ValueError: K (=28) is outside the range of acceptable values for a key of this shift cryptosystem.
1757
+ sage: S(-2)
1758
+ Traceback (most recent call last):
1759
+ ...
1760
+ ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem.
1761
+
1762
+ The hexadecimal number system::
1763
+
1764
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
1765
+ sage: S(1 + S.alphabet_size())
1766
+ Traceback (most recent call last):
1767
+ ...
1768
+ ValueError: K (=17) is outside the range of acceptable values for a key of this shift cryptosystem.
1769
+ sage: S(-1)
1770
+ Traceback (most recent call last):
1771
+ ...
1772
+ ValueError: K (=-1) is outside the range of acceptable values for a key of this shift cryptosystem.
1773
+
1774
+ The binary number system::
1775
+
1776
+ sage: S = ShiftCryptosystem(BinaryStrings())
1777
+ sage: S(1 + S.alphabet_size())
1778
+ Traceback (most recent call last):
1779
+ ...
1780
+ ValueError: K (=3) is outside the range of acceptable values for a key of this shift cryptosystem.
1781
+ sage: S(-2)
1782
+ Traceback (most recent call last):
1783
+ ...
1784
+ ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem.
1785
+ """
1786
+
1787
+ def __init__(self, A):
1788
+ r"""
1789
+ See ``ShiftCryptosystem`` for full documentation.
1790
+
1791
+ Create a shift cryptosystem defined over the alphabet ``A``.
1792
+
1793
+ INPUT:
1794
+
1795
+ - ``A`` -- string monoid over some alphabet; this is the non-empty
1796
+ alphabet over which the plaintext and ciphertext spaces
1797
+ are defined
1798
+
1799
+ OUTPUT: a shift cryptosystem over the alphabet ``A``
1800
+
1801
+ EXAMPLES::
1802
+
1803
+ sage: S = ShiftCryptosystem(AlphabeticStrings()); S
1804
+ Shift cryptosystem on Free alphabetic string monoid on A-Z
1805
+ sage: P = S.encoding("The shift cryptosystem generalizes the Caesar cipher.")
1806
+ sage: P
1807
+ THESHIFTCRYPTOSYSTEMGENERALIZESTHECAESARCIPHER
1808
+ sage: K = 7
1809
+ sage: C = S.enciphering(K, P); C
1810
+ AOLZOPMAJYFWAVZFZALTNLULYHSPGLZAOLJHLZHYJPWOLY
1811
+ sage: S.deciphering(K, C)
1812
+ THESHIFTCRYPTOSYSTEMGENERALIZESTHECAESARCIPHER
1813
+ sage: S.deciphering(K, C) == P
1814
+ True
1815
+ """
1816
+ # NOTE: the code here is very similar to that in the method
1817
+ # rank_by_chi_square() of the class AffineCryptosystem. The most
1818
+ # significant change in the code below is in how the secret key k
1819
+ # is processed.
1820
+
1821
+ # sanity check
1822
+ from sage.monoids.string_monoid import (
1823
+ AlphabeticStringMonoid,
1824
+ BinaryStringMonoid,
1825
+ HexadecimalStringMonoid)
1826
+ if not isinstance(A, ( AlphabeticStringMonoid,
1827
+ BinaryStringMonoid,
1828
+ HexadecimalStringMonoid )):
1829
+ raise TypeError("A (= %s) is not supported as a cipher domain of this shift cryptosystem." % A)
1830
+ # Initialize the shift cryptosystem with the plaintext, ciphertext,
1831
+ # and key spaces.
1832
+ SymmetricKeyCryptosystem.__init__(self, A, A, IntegerModRing(A.ngens()))
1833
+
1834
+ def __call__(self, K):
1835
+ r"""
1836
+ Create a shift cipher with key ``K``.
1837
+
1838
+ INPUT:
1839
+
1840
+ - ``K`` -- a secret key; this key is used for both encryption and
1841
+ decryption. For the shift cryptosystem whose plaintext and
1842
+ ciphertext spaces are `A`, a key is any integer `k` such that
1843
+ `0 \leq k < n` where `n` is the size or cardinality of the set
1844
+ `A`.
1845
+
1846
+ OUTPUT: a shift cipher with secret key ``K``
1847
+
1848
+ EXAMPLES::
1849
+
1850
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
1851
+ sage: P = S.encoding("Shifting sand."); P
1852
+ SHIFTINGSAND
1853
+ sage: K = 3
1854
+ sage: E = S(K); E
1855
+ Shift cipher on Free alphabetic string monoid on A-Z
1856
+ sage: E(P)
1857
+ VKLIWLQJVDQG
1858
+ sage: D = S(S.inverse_key(K)); D
1859
+ Shift cipher on Free alphabetic string monoid on A-Z
1860
+ sage: D(E(P))
1861
+ SHIFTINGSAND
1862
+
1863
+ TESTS:
1864
+
1865
+ The key ``K`` must satisfy the inequality `0 \leq K < n` with `n`
1866
+ being the size of the plaintext, ciphertext, and key spaces. For the
1867
+ shift cryptosystem, all these spaces are the same alphabet. This
1868
+ inequality must be satisfied for each of the supported alphabets.
1869
+ The capital letters of the English alphabet::
1870
+
1871
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
1872
+ sage: S(2 + S.alphabet_size())
1873
+ Traceback (most recent call last):
1874
+ ...
1875
+ ValueError: K (=28) is outside the range of acceptable values for a key of this shift cryptosystem.
1876
+ sage: S(-2)
1877
+ Traceback (most recent call last):
1878
+ ...
1879
+ ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem.
1880
+
1881
+ The hexadecimal number system::
1882
+
1883
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
1884
+ sage: S(1 + S.alphabet_size())
1885
+ Traceback (most recent call last):
1886
+ ...
1887
+ ValueError: K (=17) is outside the range of acceptable values for a key of this shift cryptosystem.
1888
+ sage: S(-1)
1889
+ Traceback (most recent call last):
1890
+ ...
1891
+ ValueError: K (=-1) is outside the range of acceptable values for a key of this shift cryptosystem.
1892
+
1893
+ The binary number system::
1894
+
1895
+ sage: S = ShiftCryptosystem(BinaryStrings())
1896
+ sage: S(1 + S.alphabet_size())
1897
+ Traceback (most recent call last):
1898
+ ...
1899
+ ValueError: K (=3) is outside the range of acceptable values for a key of this shift cryptosystem.
1900
+ sage: S(-2)
1901
+ Traceback (most recent call last):
1902
+ ...
1903
+ ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem.
1904
+ """
1905
+ # Sanity check: the key K must satisfy the inequality
1906
+ # 0 <= K < n with n being the size of the plaintext, ciphertext, and
1907
+ # key spaces. For the shift cryptosystem, all these spaces are the
1908
+ # same alphabet.
1909
+ if 0 <= K < self.alphabet_size():
1910
+ return ShiftCipher(self, K)
1911
+ # from sage.rings.finite_rings.integer_mod import Mod
1912
+ # return ShiftCipher(self, Mod(K, self.alphabet_size()).lift())
1913
+ else:
1914
+ raise ValueError("K (=%s) is outside the range of acceptable values for a key of this shift cryptosystem." % K)
1915
+
1916
+ def _repr_(self):
1917
+ r"""
1918
+ Return the string representation of ``self``.
1919
+
1920
+ EXAMPLES::
1921
+
1922
+ sage: ShiftCryptosystem(AlphabeticStrings())
1923
+ Shift cryptosystem on Free alphabetic string monoid on A-Z
1924
+ sage: ShiftCryptosystem(HexadecimalStrings())
1925
+ Shift cryptosystem on Free hexadecimal string monoid
1926
+ sage: ShiftCryptosystem(BinaryStrings())
1927
+ Shift cryptosystem on Free binary string monoid
1928
+ """
1929
+ # The shift cipher has the plaintext and ciphertext spaces defined
1930
+ # over the same non-empty alphabet. The cipher domain is the same
1931
+ # as the alphabet used for the plaintext and ciphertext spaces.
1932
+ return "Shift cryptosystem on %s" % self.cipher_domain()
1933
+
1934
+ def rank_by_chi_square(self, C, pdict):
1935
+ r"""
1936
+ Use the chi-square statistic to rank all possible keys. Currently,
1937
+ this method only applies to the capital letters of the English
1938
+ alphabet.
1939
+
1940
+ ALGORITHM:
1941
+
1942
+ Consider a non-empty alphabet `A` consisting of `n`
1943
+ elements, and let `C` be a ciphertext encoded using elements of
1944
+ `A`. The plaintext `P` corresponding to `C` is also encoded using
1945
+ elements of `A`. Let `M` be a candidate decipherment of `C`,
1946
+ i.e. `M` is the result of attempting to decrypt `C` using a key
1947
+ `k \in \ZZ/n\ZZ` which is not necessarily the same key used to
1948
+ encrypt `P`. Suppose `F_A(e)` is the characteristic frequency
1949
+ probability of `e \in A` and let `F_M(e)` be the message frequency
1950
+ probability with respect to `M`. The characteristic frequency
1951
+ probability distribution of an alphabet is the expected frequency
1952
+ probability distribution for that alphabet. The message frequency
1953
+ probability distribution of `M` provides a distribution of the ratio
1954
+ of character occurrences over message length. One can interpret the
1955
+ characteristic frequency probability `F_A(e)` as the expected
1956
+ probability, while the message frequency probability `F_M(e)` is
1957
+ the observed probability. If `M` is of length `L`, then the observed
1958
+ frequency of `e \in A` is
1959
+
1960
+ .. MATH::
1961
+
1962
+ O_M(e)
1963
+ =
1964
+ F_M(e) \cdot L
1965
+
1966
+ and the expected frequency of `e \in A` is
1967
+
1968
+ .. MATH::
1969
+
1970
+ E_A(e)
1971
+ =
1972
+ F_A(e) \cdot L
1973
+
1974
+ The chi-square rank `R_{\chi^2}(M)` of `M` corresponding to a key
1975
+ `k \in \ZZ/n\ZZ` is given by
1976
+
1977
+ .. MATH::
1978
+
1979
+ R_{\chi^2}(M)
1980
+ =
1981
+ \sum_{e \in A} \frac {\big( O_M(e) - E_A(e) \big)^2}
1982
+ {E_A(e)}
1983
+
1984
+ Cryptanalysis by exhaustive key search produces a candidate
1985
+ decipherment `M_{k}` for each possible key `k \in \ZZ/n\ZZ`. For
1986
+ a set
1987
+ `D = \big\{M_{k_1}, M_{k_2}, \dots, M_{k_r} \big\}`
1988
+ of all candidate decipherments corresponding to a ciphertext `C`,
1989
+ the smaller is the rank `R_{\chi^2}(M_{k_i})` the more likely
1990
+ that `k_i` is the secret key. This key ranking method is based on
1991
+ the Pearson chi-square test [PearsonTest]_.
1992
+
1993
+ INPUT:
1994
+
1995
+ - ``C`` -- the ciphertext, a non-empty string. The ciphertext
1996
+ must be encoded using the upper-case letters of the English
1997
+ alphabet.
1998
+
1999
+ - ``pdict`` -- dictionary of key, possible plaintext pairs.
2000
+ This should be the output of :func:`brute_force` with
2001
+ ``ranking="none"``.
2002
+
2003
+ OUTPUT:
2004
+
2005
+ - A list ranking the most likely keys first. Each element of the
2006
+ list is a tuple of key, possible plaintext pairs.
2007
+
2008
+ EXAMPLES:
2009
+
2010
+ Use the chi-square statistic to rank all possible keys and their
2011
+ corresponding decipherment::
2012
+
2013
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2014
+ sage: P = S.encoding("Shi."); P
2015
+ SHI
2016
+ sage: K = 5
2017
+ sage: C = S.enciphering(K, P)
2018
+ sage: Pdict = S.brute_force(C)
2019
+ sage: S.rank_by_chi_square(C, Pdict)
2020
+ <BLANKLINE>
2021
+ [(9, ODE),
2022
+ (5, SHI),
2023
+ (20, DST),
2024
+ (19, ETU),
2025
+ (21, CRS),
2026
+ (10, NCD),
2027
+ (25, YNO),
2028
+ (6, RGH),
2029
+ (12, LAB),
2030
+ (8, PEF),
2031
+ (1, WLM),
2032
+ (11, MBC),
2033
+ (18, FUV),
2034
+ (17, GVW),
2035
+ (2, VKL),
2036
+ (4, TIJ),
2037
+ (3, UJK),
2038
+ (0, XMN),
2039
+ (16, HWX),
2040
+ (15, IXY),
2041
+ (23, APQ),
2042
+ (24, ZOP),
2043
+ (22, BQR),
2044
+ (7, QFG),
2045
+ (13, KZA),
2046
+ (14, JYZ)]
2047
+
2048
+ As more ciphertext is available, the reliability of the chi-square
2049
+ ranking function increases::
2050
+
2051
+ sage: P = S.encoding("Shift cipher."); P
2052
+ SHIFTCIPHER
2053
+ sage: C = S.enciphering(K, P)
2054
+ sage: Pdict = S.brute_force(C)
2055
+ sage: S.rank_by_chi_square(C, Pdict)
2056
+ <BLANKLINE>
2057
+ [(5, SHIFTCIPHER),
2058
+ (9, ODEBPYELDAN),
2059
+ (18, FUVSGPVCURE),
2060
+ (2, VKLIWFLSKHU),
2061
+ (20, DSTQENTASPC),
2062
+ (19, ETURFOUBTQD),
2063
+ (21, CRSPDMSZROB),
2064
+ (6, RGHESBHOGDQ),
2065
+ (7, QFGDRAGNFCP),
2066
+ (12, LABYMVBIAXK),
2067
+ (17, GVWTHQWDVSF),
2068
+ (24, ZOPMAJPWOLY),
2069
+ (1, WLMJXGMTLIV),
2070
+ (0, XMNKYHNUMJW),
2071
+ (11, MBCZNWCJBYL),
2072
+ (8, PEFCQZFMEBO),
2073
+ (25, YNOLZIOVNKX),
2074
+ (10, NCDAOXDKCZM),
2075
+ (3, UJKHVEKRJGT),
2076
+ (4, TIJGUDJQIFS),
2077
+ (22, BQROCLRYQNA),
2078
+ (16, HWXUIRXEWTG),
2079
+ (15, IXYVJSYFXUH),
2080
+ (14, JYZWKTZGYVI),
2081
+ (13, KZAXLUAHZWJ),
2082
+ (23, APQNBKQXPMZ)]
2083
+
2084
+ TESTS:
2085
+
2086
+ The ciphertext cannot be an empty string::
2087
+
2088
+ sage: S.rank_by_chi_square("", Pdict)
2089
+ Traceback (most recent call last):
2090
+ ...
2091
+ AttributeError: 'str' object has no attribute 'parent'...
2092
+ sage: S.rank_by_chi_square(S.encoding(""), Pdict)
2093
+ Traceback (most recent call last):
2094
+ ...
2095
+ ValueError: The ciphertext must be a non-empty string.
2096
+ sage: S.rank_by_chi_square(S.encoding(" "), Pdict)
2097
+ Traceback (most recent call last):
2098
+ ...
2099
+ ValueError: The ciphertext must be a non-empty string.
2100
+
2101
+ The ciphertext must be encoded using the capital letters of the
2102
+ English alphabet as implemented in
2103
+ :func:`AlphabeticStrings()
2104
+ <sage.monoids.string_monoid.AlphabeticStrings>`::
2105
+
2106
+ sage: H = HexadecimalStrings()
2107
+ sage: S.rank_by_chi_square(H.encoding("shift"), Pdict)
2108
+ Traceback (most recent call last):
2109
+ ...
2110
+ TypeError: The ciphertext must be capital letters of the English alphabet.
2111
+ sage: B = BinaryStrings()
2112
+ sage: S.rank_by_chi_square(B.encoding("shift"), Pdict)
2113
+ Traceback (most recent call last):
2114
+ ...
2115
+ TypeError: The ciphertext must be capital letters of the English alphabet.
2116
+
2117
+ The dictionary ``pdict`` cannot be empty::
2118
+
2119
+ sage: S.rank_by_chi_square(C, {})
2120
+ Traceback (most recent call last):
2121
+ ...
2122
+ KeyError: 0
2123
+ """
2124
+ # NOTE: the code here is very similar to that in the method
2125
+ # rank_by_chi_square() of the class AffineCryptosystem. The most
2126
+ # significant change in the code below is in how the secret key k
2127
+ # is processed.
2128
+
2129
+ # sanity check
2130
+ from sage.monoids.string_monoid import AlphabeticStrings
2131
+ if not isinstance(C.parent(), AlphabeticStringMonoid):
2132
+ raise TypeError("The ciphertext must be capital letters of the English alphabet.")
2133
+ if str(C) == "":
2134
+ raise ValueError("The ciphertext must be a non-empty string.")
2135
+
2136
+ # compute the rank of each key
2137
+ AS = AlphabeticStrings()
2138
+ # the alphabet in question
2139
+ Alph = self.encoding("".join([str(e) for e in AS.gens()]))
2140
+ StrAlph = str(Alph)
2141
+ # message length
2142
+ L = len(C)
2143
+ # expected frequency tally
2144
+ EA = AS.characteristic_frequency()
2145
+ for e in EA:
2146
+ EA[e] *= L
2147
+ # the rank R(M, k) of M for each key
2148
+ Rank = []
2149
+ for key in range(self.alphabet_size()):
2150
+ # observed frequency tally
2151
+ OM = pdict[key].frequency_distribution().function()
2152
+ for e in Alph:
2153
+ if e in OM:
2154
+ OM[e] *= L
2155
+ else:
2156
+ OM.setdefault(e, 0.0)
2157
+ # the rank R(M, K) of M with shift key k
2158
+ RMk = [(OM[AS(e)] - EA[e])**2 / EA[e] for e in StrAlph]
2159
+ Rank.append((sum(RMk), key))
2160
+ # Sort in non-decreasing order of squared-differences statistic. It's
2161
+ # possible that two different keys share the same squared-differences
2162
+ # statistic.
2163
+ Rank = sorted(Rank)
2164
+ RankedList = []
2165
+ # In the following line, the value of val is not used at all, making
2166
+ # it redundant to access val in the first place. This line
2167
+ # of code is written with readability in mind.
2168
+ [RankedList.append((key, pdict[key])) for val, key in Rank]
2169
+ return RankedList
2170
+
2171
+ def rank_by_squared_differences(self, C, pdict):
2172
+ r"""
2173
+ Use the squared-differences measure to rank all possible keys.
2174
+ Currently, this method only applies to the capital letters of
2175
+ the English alphabet.
2176
+
2177
+ ALGORITHM:
2178
+
2179
+ Consider a non-empty alphabet `A` consisting of `n`
2180
+ elements, and let `C` be a ciphertext encoded using elements of
2181
+ `A`. The plaintext `P` corresponding to `C` is also encoded using
2182
+ elements of `A`. Let `M` be a candidate decipherment of `C`,
2183
+ i.e. `M` is the result of attempting to decrypt `C` using a key
2184
+ `k \in \ZZ/n\ZZ` which is not necessarily the same key used to
2185
+ encrypt `P`. Suppose `F_A(e)` is the characteristic frequency
2186
+ probability of `e \in A` and let `F_M(e)` be the message
2187
+ frequency probability with respect to `M`. The characteristic
2188
+ frequency probability distribution of an alphabet is the expected
2189
+ frequency probability distribution for that alphabet. The message
2190
+ frequency probability distribution of `M` provides a distribution
2191
+ of the ratio of character occurrences over message length. One can
2192
+ interpret the characteristic frequency probability `F_A(e)` as the
2193
+ expected probability, while the message frequency probability
2194
+ `F_M(e)` is the observed probability. If `M` is of length `L`, then
2195
+ the observed frequency of `e \in A` is
2196
+
2197
+ .. MATH::
2198
+
2199
+ O_M(e)
2200
+ =
2201
+ F_M(e) \cdot L
2202
+
2203
+ and the expected frequency of `e \in A` is
2204
+
2205
+ .. MATH::
2206
+
2207
+ E_A(e)
2208
+ =
2209
+ F_A(e) \cdot L
2210
+
2211
+ The squared-differences, or residual sum of squares, rank
2212
+ `R_{RSS}(M)` of `M` corresponding to a key
2213
+ `k \in \ZZ/n\ZZ` is given by
2214
+
2215
+ .. MATH::
2216
+
2217
+ R_{RSS}(M)
2218
+ =
2219
+ \sum_{e \in A} \big( O_M(e) - E_A(e) \big)^2
2220
+
2221
+ Cryptanalysis by exhaustive key search produces a candidate
2222
+ decipherment `M_{k}` for each possible key `k \in \ZZ/n\ZZ`. For
2223
+ a set
2224
+ `D = \big\{M_{k_1}, M_{k_2}, \dots, M_{k_r} \big\}`
2225
+ of all candidate decipherments corresponding to a ciphertext `C`,
2226
+ the smaller is the rank `R_{RSS}(M_{k_i})` the more likely
2227
+ that `k_i` is the secret key. This key ranking method is based
2228
+ on the residual sum of squares measure [RSS]_.
2229
+
2230
+ INPUT:
2231
+
2232
+ - ``C`` -- the ciphertext, a non-empty string. The ciphertext
2233
+ must be encoded using the upper-case letters of the English
2234
+ alphabet.
2235
+
2236
+ - ``pdict`` -- dictionary of key, possible plaintext pairs.
2237
+ This should be the output of :func:`brute_force` with
2238
+ ``ranking="none"``.
2239
+
2240
+ OUTPUT: a list ranking the most likely keys first; each element of the
2241
+ list is a tuple of key, possible plaintext pairs
2242
+
2243
+ EXAMPLES:
2244
+
2245
+ Use the method of squared differences to rank all possible keys
2246
+ and their corresponding decipherment::
2247
+
2248
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2249
+ sage: P = S.encoding("Shi."); P
2250
+ SHI
2251
+ sage: K = 5
2252
+ sage: C = S.enciphering(K, P)
2253
+ sage: Pdict = S.brute_force(C)
2254
+ sage: S.rank_by_squared_differences(C, Pdict)
2255
+ <BLANKLINE>
2256
+ [(19, ETU),
2257
+ (9, ODE),
2258
+ (20, DST),
2259
+ (5, SHI),
2260
+ (8, PEF),
2261
+ (4, TIJ),
2262
+ (25, YNO),
2263
+ (21, CRS),
2264
+ (6, RGH),
2265
+ (10, NCD),
2266
+ (12, LAB),
2267
+ (23, APQ),
2268
+ (24, ZOP),
2269
+ (0, XMN),
2270
+ (13, KZA),
2271
+ (15, IXY),
2272
+ (1, WLM),
2273
+ (16, HWX),
2274
+ (22, BQR),
2275
+ (11, MBC),
2276
+ (18, FUV),
2277
+ (2, VKL),
2278
+ (17, GVW),
2279
+ (7, QFG),
2280
+ (3, UJK),
2281
+ (14, JYZ)]
2282
+
2283
+ As more ciphertext is available, the reliability of the squared
2284
+ differences ranking function increases::
2285
+
2286
+ sage: P = S.encoding("Shift cipher."); P
2287
+ SHIFTCIPHER
2288
+ sage: C = S.enciphering(K, P)
2289
+ sage: Pdict = S.brute_force(C)
2290
+ sage: S.rank_by_squared_differences(C, Pdict)
2291
+ <BLANKLINE>
2292
+ [(20, DSTQENTASPC),
2293
+ (5, SHIFTCIPHER),
2294
+ (9, ODEBPYELDAN),
2295
+ (19, ETURFOUBTQD),
2296
+ (6, RGHESBHOGDQ),
2297
+ (16, HWXUIRXEWTG),
2298
+ (8, PEFCQZFMEBO),
2299
+ (21, CRSPDMSZROB),
2300
+ (22, BQROCLRYQNA),
2301
+ (25, YNOLZIOVNKX),
2302
+ (3, UJKHVEKRJGT),
2303
+ (18, FUVSGPVCURE),
2304
+ (4, TIJGUDJQIFS),
2305
+ (10, NCDAOXDKCZM),
2306
+ (7, QFGDRAGNFCP),
2307
+ (24, ZOPMAJPWOLY),
2308
+ (2, VKLIWFLSKHU),
2309
+ (12, LABYMVBIAXK),
2310
+ (17, GVWTHQWDVSF),
2311
+ (1, WLMJXGMTLIV),
2312
+ (13, KZAXLUAHZWJ),
2313
+ (0, XMNKYHNUMJW),
2314
+ (15, IXYVJSYFXUH),
2315
+ (14, JYZWKTZGYVI),
2316
+ (11, MBCZNWCJBYL),
2317
+ (23, APQNBKQXPMZ)]
2318
+
2319
+ TESTS:
2320
+
2321
+ The ciphertext cannot be an empty string::
2322
+
2323
+ sage: S.rank_by_squared_differences("", Pdict)
2324
+ Traceback (most recent call last):
2325
+ ...
2326
+ AttributeError: 'str' object has no attribute 'parent'...
2327
+ sage: S.rank_by_squared_differences(S.encoding(""), Pdict)
2328
+ Traceback (most recent call last):
2329
+ ...
2330
+ ValueError: The ciphertext must be a non-empty string.
2331
+ sage: S.rank_by_squared_differences(S.encoding(" "), Pdict)
2332
+ Traceback (most recent call last):
2333
+ ...
2334
+ ValueError: The ciphertext must be a non-empty string.
2335
+
2336
+ The ciphertext must be encoded using the capital letters of the
2337
+ English alphabet as implemented in
2338
+ :func:`AlphabeticStrings()
2339
+ <sage.monoids.string_monoid.AlphabeticStrings>`::
2340
+
2341
+ sage: H = HexadecimalStrings()
2342
+ sage: S.rank_by_squared_differences(H.encoding("shift"), Pdict)
2343
+ Traceback (most recent call last):
2344
+ ...
2345
+ TypeError: The ciphertext must be capital letters of the English alphabet.
2346
+ sage: B = BinaryStrings()
2347
+ sage: S.rank_by_squared_differences(B.encoding("shift"), Pdict)
2348
+ Traceback (most recent call last):
2349
+ ...
2350
+ TypeError: The ciphertext must be capital letters of the English alphabet.
2351
+
2352
+ The dictionary ``pdict`` cannot be empty::
2353
+
2354
+ sage: S.rank_by_squared_differences(C, {})
2355
+ Traceback (most recent call last):
2356
+ ...
2357
+ KeyError: 0
2358
+ """
2359
+ # NOTE: the code in this method is very similar to that in the
2360
+ # method rank_by_chi_square(). The only difference here is the
2361
+ # line that computes the list RMk.
2362
+
2363
+ # sanity check
2364
+ from sage.monoids.string_monoid import (
2365
+ AlphabeticStringMonoid,
2366
+ AlphabeticStrings)
2367
+ if not isinstance(C.parent(), AlphabeticStringMonoid):
2368
+ raise TypeError("The ciphertext must be capital letters of the English alphabet.")
2369
+ if str(C) == "":
2370
+ raise ValueError("The ciphertext must be a non-empty string.")
2371
+
2372
+ # compute the rank of each key
2373
+ AS = AlphabeticStrings()
2374
+ # the alphabet in question
2375
+ Alph = self.encoding("".join([str(e) for e in AS.gens()]))
2376
+ StrAlph = str(Alph)
2377
+ # message length
2378
+ L = len(C)
2379
+ # expected frequency tally
2380
+ EA = AS.characteristic_frequency()
2381
+ for e in EA:
2382
+ EA[e] *= L
2383
+ # the rank R(M, k) of M for each key
2384
+ Rank = []
2385
+ for key in range(self.alphabet_size()):
2386
+ # observed frequency tally
2387
+ OM = pdict[key].frequency_distribution().function()
2388
+ for e in Alph:
2389
+ if e in OM:
2390
+ OM[e] *= L
2391
+ else:
2392
+ OM.setdefault(e, 0.0)
2393
+ # the rank R(M, K) of M with shift key k
2394
+ RMk = [(OM[AS(e)] - EA[e])**2 for e in StrAlph]
2395
+ Rank.append((sum(RMk), key))
2396
+ # Sort in non-decreasing order of squared-differences statistic. It's
2397
+ # possible that two different keys share the same squared-differences
2398
+ # statistic.
2399
+ Rank = sorted(Rank)
2400
+ RankedList = []
2401
+ # In the following line, the value of val is not used at all, making
2402
+ # it redundant to access val in the first place. This line
2403
+ # of code is written with readability in mind.
2404
+ [RankedList.append((key, pdict[key])) for val, key in Rank]
2405
+ return RankedList
2406
+
2407
+ def brute_force(self, C, ranking='none'):
2408
+ r"""
2409
+ Attempt a brute force cryptanalysis of the ciphertext ``C``.
2410
+
2411
+ INPUT:
2412
+
2413
+ - ``C`` -- a ciphertext over one of the supported alphabets of this
2414
+ shift cryptosystem. See the class :class:`ShiftCryptosystem` for
2415
+ documentation on the supported alphabets.
2416
+
2417
+ - ``ranking`` -- (default: ``'none'``) the method to use for
2418
+ ranking all possible keys. If ``ranking="none"``, then do not
2419
+ use any ranking function. The following ranking functions are
2420
+ supported:
2421
+
2422
+ - ``'chisquare'`` -- the chi-square ranking function as
2423
+ implemented in the method :func:`rank_by_chi_square`
2424
+
2425
+ - ``'squared_differences'`` -- the squared differences ranking
2426
+ function as implemented in the method
2427
+ :func:`rank_by_squared_differences`.
2428
+
2429
+ OUTPUT:
2430
+
2431
+ - All the possible plaintext sequences corresponding to the
2432
+ ciphertext ``C``. This method effectively uses all the possible
2433
+ keys in this shift cryptosystem to decrypt ``C``. The method is
2434
+ also referred to as exhaustive key search. The output is
2435
+ a dictionary of key, plaintext pairs.
2436
+
2437
+ EXAMPLES:
2438
+
2439
+ Cryptanalyze using all possible keys for various alphabets. Over
2440
+ the upper-case letters of the English alphabet::
2441
+
2442
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2443
+ sage: P = S.encoding("The shift cryptosystem generalizes the Caesar cipher.")
2444
+ sage: K = 7
2445
+ sage: C = S.enciphering(K, P)
2446
+ sage: Dict = S.brute_force(C)
2447
+ sage: for k in range(len(Dict)):
2448
+ ....: if Dict[k] == P:
2449
+ ....: print("key = " + str(k))
2450
+ key = 7
2451
+
2452
+ Over the hexadecimal number system::
2453
+
2454
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
2455
+ sage: P = S.encoding("Encryption & decryption shifts along the alphabet.")
2456
+ sage: K = 5
2457
+ sage: C = S.enciphering(K, P)
2458
+ sage: Dict = S.brute_force(C)
2459
+ sage: for k in range(len(Dict)):
2460
+ ....: if Dict[k] == P:
2461
+ ....: print("key = " + str(k))
2462
+ key = 5
2463
+
2464
+ And over the binary number system::
2465
+
2466
+ sage: S = ShiftCryptosystem(BinaryStrings())
2467
+ sage: P = S.encoding("The binary alphabet is very insecure.")
2468
+ sage: K = 1
2469
+ sage: C = S.enciphering(K, P)
2470
+ sage: Dict = S.brute_force(C)
2471
+ sage: for k in range(len(Dict)):
2472
+ ....: if Dict[k] == P:
2473
+ ....: print("key = " + str(k))
2474
+ key = 1
2475
+
2476
+ Don't use any ranking functions, i.e. ``ranking="none"``::
2477
+
2478
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2479
+ sage: P = S.encoding("Shifting using modular arithmetic.")
2480
+ sage: K = 8
2481
+ sage: C = S.enciphering(K, P)
2482
+ sage: pdict = S.brute_force(C)
2483
+ sage: sorted(pdict.items())
2484
+ <BLANKLINE>
2485
+ [(0, APQNBQVOCAQVOUWLCTIZIZQBPUMBQK),
2486
+ (1, ZOPMAPUNBZPUNTVKBSHYHYPAOTLAPJ),
2487
+ (2, YNOLZOTMAYOTMSUJARGXGXOZNSKZOI),
2488
+ (3, XMNKYNSLZXNSLRTIZQFWFWNYMRJYNH),
2489
+ (4, WLMJXMRKYWMRKQSHYPEVEVMXLQIXMG),
2490
+ (5, VKLIWLQJXVLQJPRGXODUDULWKPHWLF),
2491
+ (6, UJKHVKPIWUKPIOQFWNCTCTKVJOGVKE),
2492
+ (7, TIJGUJOHVTJOHNPEVMBSBSJUINFUJD),
2493
+ (8, SHIFTINGUSINGMODULARARITHMETIC),
2494
+ (9, RGHESHMFTRHMFLNCTKZQZQHSGLDSHB),
2495
+ (10, QFGDRGLESQGLEKMBSJYPYPGRFKCRGA),
2496
+ (11, PEFCQFKDRPFKDJLARIXOXOFQEJBQFZ),
2497
+ (12, ODEBPEJCQOEJCIKZQHWNWNEPDIAPEY),
2498
+ (13, NCDAODIBPNDIBHJYPGVMVMDOCHZODX),
2499
+ (14, MBCZNCHAOMCHAGIXOFULULCNBGYNCW),
2500
+ (15, LABYMBGZNLBGZFHWNETKTKBMAFXMBV),
2501
+ (16, KZAXLAFYMKAFYEGVMDSJSJALZEWLAU),
2502
+ (17, JYZWKZEXLJZEXDFULCRIRIZKYDVKZT),
2503
+ (18, IXYVJYDWKIYDWCETKBQHQHYJXCUJYS),
2504
+ (19, HWXUIXCVJHXCVBDSJAPGPGXIWBTIXR),
2505
+ (20, GVWTHWBUIGWBUACRIZOFOFWHVASHWQ),
2506
+ (21, FUVSGVATHFVATZBQHYNENEVGUZRGVP),
2507
+ (22, ETURFUZSGEUZSYAPGXMDMDUFTYQFUO),
2508
+ (23, DSTQETYRFDTYRXZOFWLCLCTESXPETN),
2509
+ (24, CRSPDSXQECSXQWYNEVKBKBSDRWODSM),
2510
+ (25, BQROCRWPDBRWPVXMDUJAJARCQVNCRL)]
2511
+
2512
+ Use the chi-square ranking function, i.e. ``ranking="chisquare"``::
2513
+
2514
+ sage: S.brute_force(C, ranking='chisquare')
2515
+ <BLANKLINE>
2516
+ [(8, SHIFTINGUSINGMODULARARITHMETIC),
2517
+ (14, MBCZNCHAOMCHAGIXOFULULCNBGYNCW),
2518
+ (20, GVWTHWBUIGWBUACRIZOFOFWHVASHWQ),
2519
+ (13, NCDAODIBPNDIBHJYPGVMVMDOCHZODX),
2520
+ (1, ZOPMAPUNBZPUNTVKBSHYHYPAOTLAPJ),
2521
+ (23, DSTQETYRFDTYRXZOFWLCLCTESXPETN),
2522
+ (10, QFGDRGLESQGLEKMBSJYPYPGRFKCRGA),
2523
+ (6, UJKHVKPIWUKPIOQFWNCTCTKVJOGVKE),
2524
+ (22, ETURFUZSGEUZSYAPGXMDMDUFTYQFUO),
2525
+ (15, LABYMBGZNLBGZFHWNETKTKBMAFXMBV),
2526
+ (12, ODEBPEJCQOEJCIKZQHWNWNEPDIAPEY),
2527
+ (21, FUVSGVATHFVATZBQHYNENEVGUZRGVP),
2528
+ (16, KZAXLAFYMKAFYEGVMDSJSJALZEWLAU),
2529
+ (25, BQROCRWPDBRWPVXMDUJAJARCQVNCRL),
2530
+ (9, RGHESHMFTRHMFLNCTKZQZQHSGLDSHB),
2531
+ (24, CRSPDSXQECSXQWYNEVKBKBSDRWODSM),
2532
+ (3, XMNKYNSLZXNSLRTIZQFWFWNYMRJYNH),
2533
+ (5, VKLIWLQJXVLQJPRGXODUDULWKPHWLF),
2534
+ (7, TIJGUJOHVTJOHNPEVMBSBSJUINFUJD),
2535
+ (2, YNOLZOTMAYOTMSUJARGXGXOZNSKZOI),
2536
+ (18, IXYVJYDWKIYDWCETKBQHQHYJXCUJYS),
2537
+ (4, WLMJXMRKYWMRKQSHYPEVEVMXLQIXMG),
2538
+ (11, PEFCQFKDRPFKDJLARIXOXOFQEJBQFZ),
2539
+ (19, HWXUIXCVJHXCVBDSJAPGPGXIWBTIXR),
2540
+ (0, APQNBQVOCAQVOUWLCTIZIZQBPUMBQK),
2541
+ (17, JYZWKZEXLJZEXDFULCRIRIZKYDVKZT)]
2542
+
2543
+ Use the squared differences ranking function, i.e.
2544
+ ``ranking="squared_differences"``::
2545
+
2546
+ sage: S.brute_force(C, ranking='squared_differences')
2547
+ <BLANKLINE>
2548
+ [(8, SHIFTINGUSINGMODULARARITHMETIC),
2549
+ (23, DSTQETYRFDTYRXZOFWLCLCTESXPETN),
2550
+ (12, ODEBPEJCQOEJCIKZQHWNWNEPDIAPEY),
2551
+ (2, YNOLZOTMAYOTMSUJARGXGXOZNSKZOI),
2552
+ (9, RGHESHMFTRHMFLNCTKZQZQHSGLDSHB),
2553
+ (7, TIJGUJOHVTJOHNPEVMBSBSJUINFUJD),
2554
+ (21, FUVSGVATHFVATZBQHYNENEVGUZRGVP),
2555
+ (22, ETURFUZSGEUZSYAPGXMDMDUFTYQFUO),
2556
+ (1, ZOPMAPUNBZPUNTVKBSHYHYPAOTLAPJ),
2557
+ (16, KZAXLAFYMKAFYEGVMDSJSJALZEWLAU),
2558
+ (20, GVWTHWBUIGWBUACRIZOFOFWHVASHWQ),
2559
+ (24, CRSPDSXQECSXQWYNEVKBKBSDRWODSM),
2560
+ (14, MBCZNCHAOMCHAGIXOFULULCNBGYNCW),
2561
+ (13, NCDAODIBPNDIBHJYPGVMVMDOCHZODX),
2562
+ (3, XMNKYNSLZXNSLRTIZQFWFWNYMRJYNH),
2563
+ (10, QFGDRGLESQGLEKMBSJYPYPGRFKCRGA),
2564
+ (15, LABYMBGZNLBGZFHWNETKTKBMAFXMBV),
2565
+ (6, UJKHVKPIWUKPIOQFWNCTCTKVJOGVKE),
2566
+ (11, PEFCQFKDRPFKDJLARIXOXOFQEJBQFZ),
2567
+ (25, BQROCRWPDBRWPVXMDUJAJARCQVNCRL),
2568
+ (17, JYZWKZEXLJZEXDFULCRIRIZKYDVKZT),
2569
+ (19, HWXUIXCVJHXCVBDSJAPGPGXIWBTIXR),
2570
+ (4, WLMJXMRKYWMRKQSHYPEVEVMXLQIXMG),
2571
+ (0, APQNBQVOCAQVOUWLCTIZIZQBPUMBQK),
2572
+ (18, IXYVJYDWKIYDWCETKBQHQHYJXCUJYS),
2573
+ (5, VKLIWLQJXVLQJPRGXODUDULWKPHWLF)]
2574
+
2575
+ TESTS:
2576
+
2577
+ Currently, the octal number system is not supported as an alphabet for
2578
+ this shift cryptosystem::
2579
+
2580
+ sage: SA = ShiftCryptosystem(AlphabeticStrings())
2581
+ sage: OctStr = OctalStrings()
2582
+ sage: C = OctStr([1, 2, 3])
2583
+ sage: SA.brute_force(C)
2584
+ Traceback (most recent call last):
2585
+ ...
2586
+ TypeError: ciphertext must be encoded using one of the supported cipher domains of this shift cryptosystem.
2587
+
2588
+ Nor is the radix-64 alphabet supported::
2589
+
2590
+ sage: Rad64 = Radix64Strings()
2591
+ sage: C = Rad64([1, 2, 3])
2592
+ sage: SA.brute_force(C)
2593
+ Traceback (most recent call last):
2594
+ ...
2595
+ TypeError: ciphertext must be encoded using one of the supported cipher domains of this shift cryptosystem.
2596
+ """
2597
+ # Sanity check: ensure that C is encoded using one of the
2598
+ # supported alphabets of this shift cryptosystem.
2599
+ from sage.monoids.string_monoid import (
2600
+ AlphabeticStringMonoid,
2601
+ BinaryStringMonoid,
2602
+ HexadecimalStringMonoid)
2603
+ if not isinstance(C.parent(), (
2604
+ AlphabeticStringMonoid,
2605
+ BinaryStringMonoid,
2606
+ HexadecimalStringMonoid)):
2607
+ raise TypeError("ciphertext must be encoded using one of the supported cipher domains of this shift cryptosystem.")
2608
+ ranking_functions = ["none", "chisquare", "squared_differences"]
2609
+ if ranking not in ranking_functions:
2610
+ raise ValueError("Keyword 'ranking' must be either 'none', 'chisquare', or 'squared_differences'.")
2611
+
2612
+ # Now do the actual task of cryptanalysis by means of exhaustive key
2613
+ # search, also known as the brute force method.
2614
+ # let D be a dictionary of key/plaintext pairs
2615
+ D = {}
2616
+
2617
+ # NOTE: This loop is a good candidate for loop unrolling. Unless we
2618
+ # can justify that this block of code is a bottleneck on the runtime
2619
+ # of the method, we should leave it as is. For the alphabets that
2620
+ # are supported by this shift cryptosystem, it can be a waste of
2621
+ # time optimizing the code when the largest alphabet size is less
2622
+ # than 100.
2623
+ for k in range(self.alphabet_size()):
2624
+ D.setdefault(k, self.deciphering(k, C))
2625
+
2626
+ if ranking == "none":
2627
+ return D
2628
+ if ranking == "chisquare":
2629
+ return self.rank_by_chi_square(C, D)
2630
+ if ranking == "squared_differences":
2631
+ return self.rank_by_squared_differences(C, D)
2632
+
2633
+ def deciphering(self, K, C):
2634
+ r"""
2635
+ Decrypt the ciphertext ``C`` with the key ``K`` using shift cipher
2636
+ decryption.
2637
+
2638
+ INPUT:
2639
+
2640
+ - ``K`` -- a secret key; a key belonging to the key space of this
2641
+ shift cipher. This key is an integer `k` satisfying the inequality
2642
+ `0 \leq k < n`, where `n` is the size of the cipher domain.
2643
+
2644
+ - ``C`` -- string of ciphertext; possibly an empty string
2645
+ Characters in this string must be encoded using one of the
2646
+ supported alphabets. See the method :func:`encoding()`
2647
+ for more information.
2648
+
2649
+ OUTPUT: the plaintext corresponding to the ciphertext ``C``
2650
+
2651
+ EXAMPLES:
2652
+
2653
+ Let's perform decryption over the supported alphabets. Here is
2654
+ decryption over the capital letters of the English alphabet::
2655
+
2656
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2657
+ sage: P = S.encoding("Stop shifting me."); P
2658
+ STOPSHIFTINGME
2659
+ sage: K = 13
2660
+ sage: C = S.enciphering(K, P); C
2661
+ FGBCFUVSGVATZR
2662
+ sage: S.deciphering(K, C) == P
2663
+ True
2664
+
2665
+ Decryption over the hexadecimal number system::
2666
+
2667
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
2668
+ sage: P = S.encoding("Shift me now."); P
2669
+ 5368696674206d65206e6f772e
2670
+ sage: K = 7
2671
+ sage: C = S.enciphering(K, P); C
2672
+ cadfd0ddeb97d4dc97d5d6ee95
2673
+ sage: S.deciphering(K, C) == P
2674
+ True
2675
+
2676
+ Decryption over the binary number system::
2677
+
2678
+ sage: S = ShiftCryptosystem(BinaryStrings())
2679
+ sage: P = S.encoding("OK, enough shifting."); P
2680
+ 0100111101001011001011000010000001100101011011100110111101110101011001110110100000100000011100110110100001101001011001100111010001101001011011100110011100101110
2681
+ sage: K = 1
2682
+ sage: C = S.enciphering(K, P); C
2683
+ 1011000010110100110100111101111110011010100100011001000010001010100110001001011111011111100011001001011110010110100110011000101110010110100100011001100011010001
2684
+ sage: S.deciphering(K, C) == P
2685
+ True
2686
+ """
2687
+ E = self(self.inverse_key(K))
2688
+ return E(C)
2689
+
2690
+ def enciphering(self, K, P):
2691
+ r"""
2692
+ Encrypt the plaintext ``P`` with the key ``K`` using shift cipher
2693
+ encryption.
2694
+
2695
+ INPUT:
2696
+
2697
+ - ``K`` -- a key belonging to the key space of this shift cipher.
2698
+ This key is an integer `k` satisfying the inequality
2699
+ `0 \leq k < n`, where `n` is the size of the cipher domain.
2700
+
2701
+ - ``P`` -- string of plaintext; possibly an empty string.
2702
+ Characters in this string must be encoded using one of the
2703
+ supported alphabets. See the method :func:`encoding()` for more
2704
+ information.
2705
+
2706
+ OUTPUT: the ciphertext corresponding to the plaintext ``P``
2707
+
2708
+ EXAMPLES:
2709
+
2710
+ Let's perform encryption over the supported alphabets. Here is
2711
+ encryption over the capital letters of the English alphabet::
2712
+
2713
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2714
+ sage: P = S.encoding("Shift your gear."); P
2715
+ SHIFTYOURGEAR
2716
+ sage: K = 3
2717
+ sage: S.enciphering(K, P)
2718
+ VKLIWBRXUJHDU
2719
+
2720
+ Encryption over the hexadecimal number system::
2721
+
2722
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
2723
+ sage: P = S.encoding("Capitalize with the shift key."); P
2724
+ 4361706974616c697a65207769746820746865207368696674206b65792e
2725
+ sage: K = 5
2726
+ sage: S.enciphering(K, P)
2727
+ 98b6c5bec9b6b1becfba75ccbec9bd75c9bdba75c8bdbebbc975b0bace73
2728
+
2729
+ Encryption over the binary number system::
2730
+
2731
+ sage: S = ShiftCryptosystem(BinaryStrings())
2732
+ sage: P = S.encoding("Don't shift."); P
2733
+ 010001000110111101101110001001110111010000100000011100110110100001101001011001100111010000101110
2734
+ sage: K = 1
2735
+ sage: S.enciphering(K, P)
2736
+ 101110111001000010010001110110001000101111011111100011001001011110010110100110011000101111010001
2737
+ """
2738
+ E = self(K)
2739
+ return E(P)
2740
+
2741
+ def encoding(self, S):
2742
+ r"""
2743
+ The encoding of the string ``S`` over the string monoid of this
2744
+ shift cipher. For example, if the string monoid of this cryptosystem
2745
+ is
2746
+ :class:`AlphabeticStringMonoid <sage.monoids.string_monoid.AlphabeticStringMonoid>`,
2747
+ then the encoding of ``S`` would be its upper-case equivalent
2748
+ stripped of all non-alphabetic characters. The following alphabets
2749
+ are supported for the shift cipher:
2750
+
2751
+ - capital letters of the English alphabet as implemented in
2752
+ :func:`AlphabeticStrings() <sage.monoids.string_monoid.AlphabeticStrings>`
2753
+
2754
+ - the alphabet consisting of the hexadecimal number system as
2755
+ implemented in
2756
+ :func:`HexadecimalStrings() <sage.monoids.string_monoid.HexadecimalStrings>`
2757
+
2758
+ - the alphabet consisting of the binary number system as implemented in
2759
+ :func:`BinaryStrings() <sage.monoids.string_monoid.BinaryStrings>`
2760
+
2761
+ INPUT:
2762
+
2763
+ - ``S`` -- string, possibly empty
2764
+
2765
+ OUTPUT: the encoding of ``S`` over the string monoid of this
2766
+ cryptosystem; if ``S`` is an empty string, return an empty string
2767
+
2768
+ EXAMPLES:
2769
+
2770
+ Encoding over the upper-case letters of the English alphabet::
2771
+
2772
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2773
+ sage: S.encoding("Shift cipher on capital letters of the English alphabet.")
2774
+ SHIFTCIPHERONCAPITALLETTERSOFTHEENGLISHALPHABET
2775
+
2776
+ Encoding over the binary system::
2777
+
2778
+ sage: S = ShiftCryptosystem(BinaryStrings())
2779
+ sage: S.encoding("Binary")
2780
+ 010000100110100101101110011000010111001001111001
2781
+
2782
+ Encoding over the hexadecimal system::
2783
+
2784
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
2785
+ sage: S.encoding("Over hexadecimal system.")
2786
+ 4f7665722068657861646563696d616c2073797374656d2e
2787
+
2788
+ The argument ``S`` can be an empty string, in which case an empty
2789
+ string is returned::
2790
+
2791
+ sage: ShiftCryptosystem(AlphabeticStrings()).encoding("")
2792
+ <BLANKLINE>
2793
+ sage: ShiftCryptosystem(HexadecimalStrings()).encoding("")
2794
+ <BLANKLINE>
2795
+ sage: ShiftCryptosystem(BinaryStrings()).encoding("")
2796
+ <BLANKLINE>
2797
+ """
2798
+ D = self.cipher_domain()
2799
+ if isinstance(D, AlphabeticStringMonoid):
2800
+ return D(strip_encoding(S))
2801
+ try:
2802
+ return D.encoding(S)
2803
+ except Exception:
2804
+ raise TypeError("Argument S = %s does not encode in the cipher domain" % S)
2805
+
2806
+ def inverse_key(self, K):
2807
+ r"""
2808
+ The inverse key corresponding to the key ``K``. For the shift cipher,
2809
+ the inverse key corresponding to ``K`` is `-K \bmod n`, where
2810
+ `n > 0` is the size of the cipher domain, i.e. the
2811
+ plaintext/ciphertext space. A key `k` of the shift cipher is an
2812
+ integer `0 \leq k < n`. The key `k = 0` has no effect on either the
2813
+ plaintext or the ciphertext.
2814
+
2815
+ INPUT:
2816
+
2817
+ - ``K`` -- a key for this shift cipher. This must be an integer `k`
2818
+ such that `0 \leq k < n`, where `n` is the size of the cipher domain
2819
+
2820
+ OUTPUT: the inverse key corresponding to ``K``
2821
+
2822
+ EXAMPLES:
2823
+
2824
+ Some random keys and their respective inverse keys::
2825
+
2826
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2827
+ sage: key = S.random_key(); key # random
2828
+ 2
2829
+ sage: S.inverse_key(key) # random
2830
+ 24
2831
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
2832
+ sage: key = S.random_key(); key # random
2833
+ 12
2834
+ sage: S.inverse_key(key) # random
2835
+ 4
2836
+ sage: S = ShiftCryptosystem(BinaryStrings())
2837
+ sage: key = S.random_key(); key # random
2838
+ 1
2839
+ sage: S.inverse_key(key) # random
2840
+ 1
2841
+ sage: key = S.random_key(); key # random
2842
+ 0
2843
+ sage: S.inverse_key(key) # random
2844
+ 0
2845
+
2846
+ Regardless of the value of a key, the addition of the key and its
2847
+ inverse must be equal to the alphabet size. This relationship holds
2848
+ exactly when the value of the key is nonzero::
2849
+
2850
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2851
+ sage: K = S.random_key()
2852
+ sage: while K == 0:
2853
+ ....: K = S.random_key()
2854
+ sage: invK = S.inverse_key(K)
2855
+ sage: K + invK == S.alphabet_size()
2856
+ True
2857
+ sage: invK + K == S.alphabet_size()
2858
+ True
2859
+ sage: K = S.random_key()
2860
+ sage: while K != 0:
2861
+ ....: K = S.random_key()
2862
+ sage: invK = S.inverse_key(K)
2863
+ sage: K + invK != S.alphabet_size()
2864
+ True
2865
+ sage: K; invK
2866
+ 0
2867
+ 0
2868
+
2869
+ TESTS:
2870
+
2871
+ The key ``K`` must satisfy the inequality `0 \leq K < n` with `n`
2872
+ being the size of the plaintext, ciphertext, and key spaces. For the
2873
+ shift cryptosystem, all these spaces are the same alphabet. This
2874
+ inequality must be satisfied for each of the supported alphabets.
2875
+ The capital letters of the English alphabet::
2876
+
2877
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2878
+ sage: S.inverse_key(S.alphabet_size())
2879
+ Traceback (most recent call last):
2880
+ ...
2881
+ ValueError: K (=26) is outside the range of acceptable values for a key of this shift cryptosystem.
2882
+ sage: S.inverse_key(-1)
2883
+ Traceback (most recent call last):
2884
+ ...
2885
+ ValueError: K (=-1) is outside the range of acceptable values for a key of this shift cryptosystem.
2886
+
2887
+ The hexadecimal number system::
2888
+
2889
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
2890
+ sage: S.inverse_key(S.alphabet_size())
2891
+ Traceback (most recent call last):
2892
+ ...
2893
+ ValueError: K (=16) is outside the range of acceptable values for a key of this shift cryptosystem.
2894
+ sage: S.inverse_key(-1)
2895
+ Traceback (most recent call last):
2896
+ ...
2897
+ ValueError: K (=-1) is outside the range of acceptable values for a key of this shift cryptosystem.
2898
+
2899
+ The binary number system::
2900
+
2901
+ sage: S = ShiftCryptosystem(BinaryStrings())
2902
+ sage: S.inverse_key(S.alphabet_size())
2903
+ Traceback (most recent call last):
2904
+ ...
2905
+ ValueError: K (=2) is outside the range of acceptable values for a key of this shift cryptosystem.
2906
+ sage: S.inverse_key(-1)
2907
+ Traceback (most recent call last):
2908
+ ...
2909
+ ValueError: K (=-1) is outside the range of acceptable values for a key of this shift cryptosystem.
2910
+ """
2911
+ # Sanity check: the key K must satisfy the inequality
2912
+ # 0 <= K < n with n being the size of the plaintext, ciphertext, and
2913
+ # key spaces. For the shift cryptosystem, all these spaces are the
2914
+ # same alphabet.
2915
+ if 0 <= K < self.alphabet_size():
2916
+ # Let A be the alphabet of this cryptosystem and let n be the
2917
+ # number of elements in A. If k is a key, then the corresponding
2918
+ # inverse key is -k mod n.
2919
+ return self.key_space()(-Integer(K)).lift()
2920
+ else:
2921
+ raise ValueError("K (=%s) is outside the range of acceptable values for a key of this shift cryptosystem." % K)
2922
+
2923
+ def random_key(self):
2924
+ r"""
2925
+ Generate a random key within the key space of this shift cipher.
2926
+ The generated key is an integer `0 \leq k < n` with `n` being the
2927
+ size of the cipher domain. Thus there are `n` possible keys in the
2928
+ key space, which is the set `\ZZ / n\ZZ`. The key `k = 0` has no
2929
+ effect on either the plaintext or the ciphertext.
2930
+
2931
+ OUTPUT: a random key within the key space of this shift cryptosystem
2932
+
2933
+ EXAMPLES::
2934
+
2935
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2936
+ sage: S.random_key() # random
2937
+ 18
2938
+ sage: S = ShiftCryptosystem(BinaryStrings())
2939
+ sage: S.random_key() # random
2940
+ 0
2941
+ sage: S = ShiftCryptosystem(HexadecimalStrings())
2942
+ sage: S.random_key() # random
2943
+ 5
2944
+
2945
+ Regardless of the value of a key, the addition of the key and its
2946
+ inverse must be equal to the alphabet size. This relationship holds
2947
+ exactly when the value of the key is nonzero::
2948
+
2949
+ sage: S = ShiftCryptosystem(AlphabeticStrings())
2950
+ sage: K = S.random_key()
2951
+ sage: while K == 0:
2952
+ ....: K = S.random_key()
2953
+ sage: invK = S.inverse_key(K)
2954
+ sage: K + invK == S.alphabet_size()
2955
+ True
2956
+ sage: invK + K == S.alphabet_size()
2957
+ True
2958
+ sage: K = S.random_key()
2959
+ sage: while K != 0:
2960
+ ....: K = S.random_key()
2961
+ sage: invK = S.inverse_key(K)
2962
+ sage: K + invK != S.alphabet_size()
2963
+ True
2964
+ sage: K; invK
2965
+ 0
2966
+ 0
2967
+ """
2968
+ # Return a random element in ZZ/nZZ where n is the number of elements
2969
+ # in the plaintext/ciphertext alphabet and key space.
2970
+ from sage.misc.prandom import randint
2971
+ return Integer(randint(0, self.alphabet_size() - 1))
2972
+
2973
+
2974
+ class SubstitutionCryptosystem(SymmetricKeyCryptosystem):
2975
+ """
2976
+ Create a substitution cryptosystem.
2977
+
2978
+ INPUT:
2979
+
2980
+ - ``S`` -- string monoid over some alphabet
2981
+
2982
+ OUTPUT: a substitution cryptosystem over the alphabet ``S``
2983
+
2984
+ EXAMPLES::
2985
+
2986
+ sage: M = AlphabeticStrings()
2987
+ sage: E = SubstitutionCryptosystem(M)
2988
+ sage: E
2989
+ Substitution cryptosystem on Free alphabetic string monoid on A-Z
2990
+ sage: K = M([ 25-i for i in range(26) ])
2991
+ sage: K
2992
+ ZYXWVUTSRQPONMLKJIHGFEDCBA
2993
+ sage: e = E(K)
2994
+ sage: m = M("THECATINTHEHAT")
2995
+ sage: e(m)
2996
+ GSVXZGRMGSVSZG
2997
+
2998
+ TESTS::
2999
+
3000
+ sage: M = AlphabeticStrings()
3001
+ sage: E = SubstitutionCryptosystem(M)
3002
+ sage: E == loads(dumps(E))
3003
+ True
3004
+ """
3005
+
3006
+ def __init__(self, S):
3007
+ """
3008
+ See ``SubstitutionCryptosystem`` for full documentation.
3009
+
3010
+ EXAMPLES::
3011
+
3012
+ sage: M = AlphabeticStrings()
3013
+ sage: E = SubstitutionCryptosystem(M)
3014
+ sage: E
3015
+ Substitution cryptosystem on Free alphabetic string monoid on A-Z
3016
+ """
3017
+ if not isinstance(S, StringMonoid_class):
3018
+ raise TypeError("S (= %s) must be a string monoid." % S)
3019
+ SymmetricKeyCryptosystem.__init__(self, S, S, S)
3020
+
3021
+ def __call__(self, K):
3022
+ """
3023
+ Create a substitution cipher.
3024
+
3025
+ INPUT:
3026
+
3027
+ - ``K`` -- a key which is a permutation of the cryptosystem alphabet
3028
+
3029
+ EXAMPLES::
3030
+
3031
+ sage: M = AlphabeticStrings()
3032
+ sage: E = SubstitutionCryptosystem(M)
3033
+ sage: E
3034
+ Substitution cryptosystem on Free alphabetic string monoid on A-Z
3035
+ sage: K = M([ 25-i for i in range(26) ])
3036
+ sage: K
3037
+ ZYXWVUTSRQPONMLKJIHGFEDCBA
3038
+ sage: e = E(K)
3039
+ sage: m = M("THECATINTHEHAT")
3040
+ sage: e(m)
3041
+ GSVXZGRMGSVSZG
3042
+ """
3043
+ if not isinstance(K, StringMonoidElement):
3044
+ raise TypeError("K (= %s) must be a string." % K)
3045
+ if K.parent() != self.key_space():
3046
+ raise TypeError("K (= %s) must be a string in the key space." % K)
3047
+ return SubstitutionCipher(self, K)
3048
+
3049
+ def _repr_(self):
3050
+ """
3051
+ Return a string representation of ``self``.
3052
+
3053
+ EXAMPLES::
3054
+
3055
+ sage: A = AlphabeticStrings()
3056
+ sage: S = SubstitutionCryptosystem(A)
3057
+ sage: S
3058
+ Substitution cryptosystem on Free alphabetic string monoid on A-Z
3059
+ sage: S._repr_()
3060
+ 'Substitution cryptosystem on Free alphabetic string monoid on A-Z'
3061
+ """
3062
+ return "Substitution cryptosystem on %s" % self.cipher_domain()
3063
+
3064
+ def random_key(self):
3065
+ """
3066
+ Generate a random key within the key space of this substitution
3067
+ cipher. The generated key is a permutation of the cryptosystem
3068
+ alphabet. Let `n` be the length of the alphabet. Then there are
3069
+ `n!` possible keys in the key space.
3070
+
3071
+ OUTPUT: a random key within the key space of this cryptosystem
3072
+
3073
+ EXAMPLES::
3074
+
3075
+ sage: A = AlphabeticStrings()
3076
+ sage: S = SubstitutionCryptosystem(A)
3077
+ sage: K = S.random_key()
3078
+ sage: Ki = S.inverse_key(K)
3079
+ sage: M = "THECATINTHEHAT"
3080
+ sage: e = S(K)
3081
+ sage: d = S(Ki)
3082
+ sage: d(e(A(M))) == A(M)
3083
+ True
3084
+ """
3085
+ from sage.combinat.permutation import Permutations
3086
+ S = self.cipher_domain()
3087
+ n = S.ngens()
3088
+ I = Permutations(n).random_element()
3089
+ return S([ i-1 for i in I ])
3090
+
3091
+ def inverse_key(self, K):
3092
+ """
3093
+ The inverse key corresponding to the key ``K``. The specified key is a
3094
+ permutation of the cryptosystem alphabet.
3095
+
3096
+ INPUT:
3097
+
3098
+ - ``K`` -- a key belonging to the key space of this cryptosystem
3099
+
3100
+ OUTPUT: the inverse key of ``K``
3101
+
3102
+ EXAMPLES::
3103
+
3104
+ sage: S = AlphabeticStrings()
3105
+ sage: E = SubstitutionCryptosystem(S)
3106
+ sage: K = E.random_key()
3107
+ sage: L = E.inverse_key(K)
3108
+ sage: M = S("THECATINTHEHAT")
3109
+ sage: e = E(K)
3110
+ sage: c = E(L)
3111
+ sage: c(e(M))
3112
+ THECATINTHEHAT
3113
+ """
3114
+ I = K._element_list
3115
+ S = self.cipher_domain()
3116
+ n = S.ngens()
3117
+ return S([ I.index(i) for i in range(n) ])
3118
+
3119
+ def encoding(self, M):
3120
+ """
3121
+ The encoding of the string ``M`` over the string monoid of this
3122
+ substitution cipher. For example, if the string monoid of this
3123
+ cryptosystem is :class:`AlphabeticStringMonoid`, then the encoding
3124
+ of ``M`` would be its upper-case equivalent stripped of all
3125
+ non-alphabetic characters.
3126
+
3127
+ INPUT:
3128
+
3129
+ - ``M`` -- string, possibly empty
3130
+
3131
+ OUTPUT: the encoding of ``M`` over the string monoid of this
3132
+ cryptosystem
3133
+
3134
+ EXAMPLES::
3135
+
3136
+ sage: M = "Peter Pan(ning) for gold."
3137
+ sage: A = AlphabeticStrings()
3138
+ sage: S = SubstitutionCryptosystem(A)
3139
+ sage: S.encoding(M) == A.encoding(M)
3140
+ True
3141
+ """
3142
+ S = self.cipher_domain()
3143
+ if isinstance(S, AlphabeticStringMonoid):
3144
+ return S(strip_encoding(M))
3145
+ try:
3146
+ return S.encoding(M)
3147
+ except Exception:
3148
+ raise TypeError("Argument M = %s does not encode in the cipher domain" % M)
3149
+
3150
+ def deciphering(self, K, C):
3151
+ """
3152
+ Decrypt the ciphertext ``C`` using the key ``K``.
3153
+
3154
+ INPUT:
3155
+
3156
+ - ``K`` -- a key belonging to the key space of this substitution cipher
3157
+
3158
+ - ``C`` -- string (possibly empty) over the string monoid of this
3159
+ cryptosystem
3160
+
3161
+ OUTPUT: the plaintext corresponding to the ciphertext ``C``
3162
+
3163
+ EXAMPLES::
3164
+
3165
+ sage: S = SubstitutionCryptosystem(AlphabeticStrings())
3166
+ sage: K = S.random_key()
3167
+ sage: M = S.encoding("Don't substitute me!")
3168
+ sage: S.deciphering(K, S.enciphering(K, M)) == M
3169
+ True
3170
+ """
3171
+ i = self(self.inverse_key(K))
3172
+ return i(C)
3173
+
3174
+ def enciphering(self, K, M):
3175
+ """
3176
+ Encrypt the plaintext ``M`` using the key ``K``.
3177
+
3178
+ INPUT:
3179
+
3180
+ - ``K`` -- a key belonging to the key space of this substitution cipher
3181
+
3182
+ - ``M`` -- string (possibly empty) over the string monoid of this
3183
+ cryptosystem
3184
+
3185
+ OUTPUT: the ciphertext corresponding to the plaintext ``M``
3186
+
3187
+ EXAMPLES::
3188
+
3189
+ sage: S = SubstitutionCryptosystem(AlphabeticStrings())
3190
+ sage: K = S.random_key()
3191
+ sage: M = S.encoding("Don't substitute me.")
3192
+ sage: S.deciphering(K, S.enciphering(K, M)) == M
3193
+ True
3194
+ """
3195
+ e = self(K)
3196
+ return e(M)
3197
+
3198
+
3199
+ class TranspositionCryptosystem(SymmetricKeyCryptosystem):
3200
+ """
3201
+ Create a transposition cryptosystem of block length ``n``.
3202
+
3203
+ INPUT:
3204
+
3205
+ - ``S`` -- string monoid over some alphabet
3206
+
3207
+ - ``n`` -- integer `> 0`; a block length of a block permutation
3208
+
3209
+ OUTPUT:
3210
+
3211
+ - A transposition cryptosystem of block length ``n`` over the
3212
+ alphabet ``S``.
3213
+
3214
+ EXAMPLES::
3215
+
3216
+ sage: S = AlphabeticStrings()
3217
+ sage: E = TranspositionCryptosystem(S,14); E # needs sage.groups
3218
+ Transposition cryptosystem on
3219
+ Free alphabetic string monoid on A-Z of block length 14
3220
+ sage: K = [14 - i for i in range(14)]; K
3221
+ [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
3222
+ sage: e = E(K) # needs sage.groups
3223
+ sage: e(S("THECATINTHEHAT")) # needs sage.groups
3224
+ TAHEHTNITACEHT
3225
+
3226
+ TESTS::
3227
+
3228
+ sage: S = AlphabeticStrings()
3229
+ sage: E = TranspositionCryptosystem(S,14) # needs sage.groups
3230
+ sage: E == loads(dumps(E)) # needs sage.groups
3231
+ True
3232
+ """
3233
+
3234
+ def __init__(self, S, n):
3235
+ """
3236
+ See ``TranspositionCryptosystem`` for full documentation.
3237
+
3238
+ EXAMPLES::
3239
+
3240
+ sage: S = AlphabeticStrings()
3241
+ sage: E = TranspositionCryptosystem(S,14); E # needs sage.groups
3242
+ Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14
3243
+ """
3244
+ if not isinstance(S, StringMonoid_class):
3245
+ raise TypeError("S (= %s) must be a string monoid." % S)
3246
+ key_space = SymmetricGroup(n)
3247
+ SymmetricKeyCryptosystem.__init__(self, S, S, key_space, block_length=n)
3248
+
3249
+ def __call__(self, K):
3250
+ """
3251
+ Create a transposition cipher.
3252
+
3253
+ INPUT:
3254
+
3255
+ - ``K`` -- a key which specifies a block permutation
3256
+
3257
+ EXAMPLES::
3258
+
3259
+ sage: M = AlphabeticStrings()
3260
+ sage: E = TranspositionCryptosystem(M,14); E # needs sage.groups
3261
+ Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14
3262
+ sage: K = [14 - i for i in range(14)]; K
3263
+ [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
3264
+ sage: e = E(K) # needs sage.groups
3265
+ sage: m = M("THECATINTHEHAT")
3266
+ sage: e(m) # needs sage.groups
3267
+ TAHEHTNITACEHT
3268
+ """
3269
+ G = self.key_space()
3270
+ if isinstance(K, list):
3271
+ try:
3272
+ K = G(K)
3273
+ except Exception:
3274
+ raise TypeError("K (= %s) must specify a permutation." % K)
3275
+ if not isinstance(K, PermutationGroupElement) and K.parent() == G:
3276
+ raise TypeError("K (= %s) must be a permutation or list specifying a permutation." % K)
3277
+ return TranspositionCipher(self, K)
3278
+
3279
+ def _repr_(self):
3280
+ """
3281
+ Return a string representation of ``self``.
3282
+
3283
+ EXAMPLES::
3284
+
3285
+ sage: A = AlphabeticStrings()
3286
+ sage: T = TranspositionCryptosystem(A, 14); T # needs sage.groups
3287
+ Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14
3288
+ sage: T._repr_() # needs sage.groups
3289
+ 'Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14'
3290
+ """
3291
+ return "Transposition cryptosystem on %s of block length %s" % (
3292
+ self.cipher_domain(), self.block_length())
3293
+
3294
+ def random_key(self):
3295
+ """
3296
+ Generate a random key within the key space of this transposition
3297
+ cryptosystem. Let `n > 0` be the block length of this cryptosystem.
3298
+ Then there are `n!` possible keys.
3299
+
3300
+ OUTPUT: a random key within the key space of this cryptosystem
3301
+
3302
+ EXAMPLES::
3303
+
3304
+ sage: # needs sage.groups
3305
+ sage: S = AlphabeticStrings()
3306
+ sage: E = TranspositionCryptosystem(S, 14)
3307
+ sage: K = E.random_key()
3308
+ sage: Ki = E.inverse_key(K)
3309
+ sage: e = E(K)
3310
+ sage: d = E(Ki)
3311
+ sage: M = "THECATINTHEHAT"
3312
+ sage: C = e(S(M))
3313
+ sage: d(S(C)) == S(M)
3314
+ True
3315
+ """
3316
+ n = self.block_length()
3317
+ return SymmetricGroup(n).random_element()
3318
+
3319
+ def inverse_key(self, K, check=True):
3320
+ """
3321
+ The inverse key corresponding to the key ``K``.
3322
+
3323
+ INPUT:
3324
+
3325
+ - ``K`` -- a key belonging to the key space of this transposition
3326
+ cipher
3327
+
3328
+ - ``check`` -- boolean (default: ``True``); check that ``K`` belongs to
3329
+ the key space of this cryptosystem
3330
+
3331
+ OUTPUT: the inverse key corresponding to ``K``
3332
+
3333
+ EXAMPLES::
3334
+
3335
+ sage: # needs sage.groups
3336
+ sage: S = AlphabeticStrings()
3337
+ sage: E = TranspositionCryptosystem(S, 14)
3338
+ sage: K = E.random_key()
3339
+ sage: Ki = E.inverse_key(K)
3340
+ sage: e = E(K)
3341
+ sage: d = E(Ki)
3342
+ sage: M = "THECATINTHEHAT"
3343
+ sage: C = e(S(M))
3344
+ sage: d(S(C)) == S(M)
3345
+ True
3346
+ """
3347
+ if check:
3348
+ if K not in self.key_space():
3349
+ raise TypeError("Argument K (= %s) is not in the key space." % K)
3350
+ return K**-1
3351
+
3352
+ def encoding(self, M):
3353
+ """
3354
+ The encoding of the string ``M`` over the string monoid of this
3355
+ transposition cipher. For example, if the string monoid of this
3356
+ cryptosystem is :class:`AlphabeticStringMonoid`, then the encoding
3357
+ of ``M`` would be its upper-case equivalent stripped of all
3358
+ non-alphabetic characters.
3359
+
3360
+ INPUT:
3361
+
3362
+ - ``M`` -- string, possibly empty
3363
+
3364
+ OUTPUT: the encoding of ``M`` over the string monoid of this
3365
+ cryptosystem
3366
+
3367
+ EXAMPLES::
3368
+
3369
+ sage: M = "Transposition cipher is not about matrix transpose."
3370
+ sage: A = AlphabeticStrings()
3371
+ sage: T = TranspositionCryptosystem(A, 11) # needs sage.groups
3372
+ sage: T.encoding(M) == A.encoding(M) # needs sage.groups
3373
+ True
3374
+ """
3375
+ S = self.cipher_domain()
3376
+ if isinstance(S, AlphabeticStringMonoid):
3377
+ return S(strip_encoding(M))
3378
+ try:
3379
+ return S.encoding(M)
3380
+ except Exception:
3381
+ raise TypeError("Argument M = %s does not encode in the cipher domain" % M)
3382
+
3383
+ def deciphering(self, K, C):
3384
+ """
3385
+ Decrypt the ciphertext ``C`` using the key ``K``.
3386
+
3387
+ INPUT:
3388
+
3389
+ - ``K`` -- a key belonging to the key space of this transposition
3390
+ cipher
3391
+
3392
+ - ``C`` -- string (possibly empty) over the string monoid of this
3393
+ cryptosystem
3394
+
3395
+ OUTPUT: the plaintext corresponding to the ciphertext ``C``
3396
+
3397
+ EXAMPLES::
3398
+
3399
+ sage: # needs sage.groups
3400
+ sage: T = TranspositionCryptosystem(AlphabeticStrings(), 14)
3401
+ sage: K = T.random_key()
3402
+ sage: M = T.encoding("The cat in the hat.")
3403
+ sage: T.deciphering(K, T.enciphering(K, M)) == M
3404
+ True
3405
+ """
3406
+ i = self(self.inverse_key(K))
3407
+ return i(C)
3408
+
3409
+ def enciphering(self, K, M):
3410
+ """
3411
+ Encrypt the plaintext ``M`` using the key ``K``.
3412
+
3413
+ INPUT:
3414
+
3415
+ - ``K`` -- a key belonging to the key space of this transposition
3416
+ cipher
3417
+
3418
+ - ``M`` -- string (possibly empty) over the string monoid of this
3419
+ cryptosystem
3420
+
3421
+ OUTPUT: the ciphertext corresponding to the plaintext ``M``
3422
+
3423
+ EXAMPLES::
3424
+
3425
+ sage: # needs sage.groups
3426
+ sage: T = TranspositionCryptosystem(AlphabeticStrings(), 14)
3427
+ sage: K = T.random_key()
3428
+ sage: M = T.encoding("The cat in the hat.")
3429
+ sage: T.deciphering(K, T.enciphering(K, M)) == M
3430
+ True
3431
+ """
3432
+ e = self(K)
3433
+ return e(M)
3434
+
3435
+
3436
+ class VigenereCryptosystem(SymmetricKeyCryptosystem):
3437
+ """
3438
+ Create a Vigenere cryptosystem of block length ``n``.
3439
+
3440
+ INPUT:
3441
+
3442
+ - ``S`` -- string monoid over some alphabet
3443
+
3444
+ - ``n`` -- integer `> 0`; block length of an encryption/decryption key
3445
+
3446
+ OUTPUT:
3447
+
3448
+ - A Vigenere cryptosystem of block length ``n`` over the alphabet
3449
+ ``S``.
3450
+
3451
+ EXAMPLES::
3452
+
3453
+ sage: S = AlphabeticStrings()
3454
+ sage: E = VigenereCryptosystem(S,14)
3455
+ sage: E
3456
+ Vigenere cryptosystem on Free alphabetic string monoid on A-Z of period 14
3457
+ sage: K = S('ABCDEFGHIJKLMN')
3458
+ sage: K
3459
+ ABCDEFGHIJKLMN
3460
+ sage: e = E(K)
3461
+ sage: e
3462
+ Cipher on Free alphabetic string monoid on A-Z
3463
+ sage: e(S("THECATINTHEHAT"))
3464
+ TIGFEYOUBQOSMG
3465
+
3466
+ TESTS::
3467
+
3468
+ sage: S = AlphabeticStrings()
3469
+ sage: E = VigenereCryptosystem(S,14)
3470
+ sage: E == loads(dumps(E))
3471
+ True
3472
+ """
3473
+
3474
+ def __init__(self, S, n):
3475
+ """
3476
+ See ``VigenereCryptosystem`` for full documentation.
3477
+
3478
+ EXAMPLES::
3479
+
3480
+ sage: S = AlphabeticStrings()
3481
+ sage: E = VigenereCryptosystem(S,14)
3482
+ sage: E
3483
+ Vigenere cryptosystem on Free alphabetic string monoid on A-Z of period 14
3484
+ """
3485
+ if not isinstance(S, StringMonoid_class):
3486
+ raise TypeError("S (= %s) must be a string monoid." % S)
3487
+ SymmetricKeyCryptosystem.__init__(self, S, S, S, block_length=1, period=n)
3488
+
3489
+ def __call__(self, K):
3490
+ """
3491
+ Create a Vigenere cipher.
3492
+
3493
+ INPUT:
3494
+
3495
+ - ``K`` -- a key which specifies a block permutation
3496
+
3497
+ EXAMPLES::
3498
+
3499
+ sage: S = AlphabeticStrings()
3500
+ sage: E = VigenereCryptosystem(S,14)
3501
+ sage: E
3502
+ Vigenere cryptosystem on Free alphabetic string monoid on A-Z of period 14
3503
+ sage: K = S('ABCDEFGHIJKLMN')
3504
+ sage: K
3505
+ ABCDEFGHIJKLMN
3506
+ sage: e = E(K)
3507
+ sage: e
3508
+ Cipher on Free alphabetic string monoid on A-Z
3509
+ sage: e(S("THECATINTHEHAT"))
3510
+ TIGFEYOUBQOSMG
3511
+ """
3512
+ S = self.key_space()
3513
+ m = self.period()
3514
+ if isinstance(K, list):
3515
+ try:
3516
+ K = S(K)
3517
+ except Exception:
3518
+ raise TypeError("K (= %s) must specify a string of length %s." % (K, m))
3519
+ if not len(K) == m:
3520
+ raise TypeError("K (= %s) must specify a string of length %s." % (K, m))
3521
+ return VigenereCipher(self, K)
3522
+
3523
+ def _repr_(self):
3524
+ """
3525
+ Return a string representation of ``self``.
3526
+
3527
+ EXAMPLES::
3528
+
3529
+ sage: A = AlphabeticStrings()
3530
+ sage: V = VigenereCryptosystem(A, 14)
3531
+ sage: V
3532
+ Vigenere cryptosystem on Free alphabetic string monoid on A-Z of period 14
3533
+ sage: V._repr_()
3534
+ 'Vigenere cryptosystem on Free alphabetic string monoid on A-Z of period 14'
3535
+ """
3536
+ return "Vigenere cryptosystem on %s of period %s" % (
3537
+ self.cipher_domain(), self.period())
3538
+
3539
+ def random_key(self):
3540
+ """
3541
+ Generate a random key within the key space of this Vigenere
3542
+ cryptosystem. Let `n > 0` be the length of the cryptosystem alphabet
3543
+ and let `m > 0` be the block length of this cryptosystem. Then there
3544
+ are `n^m` possible keys.
3545
+
3546
+ OUTPUT: a random key within the key space of this cryptosystem
3547
+
3548
+ EXAMPLES::
3549
+
3550
+ sage: A = AlphabeticStrings()
3551
+ sage: V = VigenereCryptosystem(A, 14)
3552
+ sage: M = "THECATINTHEHAT"
3553
+ sage: K = V.random_key()
3554
+ sage: Ki = V.inverse_key(K)
3555
+ sage: e = V(K)
3556
+ sage: d = V(Ki)
3557
+ sage: d(e(A(M))) == A(M)
3558
+ True
3559
+ """
3560
+ S = self.key_space()
3561
+ n = S.ngens()
3562
+ m = self.period()
3563
+ return S([ randint(0, n-1) for i in range(m) ])
3564
+
3565
+ def inverse_key(self, K):
3566
+ """
3567
+ The inverse key corresponding to the key ``K``.
3568
+
3569
+ INPUT:
3570
+
3571
+ - ``K`` -- a key within the key space of this Vigenere cryptosystem
3572
+
3573
+ OUTPUT: the inverse key corresponding to ``K``
3574
+
3575
+ EXAMPLES::
3576
+
3577
+ sage: S = AlphabeticStrings()
3578
+ sage: E = VigenereCryptosystem(S,14)
3579
+ sage: K = E.random_key()
3580
+ sage: L = E.inverse_key(K)
3581
+ sage: M = S("THECATINTHEHAT")
3582
+ sage: e = E(K)
3583
+ sage: c = E(L)
3584
+ sage: c(e(M))
3585
+ THECATINTHEHAT
3586
+ """
3587
+ S = self.key_space()
3588
+ n = S.ngens()
3589
+ return S([ (-i) % (n) for i in K._element_list ])
3590
+
3591
+ def encoding(self, M):
3592
+ """
3593
+ The encoding of the string ``M`` over the string monoid of this
3594
+ Vigenere cipher. For example, if the string monoid of this
3595
+ cryptosystem is :class:`AlphabeticStringMonoid`, then the encoding
3596
+ of ``M`` would be its upper-case equivalent stripped of all
3597
+ non-alphabetic characters.
3598
+
3599
+ INPUT:
3600
+
3601
+ - ``M`` -- string, possibly empty
3602
+
3603
+ OUTPUT: the encoding of ``M`` over the string monoid of this
3604
+ cryptosystem
3605
+
3606
+ EXAMPLES::
3607
+
3608
+ sage: A = AlphabeticStrings()
3609
+ sage: V = VigenereCryptosystem(A, 24)
3610
+ sage: M = "Jack and Jill went up the hill."
3611
+ sage: V.encoding(M) == A.encoding(M)
3612
+ True
3613
+ """
3614
+ S = self.cipher_domain()
3615
+ if isinstance(S, AlphabeticStringMonoid):
3616
+ return S(strip_encoding(M))
3617
+ try:
3618
+ return S.encoding(M)
3619
+ except Exception:
3620
+ raise TypeError("Argument M = %s does not encode in the cipher domain" % M)
3621
+
3622
+ def deciphering(self, K, C):
3623
+ """
3624
+ Decrypt the ciphertext ``C`` using the key ``K``.
3625
+
3626
+ INPUT:
3627
+
3628
+ - ``K`` -- a key belonging to the key space of this Vigenere cipher
3629
+
3630
+ - ``C`` -- string (possibly empty) over the string monoid of this
3631
+ cryptosystem
3632
+
3633
+ OUTPUT: the plaintext corresponding to the ciphertext ``C``
3634
+
3635
+ EXAMPLES::
3636
+
3637
+ sage: V = VigenereCryptosystem(AlphabeticStrings(), 24)
3638
+ sage: K = V.random_key()
3639
+ sage: M = V.encoding("Jack and Jill went up the hill.")
3640
+ sage: V.deciphering(K, V.enciphering(K, M)) == M
3641
+ True
3642
+ """
3643
+ i = self(self.inverse_key(K))
3644
+ return i(C)
3645
+
3646
+ def enciphering(self, K, M):
3647
+ """
3648
+ Encrypt the plaintext ``M`` using the key ``K``.
3649
+
3650
+ INPUT:
3651
+
3652
+ - ``K`` -- a key belonging to the key space of this Vigenere cipher
3653
+
3654
+ - ``M`` -- string (possibly empty) over the string monoid of this
3655
+ cryptosystem
3656
+
3657
+ OUTPUT: the ciphertext corresponding to the plaintext ``M``
3658
+
3659
+ EXAMPLES::
3660
+
3661
+ sage: V = VigenereCryptosystem(AlphabeticStrings(), 24)
3662
+ sage: K = V.random_key()
3663
+ sage: M = V.encoding("Jack and Jill went up the hill.")
3664
+ sage: V.deciphering(K, V.enciphering(K, M)) == M
3665
+ True
3666
+ """
3667
+ e = self(K)
3668
+ return e(M)