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