passagemath-modules 10.6.31rc3__cp314-cp314-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-modules might be problematic. Click here for more details.
- 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 +5 -0
- passagemath_modules-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_modules.libs/libgcc_s-2d945d6c.so.1 +0 -0
- passagemath_modules.libs/libgfortran-67378ab2.so.5.0.0 +0 -0
- passagemath_modules.libs/libgmp-28992bcb.so.10.5.0 +0 -0
- passagemath_modules.libs/libgsl-23768756.so.28.0.0 +0 -0
- passagemath_modules.libs/libmpc-7897025b.so.3.3.1 +0 -0
- passagemath_modules.libs/libmpfr-e34bb864.so.6.2.1 +0 -0
- passagemath_modules.libs/libopenblasp-r0-503f0c35.3.29.so +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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/calculus/integration.pyx +698 -0
- sage/calculus/interpolation.cpython-314-aarch64-linux-musl.so +0 -0
- sage/calculus/interpolation.pxd +13 -0
- sage/calculus/interpolation.pyx +387 -0
- sage/calculus/interpolators.cpython-314-aarch64-linux-musl.so +0 -0
- sage/calculus/interpolators.pyx +326 -0
- sage/calculus/ode.cpython-314-aarch64-linux-musl.so +0 -0
- sage/calculus/ode.pxd +5 -0
- sage/calculus/ode.pyx +610 -0
- sage/calculus/riemann.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/calculus/transforms/dwt.pxd +7 -0
- sage/calculus/transforms/dwt.pyx +160 -0
- sage/calculus/transforms/fft.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/libs/mpmath/utils.pxd +4 -0
- sage/libs/mpmath/utils.pyx +319 -0
- sage/matrix/action.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/matrix/constructor.pyx +750 -0
- sage/matrix/docs.py +430 -0
- sage/matrix/echelon_matrix.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matrix/echelon_matrix.pyx +155 -0
- sage/matrix/matrix.pxd +2 -0
- sage/matrix/matrix0.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix0.pxd +68 -0
- sage/matrix/matrix0.pyx +6324 -0
- sage/matrix/matrix1.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix1.pxd +8 -0
- sage/matrix/matrix1.pyx +2851 -0
- sage/matrix/matrix2.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix2.pxd +25 -0
- sage/matrix/matrix2.pyx +20181 -0
- sage/matrix/matrix_cdv.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix_sparse.pxd +5 -0
- sage/matrix/matrix_sparse.pyx +1222 -0
- sage/matrix/matrix_window.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matrix/matrix_window.pxd +37 -0
- sage/matrix/matrix_window.pyx +242 -0
- sage/matrix/misc_mpfr.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/matroids/extension.pxd +34 -0
- sage/matroids/extension.pyx +519 -0
- sage/matroids/flats_matroid.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/matroids/graphic_matroid.pxd +39 -0
- sage/matroids/graphic_matroid.pyx +2024 -0
- sage/matroids/lean_matrix.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matroids/lean_matrix.pxd +126 -0
- sage/matroids/lean_matrix.pyx +3667 -0
- sage/matroids/linear_matroid.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matroids/linear_matroid.pxd +180 -0
- sage/matroids/linear_matroid.pyx +6649 -0
- sage/matroids/matroid.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/matroids/set_system.pxd +38 -0
- sage/matroids/set_system.pyx +800 -0
- sage/matroids/transversal_matroid.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matroids/transversal_matroid.pxd +14 -0
- sage/matroids/transversal_matroid.pyx +893 -0
- sage/matroids/union_matroid.cpython-314-aarch64-linux-musl.so +0 -0
- sage/matroids/union_matroid.pxd +20 -0
- sage/matroids/union_matroid.pyx +331 -0
- sage/matroids/unpickling.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/rings/complex_conversion.pxd +8 -0
- sage/rings/complex_conversion.pyx +23 -0
- sage/rings/complex_double.cpython-314-aarch64-linux-musl.so +0 -0
- sage/rings/complex_double.pxd +21 -0
- sage/rings/complex_double.pyx +2654 -0
- sage/rings/complex_mpc.cpython-314-aarch64-linux-musl.so +0 -0
- sage/rings/complex_mpc.pxd +21 -0
- sage/rings/complex_mpc.pyx +2576 -0
- sage/rings/complex_mpfr.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/rings/function_field/hermite_form_polynomial.pyx +188 -0
- sage/rings/function_field/khuri_makdisi.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/rings/real_mpfr.pxd +29 -0
- sage/rings/real_mpfr.pyx +6122 -0
- sage/rings/ring_extension.cpython-314-aarch64-linux-musl.so +0 -0
- sage/rings/ring_extension.pxd +42 -0
- sage/rings/ring_extension.pyx +2779 -0
- sage/rings/ring_extension_conversion.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.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-aarch64-linux-musl.so +0 -0
- sage/stats/hmm/chmm.pyx +1595 -0
- sage/stats/hmm/distributions.cpython-314-aarch64-linux-musl.so +0 -0
- sage/stats/hmm/distributions.pxd +29 -0
- sage/stats/hmm/distributions.pyx +531 -0
- sage/stats/hmm/hmm.cpython-314-aarch64-linux-musl.so +0 -0
- sage/stats/hmm/hmm.pxd +17 -0
- sage/stats/hmm/hmm.pyx +1388 -0
- sage/stats/hmm/util.cpython-314-aarch64-linux-musl.so +0 -0
- sage/stats/hmm/util.pxd +7 -0
- sage/stats/hmm/util.pyx +165 -0
- sage/stats/intlist.cpython-314-aarch64-linux-musl.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-aarch64-linux-musl.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
sage/crypto/sbox.pyx
ADDED
|
@@ -0,0 +1,2090 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-modules
|
|
2
|
+
# sage.doctest: needs sage.modules sage.rings.finite_rings
|
|
3
|
+
r"""
|
|
4
|
+
S-Boxes and Their Algebraic Representations
|
|
5
|
+
"""
|
|
6
|
+
cimport cython
|
|
7
|
+
from cysignals.memory cimport check_allocarray, sig_free
|
|
8
|
+
|
|
9
|
+
from sage.structure.sage_object cimport SageObject
|
|
10
|
+
from sage.structure.element cimport Element
|
|
11
|
+
|
|
12
|
+
from sage.combinat.integer_vector import IntegerVectors
|
|
13
|
+
from sage.crypto.boolean_function import BooleanFunction
|
|
14
|
+
from sage.crypto.boolean_function cimport hamming_weight, walsh_hadamard
|
|
15
|
+
from sage.matrix.constructor import matrix
|
|
16
|
+
from sage.matrix.matrix0 cimport Matrix
|
|
17
|
+
from sage.misc.cachefunc import cached_method
|
|
18
|
+
from sage.misc.functional import is_even
|
|
19
|
+
from sage.misc.misc_c import prod as mul
|
|
20
|
+
from sage.modules.free_module_element import vector
|
|
21
|
+
from sage.rings.finite_rings.finite_field_base import FiniteField
|
|
22
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
|
|
23
|
+
from sage.rings.ideal import FieldIdeal, Ideal
|
|
24
|
+
from sage.rings.integer_ring import ZZ
|
|
25
|
+
from sage.rings.integer cimport Integer
|
|
26
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
cdef Py_ssize_t _nterms(Py_ssize_t nvars, Py_ssize_t deg) noexcept:
|
|
30
|
+
"""
|
|
31
|
+
Return the number of monomials possible up to a given
|
|
32
|
+
degree.
|
|
33
|
+
|
|
34
|
+
INPUT:
|
|
35
|
+
|
|
36
|
+
- ``nvars`` -- number of variables
|
|
37
|
+
|
|
38
|
+
- ``deg`` -- degree
|
|
39
|
+
|
|
40
|
+
TESTS::
|
|
41
|
+
|
|
42
|
+
sage: from sage.crypto.sbox import SBox
|
|
43
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
44
|
+
sage: F = S.polynomials(degree=3) # indirect doctest # needs sage.libs.singular
|
|
45
|
+
"""
|
|
46
|
+
cdef Py_ssize_t total = 1
|
|
47
|
+
cdef Py_ssize_t divisor = 1
|
|
48
|
+
cdef Py_ssize_t var_choices = 1
|
|
49
|
+
|
|
50
|
+
cdef Py_ssize_t d
|
|
51
|
+
for d in range(1, deg+1):
|
|
52
|
+
var_choices *= (nvars - d + 1)
|
|
53
|
+
divisor *= d
|
|
54
|
+
total += var_choices // divisor
|
|
55
|
+
|
|
56
|
+
return total
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@cython.auto_pickle(True)
|
|
60
|
+
cdef class SBox(SageObject):
|
|
61
|
+
r"""
|
|
62
|
+
A substitution box or S-box is one of the basic components of
|
|
63
|
+
symmetric key cryptography. In general, an S-box takes `m` input
|
|
64
|
+
bits and transforms them into `n` output bits. This is called an
|
|
65
|
+
`m \times n` S-box and is often implemented as a lookup table. These
|
|
66
|
+
S-boxes are carefully chosen to resist linear and differential
|
|
67
|
+
cryptanalysis [He2002]_.
|
|
68
|
+
|
|
69
|
+
This module implements an S-box class which allows an algebraic
|
|
70
|
+
treatment and determine various cryptographic properties.
|
|
71
|
+
|
|
72
|
+
EXAMPLES:
|
|
73
|
+
|
|
74
|
+
We consider the S-box of the block cipher PRESENT [BKLPPRSV2007]_::
|
|
75
|
+
|
|
76
|
+
sage: from sage.crypto.sbox import SBox
|
|
77
|
+
sage: S = SBox(12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2); S
|
|
78
|
+
(12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2)
|
|
79
|
+
sage: S(1)
|
|
80
|
+
5
|
|
81
|
+
|
|
82
|
+
Note that by default bits are interpreted in big endian
|
|
83
|
+
order. This is not consistent with the rest of Sage, which has a
|
|
84
|
+
strong bias towards little endian, but is consistent with most
|
|
85
|
+
cryptographic literature::
|
|
86
|
+
|
|
87
|
+
sage: S([0,0,0,1])
|
|
88
|
+
[0, 1, 0, 1]
|
|
89
|
+
|
|
90
|
+
sage: S = SBox(12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2, big_endian=False)
|
|
91
|
+
sage: S(1)
|
|
92
|
+
5
|
|
93
|
+
sage: S([0,0,0,1])
|
|
94
|
+
[1, 1, 0, 0]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
Now we construct an ``SBox`` object for the 4-bit small scale AES
|
|
98
|
+
S-Box (cf. :mod:`sage.crypto.mq.sr`)::
|
|
99
|
+
|
|
100
|
+
sage: sr = mq.SR(1,1,1,4, allow_zero_inversions=True)
|
|
101
|
+
sage: S = SBox([sr.sub_byte(e) for e in list(sr.k)])
|
|
102
|
+
sage: S
|
|
103
|
+
(6, 5, 2, 9, 4, 7, 3, 12, 14, 15, 10, 0, 8, 1, 13, 11)
|
|
104
|
+
|
|
105
|
+
AUTHORS:
|
|
106
|
+
|
|
107
|
+
- Rusydi H. Makarim (2016-03-31) : added more functions to determine related cryptographic properties
|
|
108
|
+
- Yann Laigle-Chapuy (2009-07-01): improve linear and difference matrix computation
|
|
109
|
+
- Martin R. Albrecht (2008-03-12): initial implementation
|
|
110
|
+
|
|
111
|
+
REFERENCES:
|
|
112
|
+
|
|
113
|
+
- [He2002]_
|
|
114
|
+
- [BKLPPRSV2007]_
|
|
115
|
+
- [CDL2015]_
|
|
116
|
+
"""
|
|
117
|
+
cdef list _S_list
|
|
118
|
+
cdef object _ring
|
|
119
|
+
cdef Py_ssize_t m
|
|
120
|
+
cdef Py_ssize_t n
|
|
121
|
+
cdef bint _big_endian
|
|
122
|
+
cdef dict __dict__ # for cached_methods
|
|
123
|
+
|
|
124
|
+
def __init__(self, *args, **kwargs):
|
|
125
|
+
r"""
|
|
126
|
+
Construct a substitution box (S-box) for a given lookup table `S`.
|
|
127
|
+
|
|
128
|
+
INPUT:
|
|
129
|
+
|
|
130
|
+
- ``S`` -- a finite iterable defining the S-box with integer or
|
|
131
|
+
finite field elements
|
|
132
|
+
|
|
133
|
+
- ``big_endian`` -- boolean (default: ``True``); controls whether bits
|
|
134
|
+
shall be ordered in big endian order
|
|
135
|
+
|
|
136
|
+
EXAMPLES:
|
|
137
|
+
|
|
138
|
+
We construct a 3-bit S-box where e.g. the bits (0,0,1) are
|
|
139
|
+
mapped to (1,1,1).::
|
|
140
|
+
|
|
141
|
+
sage: from sage.crypto.sbox import SBox
|
|
142
|
+
sage: S = SBox(7,6,0,4,2,5,1,3); S
|
|
143
|
+
(7, 6, 0, 4, 2, 5, 1, 3)
|
|
144
|
+
|
|
145
|
+
sage: S(0)
|
|
146
|
+
7
|
|
147
|
+
|
|
148
|
+
Construct S-box from univariate polynomial.::
|
|
149
|
+
|
|
150
|
+
sage: R = PolynomialRing(GF(2**3), 'x')
|
|
151
|
+
sage: inv = R.gen()**(2**3-2)
|
|
152
|
+
sage: inv = SBox(inv); inv
|
|
153
|
+
(0, 1, 5, 6, 7, 2, 3, 4)
|
|
154
|
+
sage: inv.differential_uniformity()
|
|
155
|
+
2
|
|
156
|
+
|
|
157
|
+
sage: SBox(PolynomialRing(GF(3**3), 'x').gen())
|
|
158
|
+
Traceback (most recent call last):
|
|
159
|
+
...
|
|
160
|
+
TypeError: only polynomials over rings with characteristic 2 allowed
|
|
161
|
+
|
|
162
|
+
TESTS::
|
|
163
|
+
|
|
164
|
+
sage: from sage.crypto.sbox import SBox
|
|
165
|
+
sage: S = SBox()
|
|
166
|
+
Traceback (most recent call last):
|
|
167
|
+
...
|
|
168
|
+
TypeError: no lookup table provided
|
|
169
|
+
sage: S = SBox(1, 2, 3)
|
|
170
|
+
Traceback (most recent call last):
|
|
171
|
+
...
|
|
172
|
+
TypeError: lookup table length is not a power of 2
|
|
173
|
+
sage: S = SBox(5, 6, 0, 3, 4, 2, 1, 2)
|
|
174
|
+
sage: S.output_size()
|
|
175
|
+
3
|
|
176
|
+
"""
|
|
177
|
+
from sage.rings.polynomial.polynomial_element import Polynomial
|
|
178
|
+
|
|
179
|
+
if "S" in kwargs:
|
|
180
|
+
args = kwargs["S"]
|
|
181
|
+
|
|
182
|
+
if len(args) == 1 and isinstance(args[0], Polynomial):
|
|
183
|
+
# SBox defined via Univariate Polynomial, compute lookup table
|
|
184
|
+
# by evaluating the polynomial on every base_ring element
|
|
185
|
+
poly = args[0]
|
|
186
|
+
R = poly.parent().base_ring()
|
|
187
|
+
if R.characteristic() != 2:
|
|
188
|
+
raise TypeError("only polynomials over rings with characteristic 2 allowed")
|
|
189
|
+
S = [poly(v) for v in sorted(R)]
|
|
190
|
+
elif len(args) == 1: # iterables
|
|
191
|
+
S = args[0]
|
|
192
|
+
elif len(args) > 1:
|
|
193
|
+
S = args
|
|
194
|
+
else:
|
|
195
|
+
raise TypeError("no lookup table provided")
|
|
196
|
+
|
|
197
|
+
_S_list = []
|
|
198
|
+
for e in S:
|
|
199
|
+
if isinstance(e, Element) and isinstance(e.parent(), FiniteField):
|
|
200
|
+
e = e.polynomial().change_ring(ZZ).subs(e.parent().characteristic())
|
|
201
|
+
_S_list.append(e)
|
|
202
|
+
S = _S_list
|
|
203
|
+
|
|
204
|
+
if not ZZ(len(S)).is_power_of(2):
|
|
205
|
+
raise TypeError("lookup table length is not a power of 2")
|
|
206
|
+
self._S_list = S
|
|
207
|
+
|
|
208
|
+
self.m = ZZ(len(S)).exact_log(2)
|
|
209
|
+
self.n = ZZ(max(S)).nbits()
|
|
210
|
+
self._big_endian = kwargs.get("big_endian", True)
|
|
211
|
+
|
|
212
|
+
cdef Py_ssize_t i
|
|
213
|
+
self._ring = PolynomialRing(
|
|
214
|
+
GF(2),
|
|
215
|
+
self.m + self.n,
|
|
216
|
+
["x%d" % i for i in range(self.m)] + ["y%d" % i for i in range(self.n)])
|
|
217
|
+
|
|
218
|
+
def _repr_(self):
|
|
219
|
+
"""
|
|
220
|
+
EXAMPLES::
|
|
221
|
+
|
|
222
|
+
sage: from sage.crypto.sbox import SBox
|
|
223
|
+
sage: SBox(7,6,0,4,2,5,1,3) # indirect doctest
|
|
224
|
+
(7, 6, 0, 4, 2, 5, 1, 3)
|
|
225
|
+
"""
|
|
226
|
+
return "(" + ", ".join(map(str, self)) + ")"
|
|
227
|
+
|
|
228
|
+
def __len__(self):
|
|
229
|
+
"""
|
|
230
|
+
Return the length of input bit strings.
|
|
231
|
+
|
|
232
|
+
EXAMPLES::
|
|
233
|
+
|
|
234
|
+
sage: from sage.crypto.sbox import SBox
|
|
235
|
+
sage: len(SBox(7,6,0,4,2,5,1,3))
|
|
236
|
+
3
|
|
237
|
+
"""
|
|
238
|
+
return self.m
|
|
239
|
+
|
|
240
|
+
def __eq__(self, rhs):
|
|
241
|
+
"""
|
|
242
|
+
S-boxes are considered to be equal if all construction
|
|
243
|
+
parameters match.
|
|
244
|
+
|
|
245
|
+
EXAMPLES::
|
|
246
|
+
|
|
247
|
+
sage: from sage.crypto.sbox import SBox
|
|
248
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
249
|
+
sage: loads(dumps(S)) == S
|
|
250
|
+
True
|
|
251
|
+
"""
|
|
252
|
+
if not isinstance(rhs, SBox):
|
|
253
|
+
raise NotImplemented
|
|
254
|
+
|
|
255
|
+
cdef SBox other = <SBox> rhs
|
|
256
|
+
return (self._S_list == other._S_list) and (self._big_endian == self._big_endian)
|
|
257
|
+
|
|
258
|
+
def __ne__(self, other):
|
|
259
|
+
"""
|
|
260
|
+
S-boxes are considered to be equal if all construction
|
|
261
|
+
parameters match.
|
|
262
|
+
|
|
263
|
+
EXAMPLES::
|
|
264
|
+
|
|
265
|
+
sage: from sage.crypto.sbox import SBox
|
|
266
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
267
|
+
sage: S != S
|
|
268
|
+
False
|
|
269
|
+
"""
|
|
270
|
+
return not self.__eq__(other)
|
|
271
|
+
|
|
272
|
+
cpdef list to_bits(self, x, n=None):
|
|
273
|
+
"""
|
|
274
|
+
Return bitstring of length ``n`` for integer ``x``. The
|
|
275
|
+
returned bitstring is guaranteed to have length ``n``.
|
|
276
|
+
|
|
277
|
+
INPUT:
|
|
278
|
+
|
|
279
|
+
- ``x`` -- integer
|
|
280
|
+
|
|
281
|
+
- ``n`` -- bit length (optional)
|
|
282
|
+
|
|
283
|
+
EXAMPLES::
|
|
284
|
+
|
|
285
|
+
sage: from sage.crypto.sbox import SBox
|
|
286
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
287
|
+
sage: S.to_bits(6)
|
|
288
|
+
[1, 1, 0]
|
|
289
|
+
|
|
290
|
+
sage: S.to_bits( S(6) )
|
|
291
|
+
[0, 0, 1]
|
|
292
|
+
|
|
293
|
+
sage: S( S.to_bits( 6 ) )
|
|
294
|
+
[0, 0, 1]
|
|
295
|
+
"""
|
|
296
|
+
if n is None and self.m == self.n:
|
|
297
|
+
n = self.n
|
|
298
|
+
|
|
299
|
+
F = GF(2)
|
|
300
|
+
cdef list xs = [F(i) for i in ZZ(x).digits(base=2, padto=n)]
|
|
301
|
+
|
|
302
|
+
if self._big_endian:
|
|
303
|
+
xs.reverse()
|
|
304
|
+
|
|
305
|
+
return xs
|
|
306
|
+
|
|
307
|
+
def from_bits(self, x, n=None):
|
|
308
|
+
"""
|
|
309
|
+
Return integer for bitstring ``x`` of length ``n``.
|
|
310
|
+
|
|
311
|
+
INPUT:
|
|
312
|
+
|
|
313
|
+
- ``x`` -- a bitstring
|
|
314
|
+
|
|
315
|
+
- ``n`` -- bit length (optional)
|
|
316
|
+
|
|
317
|
+
EXAMPLES::
|
|
318
|
+
|
|
319
|
+
sage: from sage.crypto.sbox import SBox
|
|
320
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
321
|
+
sage: S.from_bits( [1,1,0])
|
|
322
|
+
6
|
|
323
|
+
|
|
324
|
+
sage: S( S.from_bits( [1,1,0] ) )
|
|
325
|
+
1
|
|
326
|
+
sage: S.from_bits( S( [1,1,0] ) )
|
|
327
|
+
1
|
|
328
|
+
"""
|
|
329
|
+
if n is None and self.m == self.n:
|
|
330
|
+
n = self.n
|
|
331
|
+
|
|
332
|
+
if self._big_endian:
|
|
333
|
+
x = list(reversed(x))
|
|
334
|
+
|
|
335
|
+
return ZZ(self._rpad(x, n), 2)
|
|
336
|
+
|
|
337
|
+
cdef list _rpad(self, list x, Py_ssize_t n=-1):
|
|
338
|
+
"""
|
|
339
|
+
Right pads ``x`` such that ``len(x) == n``.
|
|
340
|
+
|
|
341
|
+
EXAMPLES::
|
|
342
|
+
|
|
343
|
+
sage: from sage.crypto.sbox import SBox
|
|
344
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
345
|
+
sage: S._rpad([1,1]) # not tested
|
|
346
|
+
[1, 1, 0]
|
|
347
|
+
"""
|
|
348
|
+
if n == -1 and self.m == self.n:
|
|
349
|
+
n = self.n
|
|
350
|
+
return x + [GF(2).zero()] * (n-len(x))
|
|
351
|
+
|
|
352
|
+
def __call__(self, X):
|
|
353
|
+
r"""
|
|
354
|
+
Apply substitution to ``X``.
|
|
355
|
+
|
|
356
|
+
If ``X`` is a list, it is interpreted as a sequence of bits
|
|
357
|
+
depending on the bit order of this S-box.
|
|
358
|
+
|
|
359
|
+
INPUT:
|
|
360
|
+
|
|
361
|
+
- ``X`` -- either an integer, a tuple of `\GF{2}` elements of
|
|
362
|
+
length ``len(self)`` or a finite field element in
|
|
363
|
+
`\GF{2^n}`. As a last resort this function tries to convert
|
|
364
|
+
``X`` to an integer.
|
|
365
|
+
|
|
366
|
+
EXAMPLES:
|
|
367
|
+
|
|
368
|
+
We can call SBoxes with integers as inputs, this will
|
|
369
|
+
return an integer::
|
|
370
|
+
|
|
371
|
+
sage: from sage.crypto.sbox import SBox
|
|
372
|
+
sage: S = SBox(3, 0, 1, 3, 1, 0, 2, 2)
|
|
373
|
+
sage: S(0)
|
|
374
|
+
3
|
|
375
|
+
|
|
376
|
+
sage: S([0,0,0])
|
|
377
|
+
[1, 1]
|
|
378
|
+
|
|
379
|
+
sage: S = SBox([7,6,0,4,2,5,1,3])
|
|
380
|
+
sage: S(7)
|
|
381
|
+
3
|
|
382
|
+
|
|
383
|
+
sage: S[0]
|
|
384
|
+
7
|
|
385
|
+
|
|
386
|
+
sage: S(QQ(3))
|
|
387
|
+
4
|
|
388
|
+
|
|
389
|
+
Alternatively, we can call in with a list-like object, which
|
|
390
|
+
will return a list::
|
|
391
|
+
|
|
392
|
+
sage: S((0,2,3))
|
|
393
|
+
[0, 1, 1]
|
|
394
|
+
|
|
395
|
+
sage: S((0,0,1))
|
|
396
|
+
[1, 1, 0]
|
|
397
|
+
|
|
398
|
+
Calling it with a vector will return a vector::
|
|
399
|
+
|
|
400
|
+
sage: S(vector(GF(2), [0,0,1]))
|
|
401
|
+
(1, 1, 0)
|
|
402
|
+
|
|
403
|
+
sage: type(vector(GF(2), [0,0,1])) == type(S(vector(GF(2), [0,0,1])))
|
|
404
|
+
True
|
|
405
|
+
|
|
406
|
+
An input from a finite field will be interpreted as given the
|
|
407
|
+
coefficient vector as input::
|
|
408
|
+
|
|
409
|
+
sage: k.<a> = GF(2^3)
|
|
410
|
+
sage: S(a^2) # interpreted as (0,0,1)
|
|
411
|
+
a + 1
|
|
412
|
+
sage: S([0, 0, 1])
|
|
413
|
+
[1, 1, 0]
|
|
414
|
+
sage: vector(a + 1)
|
|
415
|
+
(1, 1, 0)
|
|
416
|
+
|
|
417
|
+
sage: id = SBox(range(8))
|
|
418
|
+
sage: all([x == id(x) for x in k])
|
|
419
|
+
True
|
|
420
|
+
|
|
421
|
+
Some examples for inputs that throw an :exc:`TypeError`::
|
|
422
|
+
|
|
423
|
+
sage: S([1]*10^6)
|
|
424
|
+
Traceback (most recent call last):
|
|
425
|
+
...
|
|
426
|
+
TypeError: cannot apply SBox to provided element
|
|
427
|
+
|
|
428
|
+
sage: S(1/2)
|
|
429
|
+
Traceback (most recent call last):
|
|
430
|
+
...
|
|
431
|
+
TypeError: cannot apply SBox to 1/2
|
|
432
|
+
"""
|
|
433
|
+
# Handle integer inputs
|
|
434
|
+
if isinstance(X, int):
|
|
435
|
+
return self._S_list[<int> X]
|
|
436
|
+
if isinstance(X, Integer):
|
|
437
|
+
return self._S_list[<Integer> X]
|
|
438
|
+
|
|
439
|
+
# Handle non-integer inputs: vectors, finite field elements to-integer-coercible elements
|
|
440
|
+
# cdef int i
|
|
441
|
+
if isinstance(X, Element):
|
|
442
|
+
K = X.parent()
|
|
443
|
+
if K.base_ring().characteristic() != 2:
|
|
444
|
+
try:
|
|
445
|
+
X = ZZ(X)
|
|
446
|
+
return K(self._S_list[<Integer> X])
|
|
447
|
+
except TypeError:
|
|
448
|
+
raise TypeError("cannot apply SBox to %s" % (X,))
|
|
449
|
+
V = None
|
|
450
|
+
try:
|
|
451
|
+
V = K.vector_space(map=False)
|
|
452
|
+
except AttributeError:
|
|
453
|
+
try:
|
|
454
|
+
return self._S_list[ZZ(X)]
|
|
455
|
+
except TypeError:
|
|
456
|
+
pass
|
|
457
|
+
except TypeError:
|
|
458
|
+
V = K.vector_space()
|
|
459
|
+
# convert finite field element to vector
|
|
460
|
+
if V is not None:
|
|
461
|
+
X = V(X)
|
|
462
|
+
else:
|
|
463
|
+
K = None
|
|
464
|
+
|
|
465
|
+
# At this point, we only handle X being a vector or list-like
|
|
466
|
+
cdef list out
|
|
467
|
+
cdef Py_ssize_t ell
|
|
468
|
+
try:
|
|
469
|
+
ell = len(X)
|
|
470
|
+
X = list(X)
|
|
471
|
+
except TypeError:
|
|
472
|
+
# This will not pass the next test and ultimately raise an error
|
|
473
|
+
ell = -1
|
|
474
|
+
|
|
475
|
+
if ell == self.m:
|
|
476
|
+
if self._big_endian:
|
|
477
|
+
X = list(reversed(X))
|
|
478
|
+
X = ZZ(X, 2)
|
|
479
|
+
out = self.to_bits(self._S_list[X], self.n)
|
|
480
|
+
if K is not None:
|
|
481
|
+
return K(out)
|
|
482
|
+
# NOTE: Parts of the code assume that when a list is passed
|
|
483
|
+
# in that a list is returned
|
|
484
|
+
return out
|
|
485
|
+
|
|
486
|
+
if len(str(X)) > 50:
|
|
487
|
+
raise TypeError("cannot apply SBox to provided element")
|
|
488
|
+
else:
|
|
489
|
+
raise TypeError("cannot apply SBox to %s" % (X,))
|
|
490
|
+
|
|
491
|
+
def __getitem__(self, X):
|
|
492
|
+
"""
|
|
493
|
+
See :meth:`SBox.__call__`.
|
|
494
|
+
|
|
495
|
+
EXAMPLES::
|
|
496
|
+
|
|
497
|
+
sage: from sage.crypto.sbox import SBox
|
|
498
|
+
sage: S = SBox([7,6,0,4,2,5,1,3])
|
|
499
|
+
sage: S[7]
|
|
500
|
+
3
|
|
501
|
+
"""
|
|
502
|
+
return self(X)
|
|
503
|
+
|
|
504
|
+
def input_size(self):
|
|
505
|
+
"""
|
|
506
|
+
Return the input size of this S-Box.
|
|
507
|
+
|
|
508
|
+
EXAMPLES::
|
|
509
|
+
|
|
510
|
+
sage: from sage.crypto.sbox import SBox
|
|
511
|
+
sage: S = SBox([0, 3, 2, 1, 1, 3, 2, 0])
|
|
512
|
+
sage: S.input_size()
|
|
513
|
+
3
|
|
514
|
+
"""
|
|
515
|
+
return self.m
|
|
516
|
+
|
|
517
|
+
def output_size(self):
|
|
518
|
+
"""
|
|
519
|
+
Return the output size of this S-Box.
|
|
520
|
+
|
|
521
|
+
EXAMPLES::
|
|
522
|
+
|
|
523
|
+
sage: from sage.crypto.sbox import SBox
|
|
524
|
+
sage: S = SBox([0, 3, 2, 1, 1, 3, 2, 0])
|
|
525
|
+
sage: S.output_size()
|
|
526
|
+
2
|
|
527
|
+
"""
|
|
528
|
+
return self.n
|
|
529
|
+
|
|
530
|
+
def is_permutation(self):
|
|
531
|
+
r"""
|
|
532
|
+
Return ``True`` if this S-Box is a permutation.
|
|
533
|
+
|
|
534
|
+
EXAMPLES::
|
|
535
|
+
|
|
536
|
+
sage: from sage.crypto.sbox import SBox
|
|
537
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
538
|
+
sage: S.is_permutation()
|
|
539
|
+
True
|
|
540
|
+
|
|
541
|
+
sage: S = SBox(3,2,0,0,2,1,1,3)
|
|
542
|
+
sage: S.is_permutation()
|
|
543
|
+
False
|
|
544
|
+
"""
|
|
545
|
+
if self.m != self.n:
|
|
546
|
+
return False
|
|
547
|
+
cdef Py_ssize_t m = self.m
|
|
548
|
+
cdef Py_ssize_t i
|
|
549
|
+
return len(set([self._S_list[i] for i in range(1 << m)])) == 1 << m
|
|
550
|
+
|
|
551
|
+
def __iter__(self):
|
|
552
|
+
"""
|
|
553
|
+
EXAMPLES::
|
|
554
|
+
|
|
555
|
+
sage: from sage.crypto.sbox import SBox
|
|
556
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
557
|
+
sage: [e for e in S]
|
|
558
|
+
[7, 6, 0, 4, 2, 5, 1, 3]
|
|
559
|
+
"""
|
|
560
|
+
cdef Py_ssize_t i
|
|
561
|
+
for i in range(1 << self.m):
|
|
562
|
+
yield self._S_list[i]
|
|
563
|
+
|
|
564
|
+
def derivative(self, u):
|
|
565
|
+
r"""
|
|
566
|
+
Return the derivative in direction of ``u``.
|
|
567
|
+
|
|
568
|
+
INPUT:
|
|
569
|
+
|
|
570
|
+
- ``u`` -- either an integer or a tuple/list of `\GF{2}` elements
|
|
571
|
+
of length equal to ``m``
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
The derivative of `F` in direction of `u` is defined as
|
|
575
|
+
`x \mapsto F(x) + F(x + u)`.
|
|
576
|
+
|
|
577
|
+
EXAMPLES::
|
|
578
|
+
|
|
579
|
+
sage: from sage.crypto.sbox import SBox
|
|
580
|
+
sage: s = SBox(0,1,2,3)
|
|
581
|
+
sage: s.derivative(1)
|
|
582
|
+
(1, 1, 1, 1)
|
|
583
|
+
sage: u = [1,0]
|
|
584
|
+
sage: s.derivative(u)
|
|
585
|
+
(1, 1, 1, 1)
|
|
586
|
+
sage: v = vector(GF(2), [1,0])
|
|
587
|
+
sage: s.derivative(v)
|
|
588
|
+
(1, 1, 1, 1)
|
|
589
|
+
sage: s.derivative(4)
|
|
590
|
+
Traceback (most recent call last):
|
|
591
|
+
...
|
|
592
|
+
IndexError: list index out of range
|
|
593
|
+
sage: from sage.crypto.sboxes import PRESENT
|
|
594
|
+
sage: PRESENT.derivative(1).max_degree() < PRESENT.max_degree() # needs sage.rings.polynomial.pbori
|
|
595
|
+
True
|
|
596
|
+
"""
|
|
597
|
+
from sage.structure.element import Vector
|
|
598
|
+
nvars = self.m
|
|
599
|
+
|
|
600
|
+
if isinstance(u, (tuple, list)):
|
|
601
|
+
v = ZZ(u, base=2)
|
|
602
|
+
elif isinstance(u, Vector):
|
|
603
|
+
if u.base_ring() != GF(2):
|
|
604
|
+
raise TypeError("base ring of input vector must be GF(2)")
|
|
605
|
+
elif u.parent().dimension() != nvars:
|
|
606
|
+
raise TypeError("input vector must be an element of a vector space with dimension %d" % (nvars,))
|
|
607
|
+
v = ZZ(u.list(), base=2)
|
|
608
|
+
else:
|
|
609
|
+
v = u
|
|
610
|
+
|
|
611
|
+
return SBox([self(x) ^ self(x ^ v)
|
|
612
|
+
for x in range(1 << self.input_size())])
|
|
613
|
+
|
|
614
|
+
@cached_method
|
|
615
|
+
def difference_distribution_table(self):
|
|
616
|
+
"""
|
|
617
|
+
Return difference distribution table (DDT) ``A`` for this S-box.
|
|
618
|
+
|
|
619
|
+
The rows of ``A`` encode the differences ``Delta I`` of the
|
|
620
|
+
input and the columns encode the difference ``Delta O`` for
|
|
621
|
+
the output. The bits are ordered according to the endianess of
|
|
622
|
+
this S-box. The value at ``A[Delta I,Delta O]`` encodes how
|
|
623
|
+
often ``Delta O`` is the actual output difference given
|
|
624
|
+
``Delta I`` as input difference.
|
|
625
|
+
|
|
626
|
+
See [He2002]_ for an introduction to differential
|
|
627
|
+
cryptanalysis.
|
|
628
|
+
|
|
629
|
+
EXAMPLES::
|
|
630
|
+
|
|
631
|
+
sage: from sage.crypto.sbox import SBox
|
|
632
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
633
|
+
sage: S.difference_distribution_table()
|
|
634
|
+
[8 0 0 0 0 0 0 0]
|
|
635
|
+
[0 2 2 0 2 0 0 2]
|
|
636
|
+
[0 0 2 2 0 0 2 2]
|
|
637
|
+
[0 2 0 2 2 0 2 0]
|
|
638
|
+
[0 2 0 2 0 2 0 2]
|
|
639
|
+
[0 0 2 2 2 2 0 0]
|
|
640
|
+
[0 2 2 0 0 2 2 0]
|
|
641
|
+
[0 0 0 0 2 2 2 2]
|
|
642
|
+
sage: S = SBox(7,4,8,6)
|
|
643
|
+
sage: S.difference_distribution_table()
|
|
644
|
+
[4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
|
645
|
+
[0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0]
|
|
646
|
+
[0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 2]
|
|
647
|
+
[0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0]
|
|
648
|
+
|
|
649
|
+
TESTS:
|
|
650
|
+
|
|
651
|
+
Testing square SBoxes::
|
|
652
|
+
|
|
653
|
+
sage: from sage.crypto.sbox import SBox
|
|
654
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
655
|
+
sage: S.difference_distribution_table()
|
|
656
|
+
[8 0 0 0 0 0 0 0]
|
|
657
|
+
[0 2 2 0 2 0 0 2]
|
|
658
|
+
[0 0 2 2 0 0 2 2]
|
|
659
|
+
[0 2 0 2 2 0 2 0]
|
|
660
|
+
[0 2 0 2 0 2 0 2]
|
|
661
|
+
[0 0 2 2 2 2 0 0]
|
|
662
|
+
[0 2 2 0 0 2 2 0]
|
|
663
|
+
[0 0 0 0 2 2 2 2]
|
|
664
|
+
|
|
665
|
+
Testing non-square SBoxes::
|
|
666
|
+
|
|
667
|
+
sage: from sage.crypto.sbox import SBox
|
|
668
|
+
sage: S = SBox(8,8,8,8)
|
|
669
|
+
sage: S.difference_distribution_table()
|
|
670
|
+
[4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
|
671
|
+
[4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
|
672
|
+
[4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
|
673
|
+
[4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
|
674
|
+
sage: S = SBox(7,4,8,6)
|
|
675
|
+
sage: S.difference_distribution_table()
|
|
676
|
+
[4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
|
|
677
|
+
[0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0]
|
|
678
|
+
[0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 2]
|
|
679
|
+
[0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0]
|
|
680
|
+
sage: S = SBox(0,0,0,1,0,0,1,3)
|
|
681
|
+
sage: S.difference_distribution_table()
|
|
682
|
+
[8 0 0 0]
|
|
683
|
+
[4 2 2 0]
|
|
684
|
+
[2 4 0 2]
|
|
685
|
+
[2 4 0 2]
|
|
686
|
+
[4 2 2 0]
|
|
687
|
+
[6 0 0 2]
|
|
688
|
+
[2 4 0 2]
|
|
689
|
+
[2 4 0 2]
|
|
690
|
+
"""
|
|
691
|
+
cdef Py_ssize_t nrows = 1 << self.m
|
|
692
|
+
cdef Py_ssize_t ncols = 1 << self.n
|
|
693
|
+
cdef Py_ssize_t i, di
|
|
694
|
+
|
|
695
|
+
cdef list L = [0]*(nrows*ncols)
|
|
696
|
+
|
|
697
|
+
for i in range(nrows):
|
|
698
|
+
si = self._S_list[i]
|
|
699
|
+
for di in range(nrows):
|
|
700
|
+
L[di*ncols + si ^ self._S_list[i ^ di]] += 1
|
|
701
|
+
|
|
702
|
+
A = matrix(ZZ, nrows, ncols, L)
|
|
703
|
+
A.set_immutable()
|
|
704
|
+
|
|
705
|
+
return A
|
|
706
|
+
|
|
707
|
+
def maximal_difference_probability_absolute(self):
|
|
708
|
+
"""
|
|
709
|
+
Return the difference probability of the difference with the
|
|
710
|
+
highest probability in absolute terms, i.e. how often it
|
|
711
|
+
occurs in total.
|
|
712
|
+
|
|
713
|
+
Equivalently, this is equal to the differential uniformity
|
|
714
|
+
of this S-Box.
|
|
715
|
+
|
|
716
|
+
EXAMPLES::
|
|
717
|
+
|
|
718
|
+
sage: from sage.crypto.sbox import SBox
|
|
719
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
720
|
+
sage: S.maximal_difference_probability_absolute()
|
|
721
|
+
2
|
|
722
|
+
|
|
723
|
+
.. NOTE::
|
|
724
|
+
|
|
725
|
+
This code is mainly called internally.
|
|
726
|
+
"""
|
|
727
|
+
A = self.difference_distribution_table().__copy__()
|
|
728
|
+
A[0, 0] = 0
|
|
729
|
+
return max(map(abs, A.list()))
|
|
730
|
+
|
|
731
|
+
differential_uniformity = maximal_difference_probability_absolute
|
|
732
|
+
|
|
733
|
+
def maximal_difference_probability(self):
|
|
734
|
+
r"""
|
|
735
|
+
Return the difference probability of the difference with the
|
|
736
|
+
highest probability in the range between 0.0 and 1.0
|
|
737
|
+
indicating 0\% or 100\% respectively.
|
|
738
|
+
|
|
739
|
+
EXAMPLES::
|
|
740
|
+
|
|
741
|
+
sage: from sage.crypto.sbox import SBox
|
|
742
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
743
|
+
sage: S.maximal_difference_probability()
|
|
744
|
+
0.25
|
|
745
|
+
"""
|
|
746
|
+
return self.maximal_difference_probability_absolute() / (2.0**self.output_size())
|
|
747
|
+
|
|
748
|
+
@cached_method
|
|
749
|
+
def linear_approximation_table(self, scale='absolute_bias'):
|
|
750
|
+
r"""
|
|
751
|
+
Return linear approximation table (LAT) `A` for this S-box.
|
|
752
|
+
|
|
753
|
+
The entry `A[\alpha,\beta]` corresponds to the probability
|
|
754
|
+
`Pr[\alpha\cdot x = \beta\cdot S(x)]`, where `S` is this S-box
|
|
755
|
+
mapping `n`-bit inputs to `m`-bit outputs.
|
|
756
|
+
There are three typical notations for this probability used in
|
|
757
|
+
the literature:
|
|
758
|
+
|
|
759
|
+
- `Pr[\alpha\cdot x = \beta\cdot S(x)] = 1/2 + e(\alpha, \beta)`,
|
|
760
|
+
where `e(\alpha, \beta)` is called the bias,
|
|
761
|
+
- `2\cdot Pr[\alpha\cdot x = \beta\cdot S(x)] = 1 + c(\alpha, \beta)`,
|
|
762
|
+
where `c(\alpha, \beta) = 2\cdot e(\alpha, \beta)` is the
|
|
763
|
+
correlation, and
|
|
764
|
+
- `2^{(m+1)}\cdot Pr[\alpha\cdot x = \beta\cdot S(x)] = 2^m +
|
|
765
|
+
\hat{S}(\alpha, \beta)`, where `\hat{S}(\alpha, \beta)` is
|
|
766
|
+
the Fourier coefficient of S.
|
|
767
|
+
|
|
768
|
+
See [He2002]_ for an introduction to linear cryptanalysis.
|
|
769
|
+
|
|
770
|
+
INPUT:
|
|
771
|
+
|
|
772
|
+
- ``scale`` -- string to choose the scaling for the LAT, one of
|
|
773
|
+
|
|
774
|
+
* "bias": elements are `e(\alpha, \beta)`
|
|
775
|
+
* "correlation": elements are `c(\alpha, \beta)`
|
|
776
|
+
* "absolute_bias": elements are `2^m\cdot e(\alpha, \beta)` (default)
|
|
777
|
+
* "fourier_coefficient": elements are `\hat{S}(\alpha, \beta)`
|
|
778
|
+
|
|
779
|
+
EXAMPLES::
|
|
780
|
+
|
|
781
|
+
sage: from sage.crypto.sbox import SBox
|
|
782
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
783
|
+
sage: lat_abs_bias = S.linear_approximation_table()
|
|
784
|
+
sage: lat_abs_bias
|
|
785
|
+
[ 4 0 0 0 0 0 0 0]
|
|
786
|
+
[ 0 0 0 0 2 2 2 -2]
|
|
787
|
+
[ 0 0 -2 -2 -2 2 0 0]
|
|
788
|
+
[ 0 0 -2 2 0 0 -2 -2]
|
|
789
|
+
[ 0 2 0 2 -2 0 2 0]
|
|
790
|
+
[ 0 -2 0 2 0 2 0 2]
|
|
791
|
+
[ 0 -2 -2 0 0 -2 2 0]
|
|
792
|
+
[ 0 -2 2 0 -2 0 0 -2]
|
|
793
|
+
|
|
794
|
+
sage: lat_abs_bias/(1 << S.input_size()) == S.linear_approximation_table(scale='bias')
|
|
795
|
+
True
|
|
796
|
+
|
|
797
|
+
sage: lat_abs_bias/(1 << (S.input_size()-1)) == S.linear_approximation_table(scale='correlation')
|
|
798
|
+
True
|
|
799
|
+
|
|
800
|
+
sage: lat_abs_bias*2 == S.linear_approximation_table(scale='fourier_coefficient')
|
|
801
|
+
True
|
|
802
|
+
|
|
803
|
+
According to this table the first bit of the input is equal
|
|
804
|
+
to the third bit of the output 6 out of 8 times::
|
|
805
|
+
|
|
806
|
+
sage: for i in srange(8): print(S.to_bits(i)[0] == S.to_bits(S(i))[2])
|
|
807
|
+
False
|
|
808
|
+
True
|
|
809
|
+
True
|
|
810
|
+
True
|
|
811
|
+
False
|
|
812
|
+
True
|
|
813
|
+
True
|
|
814
|
+
True
|
|
815
|
+
"""
|
|
816
|
+
cdef Py_ssize_t m = self.m
|
|
817
|
+
cdef Py_ssize_t n = self.n
|
|
818
|
+
|
|
819
|
+
cdef Py_ssize_t nrows = 1 << m
|
|
820
|
+
cdef Py_ssize_t ncols = 1 << n
|
|
821
|
+
|
|
822
|
+
# directly compute the walsh_hadamard transform here, without
|
|
823
|
+
# creating the BooleanFunction object
|
|
824
|
+
cdef long* temp = <long*> check_allocarray(nrows*ncols, sizeof(long))
|
|
825
|
+
cdef Py_ssize_t i, j
|
|
826
|
+
|
|
827
|
+
for i in range(ncols):
|
|
828
|
+
for j in range(nrows):
|
|
829
|
+
temp[i*nrows + j] = 1 - (<int>(hamming_weight(i & self._S_list[j]) & 1) << 1)
|
|
830
|
+
walsh_hadamard(&temp[i*nrows], m)
|
|
831
|
+
|
|
832
|
+
cdef list L = [temp[i*nrows + j] for j in range(nrows) for i in range(ncols)]
|
|
833
|
+
sig_free(temp)
|
|
834
|
+
|
|
835
|
+
A = matrix(ZZ, nrows, ncols, L)
|
|
836
|
+
|
|
837
|
+
if (scale is None) or (scale == "absolute_bias"):
|
|
838
|
+
A /= 2
|
|
839
|
+
elif scale == "bias":
|
|
840
|
+
A /= 1 << (m+1)
|
|
841
|
+
elif scale == "correlation":
|
|
842
|
+
A /= 1 << m
|
|
843
|
+
elif scale == "fourier_coefficient":
|
|
844
|
+
pass
|
|
845
|
+
else:
|
|
846
|
+
raise ValueError("no such scaling for the LAT: %s" % scale)
|
|
847
|
+
|
|
848
|
+
A.set_immutable()
|
|
849
|
+
return A
|
|
850
|
+
|
|
851
|
+
def maximal_linear_bias_absolute(self):
|
|
852
|
+
r"""
|
|
853
|
+
Return maximal linear bias, i.e. how often the linear
|
|
854
|
+
approximation with the highest bias is true or false minus
|
|
855
|
+
`2^{n-1}`.
|
|
856
|
+
|
|
857
|
+
EXAMPLES::
|
|
858
|
+
|
|
859
|
+
sage: from sage.crypto.sbox import SBox
|
|
860
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
861
|
+
sage: S.maximal_linear_bias_absolute()
|
|
862
|
+
2
|
|
863
|
+
"""
|
|
864
|
+
cdef list L = self.linear_approximation_table().list()
|
|
865
|
+
L[0] = 0 # Set the upper left corner to be 0
|
|
866
|
+
return max(map(abs, L))
|
|
867
|
+
|
|
868
|
+
def maximal_linear_bias_relative(self):
|
|
869
|
+
"""
|
|
870
|
+
Return maximal bias of all linear approximations of this
|
|
871
|
+
S-box.
|
|
872
|
+
|
|
873
|
+
EXAMPLES::
|
|
874
|
+
|
|
875
|
+
sage: from sage.crypto.sbox import SBox
|
|
876
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
877
|
+
sage: S.maximal_linear_bias_relative()
|
|
878
|
+
0.25
|
|
879
|
+
"""
|
|
880
|
+
return self.maximal_linear_bias_absolute() / (2.0**self.m)
|
|
881
|
+
|
|
882
|
+
def ring(self):
|
|
883
|
+
"""
|
|
884
|
+
Create, return, and cache a polynomial ring for S-box polynomials.
|
|
885
|
+
|
|
886
|
+
EXAMPLES::
|
|
887
|
+
|
|
888
|
+
sage: from sage.crypto.sbox import SBox
|
|
889
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
890
|
+
sage: S.ring()
|
|
891
|
+
Multivariate Polynomial Ring in x0, x1, x2, y0, y1, y2 over
|
|
892
|
+
Finite Field of size 2
|
|
893
|
+
"""
|
|
894
|
+
return self._ring
|
|
895
|
+
|
|
896
|
+
def solutions(self, X=None, Y=None):
|
|
897
|
+
"""
|
|
898
|
+
Return a dictionary of solutions to this S-box.
|
|
899
|
+
|
|
900
|
+
INPUT:
|
|
901
|
+
|
|
902
|
+
- ``X`` -- (optional) input variables
|
|
903
|
+
|
|
904
|
+
- ``Y`` -- (optional) output variables
|
|
905
|
+
|
|
906
|
+
EXAMPLES::
|
|
907
|
+
|
|
908
|
+
sage: from sage.crypto.sbox import SBox
|
|
909
|
+
sage: S = SBox([7,6,0,4,2,5,1,3])
|
|
910
|
+
sage: F = S.polynomials() # needs sage.libs.singular
|
|
911
|
+
sage: s = S.solutions()
|
|
912
|
+
sage: any(f.subs(_s) for f in F for _s in s) # needs sage.libs.singular
|
|
913
|
+
False
|
|
914
|
+
"""
|
|
915
|
+
if X is None and Y is None:
|
|
916
|
+
P = self.ring()
|
|
917
|
+
gens = P.gens()
|
|
918
|
+
else:
|
|
919
|
+
P = X[0].parent()
|
|
920
|
+
gens = X + Y
|
|
921
|
+
|
|
922
|
+
cdef Py_ssize_t m = self.m
|
|
923
|
+
|
|
924
|
+
cdef list solution, solutions = []
|
|
925
|
+
cdef Py_ssize_t i
|
|
926
|
+
for i in range(1 << m):
|
|
927
|
+
solution = self.to_bits(i, m) + <list> self(self.to_bits(i, m))
|
|
928
|
+
solutions.append(dict(zip(gens, solution)))
|
|
929
|
+
|
|
930
|
+
return solutions
|
|
931
|
+
|
|
932
|
+
def polynomials(self, X=None, Y=None, degree=2, groebner=False):
|
|
933
|
+
"""
|
|
934
|
+
Return a list of polynomials satisfying this S-box.
|
|
935
|
+
|
|
936
|
+
First, a simple linear fitting is performed for the given
|
|
937
|
+
``degree`` (cf. for example [BC2003]_). If ``groebner=True`` a
|
|
938
|
+
Groebner basis is also computed for the result of that
|
|
939
|
+
process.
|
|
940
|
+
|
|
941
|
+
INPUT:
|
|
942
|
+
|
|
943
|
+
- ``X`` -- (optional) input variables
|
|
944
|
+
|
|
945
|
+
- ``Y`` -- (optional) output variables
|
|
946
|
+
|
|
947
|
+
- ``degree`` -- (default: ``2``) integer > 0
|
|
948
|
+
|
|
949
|
+
- ``groebner`` -- boolean (default: ``False``); calculate a reduced
|
|
950
|
+
Groebner basis of the spanning polynomials to obtain more polynomials
|
|
951
|
+
|
|
952
|
+
EXAMPLES::
|
|
953
|
+
|
|
954
|
+
sage: from sage.crypto.sbox import SBox
|
|
955
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
956
|
+
sage: P = S.ring()
|
|
957
|
+
|
|
958
|
+
By default, this method returns an indirect representation::
|
|
959
|
+
|
|
960
|
+
sage: S.polynomials() # needs sage.libs.singular
|
|
961
|
+
[x0*x2 + x1 + y1 + 1,
|
|
962
|
+
x0*x1 + x1 + x2 + y0 + y1 + y2 + 1,
|
|
963
|
+
x0*y1 + x0 + x2 + y0 + y2,
|
|
964
|
+
x0*y0 + x0*y2 + x1 + x2 + y0 + y1 + y2 + 1,
|
|
965
|
+
x1*x2 + x0 + x1 + x2 + y2 + 1,
|
|
966
|
+
x0*y0 + x1*y0 + x0 + x2 + y1 + y2,
|
|
967
|
+
x0*y0 + x1*y1 + x1 + y1 + 1,
|
|
968
|
+
x1*y2 + x1 + x2 + y0 + y1 + y2 + 1,
|
|
969
|
+
x0*y0 + x2*y0 + x1 + x2 + y1 + 1,
|
|
970
|
+
x2*y1 + x0 + y1 + y2,
|
|
971
|
+
x2*y2 + x1 + y1 + 1,
|
|
972
|
+
y0*y1 + x0 + x2 + y0 + y1 + y2,
|
|
973
|
+
y0*y2 + x1 + x2 + y0 + y1 + 1,
|
|
974
|
+
y1*y2 + x2 + y0]
|
|
975
|
+
|
|
976
|
+
We can get a direct representation by computing a
|
|
977
|
+
lexicographical Groebner basis with respect to the right
|
|
978
|
+
variable ordering, i.e. a variable ordering where the output
|
|
979
|
+
bits are greater than the input bits::
|
|
980
|
+
|
|
981
|
+
sage: P.<y0,y1,y2,x0,x1,x2> = PolynomialRing(GF(2),6,order='lex')
|
|
982
|
+
sage: S.polynomials([x0,x1,x2],[y0,y1,y2], groebner=True) # needs sage.libs.singular
|
|
983
|
+
[y0 + x0*x1 + x0*x2 + x0 + x1*x2 + x1 + 1,
|
|
984
|
+
y1 + x0*x2 + x1 + 1,
|
|
985
|
+
y2 + x0 + x1*x2 + x1 + x2 + 1]
|
|
986
|
+
|
|
987
|
+
TESTS:
|
|
988
|
+
|
|
989
|
+
Check that :issue:`22453` is fixed::
|
|
990
|
+
|
|
991
|
+
sage: from sage.crypto.sboxes import AES
|
|
992
|
+
sage: aes_polys = AES.polynomials() # long time
|
|
993
|
+
sage: aes_polys[3] # long time
|
|
994
|
+
x3*y0 + x5*y0 + x7*y0 + x6*y1 + x2*y2 + x3*y2 + x4*y2
|
|
995
|
+
+ x2*y3 + x3*y3 + x5*y4 + x6*y4 + x3*y5 + x4*y5
|
|
996
|
+
+ x4*y7 + x2 + x3 + y2 + y3 + y4 + 1
|
|
997
|
+
"""
|
|
998
|
+
cdef Py_ssize_t m = self.m
|
|
999
|
+
cdef Py_ssize_t n = self.n
|
|
1000
|
+
|
|
1001
|
+
if X is None and Y is None:
|
|
1002
|
+
P = self.ring()
|
|
1003
|
+
X = P.gens()[:m]
|
|
1004
|
+
Y = P.gens()[m:]
|
|
1005
|
+
else:
|
|
1006
|
+
P = X[0].parent()
|
|
1007
|
+
|
|
1008
|
+
gens = X + Y
|
|
1009
|
+
|
|
1010
|
+
cdef Py_ssize_t i
|
|
1011
|
+
cdef list bits = []
|
|
1012
|
+
for i in range(1 << m):
|
|
1013
|
+
bits.append(self.to_bits(i, m) + <list> self(self.to_bits(i, m)))
|
|
1014
|
+
|
|
1015
|
+
cdef Py_ssize_t ncols = (1 << m) + 1
|
|
1016
|
+
|
|
1017
|
+
A = matrix(P, _nterms(m + n, degree), ncols)
|
|
1018
|
+
|
|
1019
|
+
cdef list exponents = []
|
|
1020
|
+
cdef Py_ssize_t d
|
|
1021
|
+
for d in range(degree+1):
|
|
1022
|
+
exponents += IntegerVectors(d, max_length=m+n, min_length=m+n,
|
|
1023
|
+
min_part=0, max_part=1).list()
|
|
1024
|
+
|
|
1025
|
+
row = 0
|
|
1026
|
+
for exponent in exponents:
|
|
1027
|
+
A[row, ncols-1] = mul([gens[i]**exponent[i] for i in range(len(exponent))])
|
|
1028
|
+
for col in range(1 << m):
|
|
1029
|
+
A[row, col] = mul([bits[col][i] for i in range(len(exponent)) if exponent[i]])
|
|
1030
|
+
row += 1
|
|
1031
|
+
|
|
1032
|
+
rankSize = A.rank() - 1
|
|
1033
|
+
|
|
1034
|
+
cdef Py_ssize_t c
|
|
1035
|
+
for c in range(ncols):
|
|
1036
|
+
A[0, c] = 1
|
|
1037
|
+
|
|
1038
|
+
RR = A.echelon_form(algorithm='row_reduction')
|
|
1039
|
+
|
|
1040
|
+
# extract spanning stet
|
|
1041
|
+
gens = (RR.column(ncols-1)[rankSize:]).list()
|
|
1042
|
+
|
|
1043
|
+
if not groebner:
|
|
1044
|
+
return gens
|
|
1045
|
+
|
|
1046
|
+
FI = set(FieldIdeal(P).gens())
|
|
1047
|
+
I = Ideal(gens + list(FI))
|
|
1048
|
+
gb = I.groebner_basis()
|
|
1049
|
+
|
|
1050
|
+
gens = []
|
|
1051
|
+
for f in gb:
|
|
1052
|
+
# filter out field equations
|
|
1053
|
+
if f not in FI:
|
|
1054
|
+
gens.append(f)
|
|
1055
|
+
return gens
|
|
1056
|
+
|
|
1057
|
+
def interpolation_polynomial(self, k=None):
|
|
1058
|
+
r"""
|
|
1059
|
+
Return a univariate polynomial over an extension field
|
|
1060
|
+
representing this S-box.
|
|
1061
|
+
|
|
1062
|
+
If ``m`` is the input length of this S-box then the extension
|
|
1063
|
+
field is of degree ``m``.
|
|
1064
|
+
|
|
1065
|
+
If the output length does not match the input length then a
|
|
1066
|
+
:exc:`TypeError` is raised.
|
|
1067
|
+
|
|
1068
|
+
INPUT:
|
|
1069
|
+
|
|
1070
|
+
- ``k`` -- (optional) an instance of `\GF{2^m}`
|
|
1071
|
+
|
|
1072
|
+
EXAMPLES::
|
|
1073
|
+
|
|
1074
|
+
sage: from sage.crypto.sbox import SBox
|
|
1075
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
1076
|
+
sage: f = S.interpolation_polynomial()
|
|
1077
|
+
sage: f
|
|
1078
|
+
(a^2 + a + 1)*x^6 + a^2*x^5 + (a + 1)*x^4 + (a^2 + a)*x^3
|
|
1079
|
+
+ x^2 + a*x + a^2 + a + 1
|
|
1080
|
+
|
|
1081
|
+
sage: a = f.base_ring().gen()
|
|
1082
|
+
|
|
1083
|
+
sage: f(0), S(0)
|
|
1084
|
+
(a^2 + a + 1, 7)
|
|
1085
|
+
|
|
1086
|
+
sage: f(a^2 + 1), S(5)
|
|
1087
|
+
(a^2 + 1, 5)
|
|
1088
|
+
|
|
1089
|
+
.. NOTE::
|
|
1090
|
+
|
|
1091
|
+
The method-internal call to the S-box initially used a different
|
|
1092
|
+
endianess for handling finite field elements. This changed in
|
|
1093
|
+
:issue:`25633`, by calling the S-box directly.
|
|
1094
|
+
"""
|
|
1095
|
+
if self.m != self.n:
|
|
1096
|
+
raise TypeError("Lagrange interpolation only supported if"
|
|
1097
|
+
"self.input_size() == self.output_size()")
|
|
1098
|
+
|
|
1099
|
+
cdef Py_ssize_t m = self.m
|
|
1100
|
+
if k is None:
|
|
1101
|
+
k = GF(2**m, 'a')
|
|
1102
|
+
|
|
1103
|
+
cdef list l = []
|
|
1104
|
+
cdef int i
|
|
1105
|
+
for i in range(2**m):
|
|
1106
|
+
x = k(vector(self.to_bits(i, m)))
|
|
1107
|
+
l.append((x, self(x)))
|
|
1108
|
+
|
|
1109
|
+
P = PolynomialRing(k, 'x')
|
|
1110
|
+
return P.lagrange_polynomial(l)
|
|
1111
|
+
|
|
1112
|
+
def cnf(self, xi=None, yi=None, format=None):
|
|
1113
|
+
r"""
|
|
1114
|
+
Return a representation of this S-Box in conjunctive normal
|
|
1115
|
+
form.
|
|
1116
|
+
|
|
1117
|
+
This function examines the truth tables for each output bit of
|
|
1118
|
+
the S-Box and thus has complexity `n * 2^m` for an `m \times n`
|
|
1119
|
+
S-Box.
|
|
1120
|
+
|
|
1121
|
+
INPUT:
|
|
1122
|
+
|
|
1123
|
+
- ``xi`` -- (default: ``1...m``) indices for the input variables
|
|
1124
|
+
|
|
1125
|
+
- ``yi`` -- (default: ``m+1 ... m+n``) indices for the output variables
|
|
1126
|
+
|
|
1127
|
+
- ``format`` -- (default: ``None``) output format, see below
|
|
1128
|
+
|
|
1129
|
+
FORMATS:
|
|
1130
|
+
|
|
1131
|
+
- ``None`` -- return a list of tuples of integers where each
|
|
1132
|
+
tuple represents a clause, the absolute value of an integer
|
|
1133
|
+
represents a variable and the sign of an integer indicates
|
|
1134
|
+
inversion
|
|
1135
|
+
|
|
1136
|
+
- ``symbolic`` -- string that can be parsed by the
|
|
1137
|
+
``SymbolicLogic`` package
|
|
1138
|
+
|
|
1139
|
+
- ``dimacs`` -- string in DIMACS format which is the gold
|
|
1140
|
+
standard for SAT-solver input (cf. http://www.satlib.org/)
|
|
1141
|
+
|
|
1142
|
+
- ``dimacs_headless`` -- string in DIMACS format, but without
|
|
1143
|
+
the header; this is useful for concatenation of outputs
|
|
1144
|
+
|
|
1145
|
+
EXAMPLES:
|
|
1146
|
+
|
|
1147
|
+
We give a very small example to explain the output format::
|
|
1148
|
+
|
|
1149
|
+
sage: from sage.crypto.sbox import SBox
|
|
1150
|
+
sage: S = SBox(1,2,0,3); S
|
|
1151
|
+
(1, 2, 0, 3)
|
|
1152
|
+
sage: cnf = S.cnf(); cnf
|
|
1153
|
+
[(1, 2, -3), (1, 2, 4),
|
|
1154
|
+
(1, -2, 3), (1, -2, -4),
|
|
1155
|
+
(-1, 2, -3), (-1, 2, -4),
|
|
1156
|
+
(-1, -2, 3), (-1, -2, 4)]
|
|
1157
|
+
|
|
1158
|
+
This output completely describes the S-Box. For instance, we
|
|
1159
|
+
can check that ``S([0,1]) -> [1,0]`` satisfies every clause if
|
|
1160
|
+
the first input bit corresponds to the index ``1`` and the
|
|
1161
|
+
last output bit corresponds to the index ``3`` in the output.
|
|
1162
|
+
|
|
1163
|
+
We can convert this representation to the DIMACS format::
|
|
1164
|
+
|
|
1165
|
+
sage: print(S.cnf(format='dimacs'))
|
|
1166
|
+
p cnf 4 8
|
|
1167
|
+
1 2 -3 0
|
|
1168
|
+
1 2 4 0
|
|
1169
|
+
1 -2 3 0
|
|
1170
|
+
1 -2 -4 0
|
|
1171
|
+
-1 2 -3 0
|
|
1172
|
+
-1 2 -4 0
|
|
1173
|
+
-1 -2 3 0
|
|
1174
|
+
-1 -2 4 0
|
|
1175
|
+
|
|
1176
|
+
For concatenation we can strip the header::
|
|
1177
|
+
|
|
1178
|
+
sage: print(S.cnf(format='dimacs_headless'))
|
|
1179
|
+
1 2 -3 0
|
|
1180
|
+
1 2 4 0
|
|
1181
|
+
1 -2 3 0
|
|
1182
|
+
1 -2 -4 0
|
|
1183
|
+
-1 2 -3 0
|
|
1184
|
+
-1 2 -4 0
|
|
1185
|
+
-1 -2 3 0
|
|
1186
|
+
-1 -2 4 0
|
|
1187
|
+
|
|
1188
|
+
This might be helpful in combination with the ``xi`` and
|
|
1189
|
+
``yi`` parameter to assign indices manually::
|
|
1190
|
+
|
|
1191
|
+
sage: print(S.cnf(xi=[10,20],yi=[30,40], format='dimacs_headless'))
|
|
1192
|
+
10 20 -30 0
|
|
1193
|
+
10 20 40 0
|
|
1194
|
+
10 -20 30 0
|
|
1195
|
+
10 -20 -40 0
|
|
1196
|
+
-10 20 -30 0
|
|
1197
|
+
-10 20 -40 0
|
|
1198
|
+
-10 -20 30 0
|
|
1199
|
+
-10 -20 40 0
|
|
1200
|
+
|
|
1201
|
+
We can also return a string which is parse-able by the
|
|
1202
|
+
``SymbolicLogic`` package::
|
|
1203
|
+
|
|
1204
|
+
sage: log = SymbolicLogic()
|
|
1205
|
+
sage: s = log.statement(S.cnf(format='symbolic'))
|
|
1206
|
+
sage: log.truthtable(s)[1:]
|
|
1207
|
+
[['False', 'False', 'False', 'False', 'False'],
|
|
1208
|
+
['False', 'False', 'False', 'True', 'False'],
|
|
1209
|
+
['False', 'False', 'True', 'False', 'False'],
|
|
1210
|
+
['False', 'False', 'True', 'True', 'True'],
|
|
1211
|
+
['False', 'True', 'False', 'False', 'True'],
|
|
1212
|
+
['False', 'True', 'False', 'True', 'True'],
|
|
1213
|
+
['False', 'True', 'True', 'False', 'True'],
|
|
1214
|
+
['False', 'True', 'True', 'True', 'True'],
|
|
1215
|
+
['True', 'False', 'False', 'False', 'True'],
|
|
1216
|
+
['True', 'False', 'False', 'True', 'True'],
|
|
1217
|
+
['True', 'False', 'True', 'False', 'True'],
|
|
1218
|
+
['True', 'False', 'True', 'True', 'True'],
|
|
1219
|
+
['True', 'True', 'False', 'False', 'True'],
|
|
1220
|
+
['True', 'True', 'False', 'True', 'True'],
|
|
1221
|
+
['True', 'True', 'True', 'False', 'True'],
|
|
1222
|
+
['True', 'True', 'True', 'True', 'True']]
|
|
1223
|
+
|
|
1224
|
+
This function respects endianness of the S-Box::
|
|
1225
|
+
|
|
1226
|
+
sage: S = SBox(1,2,0,3, big_endian=False); S
|
|
1227
|
+
(1, 2, 0, 3)
|
|
1228
|
+
sage: cnf = S.cnf(); cnf
|
|
1229
|
+
[(1, 2, -4), (1, 2, 3),
|
|
1230
|
+
(-1, 2, 4), (-1, 2, -3),
|
|
1231
|
+
(1, -2, -4), (1, -2, -3),
|
|
1232
|
+
(-1, -2, 4), (-1, -2, 3)]
|
|
1233
|
+
|
|
1234
|
+
S-Boxes with m!=n also work:
|
|
1235
|
+
|
|
1236
|
+
sage: o = list(range(8)) + list(range(8))
|
|
1237
|
+
sage: shuffle(o)
|
|
1238
|
+
sage: S = SBox(o)
|
|
1239
|
+
sage: S.is_permutation()
|
|
1240
|
+
False
|
|
1241
|
+
|
|
1242
|
+
sage: len(S.cnf()) == 3*2^4
|
|
1243
|
+
True
|
|
1244
|
+
|
|
1245
|
+
TESTS:
|
|
1246
|
+
|
|
1247
|
+
sage: from sage.crypto.sbox import SBox
|
|
1248
|
+
sage: S = SBox(1,2,0,3, big_endian=False)
|
|
1249
|
+
sage: S.cnf([1000,1001,1002], [2000,2001,2002])
|
|
1250
|
+
Traceback (most recent call last):
|
|
1251
|
+
...
|
|
1252
|
+
TypeError: first arg required to have length 2, got 3 instead
|
|
1253
|
+
"""
|
|
1254
|
+
cdef Py_ssize_t m = self.m
|
|
1255
|
+
cdef Py_ssize_t n = self.n
|
|
1256
|
+
|
|
1257
|
+
cdef Py_ssize_t i
|
|
1258
|
+
if xi is None:
|
|
1259
|
+
xi = [i+1 for i in range(m)]
|
|
1260
|
+
|
|
1261
|
+
if yi is None:
|
|
1262
|
+
yi = [m+i+1 for i in range(n)]
|
|
1263
|
+
|
|
1264
|
+
if len(xi) != m:
|
|
1265
|
+
raise TypeError("first arg required to have length %d, got %d instead" % (m, len(xi)))
|
|
1266
|
+
|
|
1267
|
+
if len(yi) != n:
|
|
1268
|
+
raise TypeError("second arg required to have length %d, got %d instead" % (n, len(yi)))
|
|
1269
|
+
|
|
1270
|
+
cdef list output_bits = list(range(n))
|
|
1271
|
+
if not self._big_endian:
|
|
1272
|
+
output_bits = list(reversed(output_bits))
|
|
1273
|
+
|
|
1274
|
+
C = [] # the set of clauses
|
|
1275
|
+
cdef Py_ssize_t e, output_bit, v
|
|
1276
|
+
cdef list x, y
|
|
1277
|
+
for e in range(1 << m):
|
|
1278
|
+
x = self.to_bits(e, m)
|
|
1279
|
+
y = <list> self(x) # evaluate at x
|
|
1280
|
+
for output_bit in output_bits: # consider each bit
|
|
1281
|
+
clause = [(-1)**(int(v)) * i for v, i in zip(x, xi)]
|
|
1282
|
+
clause.append((-1)**(1-int(y[output_bit])) * yi[output_bit])
|
|
1283
|
+
C.append(tuple(clause))
|
|
1284
|
+
|
|
1285
|
+
if format is None:
|
|
1286
|
+
return C
|
|
1287
|
+
elif format == 'symbolic':
|
|
1288
|
+
gd = self.ring().gens()
|
|
1289
|
+
formula = []
|
|
1290
|
+
for clause in C:
|
|
1291
|
+
clause = "|".join([str(gd[abs(v)-1]).replace("-", "~") for v in clause])
|
|
1292
|
+
formula.append("("+clause+")")
|
|
1293
|
+
return " & ".join(formula)
|
|
1294
|
+
|
|
1295
|
+
elif format.startswith('dimacs'):
|
|
1296
|
+
if format == "dimacs_headless":
|
|
1297
|
+
header = ""
|
|
1298
|
+
else:
|
|
1299
|
+
header = "p cnf %d %d\n" % (m + n, len(C))
|
|
1300
|
+
values = " 0\n".join([" ".join(map(str, line)) for line in C])
|
|
1301
|
+
return header + values + " 0\n"
|
|
1302
|
+
else:
|
|
1303
|
+
raise ValueError("Format '%s' not supported" % (format,))
|
|
1304
|
+
|
|
1305
|
+
def component_function(self, b):
|
|
1306
|
+
r"""
|
|
1307
|
+
Return a Boolean function corresponding to the component function
|
|
1308
|
+
`b \cdot S(x)`.
|
|
1309
|
+
|
|
1310
|
+
If `S` is an `m \times n` S-Box, then `b \in \GF{2}^n` and
|
|
1311
|
+
`\cdot` denotes dot product of two vectors.
|
|
1312
|
+
|
|
1313
|
+
INPUT:
|
|
1314
|
+
|
|
1315
|
+
- ``b`` -- either an integer or a list/tuple/vector of `\GF{2}`
|
|
1316
|
+
elements of length ``self.output_size()``
|
|
1317
|
+
|
|
1318
|
+
EXAMPLES::
|
|
1319
|
+
|
|
1320
|
+
sage: from sage.crypto.sbox import SBox
|
|
1321
|
+
sage: S = SBox([7,6,0,4,2,5,1,3])
|
|
1322
|
+
sage: f3 = S.component_function(3)
|
|
1323
|
+
sage: f3.algebraic_normal_form() # needs sage.rings.polynomial.pbori
|
|
1324
|
+
x0*x1 + x0*x2 + x0 + x2
|
|
1325
|
+
|
|
1326
|
+
sage: f5 = S.component_function([1, 0, 1])
|
|
1327
|
+
sage: f5.algebraic_normal_form() # needs sage.rings.polynomial.pbori
|
|
1328
|
+
x0*x2 + x0 + x1*x2
|
|
1329
|
+
|
|
1330
|
+
TESTS::
|
|
1331
|
+
|
|
1332
|
+
sage: from sage.crypto.sboxes import SBox
|
|
1333
|
+
sage: sb = SBox([0, 1, 2, 3, 0, 1, 2, 3])
|
|
1334
|
+
sage: sb.component_function([1, 0])
|
|
1335
|
+
Boolean function with 3 variables
|
|
1336
|
+
"""
|
|
1337
|
+
cdef Py_ssize_t m = self.m
|
|
1338
|
+
cdef Py_ssize_t n = self.n
|
|
1339
|
+
|
|
1340
|
+
try:
|
|
1341
|
+
b = list(b)
|
|
1342
|
+
if len(b) > n:
|
|
1343
|
+
raise ValueError("input (%s) is too long and would be truncated" % (b,))
|
|
1344
|
+
b = self.from_bits(b, n)
|
|
1345
|
+
except TypeError:
|
|
1346
|
+
try:
|
|
1347
|
+
b = ZZ(b)
|
|
1348
|
+
except TypeError:
|
|
1349
|
+
raise TypeError("cannot handle input argument %s" % (b,))
|
|
1350
|
+
|
|
1351
|
+
cdef Py_ssize_t x
|
|
1352
|
+
ret = BooleanFunction([ZZ(b & self._S_list[x]).popcount() & 1 for x in range(1 << m)])
|
|
1353
|
+
|
|
1354
|
+
return ret
|
|
1355
|
+
|
|
1356
|
+
def nonlinearity(self):
|
|
1357
|
+
"""
|
|
1358
|
+
Return the nonlinearity of this S-Box.
|
|
1359
|
+
|
|
1360
|
+
The nonlinearity of an S-Box is defined as the minimum nonlinearity
|
|
1361
|
+
of all its component functions.
|
|
1362
|
+
|
|
1363
|
+
EXAMPLES::
|
|
1364
|
+
|
|
1365
|
+
sage: from sage.crypto.sbox import SBox
|
|
1366
|
+
sage: S = mq.SR(1,4,4,8).sbox()
|
|
1367
|
+
sage: S.nonlinearity()
|
|
1368
|
+
112
|
|
1369
|
+
"""
|
|
1370
|
+
return (1 << (self.m-1)) - self.maximal_linear_bias_absolute()
|
|
1371
|
+
|
|
1372
|
+
def linearity(self):
|
|
1373
|
+
"""
|
|
1374
|
+
Return the linearity of this S-Box.
|
|
1375
|
+
|
|
1376
|
+
EXAMPLES::
|
|
1377
|
+
|
|
1378
|
+
sage: from sage.crypto.sbox import SBox
|
|
1379
|
+
sage: S = mq.SR(1, 4, 4, 8).sbox()
|
|
1380
|
+
sage: S.linearity()
|
|
1381
|
+
32
|
|
1382
|
+
"""
|
|
1383
|
+
return self.maximal_linear_bias_absolute() << 1
|
|
1384
|
+
|
|
1385
|
+
def is_apn(self):
|
|
1386
|
+
r"""
|
|
1387
|
+
Return ``True`` if this S-Box is an almost perfect nonlinear (APN)
|
|
1388
|
+
function.
|
|
1389
|
+
|
|
1390
|
+
An `m \times m` S-Box `S` is called almost perfect nonlinear if for
|
|
1391
|
+
every nonzero `\alpha \in \GF{2}^m` and every
|
|
1392
|
+
`\beta \in \GF{2}^m`, the equation
|
|
1393
|
+
`S(x) \oplus S(x \oplus \alpha) = \beta` has 0 or 2 solutions.
|
|
1394
|
+
Equivalently, the differential uniformity of `S` is equal to 2.
|
|
1395
|
+
|
|
1396
|
+
EXAMPLES::
|
|
1397
|
+
|
|
1398
|
+
sage: from sage.crypto.sbox import SBox
|
|
1399
|
+
sage: S = SBox([0,1,3,6,7,4,5,2])
|
|
1400
|
+
sage: S.is_apn()
|
|
1401
|
+
True
|
|
1402
|
+
sage: S.differential_uniformity()
|
|
1403
|
+
2
|
|
1404
|
+
"""
|
|
1405
|
+
if self.input_size() != self.output_size():
|
|
1406
|
+
raise TypeError("APN function is only defined for"
|
|
1407
|
+
"self.input_size() == self.output_size()")
|
|
1408
|
+
return self.differential_uniformity() == 2
|
|
1409
|
+
|
|
1410
|
+
def differential_branch_number(self):
|
|
1411
|
+
r"""
|
|
1412
|
+
Return differential branch number of this S-Box.
|
|
1413
|
+
|
|
1414
|
+
The differential branch number of an S-Box `S` is defined as
|
|
1415
|
+
|
|
1416
|
+
.. MATH::
|
|
1417
|
+
|
|
1418
|
+
\min_{v, w \neq v} \{ \mathrm{wt}(v \oplus w)
|
|
1419
|
+
+ \mathrm{wt}(S(v) \oplus S(w)) \},
|
|
1420
|
+
|
|
1421
|
+
where `\mathrm{wt}(x)` denotes the Hamming weight of vector `x`.
|
|
1422
|
+
|
|
1423
|
+
EXAMPLES::
|
|
1424
|
+
|
|
1425
|
+
sage: from sage.crypto.sbox import SBox
|
|
1426
|
+
sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2])
|
|
1427
|
+
sage: S.differential_branch_number()
|
|
1428
|
+
3
|
|
1429
|
+
"""
|
|
1430
|
+
cdef Py_ssize_t m = self.m
|
|
1431
|
+
cdef Py_ssize_t n = self.n
|
|
1432
|
+
cdef Py_ssize_t ret = (1 << m) + (1 << n)
|
|
1433
|
+
|
|
1434
|
+
cdef Py_ssize_t a, b
|
|
1435
|
+
cdef Py_ssize_t x, y, w
|
|
1436
|
+
for a in range(1 << m):
|
|
1437
|
+
for b in range(1 << n):
|
|
1438
|
+
if a != b:
|
|
1439
|
+
x = a ^ b
|
|
1440
|
+
y = self._S_list[a] ^ self._S_list[b]
|
|
1441
|
+
w = hamming_weight(x) + hamming_weight(y)
|
|
1442
|
+
if w < ret:
|
|
1443
|
+
ret = w
|
|
1444
|
+
return ret
|
|
1445
|
+
|
|
1446
|
+
def linear_branch_number(self):
|
|
1447
|
+
r"""
|
|
1448
|
+
Return linear branch number of this S-Box.
|
|
1449
|
+
|
|
1450
|
+
The linear branch number of an S-Box `S` is defined as
|
|
1451
|
+
|
|
1452
|
+
.. MATH::
|
|
1453
|
+
|
|
1454
|
+
\min_{\substack{\alpha \neq 0, \beta \\ \mathrm{LAM}(\alpha, \beta) \neq 0}}
|
|
1455
|
+
\{ \mathrm{wt}(\alpha) + \mathrm{wt}(\beta) \},
|
|
1456
|
+
|
|
1457
|
+
where `\mathrm{LAM}(\alpha, \beta)` is the entry at row `\alpha` and
|
|
1458
|
+
column `\beta` of linear approximation matrix correspond to this
|
|
1459
|
+
S-Box. The `\mathrm{wt}(x)` denotes the Hamming weight of `x`.
|
|
1460
|
+
|
|
1461
|
+
EXAMPLES::
|
|
1462
|
+
|
|
1463
|
+
sage: from sage.crypto.sbox import SBox
|
|
1464
|
+
sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2])
|
|
1465
|
+
sage: S.linear_branch_number()
|
|
1466
|
+
2
|
|
1467
|
+
|
|
1468
|
+
TESTS::
|
|
1469
|
+
|
|
1470
|
+
sage: f = SBox([0, 2, 0, 6, 2, 2, 3, 7])
|
|
1471
|
+
sage: f.linear_branch_number()
|
|
1472
|
+
1
|
|
1473
|
+
"""
|
|
1474
|
+
cdef Py_ssize_t m = self.m
|
|
1475
|
+
cdef Py_ssize_t n = self.n
|
|
1476
|
+
cdef Matrix lat = <Matrix> self.linear_approximation_table()
|
|
1477
|
+
cdef Py_ssize_t ret = (1 << m) + (1 << n)
|
|
1478
|
+
|
|
1479
|
+
cdef Py_ssize_t a, b, w
|
|
1480
|
+
for a in range(1 << m):
|
|
1481
|
+
for b in range(1, 1 << n):
|
|
1482
|
+
if lat.get_unsafe(a, b) != 0:
|
|
1483
|
+
w = hamming_weight(a) + hamming_weight(b)
|
|
1484
|
+
if w < ret:
|
|
1485
|
+
ret = w
|
|
1486
|
+
return ret
|
|
1487
|
+
|
|
1488
|
+
@cached_method
|
|
1489
|
+
def autocorrelation_table(self):
|
|
1490
|
+
r"""
|
|
1491
|
+
Return the autocorrelation table corresponding to this S-Box.
|
|
1492
|
+
|
|
1493
|
+
for an `m \times n` S-Box `S`, its autocorrelation table entry at
|
|
1494
|
+
row `a \in \GF{2}^m` and column `b \in \GF{2}^n`
|
|
1495
|
+
(considering their integer representation) is defined as:
|
|
1496
|
+
|
|
1497
|
+
.. MATH::
|
|
1498
|
+
|
|
1499
|
+
\sum_{x \in \GF{2}^m} (-1)^{b \cdot S(x) \oplus
|
|
1500
|
+
b \cdot S(x \oplus a)}.
|
|
1501
|
+
|
|
1502
|
+
Equivalently, the columns `b` of autocorrelation table correspond to
|
|
1503
|
+
the autocorrelation spectrum of component function `b \cdot S(x)`.
|
|
1504
|
+
|
|
1505
|
+
EXAMPLES::
|
|
1506
|
+
|
|
1507
|
+
sage: from sage.crypto.sbox import SBox
|
|
1508
|
+
sage: S = SBox(7,6,0,4,2,5,1,3)
|
|
1509
|
+
sage: S.autocorrelation_table() # needs sage.combinat
|
|
1510
|
+
[ 8 8 8 8 8 8 8 8]
|
|
1511
|
+
[ 8 0 0 0 0 0 0 -8]
|
|
1512
|
+
[ 8 0 -8 0 0 0 0 0]
|
|
1513
|
+
[ 8 0 0 0 0 -8 0 0]
|
|
1514
|
+
[ 8 -8 0 0 0 0 0 0]
|
|
1515
|
+
[ 8 0 0 0 0 0 -8 0]
|
|
1516
|
+
[ 8 0 0 -8 0 0 0 0]
|
|
1517
|
+
[ 8 0 0 0 -8 0 0 0]
|
|
1518
|
+
"""
|
|
1519
|
+
from sage.combinat.matrices.hadamard_matrix import hadamard_matrix
|
|
1520
|
+
A = self.difference_distribution_table() * hadamard_matrix(1 << self.n)
|
|
1521
|
+
A.set_immutable()
|
|
1522
|
+
|
|
1523
|
+
return A
|
|
1524
|
+
|
|
1525
|
+
@cached_method
|
|
1526
|
+
def boomerang_connectivity_table(self):
|
|
1527
|
+
r"""
|
|
1528
|
+
Return the boomerang connectivity table (BCT) for this S-Box.
|
|
1529
|
+
|
|
1530
|
+
Boomerang connectivity matrix of an invertible `m \times m`
|
|
1531
|
+
S-Box `S` is an `2^m \times 2^m` matrix with entry at row
|
|
1532
|
+
`\Delta_i \in \GF{2}^m` and column `\Delta_o \in \GF{2}^m`
|
|
1533
|
+
equal to
|
|
1534
|
+
|
|
1535
|
+
.. MATH::
|
|
1536
|
+
|
|
1537
|
+
|\{ x \in \GF{2}^m | S^{-1}( S(x) \oplus \Delta_o) \oplus
|
|
1538
|
+
S^{-1}( S(x \oplus \Delta_i) \oplus \Delta_o) = \Delta_i\}|.
|
|
1539
|
+
|
|
1540
|
+
For more results concerning boomerang connectivity matrix,
|
|
1541
|
+
see [CHPSS18]_. The algorithm used here is the one from
|
|
1542
|
+
Dunkelman [Du2018]_.
|
|
1543
|
+
|
|
1544
|
+
EXAMPLES::
|
|
1545
|
+
|
|
1546
|
+
sage: from sage.crypto.sboxes import PRESENT
|
|
1547
|
+
sage: PRESENT.boomerang_connectivity_table()
|
|
1548
|
+
[16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16]
|
|
1549
|
+
[16 0 4 4 0 16 4 4 4 4 0 0 4 4 0 0]
|
|
1550
|
+
[16 0 0 6 0 4 6 0 0 0 2 0 2 2 2 0]
|
|
1551
|
+
[16 2 0 6 2 4 4 2 0 0 2 2 0 0 0 0]
|
|
1552
|
+
[16 0 0 0 0 4 2 2 0 6 2 0 6 0 2 0]
|
|
1553
|
+
[16 2 0 0 2 4 0 0 0 6 2 2 4 2 0 0]
|
|
1554
|
+
[16 4 2 0 4 0 2 0 2 0 0 4 2 0 4 8]
|
|
1555
|
+
[16 4 2 0 4 0 2 0 2 0 0 4 2 0 4 8]
|
|
1556
|
+
[16 4 0 2 4 0 0 2 0 2 0 4 0 2 4 8]
|
|
1557
|
+
[16 4 2 0 4 0 2 0 2 0 0 4 2 0 4 8]
|
|
1558
|
+
[16 0 2 2 0 4 0 0 6 0 2 0 0 6 2 0]
|
|
1559
|
+
[16 2 0 0 2 4 0 0 4 2 2 2 0 6 0 0]
|
|
1560
|
+
[16 0 6 0 0 4 0 6 2 2 2 0 0 0 2 0]
|
|
1561
|
+
[16 2 4 2 2 4 0 6 0 0 2 2 0 0 0 0]
|
|
1562
|
+
[16 0 2 2 0 0 2 2 2 2 0 0 2 2 0 0]
|
|
1563
|
+
[16 8 0 0 8 0 0 0 0 0 0 8 0 0 8 16]
|
|
1564
|
+
"""
|
|
1565
|
+
from itertools import product
|
|
1566
|
+
|
|
1567
|
+
cdef SBox Si = self.inverse()
|
|
1568
|
+
|
|
1569
|
+
cdef Py_ssize_t nrows = 1 << self.m
|
|
1570
|
+
cdef Py_ssize_t ncols = 1 << self.n
|
|
1571
|
+
|
|
1572
|
+
cdef list L = []
|
|
1573
|
+
|
|
1574
|
+
cdef Py_ssize_t delta_in, x, i, j
|
|
1575
|
+
cdef list l, table, row
|
|
1576
|
+
for delta_in in range(ncols):
|
|
1577
|
+
table = [[] for _ in range(ncols)]
|
|
1578
|
+
for x in range(nrows):
|
|
1579
|
+
table[x ^ self._S_list[Si._S_list[x] ^ delta_in]].append(x)
|
|
1580
|
+
|
|
1581
|
+
row = [0]*ncols
|
|
1582
|
+
for l in table:
|
|
1583
|
+
for i, j in product(l, l):
|
|
1584
|
+
row[i ^ j] += 1
|
|
1585
|
+
L.extend(row)
|
|
1586
|
+
|
|
1587
|
+
A = matrix(ZZ, nrows, ncols, L)
|
|
1588
|
+
A.set_immutable()
|
|
1589
|
+
return A
|
|
1590
|
+
|
|
1591
|
+
def boomerang_uniformity(self):
|
|
1592
|
+
"""
|
|
1593
|
+
Return the boomerang uniformity.
|
|
1594
|
+
|
|
1595
|
+
The boomerang uniformity is defined as the highest entry in the
|
|
1596
|
+
boomerang connectivity table, ignoring the first row and column.
|
|
1597
|
+
|
|
1598
|
+
EXAMPLES::
|
|
1599
|
+
|
|
1600
|
+
sage: from sage.crypto.sboxes import AES
|
|
1601
|
+
sage: AES.boomerang_uniformity()
|
|
1602
|
+
6
|
|
1603
|
+
"""
|
|
1604
|
+
bct = self.boomerang_connectivity_table()
|
|
1605
|
+
return max(bct.delete_rows([0]).delete_columns([0]).list())
|
|
1606
|
+
|
|
1607
|
+
def linear_structures(self):
|
|
1608
|
+
r"""
|
|
1609
|
+
Return a list of 3-valued tuple `(b, \alpha, c)` such that `\alpha` is
|
|
1610
|
+
a `c`-linear structure of the component function `b \cdot S(x)`.
|
|
1611
|
+
|
|
1612
|
+
A Boolean function `f : \GF{2}^m \mapsto \GF{2}` is said
|
|
1613
|
+
to have a `c`-linear structure if there exists a nonzero `\alpha` such
|
|
1614
|
+
that `f(x) \oplus f(x \oplus \alpha)` is a constant function `c`.
|
|
1615
|
+
|
|
1616
|
+
An `m \times n` S-Box `S` has a linear structure if there exists a
|
|
1617
|
+
component function `b \cdot S(x)` that has a linear structure.
|
|
1618
|
+
|
|
1619
|
+
The three valued tuple `(b, \alpha, c)` shows that `\alpha` is a
|
|
1620
|
+
`c`-linear structure of the component function `b \cdot S(x)`. This
|
|
1621
|
+
implies that for all output differences `\beta` of the S-Box
|
|
1622
|
+
correspond to input difference `\alpha`, we have `b \cdot \beta = c`.
|
|
1623
|
+
|
|
1624
|
+
.. SEEALSO::
|
|
1625
|
+
|
|
1626
|
+
:meth:`is_linear_structure`,
|
|
1627
|
+
:meth:`has_linear_structure`
|
|
1628
|
+
|
|
1629
|
+
EXAMPLES::
|
|
1630
|
+
|
|
1631
|
+
sage: from sage.crypto.sbox import SBox
|
|
1632
|
+
sage: S = SBox([0,1,3,6,7,4,5,2])
|
|
1633
|
+
sage: S.linear_structures() # needs sage.combinat
|
|
1634
|
+
[(1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1),
|
|
1635
|
+
(5, 5, 1), (6, 6, 1), (7, 7, 1)]
|
|
1636
|
+
"""
|
|
1637
|
+
cdef Py_ssize_t n = self.n
|
|
1638
|
+
cdef Py_ssize_t m = self.m
|
|
1639
|
+
cdef Matrix act = <Matrix> self.autocorrelation_table()
|
|
1640
|
+
cdef list ret = []
|
|
1641
|
+
|
|
1642
|
+
cdef Py_ssize_t j, i, c
|
|
1643
|
+
for j in range(1, 1 << n):
|
|
1644
|
+
for i in range(1, 1 << m):
|
|
1645
|
+
if abs(act.get_unsafe(i, j)) == (1 << m):
|
|
1646
|
+
c = ((1 - (act.get_unsafe(i, j) >> m)) >> 1)
|
|
1647
|
+
ret.append((j, i, c))
|
|
1648
|
+
return ret
|
|
1649
|
+
|
|
1650
|
+
def has_linear_structure(self) -> bool:
|
|
1651
|
+
"""
|
|
1652
|
+
Return ``True`` if there exists a nonzero component function of this
|
|
1653
|
+
S-Box that has a linear structure.
|
|
1654
|
+
|
|
1655
|
+
.. SEEALSO::
|
|
1656
|
+
|
|
1657
|
+
:meth:`is_linear_structure`,
|
|
1658
|
+
:meth:`linear_structures`.
|
|
1659
|
+
|
|
1660
|
+
EXAMPLES::
|
|
1661
|
+
|
|
1662
|
+
sage: from sage.crypto.sbox import SBox
|
|
1663
|
+
sage: S = SBox(12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2)
|
|
1664
|
+
sage: S.has_linear_structure()
|
|
1665
|
+
True
|
|
1666
|
+
"""
|
|
1667
|
+
cdef Py_ssize_t i
|
|
1668
|
+
return any(self.component_function(i).has_linear_structure()
|
|
1669
|
+
for i in range(1, 1 << self.n))
|
|
1670
|
+
|
|
1671
|
+
def is_linear_structure(self, a, b):
|
|
1672
|
+
r"""
|
|
1673
|
+
Return ``True`` if `a` is a linear structure of the component function
|
|
1674
|
+
`b \cdot S(x)` where S is this `m \times n` S-Box.
|
|
1675
|
+
|
|
1676
|
+
INPUT:
|
|
1677
|
+
|
|
1678
|
+
- ``a`` -- either an integer or a tuple of `\GF{2}` elements of
|
|
1679
|
+
length equal to the input size of SBox
|
|
1680
|
+
- ``b`` -- either an integer or a tuple of `\GF{2}` elements of
|
|
1681
|
+
length equal to the output size of SBox
|
|
1682
|
+
|
|
1683
|
+
.. SEEALSO::
|
|
1684
|
+
|
|
1685
|
+
:meth:`linear_structures`,
|
|
1686
|
+
:meth:`has_linear_structure`
|
|
1687
|
+
|
|
1688
|
+
EXAMPLES::
|
|
1689
|
+
|
|
1690
|
+
sage: from sage.crypto.sbox import SBox
|
|
1691
|
+
sage: S = SBox(12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2)
|
|
1692
|
+
sage: S.component_function(1).autocorrelation()
|
|
1693
|
+
(16, -16, 0, 0, 0, 0, 0, 0, -16, 16, 0, 0, 0, 0, 0, 0)
|
|
1694
|
+
sage: S.is_linear_structure(1, 1)
|
|
1695
|
+
True
|
|
1696
|
+
sage: S.is_linear_structure([1, 0, 0, 1], [0, 0, 0, 1])
|
|
1697
|
+
True
|
|
1698
|
+
sage: S.is_linear_structure([0, 1, 1, 1], 1)
|
|
1699
|
+
False
|
|
1700
|
+
"""
|
|
1701
|
+
return self.component_function(b).is_linear_structure(a)
|
|
1702
|
+
|
|
1703
|
+
def max_degree(self):
|
|
1704
|
+
"""
|
|
1705
|
+
Return the maximal algebraic degree of all its component functions.
|
|
1706
|
+
|
|
1707
|
+
EXAMPLES::
|
|
1708
|
+
|
|
1709
|
+
sage: from sage.crypto.sbox import SBox
|
|
1710
|
+
sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2])
|
|
1711
|
+
sage: S.max_degree() # needs sage.rings.polynomial.pbori
|
|
1712
|
+
3
|
|
1713
|
+
"""
|
|
1714
|
+
ret = ZZ.zero()
|
|
1715
|
+
|
|
1716
|
+
cdef Py_ssize_t i
|
|
1717
|
+
for i in range(self.n):
|
|
1718
|
+
deg_Si = self.component_function(1 << i).algebraic_degree()
|
|
1719
|
+
if deg_Si > ret:
|
|
1720
|
+
ret = deg_Si
|
|
1721
|
+
return ret
|
|
1722
|
+
|
|
1723
|
+
def min_degree(self):
|
|
1724
|
+
"""
|
|
1725
|
+
Return the minimal algebraic degree of all its component functions.
|
|
1726
|
+
|
|
1727
|
+
EXAMPLES::
|
|
1728
|
+
|
|
1729
|
+
sage: from sage.crypto.sbox import SBox
|
|
1730
|
+
sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2])
|
|
1731
|
+
sage: S.min_degree() # needs sage.rings.polynomial.pbori
|
|
1732
|
+
2
|
|
1733
|
+
"""
|
|
1734
|
+
ret = ZZ(self.m)
|
|
1735
|
+
|
|
1736
|
+
cdef Py_ssize_t b
|
|
1737
|
+
for b in range(1, 1 << self.n):
|
|
1738
|
+
deg_bS = self.component_function(b).algebraic_degree()
|
|
1739
|
+
if deg_bS < ret:
|
|
1740
|
+
ret = deg_bS
|
|
1741
|
+
return ret
|
|
1742
|
+
|
|
1743
|
+
def is_balanced(self):
|
|
1744
|
+
r"""
|
|
1745
|
+
Return ``True`` if this S-Box is balanced.
|
|
1746
|
+
|
|
1747
|
+
An S-Box is balanced if all its component functions are balanced.
|
|
1748
|
+
|
|
1749
|
+
EXAMPLES::
|
|
1750
|
+
|
|
1751
|
+
sage: from sage.crypto.sbox import SBox
|
|
1752
|
+
sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2])
|
|
1753
|
+
sage: S.is_balanced()
|
|
1754
|
+
True
|
|
1755
|
+
"""
|
|
1756
|
+
cdef Py_ssize_t b
|
|
1757
|
+
for b in range(1, 1 << self.n):
|
|
1758
|
+
bS = self.component_function(b)
|
|
1759
|
+
if not bS.is_balanced():
|
|
1760
|
+
return False
|
|
1761
|
+
return True
|
|
1762
|
+
|
|
1763
|
+
def is_almost_bent(self):
|
|
1764
|
+
r"""
|
|
1765
|
+
Return ``True`` if this S-Box is an almost bent (AB) function.
|
|
1766
|
+
|
|
1767
|
+
An `m \times m` S-Box `S`, for `m` odd, is called almost bent if its
|
|
1768
|
+
nonlinearity is equal to `2^{m-1} - 2^{(m-1)/2}`.
|
|
1769
|
+
|
|
1770
|
+
EXAMPLES::
|
|
1771
|
+
|
|
1772
|
+
sage: from sage.crypto.sbox import SBox
|
|
1773
|
+
sage: S = SBox([0,1,3,6,7,4,5,2])
|
|
1774
|
+
sage: S.is_almost_bent()
|
|
1775
|
+
True
|
|
1776
|
+
"""
|
|
1777
|
+
if self.m != self.n:
|
|
1778
|
+
raise TypeError("almost bent function only exists for"
|
|
1779
|
+
" self.input_size() == self.output_size()")
|
|
1780
|
+
|
|
1781
|
+
cdef Py_ssize_t m = self.m
|
|
1782
|
+
|
|
1783
|
+
if is_even(m):
|
|
1784
|
+
return False
|
|
1785
|
+
|
|
1786
|
+
return self.nonlinearity() == 2**(m-1) - 2**((m-1)//2)
|
|
1787
|
+
|
|
1788
|
+
def fixed_points(self):
|
|
1789
|
+
"""
|
|
1790
|
+
Return a list of all fixed points of this S-Box.
|
|
1791
|
+
|
|
1792
|
+
EXAMPLES::
|
|
1793
|
+
|
|
1794
|
+
sage: from sage.crypto.sbox import SBox
|
|
1795
|
+
sage: S = SBox([0,1,3,6,7,4,5,2])
|
|
1796
|
+
sage: S.fixed_points()
|
|
1797
|
+
[0, 1]
|
|
1798
|
+
"""
|
|
1799
|
+
cdef Py_ssize_t i
|
|
1800
|
+
return [i for i in range(1 << self.m) if i == self._S_list[i]]
|
|
1801
|
+
|
|
1802
|
+
def inverse(self):
|
|
1803
|
+
"""
|
|
1804
|
+
Return the inverse of this S-Box.
|
|
1805
|
+
|
|
1806
|
+
Note that the S-Box must be invertible, otherwise it will raise
|
|
1807
|
+
a :exc:`TypeError`.
|
|
1808
|
+
|
|
1809
|
+
EXAMPLES::
|
|
1810
|
+
|
|
1811
|
+
sage: from sage.crypto.sbox import SBox
|
|
1812
|
+
sage: S = SBox([0, 1, 3, 6, 7, 4, 5, 2])
|
|
1813
|
+
sage: Sinv = S.inverse()
|
|
1814
|
+
sage: [Sinv(S(i)) for i in range(8)]
|
|
1815
|
+
[0, 1, 2, 3, 4, 5, 6, 7]
|
|
1816
|
+
"""
|
|
1817
|
+
if not self.is_permutation():
|
|
1818
|
+
raise TypeError("S-Box must be a permutation")
|
|
1819
|
+
|
|
1820
|
+
cdef Py_ssize_t i
|
|
1821
|
+
cdef list L = [self._S_list[i] for i in range(1 << self.m)]
|
|
1822
|
+
|
|
1823
|
+
return SBox([L.index(i) for i in range(1 << self.m)],
|
|
1824
|
+
big_endian=self._big_endian)
|
|
1825
|
+
|
|
1826
|
+
def is_monomial_function(self):
|
|
1827
|
+
r"""
|
|
1828
|
+
Return ``True`` if this S-Box is a monomial/power function.
|
|
1829
|
+
|
|
1830
|
+
EXAMPLES::
|
|
1831
|
+
|
|
1832
|
+
sage: from sage.crypto.sbox import SBox
|
|
1833
|
+
sage: S = SBox([0,1,3,6,7,4,5,2])
|
|
1834
|
+
sage: S.is_monomial_function()
|
|
1835
|
+
False
|
|
1836
|
+
sage: S_poly = S.interpolation_polynomial(); S_poly
|
|
1837
|
+
(a + 1)*x^6 + (a^2 + a + 1)*x^5 + (a^2 + a)*x^4
|
|
1838
|
+
+ (a^2 + 1)*x^3 + a*x^2 + a*x
|
|
1839
|
+
|
|
1840
|
+
sage: all([S(x) == S_poly(x) for x in S_poly.base_ring()])
|
|
1841
|
+
True
|
|
1842
|
+
|
|
1843
|
+
sage: S = SBox(0,3,2,1)
|
|
1844
|
+
sage: S.interpolation_polynomial()
|
|
1845
|
+
x^2
|
|
1846
|
+
sage: S.is_monomial_function()
|
|
1847
|
+
True
|
|
1848
|
+
"""
|
|
1849
|
+
return self.interpolation_polynomial().is_monomial()
|
|
1850
|
+
|
|
1851
|
+
def is_plateaued(self):
|
|
1852
|
+
r"""
|
|
1853
|
+
Return ``True`` if this S-Box is plateaued, i.e. for all nonzero
|
|
1854
|
+
`b \in \GF{2}^n` the Boolean function `b \cdot S(x)`
|
|
1855
|
+
is plateaued.
|
|
1856
|
+
|
|
1857
|
+
EXAMPLES::
|
|
1858
|
+
|
|
1859
|
+
sage: from sage.crypto.sbox import SBox
|
|
1860
|
+
sage: S = SBox(0, 3, 1, 2, 4, 6, 7, 5)
|
|
1861
|
+
sage: S.is_plateaued()
|
|
1862
|
+
True
|
|
1863
|
+
"""
|
|
1864
|
+
cdef Py_ssize_t b
|
|
1865
|
+
for b in range(1, 1 << self.n):
|
|
1866
|
+
bS = self.component_function(b)
|
|
1867
|
+
if not bS.is_plateaued():
|
|
1868
|
+
return False
|
|
1869
|
+
return True
|
|
1870
|
+
|
|
1871
|
+
def is_bent(self):
|
|
1872
|
+
r"""
|
|
1873
|
+
Return ``True`` if this S-Box is bent, i.e. its nonlinearity
|
|
1874
|
+
is equal to `2^{m-1} - 2^{m/2 - 1}` where `m` is the input size
|
|
1875
|
+
of the S-Box.
|
|
1876
|
+
|
|
1877
|
+
EXAMPLES::
|
|
1878
|
+
|
|
1879
|
+
sage: from sage.crypto.sbox import SBox
|
|
1880
|
+
sage: R.<x> = GF(2**2, 'a')[]
|
|
1881
|
+
sage: base = R.base_ring()
|
|
1882
|
+
sage: a = base.gen()
|
|
1883
|
+
sage: G = a * x^2 + 1
|
|
1884
|
+
sage: S = SBox([G(x * y**(14)) for x in sorted(base) for y in sorted(base)])
|
|
1885
|
+
sage: S.is_bent()
|
|
1886
|
+
True
|
|
1887
|
+
sage: S.nonlinearity()
|
|
1888
|
+
6
|
|
1889
|
+
sage: S.linear_approximation_table()
|
|
1890
|
+
[ 8 -2 2 -2]
|
|
1891
|
+
[ 0 -2 2 -2]
|
|
1892
|
+
[ 0 -2 2 -2]
|
|
1893
|
+
[ 0 -2 2 -2]
|
|
1894
|
+
[ 0 -2 2 -2]
|
|
1895
|
+
[ 0 -2 -2 2]
|
|
1896
|
+
[ 0 2 2 2]
|
|
1897
|
+
[ 0 2 -2 -2]
|
|
1898
|
+
[ 0 -2 2 -2]
|
|
1899
|
+
[ 0 2 -2 -2]
|
|
1900
|
+
[ 0 -2 -2 2]
|
|
1901
|
+
[ 0 2 2 2]
|
|
1902
|
+
[ 0 -2 2 -2]
|
|
1903
|
+
[ 0 2 2 2]
|
|
1904
|
+
[ 0 2 -2 -2]
|
|
1905
|
+
[ 0 -2 -2 2]
|
|
1906
|
+
"""
|
|
1907
|
+
cdef Py_ssize_t m = self.m
|
|
1908
|
+
cdef Py_ssize_t n = self.n
|
|
1909
|
+
|
|
1910
|
+
if not is_even(m) or n > m // 2:
|
|
1911
|
+
return False
|
|
1912
|
+
|
|
1913
|
+
return self.nonlinearity() == 2**(m-1) - 2**(m//2 - 1)
|
|
1914
|
+
|
|
1915
|
+
def is_involution(self):
|
|
1916
|
+
r"""
|
|
1917
|
+
Return ``True`` if this S-Box is an involution, i.e. the inverse S-Box
|
|
1918
|
+
is equal itself.
|
|
1919
|
+
|
|
1920
|
+
EXAMPLES::
|
|
1921
|
+
|
|
1922
|
+
sage: from sage.crypto.sbox import SBox
|
|
1923
|
+
sage: S = SBox([x**254 for x in sorted(GF(2**8))])
|
|
1924
|
+
sage: S.is_involution()
|
|
1925
|
+
True
|
|
1926
|
+
"""
|
|
1927
|
+
return self == self.inverse()
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
cdef Py_ssize_t feistel_substitute(Py_ssize_t x, Py_ssize_t input_size, list sboxes) noexcept:
|
|
1931
|
+
"""
|
|
1932
|
+
Compute a Feistel output using the given sboxes.
|
|
1933
|
+
|
|
1934
|
+
INPUT:
|
|
1935
|
+
|
|
1936
|
+
- ``x`` -- integer; the input to the Feistel construction
|
|
1937
|
+
- ``input_size`` -- integer; the bitsize of the Feistel construction
|
|
1938
|
+
- ``sboxes`` -- list of SBox; the sboxes applied in the Feistel construction
|
|
1939
|
+
|
|
1940
|
+
EXAMPLES::
|
|
1941
|
+
|
|
1942
|
+
sage: from sage.crypto.sbox import feistel_construction
|
|
1943
|
+
sage: from sage.crypto.sboxes import PRESENT as s
|
|
1944
|
+
sage: S = feistel_construction(s, s, s) # indirect doctest
|
|
1945
|
+
"""
|
|
1946
|
+
cdef Py_ssize_t mask = (1 << input_size) - 1
|
|
1947
|
+
cdef Py_ssize_t xl = (x >> input_size) & mask
|
|
1948
|
+
cdef Py_ssize_t xr = x & mask
|
|
1949
|
+
|
|
1950
|
+
cdef SBox sb
|
|
1951
|
+
for sb in sboxes:
|
|
1952
|
+
xl, xr = sb(xl) ^ xr, xl
|
|
1953
|
+
|
|
1954
|
+
return (xl << input_size) | xr
|
|
1955
|
+
|
|
1956
|
+
|
|
1957
|
+
cdef Py_ssize_t misty_substitute(Py_ssize_t x, Py_ssize_t input_size, list sboxes) noexcept:
|
|
1958
|
+
"""
|
|
1959
|
+
Compute a Misty output using the given sboxes.
|
|
1960
|
+
|
|
1961
|
+
INPUT:
|
|
1962
|
+
|
|
1963
|
+
- ``x`` -- integer; the input to the Misty construction
|
|
1964
|
+
- ``input_size`` -- integer; the bitsize of the Misty construction
|
|
1965
|
+
- ``sboxes`` -- list of SBox; the sboxes applied in the Misty construction
|
|
1966
|
+
|
|
1967
|
+
EXAMPLES::
|
|
1968
|
+
|
|
1969
|
+
sage: from sage.crypto.sbox import misty_construction
|
|
1970
|
+
sage: from sage.crypto.sboxes import PRESENT as s
|
|
1971
|
+
sage: S = misty_construction(s, s, s) # indirect doctest
|
|
1972
|
+
"""
|
|
1973
|
+
cdef Py_ssize_t mask = (1 << input_size) - 1
|
|
1974
|
+
cdef Py_ssize_t xl = (x >> input_size) & mask
|
|
1975
|
+
cdef Py_ssize_t xr = x & mask
|
|
1976
|
+
|
|
1977
|
+
cdef SBox sb
|
|
1978
|
+
for sb in sboxes:
|
|
1979
|
+
xl, xr = sb(xr) ^ xl, xl
|
|
1980
|
+
|
|
1981
|
+
return (xl << input_size) | xr
|
|
1982
|
+
|
|
1983
|
+
|
|
1984
|
+
ctypedef Py_ssize_t (*_SBOX_CONSTR) (Py_ssize_t, Py_ssize_t, list) noexcept
|
|
1985
|
+
|
|
1986
|
+
|
|
1987
|
+
cdef sbox_construction(_SBOX_CONSTR construction, list args):
|
|
1988
|
+
"""
|
|
1989
|
+
Construct an Sbox from the given input sboxes that has a twice
|
|
1990
|
+
as big input size.
|
|
1991
|
+
|
|
1992
|
+
INPUT:
|
|
1993
|
+
|
|
1994
|
+
- ``args`` -- a finite iterable SBox objects
|
|
1995
|
+
|
|
1996
|
+
EXAMPLES::
|
|
1997
|
+
|
|
1998
|
+
sage: from sage.crypto.sbox import feistel_construction
|
|
1999
|
+
sage: from sage.crypto.sboxes import PRESENT as s
|
|
2000
|
+
sage: S = feistel_construction(s, s, s) # indirect doctest
|
|
2001
|
+
"""
|
|
2002
|
+
if len(args) == 1:
|
|
2003
|
+
if isinstance(args[0], SBox):
|
|
2004
|
+
sboxes = [args[0]]
|
|
2005
|
+
else:
|
|
2006
|
+
sboxes = args[0]
|
|
2007
|
+
elif len(args) > 1:
|
|
2008
|
+
sboxes = args
|
|
2009
|
+
else:
|
|
2010
|
+
raise TypeError("no input provided")
|
|
2011
|
+
|
|
2012
|
+
for sb in sboxes:
|
|
2013
|
+
if not isinstance(sb, SBox):
|
|
2014
|
+
raise TypeError("all inputs must be an instance of SBox object")
|
|
2015
|
+
|
|
2016
|
+
cdef Py_ssize_t input_size = sboxes[0].input_size()
|
|
2017
|
+
cdef Py_ssize_t m = 2 * input_size
|
|
2018
|
+
|
|
2019
|
+
cdef Py_ssize_t i
|
|
2020
|
+
return SBox([construction(i, input_size, sboxes) for i in range(1 << m)])
|
|
2021
|
+
|
|
2022
|
+
|
|
2023
|
+
def feistel_construction(*args):
|
|
2024
|
+
r"""
|
|
2025
|
+
Return an S-Box constructed by Feistel structure using smaller S-Boxes in
|
|
2026
|
+
``args``. The number of round in the construction is equal to the number of
|
|
2027
|
+
S-Boxes provided as input. For more results concerning the differential
|
|
2028
|
+
uniformity and the nonlinearity of S-Boxes constructed by Feistel structures
|
|
2029
|
+
see [CDL2015]_.
|
|
2030
|
+
|
|
2031
|
+
INPUT:
|
|
2032
|
+
|
|
2033
|
+
- ``args`` -- a finite iterable SBox objects
|
|
2034
|
+
|
|
2035
|
+
EXAMPLES:
|
|
2036
|
+
|
|
2037
|
+
Suppose we construct an `8 \times 8` S-Box with 3-round Feistel
|
|
2038
|
+
construction from the S-Box of PRESENT::
|
|
2039
|
+
|
|
2040
|
+
sage: from sage.crypto.sbox import SBox
|
|
2041
|
+
sage: s = SBox(12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2)
|
|
2042
|
+
sage: from sage.crypto.sbox import feistel_construction
|
|
2043
|
+
sage: S = feistel_construction(s, s, s)
|
|
2044
|
+
|
|
2045
|
+
The properties of the constructed S-Box can be easily examined::
|
|
2046
|
+
|
|
2047
|
+
sage: S.nonlinearity()
|
|
2048
|
+
96
|
|
2049
|
+
sage: S.differential_branch_number()
|
|
2050
|
+
2
|
|
2051
|
+
sage: S.linear_branch_number()
|
|
2052
|
+
2
|
|
2053
|
+
"""
|
|
2054
|
+
return sbox_construction(feistel_substitute, list(args))
|
|
2055
|
+
|
|
2056
|
+
|
|
2057
|
+
def misty_construction(*args):
|
|
2058
|
+
r"""
|
|
2059
|
+
Return an S-Box constructed by MISTY structure using smaller
|
|
2060
|
+
S-Boxes in ``args``.
|
|
2061
|
+
|
|
2062
|
+
The number of round in the construction is equal to the number
|
|
2063
|
+
of S-Boxes provided as input. For further result related to the
|
|
2064
|
+
nonlinearity and differential uniformity of the constructed S-Box
|
|
2065
|
+
one may consult [CDL2015]_.
|
|
2066
|
+
|
|
2067
|
+
INPUT:
|
|
2068
|
+
|
|
2069
|
+
- ``args`` -- a finite iterable SBox objects
|
|
2070
|
+
|
|
2071
|
+
EXAMPLES:
|
|
2072
|
+
|
|
2073
|
+
We construct an `8 \times 8` S-Box using 3-round MISTY structure with the
|
|
2074
|
+
following `4 \times 4` S-Boxes `S1, S2, S3` (see Example 2 in [CDL2015]_)::
|
|
2075
|
+
|
|
2076
|
+
sage: from sage.crypto.sbox import SBox
|
|
2077
|
+
sage: S1 = SBox([0x4,0x0,0x1,0xF,0x2,0xB,0x6,0x7,0x3,0x9,0xA,0x5,0xC,0xD,0xE,0x8])
|
|
2078
|
+
sage: S2 = SBox([0x0,0x0,0x0,0x1,0x0,0xA,0x8,0x3,0x0,0x8,0x2,0xB,0x4,0x6,0xE,0xD])
|
|
2079
|
+
sage: S3 = SBox([0x0,0x7,0xB,0xD,0x4,0x1,0xB,0xF,0x1,0x2,0xC,0xE,0xD,0xC,0x5,0x5])
|
|
2080
|
+
sage: from sage.crypto.sbox import misty_construction
|
|
2081
|
+
sage: S = misty_construction(S1, S2, S3)
|
|
2082
|
+
|
|
2083
|
+
The properties of the constructed S-Box can be easily examined::
|
|
2084
|
+
|
|
2085
|
+
sage: S.differential_uniformity()
|
|
2086
|
+
8
|
|
2087
|
+
sage: S.linearity()
|
|
2088
|
+
64
|
|
2089
|
+
"""
|
|
2090
|
+
return sbox_construction(misty_substitute, list(args))
|