diff --git a/src/sage/crypto/block_cipher/des.py b/src/sage/crypto/block_cipher/des.py index e0d7f8b0471..08afe715710 100644 --- a/src/sage/crypto/block_cipher/des.py +++ b/src/sage/crypto/block_cipher/des.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" DES @@ -77,7 +78,7 @@ from sage.rings.integer_ring import ZZ from sage.modules.free_module_element import vector from sage.rings.finite_rings.finite_field_constructor import GF -from sage.modules.vector_mod2_dense import Vector_mod2_dense +from sage.structure.element import Vector from sage.rings.integer import Integer from sage.crypto.sboxes import DES_S1_1, DES_S1_2, DES_S1_3, DES_S1_4 from sage.crypto.sboxes import DES_S2_1, DES_S2_2, DES_S2_3, DES_S2_4 @@ -511,7 +512,7 @@ def encrypt(self, plaintext, key): sage: des.encrypt(P, K56) == C True """ - if isinstance(plaintext, (list, tuple, Vector_mod2_dense)): + if isinstance(plaintext, (list, tuple, Vector)): inputType = 'vector' elif isinstance(plaintext, (Integer, int)): inputType = 'integer' @@ -568,7 +569,7 @@ def decrypt(self, ciphertext, key): sage: des.decrypt(C, K56).hex() == P True """ - if isinstance(ciphertext, (list, tuple, Vector_mod2_dense)): + if isinstance(ciphertext, (list, tuple, Vector)): inputType = 'vector' elif isinstance(ciphertext, (Integer, int)): inputType = 'integer' @@ -868,7 +869,7 @@ def __call__(self, key): pass a ``masterKey`` value on initialisation. Otherwise you can omit ``masterKey`` and pass a key when you call the object. """ - if isinstance(key, (list, tuple, Vector_mod2_dense)): + if isinstance(key, (list, tuple, Vector)): inputType = 'vector' elif isinstance(key, (Integer, int)): inputType = 'integer' diff --git a/src/sage/crypto/block_cipher/miniaes.py b/src/sage/crypto/block_cipher/miniaes.py index 95fcf1a258c..6eb9568b9fc 100644 --- a/src/sage/crypto/block_cipher/miniaes.py +++ b/src/sage/crypto/block_cipher/miniaes.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat sage.modules sage.rings.finite_rings r""" Mini-AES diff --git a/src/sage/crypto/block_cipher/present.py b/src/sage/crypto/block_cipher/present.py index 1774e6d5977..6e5605692a1 100644 --- a/src/sage/crypto/block_cipher/present.py +++ b/src/sage/crypto/block_cipher/present.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" PRESENT @@ -63,8 +64,8 @@ from sage.rings.integer import Integer from sage.modules.free_module_element import vector from sage.rings.finite_rings.finite_field_constructor import GF +from sage.structure.element import Vector from sage.crypto.sboxes import PRESENT as PRESENTSBOX -from sage.modules.vector_mod2_dense import Vector_mod2_dense def _smallscale_present_linearlayer(nsboxes=16): @@ -417,7 +418,7 @@ def encrypt(self, plaintext, key): \leq 32` and current STATE `b_{63} \dots b_0`, addRoundkey consists of the operation for `0 \leq j \leq 63`, `b_j = b_j \oplus \kappa^i_j`. """ - if isinstance(plaintext, (list, tuple, Vector_mod2_dense)): + if isinstance(plaintext, (list, tuple, Vector)): inputType = 'vector' elif isinstance(plaintext, (Integer, int)): inputType = 'integer' @@ -473,7 +474,7 @@ def decrypt(self, ciphertext, key): sage: present.decrypt(c4, k4) == p4 True """ - if isinstance(ciphertext, (list, tuple, Vector_mod2_dense)): + if isinstance(ciphertext, (list, tuple, Vector)): inputType = 'vector' elif isinstance(ciphertext, (Integer, int)): inputType = 'integer' @@ -773,7 +774,7 @@ def __call__(self, K): pass a ``master_key`` value on initialisation. Otherwise you can omit ``master_key`` and pass a key when you call the object. """ - if isinstance(K, (list, tuple, Vector_mod2_dense)): + if isinstance(K, (list, tuple, Vector)): inputType = 'vector' elif isinstance(K, (Integer, int)): inputType = 'integer' diff --git a/src/sage/crypto/block_cipher/sdes.py b/src/sage/crypto/block_cipher/sdes.py index 02a7a14772c..30b8cf2516c 100644 --- a/src/sage/crypto/block_cipher/sdes.py +++ b/src/sage/crypto/block_cipher/sdes.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat sage.rings.finite_rings r""" Simplified DES diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index 13d0a043ff8..fa99fab5ea3 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -10,14 +10,14 @@ and also algebraic immunity. EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: R. = GF(2^8,'a')[] sage: from sage.crypto.boolean_function import BooleanFunction - sage: B = BooleanFunction( x^254 ) # the Boolean function Tr(x^254) - sage: B + sage: B = BooleanFunction(x^254); B # the Boolean function Tr(x^254) Boolean function with 8 variables sage: B.nonlinearity() 112 - sage: B.algebraic_immunity() + sage: B.algebraic_immunity() # needs sage.rings.polynomial.pbori 4 AUTHOR: @@ -32,18 +32,20 @@ AUTHOR: from cysignals.signals cimport sig_check from libc.string cimport memcpy -from sage.structure.sage_object cimport SageObject -from sage.structure.richcmp cimport rich_to_bool -from sage.rings.integer_ring import ZZ -from sage.rings.integer cimport Integer -from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.polynomial.pbori.pbori import BooleanPolynomial +from sage.data_structures.bitset_base cimport * +from sage.misc.superseded import deprecated_function_alias from sage.rings.finite_rings.finite_field_base import FiniteField +from sage.rings.finite_rings.finite_field_constructor import GF +from sage.rings.integer cimport Integer +from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_element import Polynomial +from sage.structure.richcmp cimport rich_to_bool +from sage.structure.sage_object cimport SageObject -from sage.misc.superseded import deprecated_function_alias - -from sage.data_structures.bitset_base cimport * +try: + from sage.rings.polynomial.pbori.pbori import BooleanPolynomial +except ImportError: + BooleanPolynomial = () # for details about the implementation of hamming_weight (in .pxd), # walsh_hadamard transform, reed_muller transform, and a lot @@ -61,7 +63,7 @@ cdef walsh_hadamard(long *f, int ldn): sage: from sage.crypto.boolean_function import BooleanFunction sage: B = BooleanFunction([1,0,0,1]) - sage: B.walsh_hadamard_transform() # indirect doctest + sage: B.walsh_hadamard_transform() # indirect doctest (0, 0, 0, -4) """ cdef long n, ldm, m, mh, t1, t2, r, j, u, v @@ -89,11 +91,12 @@ cdef long yellow_code(unsigned long a): EXAMPLES:: + sage: # needs sage.rings.polynomial.pbori sage: from sage.crypto.boolean_function import BooleanFunction sage: R. = BooleanPolynomialRing(3) sage: P = x*y - sage: B = BooleanFunction( P ) - sage: B.truth_table() # indirect doctest + sage: B = BooleanFunction(P) + sage: B.truth_table() # indirect doctest (False, False, False, True, False, False, False, True) """ cdef unsigned long s = (8*sizeof(unsigned long)) >> 1 @@ -122,11 +125,12 @@ cdef reed_muller(mp_limb_t* f, int ldn): EXAMPLES:: + sage: # needs sage.rings.polynomial.pbori sage: from sage.crypto.boolean_function import BooleanFunction sage: R. = BooleanPolynomialRing(3) sage: P = x*y - sage: B = BooleanFunction( P ) - sage: B.truth_table() # indirect doctest + sage: B = BooleanFunction(P) + sage: B.truth_table() # indirect doctest (False, False, False, True, False, False, False, True) """ cdef long n, ldm, m, mh, t1, t2, r, j @@ -154,15 +158,15 @@ cdef class BooleanFunction(SageObject): We can construct a Boolean Function from either: - - an integer - the result is the zero function with ``x`` variables; - - a list - it is expected to be the truth table of the + - an integer -- the result is the zero function with ``x`` variables; + - a list -- it is expected to be the truth table of the result. Therefore it must be of length a power of 2, and its elements are interpreted as Booleans; - - a string - representing the truth table in hexadecimal; - - a Boolean polynomial - the result is the corresponding Boolean function; - - a polynomial P over an extension of GF(2) - the result is - the Boolean function with truth table ``( Tr(P(x)) for x in - GF(2^k) )`` + - a string -- representing the truth table in hexadecimal; + - a Boolean polynomial -- the result is the corresponding Boolean function; + - a polynomial `P` over an extension of `\GF{2}` -- the result is + the Boolean function with truth table ``(Tr(P(x)) for x in + GF(2^k))`` EXAMPLES: @@ -179,10 +183,9 @@ cdef class BooleanFunction(SageObject): note that elements can be of different types:: - sage: B = BooleanFunction([False, sqrt(2)]) - sage: B + sage: B = BooleanFunction([False, sqrt(2)]); B # needs sage.symbolic Boolean function with 1 variable - sage: [b for b in B] + sage: [b for b in B] # needs sage.symbolic [False, True] from a string:: @@ -192,21 +195,20 @@ cdef class BooleanFunction(SageObject): from a :class:`sage.rings.polynomial.pbori.BooleanPolynomial`:: - sage: R. = BooleanPolynomialRing(3) - sage: P = x*y - sage: BooleanFunction( P ) + sage: R. = BooleanPolynomialRing(3) # needs sage.rings.polynomial.pbori + sage: P = x*y # needs sage.rings.polynomial.pbori + sage: BooleanFunction(P) # needs sage.rings.polynomial.pbori Boolean function with 3 variables from a polynomial over a binary field:: - sage: R. = GF(2^8,'a')[] - sage: B = BooleanFunction( x^7 ) - sage: B + sage: R. = GF(2^8,'a')[] # needs sage.rings.finite_rings + sage: B = BooleanFunction(x^7); B # needs sage.rings.finite_rings Boolean function with 8 variables two failure cases:: - sage: BooleanFunction(sqrt(2)) + sage: BooleanFunction(sqrt(2)) # needs sage.symbolic Traceback (most recent call last): ... TypeError: unable to init the Boolean function @@ -255,29 +257,27 @@ cdef class BooleanFunction(SageObject): note that elements can be of different types:: - sage: B = BooleanFunction([False, sqrt(2)]) - sage: B + sage: B = BooleanFunction([False, sqrt(2)]); B # needs sage.symbolic Boolean function with 1 variable - sage: [b for b in B] + sage: [b for b in B] # needs sage.symbolic [False, True] from a :class:`sage.rings.polynomial.pbori.BooleanPolynomial`:: - sage: R. = BooleanPolynomialRing(3) - sage: P = x*y - sage: BooleanFunction( P ) + sage: R. = BooleanPolynomialRing(3) # needs sage.rings.polynomial.pbori + sage: P = x*y # needs sage.rings.polynomial.pbori + sage: BooleanFunction(P) # needs sage.rings.polynomial.pbori Boolean function with 3 variables from a polynomial over a binary field:: - sage: R. = GF(2^8,'a')[] - sage: B = BooleanFunction( x^7 ) - sage: B + sage: R. = GF(2^8,'a')[] # needs sage.rings.finite_rings + sage: B = BooleanFunction(x^7); B # needs sage.rings.finite_rings Boolean function with 8 variables two failure cases:: - sage: BooleanFunction(sqrt(2)) + sage: BooleanFunction(sqrt(2)) # needs sage.symbolic Traceback (most recent call last): ... TypeError: unable to init the Boolean function @@ -396,8 +396,8 @@ cdef class BooleanFunction(SageObject): it also corresponds to the addition of algebraic normal forms:: - sage: S = A.algebraic_normal_form() + B.algebraic_normal_form() - sage: (A+B).algebraic_normal_form() == S + sage: S = A.algebraic_normal_form() + B.algebraic_normal_form() # needs sage.rings.polynomial.pbori + sage: (A+B).algebraic_normal_form() == S # needs sage.rings.polynomial.pbori True TESTS:: @@ -428,8 +428,8 @@ cdef class BooleanFunction(SageObject): it also corresponds to the multiplication of algebraic normal forms:: - sage: P = A.algebraic_normal_form() * B.algebraic_normal_form() - sage: (A*B).algebraic_normal_form() == P + sage: P = A.algebraic_normal_form() * B.algebraic_normal_form() # needs sage.rings.polynomial.pbori + sage: (A*B).algebraic_normal_form() == P # needs sage.rings.polynomial.pbori True TESTS:: @@ -499,10 +499,9 @@ cdef class BooleanFunction(SageObject): sage: from sage.crypto.boolean_function import BooleanFunction sage: B = BooleanFunction([0,1,1,0,1,0,1,1]) - sage: P = B.algebraic_normal_form() - sage: P + sage: P = B.algebraic_normal_form(); P # needs sage.rings.polynomial.pbori x0*x1*x2 + x0 + x1*x2 + x1 + x2 - sage: [ P(*ZZ(i).digits(base=2,padto=3)) for i in range(8) ] + sage: [P(*ZZ(i).digits(base=2, padto=3)) for i in range(8)] # needs sage.rings.polynomial.pbori [0, 1, 1, 0, 1, 0, 1, 1] """ cdef bitset_t anf @@ -548,15 +547,16 @@ cdef class BooleanFunction(SageObject): INPUT: a string representing the desired format, can be either - - 'bin' (default) : we return a tuple of Boolean values - - 'int' : we return a tuple of 0 or 1 values - - 'hex' : we return a string representing the truth_table in hexadecimal + - ``'bin'`` (default): we return a tuple of Boolean values + - ``'int'``: we return a tuple of 0 or 1 values + - ``'hex'``: we return a string representing the truth table in hexadecimal EXAMPLES:: + sage: # needs sage.rings.polynomial.pbori sage: from sage.crypto.boolean_function import BooleanFunction sage: R. = BooleanPolynomialRing(3) - sage: B = BooleanFunction( x*y*z + z + y + 1 ) + sage: B = BooleanFunction(x*y*z + z + y + 1) sage: B.truth_table() (True, True, False, False, False, False, True, False) sage: B.truth_table(format='int') @@ -564,9 +564,10 @@ cdef class BooleanFunction(SageObject): sage: B.truth_table(format='hex') '43' - sage: BooleanFunction('00ab').truth_table(format='hex') + sage: BooleanFunction('00ab').truth_table(format='hex') # needs sage.rings.polynomial.pbori '00ab' + sage: # needs sage.rings.polynomial.pbori sage: H = '0abbacadabbacad0' sage: len(H) 16 @@ -703,9 +704,9 @@ cdef class BooleanFunction(SageObject): EXAMPLES:: sage: from sage.crypto.boolean_function import BooleanFunction - sage: R. = GF(2^3,'a')[] - sage: B = BooleanFunction( x^3 ) - sage: B.walsh_hadamard_transform() + sage: R. = GF(2^3,'a')[] # needs sage.rings.finite_rings + sage: B = BooleanFunction(x^3) # needs sage.rings.finite_rings + sage: B.walsh_hadamard_transform() # needs sage.rings.finite_rings (0, -4, 0, 4, 0, 4, 0, 4) """ cdef long *temp @@ -837,8 +838,8 @@ cdef class BooleanFunction(SageObject): correlation immune of order `m`. A Boolean function is said to be correlation immune of order - `m`, if the output of the function is statistically - independent of the combination of any m of its inputs. + `m` if the output of the function is statistically + independent of the combination of any `m` of its inputs. EXAMPLES:: @@ -866,7 +867,7 @@ cdef class BooleanFunction(SageObject): A Boolean function is said to be resilient of order `m` if it is balanced and correlation immune of order `m`. - If the function is not balanced, we return -1. + If the function is not balanced, we return `-1`. EXAMPLES:: @@ -991,17 +992,17 @@ cdef class BooleanFunction(SageObject): INPUT: - ``d`` -- an integer; - - ``dim`` -- a Boolean (default: False), if True, return also + - ``dim`` -- a Boolean (default: ``False``), if ``True``, return also the dimension of the annihilator vector space. EXAMPLES:: sage: from sage.crypto.boolean_function import BooleanFunction sage: f = BooleanFunction("7969817CC5893BA6AC326E47619F5AD0") - sage: f.annihilator(1) is None + sage: f.annihilator(1) is None # needs sage.rings.polynomial.pbori True - sage: g = BooleanFunction( f.annihilator(3) ) - sage: set([ fi*g(i) for i,fi in enumerate(f) ]) + sage: g = BooleanFunction(f.annihilator(3)) # needs sage.rings.polynomial.pbori + sage: set(fi*g(i) for i,fi in enumerate(f)) # needs sage.rings.polynomial.pbori {0} """ # NOTE: this is a toy implementation @@ -1054,8 +1055,8 @@ cdef class BooleanFunction(SageObject): """ Return the algebraic immunity of the Boolean function. - This is the smallest integer `i` such that there exists a non - trivial annihilator for `self` or `~self`. + This is the smallest integer `i` such that there exists a + nontrivial annihilator for ``self`` or ``~self``. INPUT: @@ -1064,15 +1065,17 @@ cdef class BooleanFunction(SageObject): EXAMPLES:: + sage: # needs sage.rings.polynomial.pbori sage: from sage.crypto.boolean_function import BooleanFunction sage: R. = BooleanPolynomialRing(6) sage: B = BooleanFunction(x0*x1 + x1*x2 + x2*x3 + x3*x4 + x4*x5) sage: B.algebraic_immunity(annihilator=True) (2, x0*x1 + x1*x2 + x2*x3 + x3*x4 + x4*x5 + 1) - sage: B[0] +=1 + sage: B[0] += 1 sage: B.algebraic_immunity() 2 + sage: # needs sage.rings.finite_rings sage.rings.polynomial.pbori sage: R. = GF(2^8,'a')[] sage: B = BooleanFunction(x^31) sage: B.algebraic_immunity() @@ -1097,10 +1100,11 @@ cdef class BooleanFunction(SageObject): The algebraic degree of a Boolean function is defined as the degree of its algebraic normal form. Note that the degree of the constant - zero function is defined to be equal to -1. + zero function is defined to be equal to `-1`. EXAMPLES:: + sage: # needs sage.rings.polynomial.pbori sage: from sage.crypto.boolean_function import BooleanFunction sage: B. = BooleanPolynomialRing() sage: f = BooleanFunction(x1*x2 + x1*x2*x3 + x1) @@ -1120,6 +1124,7 @@ cdef class BooleanFunction(SageObject): EXAMPLES:: + sage: # needs sage.rings.polynomial.pbori sage: from sage.crypto.boolean_function import BooleanFunction sage: R. = BooleanPolynomialRing() sage: f = BooleanFunction(x0*x1 + x2 + x3) @@ -1160,7 +1165,7 @@ cdef class BooleanFunction(SageObject): True sage: f.is_linear_structure(7) False - sage: f.is_linear_structure(20) #parameter is out of range + sage: f.is_linear_structure(20) # parameter is out of range Traceback (most recent call last): ... IndexError: index out of range @@ -1174,7 +1179,7 @@ cdef class BooleanFunction(SageObject): Traceback (most recent call last): ... TypeError: input vector must be an element of a vector space with dimension 4 - sage: f.is_linear_structure('X') #failure case + sage: f.is_linear_structure('X') # failure case Traceback (most recent call last): ... TypeError: cannot compute is_linear_structure() using parameter X @@ -1246,6 +1251,7 @@ cdef class BooleanFunction(SageObject): EXAMPLES:: + sage: # needs sage.modules sage: from sage.crypto.boolean_function import BooleanFunction sage: f = BooleanFunction([0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0]) sage: LS = f.linear_structures() @@ -1281,6 +1287,7 @@ cdef class BooleanFunction(SageObject): EXAMPLES:: + sage: # needs sage.rings.polynomial.pbori sage: from sage.crypto.boolean_function import BooleanFunction sage: f = BooleanFunction([0,1,0,1,0,1,0,1]) sage: f.derivative(1).algebraic_normal_form() @@ -1288,8 +1295,8 @@ cdef class BooleanFunction(SageObject): sage: u = [1,0,0] sage: f.derivative(u).algebraic_normal_form() 1 - sage: v = vector(GF(2), u) - sage: f.derivative(u).algebraic_normal_form() + sage: v = vector(GF(2), u) # needs sage.modules + sage: f.derivative(v).algebraic_normal_form() # needs sage.modules 1 sage: f.derivative(8).algebraic_normal_form() Traceback (most recent call last): @@ -1391,7 +1398,7 @@ def unpickle_BooleanFunction(bool_list): sage: from sage.crypto.boolean_function import BooleanFunction sage: B = BooleanFunction([0,1,1,0]) - sage: loads(dumps(B)) == B # indirect doctest + sage: loads(dumps(B)) == B # indirect doctest True """ return BooleanFunction(bool_list) @@ -1424,7 +1431,7 @@ cdef class BooleanFunctionIterator: sage: from sage.crypto.boolean_function import BooleanFunction sage: B = BooleanFunction(1) - sage: [b for b in B] # indirect doctest + sage: [b for b in B] # indirect doctest [False, False] """ return self diff --git a/src/sage/crypto/cipher.py b/src/sage/crypto/cipher.py index 6d6ed6dd2b3..14e9df6a504 100644 --- a/src/sage/crypto/cipher.py +++ b/src/sage/crypto/cipher.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat """ Ciphers """ diff --git a/src/sage/crypto/classical.py b/src/sage/crypto/classical.py index 132a18cdee0..616dc861652 100644 --- a/src/sage/crypto/classical.py +++ b/src/sage/crypto/classical.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat r""" Classical Cryptosystems @@ -45,19 +46,22 @@ # - methods to cryptanalyze the Hill, substitution, transposition, and # Vigenere ciphers +from random import randint + +from sage.arith.misc import inverse_mod, xgcd +from sage.misc.lazy_import import lazy_import from sage.monoids.string_monoid import ( StringMonoid_class, AlphabeticStringMonoid) from sage.monoids.string_monoid_element import StringMonoidElement from sage.monoids.string_ops import strip_encoding -from sage.groups.perm_gps.permgroup_named import SymmetricGroup -from sage.groups.perm_gps.permgroup_element import PermutationGroupElement from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing -from sage.arith.misc import inverse_mod, xgcd -from random import randint -from sage.matrix.matrix_space import MatrixSpace + +lazy_import('sage.groups.perm_gps.permgroup_named', 'SymmetricGroup') +lazy_import('sage.groups.perm_gps.permgroup_element', 'PermutationGroupElement') +lazy_import('sage.matrix.matrix_space', 'MatrixSpace') from .cryptosystem import SymmetricKeyCryptosystem from .classical_cipher import ( @@ -1201,7 +1205,7 @@ def inverse_key(self, a, b): capital letters of the English alphabet, there are 12 such integers relatively prime to `n`:: - sage: euler_phi(A.alphabet_size()) + sage: euler_phi(A.alphabet_size()) # needs sage.libs.pari 12 And here is a list of those integers:: @@ -1285,7 +1289,7 @@ def random_key(self): class HillCryptosystem(SymmetricKeyCryptosystem): r""" - Create a Hill cryptosystem defined by the `m` x `m` matrix space + Create a Hill cryptosystem defined by the `m \times m` matrix space over `\ZZ / N \ZZ`, where `N` is the alphabet size of the string monoid ``S``. @@ -1302,19 +1306,17 @@ class HillCryptosystem(SymmetricKeyCryptosystem): EXAMPLES:: + sage: # needs sage.modules sage: S = AlphabeticStrings() - sage: E = HillCryptosystem(S,3) - sage: E + sage: E = HillCryptosystem(S, 3); E Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3 sage: R = IntegerModRing(26) sage: M = MatrixSpace(R,3,3) - sage: A = M([[1,0,1],[0,1,1],[2,2,3]]) - sage: A + sage: A = M([[1,0,1],[0,1,1],[2,2,3]]); A [1 0 1] [0 1 1] [2 2 3] - sage: e = E(A) - sage: e + sage: e = E(A); e Hill cipher on Free alphabetic string monoid on A-Z of block length 3 sage: e(S("LAMAISONBLANCHE")) JYVKSKQPELAYKPV @@ -1322,8 +1324,8 @@ class HillCryptosystem(SymmetricKeyCryptosystem): TESTS:: sage: S = AlphabeticStrings() - sage: E = HillCryptosystem(S,3) - sage: E == loads(dumps(E)) + sage: E = HillCryptosystem(S, 3) # needs sage.modules + sage: E == loads(dumps(E)) # needs sage.modules True """ @@ -1331,7 +1333,7 @@ def __init__(self, S, m): r""" See ``HillCryptosystem`` for full documentation. - Create a Hill cryptosystem defined by the `m` x `m` matrix space + Create a Hill cryptosystem defined by the `m \times m` matrix space over `\ZZ / N \ZZ`, where `N` is the alphabet size of the string monoid ``S``. @@ -1349,8 +1351,7 @@ def __init__(self, S, m): EXAMPLES:: sage: S = AlphabeticStrings() - sage: E = HillCryptosystem(S,3) - sage: E + sage: E = HillCryptosystem(S, 3); E # needs sage.modules Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3 """ if not isinstance(S, StringMonoid_class): @@ -1369,18 +1370,16 @@ def __call__(self, A): EXAMPLES:: + sage: # needs sage.modules sage: S = AlphabeticStrings() - sage: E = HillCryptosystem(S,3) - sage: E + sage: E = HillCryptosystem(S,3); E Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3 sage: M = E.key_space() - sage: A = M([[1,0,1],[0,1,1],[2,2,3]]) - sage: A + sage: A = M([[1,0,1],[0,1,1],[2,2,3]]); A [1 0 1] [0 1 1] [2 2 3] - sage: e = E(A) - sage: e + sage: e = E(A); e Hill cipher on Free alphabetic string monoid on A-Z of block length 3 sage: m = S("LAMAISONBLANCHE") sage: e(m) @@ -1405,10 +1404,9 @@ def _repr_(self): EXAMPLES:: sage: A = AlphabeticStrings() - sage: H = HillCryptosystem(A, 3) - sage: H + sage: H = HillCryptosystem(A, 3); H # needs sage.modules Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3 - sage: H._repr_() + sage: H._repr_() # needs sage.modules 'Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3' """ return "Hill cryptosystem on %s of block length %s" % ( @@ -1430,20 +1428,20 @@ def block_length(self): sage: A = AlphabeticStrings() sage: n = randint(1, A.ngens() - 1) - sage: H = HillCryptosystem(A, n) - sage: H.block_length() == n + sage: H = HillCryptosystem(A, n) # needs sage.modules + sage: H.block_length() == n # needs sage.modules True """ return self.key_space().nrows() def random_key(self): - """ + r""" A random key within the key space of this Hill cipher. That is, - generate a random `m` x `m` matrix to be used as a block + generate a random `m \times m` matrix to be used as a block permutation, where `m` is the block length of this Hill cipher. If `n` is the size of the cryptosystem alphabet, then there are `n^{m^2}` possible keys. However the number of valid keys, - i.e. invertible `m` x `m` square matrices, is smaller than + i.e. invertible `m \times m` square matrices, is smaller than `n^{m^2}`. OUTPUT: @@ -1452,6 +1450,7 @@ def random_key(self): EXAMPLES:: + sage: # needs sage.modules sage: A = AlphabeticStrings() sage: n = 3 sage: H = HillCryptosystem(A, n) @@ -1478,7 +1477,7 @@ def inverse_key(self, A): INPUT: - - ``A`` - an invertible matrix of the key space of this Hill cipher + - ``A`` -- an invertible matrix of the key space of this Hill cipher OUTPUT: @@ -1486,8 +1485,9 @@ def inverse_key(self, A): EXAMPLES:: + sage: # needs sage.modules sage: S = AlphabeticStrings() - sage: E = HillCryptosystem(S,3) + sage: E = HillCryptosystem(S, 3) sage: A = E.random_key() sage: B = E.inverse_key(A) sage: M = S("LAMAISONBLANCHE") @@ -1519,7 +1519,7 @@ def encoding(self, M): INPUT: - - ``M`` - a string, possibly empty + - ``M`` -- a string, possibly empty OUTPUT: @@ -1529,8 +1529,8 @@ def encoding(self, M): sage: M = "The matrix cipher by Lester S. Hill." sage: A = AlphabeticStrings() - sage: H = HillCryptosystem(A, 7) - sage: H.encoding(M) == A.encoding(M) + sage: H = HillCryptosystem(A, 7) # needs sage.modules + sage: H.encoding(M) == A.encoding(M) # needs sage.modules True """ S = self.cipher_domain() @@ -1547,9 +1547,9 @@ def deciphering(self, A, C): INPUT: - - ``A`` - a key within the key space of this Hill cipher + - ``A`` -- a key within the key space of this Hill cipher - - ``C`` - a string (possibly empty) over the string monoid of this + - ``C`` -- a string (possibly empty) over the string monoid of this Hill cipher OUTPUT: @@ -1558,6 +1558,7 @@ def deciphering(self, A, C): EXAMPLES:: + sage: # needs sage.modules sage: H = HillCryptosystem(AlphabeticStrings(), 3) sage: K = H.random_key() sage: M = H.encoding("Good day, mate! How ya going?") @@ -1585,6 +1586,7 @@ def enciphering(self, A, M): EXAMPLES:: + sage: # needs sage.modules sage: H = HillCryptosystem(AlphabeticStrings(), 3) sage: K = H.random_key() sage: M = H.encoding("Good day, mate! How ya going?") @@ -1595,6 +1597,7 @@ def enciphering(self, A, M): e = self(A) return e(M) + class ShiftCryptosystem(SymmetricKeyCryptosystem): r""" Create a shift cryptosystem. @@ -3258,21 +3261,20 @@ class TranspositionCryptosystem(SymmetricKeyCryptosystem): EXAMPLES:: sage: S = AlphabeticStrings() - sage: E = TranspositionCryptosystem(S,14) - sage: E - Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14 - sage: K = [ 14-i for i in range(14) ] - sage: K + sage: E = TranspositionCryptosystem(S,14); E # needs sage.groups + Transposition cryptosystem on + Free alphabetic string monoid on A-Z of block length 14 + sage: K = [14 - i for i in range(14)]; K [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - sage: e = E(K) - sage: e(S("THECATINTHEHAT")) + sage: e = E(K) # needs sage.groups + sage: e(S("THECATINTHEHAT")) # needs sage.groups TAHEHTNITACEHT TESTS:: sage: S = AlphabeticStrings() - sage: E = TranspositionCryptosystem(S,14) - sage: E == loads(dumps(E)) + sage: E = TranspositionCryptosystem(S,14) # needs sage.groups + sage: E == loads(dumps(E)) # needs sage.groups True """ @@ -3283,8 +3285,7 @@ def __init__(self, S, n): EXAMPLES:: sage: S = AlphabeticStrings() - sage: E = TranspositionCryptosystem(S,14) - sage: E + sage: E = TranspositionCryptosystem(S,14); E # needs sage.groups Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14 """ if not isinstance(S, StringMonoid_class): @@ -3303,15 +3304,13 @@ def __call__(self, K): EXAMPLES:: sage: M = AlphabeticStrings() - sage: E = TranspositionCryptosystem(M,14) - sage: E + sage: E = TranspositionCryptosystem(M,14); E # needs sage.groups Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14 - sage: K = [ 14-i for i in range(14) ] - sage: K + sage: K = [14 - i for i in range(14)]; K [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] - sage: e = E(K) + sage: e = E(K) # needs sage.groups sage: m = M("THECATINTHEHAT") - sage: e(m) + sage: e(m) # needs sage.groups TAHEHTNITACEHT """ G = self.key_space() @@ -3331,10 +3330,9 @@ def _repr_(self): EXAMPLES:: sage: A = AlphabeticStrings() - sage: T = TranspositionCryptosystem(A, 14) - sage: T + sage: T = TranspositionCryptosystem(A, 14); T # needs sage.groups Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14 - sage: T._repr_() + sage: T._repr_() # needs sage.groups 'Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14' """ return "Transposition cryptosystem on %s of block length %s" % ( @@ -3352,6 +3350,7 @@ def random_key(self): EXAMPLES:: + sage: # needs sage.groups sage: S = AlphabeticStrings() sage: E = TranspositionCryptosystem(S, 14) sage: K = E.random_key() @@ -3372,10 +3371,10 @@ def inverse_key(self, K, check=True): INPUT: - - ``K`` - a key belonging to the key space of this transposition + - ``K`` -- a key belonging to the key space of this transposition cipher - - ``check`` - bool (default: ``True``); check that ``K`` belongs to + - ``check`` -- bool (default: ``True``); check that ``K`` belongs to the key space of this cryptosystem. OUTPUT: @@ -3384,6 +3383,7 @@ def inverse_key(self, K, check=True): EXAMPLES:: + sage: # needs sage.groups sage: S = AlphabeticStrings() sage: E = TranspositionCryptosystem(S, 14) sage: K = E.random_key() @@ -3410,7 +3410,7 @@ def encoding(self, M): INPUT: - - ``M`` - a string, possibly empty + - ``M`` -- a string, possibly empty OUTPUT: @@ -3420,8 +3420,8 @@ def encoding(self, M): sage: M = "Transposition cipher is not about matrix transpose." sage: A = AlphabeticStrings() - sage: T = TranspositionCryptosystem(A, 11) - sage: T.encoding(M) == A.encoding(M) + sage: T = TranspositionCryptosystem(A, 11) # needs sage.groups + sage: T.encoding(M) == A.encoding(M) # needs sage.groups True """ S = self.cipher_domain() @@ -3438,10 +3438,10 @@ def deciphering(self, K, C): INPUT: - - ``K`` - a key belonging to the key space of this transposition + - ``K`` -- a key belonging to the key space of this transposition cipher - - ``C`` - a string (possibly empty) over the string monoid of this + - ``C`` -- a string (possibly empty) over the string monoid of this cryptosystem. OUTPUT: @@ -3450,6 +3450,7 @@ def deciphering(self, K, C): EXAMPLES:: + sage: # needs sage.groups sage: T = TranspositionCryptosystem(AlphabeticStrings(), 14) sage: K = T.random_key() sage: M = T.encoding("The cat in the hat.") @@ -3465,10 +3466,10 @@ def enciphering(self, K, M): INPUT: - - ``K`` - a key belonging to the key space of this transposition + - ``K`` -- a key belonging to the key space of this transposition cipher - - ``M`` - a string (possibly empty) over the string monoid of this + - ``M`` -- a string (possibly empty) over the string monoid of this cryptosystem OUTPUT: @@ -3477,6 +3478,7 @@ def enciphering(self, K, M): EXAMPLES:: + sage: # needs sage.groups sage: T = TranspositionCryptosystem(AlphabeticStrings(), 14) sage: K = T.random_key() sage: M = T.encoding("The cat in the hat.") @@ -3486,6 +3488,7 @@ def enciphering(self, K, M): e = self(K) return e(M) + class VigenereCryptosystem(SymmetricKeyCryptosystem): """ Create a Vigenere cryptosystem of block length ``n``. @@ -3494,7 +3497,7 @@ class VigenereCryptosystem(SymmetricKeyCryptosystem): - ``S``-- a string monoid over some alphabet - - ``n`` - integer `> 0`; block length of an encryption/decryption key + - ``n`` -- integer `> 0`; block length of an encryption/decryption key OUTPUT: @@ -3621,7 +3624,7 @@ def inverse_key(self, K): INPUT: - - ``K`` - a key within the key space of this Vigenere cryptosystem + - ``K`` -- a key within the key space of this Vigenere cryptosystem OUTPUT: @@ -3653,7 +3656,7 @@ def encoding(self, M): INPUT: - - ``M`` - a string, possibly empty + - ``M`` -- a string, possibly empty OUTPUT: @@ -3681,9 +3684,9 @@ def deciphering(self, K, C): INPUT: - - ``K`` - a key belonging to the key space of this Vigenere cipher + - ``K`` -- a key belonging to the key space of this Vigenere cipher - - ``C`` - a string (possibly empty) over the string monoid of this + - ``C`` -- a string (possibly empty) over the string monoid of this cryptosystem OUTPUT: @@ -3707,9 +3710,9 @@ def enciphering(self, K, M): INPUT: - - ``K`` - a key belonging to the key space of this Vigenere cipher + - ``K`` -- a key belonging to the key space of this Vigenere cipher - - ``M`` - a string (possibly empty) over the string monoid of this + - ``M`` -- a string (possibly empty) over the string monoid of this cryptosystem OUTPUT: diff --git a/src/sage/crypto/classical_cipher.py b/src/sage/crypto/classical_cipher.py index 2d5722b04df..fd2c2464293 100644 --- a/src/sage/crypto/classical_cipher.py +++ b/src/sage/crypto/classical_cipher.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat """ Classical Ciphers """ @@ -8,9 +9,12 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from .cipher import SymmetricKeyCipher +from sage.misc.lazy_import import lazy_import from sage.monoids.string_monoid_element import StringMonoidElement -from sage.modules.free_module import FreeModule + +lazy_import('sage.modules.free_module', 'FreeModule') + +from .cipher import SymmetricKeyCipher class AffineCipher(SymmetricKeyCipher): @@ -152,6 +156,7 @@ def _repr_(self): # as the alphabet used for the plaintext and ciphertext spaces. return "Affine cipher on %s" % self.parent().cipher_domain() + class HillCipher(SymmetricKeyCipher): """ Hill cipher class @@ -164,13 +169,12 @@ def __init__(self, parent, key): EXAMPLES:: + sage: # needs sage.modules sage: S = AlphabeticStrings() - sage: E = HillCryptosystem(S,3) - sage: E + sage: E = HillCryptosystem(S,3); E Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3 sage: M = E.key_space() - sage: A = M([[1,0,1],[0,1,1],[2,2,3]]) - sage: A + sage: A = M([[1,0,1],[0,1,1],[2,2,3]]); A [1 0 1] [0 1 1] [2 2 3] @@ -182,8 +186,8 @@ def __init__(self, parent, key): TESTS:: sage: S = AlphabeticStrings() - sage: E = HillCryptosystem(S,3) - sage: E == loads(dumps(E)) + sage: E = HillCryptosystem(S,3) # needs sage.modules + sage: E == loads(dumps(E)) # needs sage.modules True """ # TODO: some type checking that the key is an invertible matrix? @@ -216,6 +220,7 @@ def _repr_(self): EXAMPLES:: + sage: # needs sage.modules sage: H = HillCryptosystem(AlphabeticStrings(), 3) sage: M = MatrixSpace(IntegerModRing(26), 3, 3) sage: A = M([[1,0,1], [0,1,1], [2,2,3]]) @@ -466,12 +471,12 @@ def __init__(self, parent, key): EXAMPLES:: + sage: # needs sage.groups sage: S = AlphabeticStrings() - sage: E = TranspositionCryptosystem(S,14) - sage: E - Transposition cryptosystem on Free alphabetic string monoid on A-Z of block length 14 - sage: K = [ 14-i for i in range(14) ] - sage: K + sage: E = TranspositionCryptosystem(S,14); E + Transposition cryptosystem on + Free alphabetic string monoid on A-Z of block length 14 + sage: K = [ 14-i for i in range(14) ]; K [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] sage: e = E(K) sage: m = S("THECATINTHEHAT") @@ -480,11 +485,11 @@ def __init__(self, parent, key): EXAMPLES:: + sage: # needs sage.groups sage: S = AlphabeticStrings() sage: E = TranspositionCryptosystem(S,15) sage: m = S("THECATANDTHEHAT") - sage: G = E.key_space() - sage: G + sage: G = E.key_space(); G Symmetric group of order 15! as a permutation group sage: g = G([ 3, 2, 1, 6, 5, 4, 9, 8, 7, 12, 11, 10, 15, 14, 13 ]) sage: e = E(g) @@ -494,8 +499,8 @@ def __init__(self, parent, key): TESTS:: sage: S = AlphabeticStrings() - sage: E = TranspositionCryptosystem(S,14) - sage: E == loads(dumps(E)) + sage: E = TranspositionCryptosystem(S,14) # needs sage.groups + sage: E == loads(dumps(E)) # needs sage.groups True """ n = parent.block_length() diff --git a/src/sage/crypto/cryptosystem.py b/src/sage/crypto/cryptosystem.py index 7ea7884a834..33cc87cf08e 100644 --- a/src/sage/crypto/cryptosystem.py +++ b/src/sage/crypto/cryptosystem.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat r""" Cryptosystems @@ -96,7 +97,7 @@ class Cryptosystem(parent_old.Parent, Set_generic): Substitution cryptosystem on Free hexadecimal string monoid sage: HillCryptosystem(BinaryStrings(), 3) Hill cryptosystem on Free binary string monoid of block length 3 - sage: TranspositionCryptosystem(OctalStrings(), 5) + sage: TranspositionCryptosystem(OctalStrings(), 5) # needs sage.groups Transposition cryptosystem on Free octal string monoid of block length 5 sage: VigenereCryptosystem(Radix64Strings(), 7) Vigenere cryptosystem on Free radix 64 string monoid of period 7 @@ -129,7 +130,7 @@ def __init__(self, plaintext_space, ciphertext_space, key_space, Substitution cryptosystem on Free hexadecimal string monoid sage: HillCryptosystem(BinaryStrings(), 3) Hill cryptosystem on Free binary string monoid of block length 3 - sage: TranspositionCryptosystem(OctalStrings(), 5) + sage: TranspositionCryptosystem(OctalStrings(), 5) # needs sage.groups Transposition cryptosystem on Free octal string monoid of block length 5 sage: VigenereCryptosystem(Radix64Strings(), 7) Vigenere cryptosystem on Free radix 64 string monoid of period 7 @@ -172,9 +173,9 @@ def __eq__(self, right): sage: hill2 = HillCryptosystem(AlphabeticStrings(), 4) sage: hill1 == hill2 True - sage: tran1 = TranspositionCryptosystem(HexadecimalStrings(), 5) - sage: tran2 = TranspositionCryptosystem(HexadecimalStrings(), 5) - sage: tran1 == tran2 + sage: tran1 = TranspositionCryptosystem(HexadecimalStrings(), 5) # needs sage.groups + sage: tran2 = TranspositionCryptosystem(HexadecimalStrings(), 5) # needs sage.groups + sage: tran1 == tran2 # needs sage.groups True sage: vig1 = VigenereCryptosystem(AlphabeticStrings(), 7) sage: vig2 = VigenereCryptosystem(AlphabeticStrings(), 7) @@ -195,9 +196,9 @@ def __eq__(self, right): sage: hill2 = HillCryptosystem(Radix64Strings(), 5) sage: hill1 == hill2 False - sage: tran1 = TranspositionCryptosystem(Radix64Strings(), 3) - sage: tran2 = TranspositionCryptosystem(HexadecimalStrings(), 3) - sage: tran1 == tran2 + sage: tran1 = TranspositionCryptosystem(Radix64Strings(), 3) # needs sage.groups + sage: tran2 = TranspositionCryptosystem(HexadecimalStrings(), 3) # needs sage.groups + sage: tran1 == tran2 # needs sage.groups False sage: vig1 = VigenereCryptosystem(AlphabeticStrings(), 7) sage: vig2 = VigenereCryptosystem(Radix64Strings(), 7) @@ -225,7 +226,7 @@ def plaintext_space(self): Free hexadecimal string monoid sage: HillCryptosystem(BinaryStrings(), 3).plaintext_space() Free binary string monoid - sage: TranspositionCryptosystem(OctalStrings(), 5).plaintext_space() + sage: TranspositionCryptosystem(OctalStrings(), 5).plaintext_space() # needs sage.groups Free octal string monoid sage: VigenereCryptosystem(Radix64Strings(), 7).plaintext_space() Free radix 64 string monoid @@ -248,7 +249,7 @@ def cipher_domain(self): Free hexadecimal string monoid sage: HillCryptosystem(BinaryStrings(), 3).cipher_domain() Free binary string monoid - sage: TranspositionCryptosystem(OctalStrings(), 5).cipher_domain() + sage: TranspositionCryptosystem(OctalStrings(), 5).cipher_domain() # needs sage.groups Free octal string monoid sage: VigenereCryptosystem(Radix64Strings(), 7).cipher_domain() Free radix 64 string monoid @@ -269,7 +270,7 @@ def ciphertext_space(self): Free hexadecimal string monoid sage: HillCryptosystem(BinaryStrings(), 3).ciphertext_space() Free binary string monoid - sage: TranspositionCryptosystem(OctalStrings(), 5).ciphertext_space() + sage: TranspositionCryptosystem(OctalStrings(), 5).ciphertext_space() # needs sage.groups Free octal string monoid sage: VigenereCryptosystem(Radix64Strings(), 7).ciphertext_space() Free radix 64 string monoid @@ -292,7 +293,7 @@ def cipher_codomain(self): Free hexadecimal string monoid sage: HillCryptosystem(BinaryStrings(), 3).cipher_codomain() Free binary string monoid - sage: TranspositionCryptosystem(OctalStrings(), 5).cipher_codomain() + sage: TranspositionCryptosystem(OctalStrings(), 5).cipher_codomain() # needs sage.groups Free octal string monoid sage: VigenereCryptosystem(Radix64Strings(), 7).cipher_codomain() Free radix 64 string monoid @@ -313,7 +314,7 @@ def key_space(self): Free hexadecimal string monoid sage: HillCryptosystem(BinaryStrings(), 3).key_space() Full MatrixSpace of 3 by 3 dense matrices over Ring of integers modulo 2 - sage: TranspositionCryptosystem(OctalStrings(), 5).key_space() + sage: TranspositionCryptosystem(OctalStrings(), 5).key_space() # needs sage.groups Symmetric group of order 5! as a permutation group sage: VigenereCryptosystem(Radix64Strings(), 7).key_space() Free radix 64 string monoid @@ -335,7 +336,7 @@ def block_length(self): 1 sage: HillCryptosystem(BinaryStrings(), 3).block_length() 3 - sage: TranspositionCryptosystem(OctalStrings(), 5).block_length() + sage: TranspositionCryptosystem(OctalStrings(), 5).block_length() # needs sage.groups 5 sage: VigenereCryptosystem(Radix64Strings(), 7).block_length() 1 diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index d7ee358b9a9..e6ff758e3df 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules """ Hard Lattice Generator @@ -35,27 +36,36 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, INPUT: - ``type`` -- one of the following strings - - ``'modular'`` (default) -- A class of lattices for which - asymptotic worst-case to average-case connections hold. For - more refer to [Aj1996]_. - - ``'random'`` -- Special case of modular (n=1). A dense class - of lattice used for testing basis reduction algorithms - proposed by Goldstein and Mayer [GM2002]_. - - ``'ideal'`` -- Special case of modular. Allows for a more - compact representation proposed by [LM2006]_. - - ``'cyclotomic'`` -- Special case of ideal. Allows for - efficient processing proposed by [LM2006]_. - - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. + + - ``'modular'`` (default) -- A class of lattices for which + asymptotic worst-case to average-case connections hold. For + more refer to [Aj1996]_. + - ``'random'`` -- Special case of modular (n=1). A dense class + of lattice used for testing basis reduction algorithms + proposed by Goldstein and Mayer [GM2002]_. + - ``'ideal'`` -- Special case of modular. Allows for a more + compact representation proposed by [LM2006]_. + - ``'cyclotomic'`` -- Special case of ideal. Allows for + efficient processing proposed by [LM2006]_. + + - ``n`` -- Determinant size, primal: `det(L) = q^n`, dual: `det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. + - ``m`` -- Lattice dimension, `L \subseteq Z^m`. + - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. + - ``seed`` -- Randomness seed. - - ``quotient`` -- For the type ideal, this determines the quotient + + - ``quotient`` -- For the type ``'ideal'``, this determines the quotient polynomial. Ignored for all other types. + - ``dual`` -- Set this flag if you want a basis for `q-dual(L)`, for example for Regev's LWE bases [Reg2005]_. + - ``ntl`` -- Set this flag if you want the lattice basis in NTL readable format. + - ``lattice`` -- Set this flag if you want a :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. @@ -95,9 +105,9 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] - Ideal bases with quotient x^n-1, m=2*n are NTRU bases:: + Ideal bases with quotient `x^n-1`, `m=2*n` are NTRU bases:: - sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) + sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4 - 1) # needs sage.symbolic [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] @@ -110,7 +120,7 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, Ideal bases also work with polynomials:: sage: R. = PolynomialRing(ZZ) - sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=t^4-1) + sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=t^4 - 1) # needs sage.libs.pari [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] @@ -122,7 +132,7 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, Cyclotomic bases with n=2^k are SWIFFT bases:: - sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) + sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) # needs sage.libs.pari [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] @@ -149,9 +159,9 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, Relation of primal and dual bases:: - sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) - sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) - sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) + sage: B_primal = sage.crypto.gen_lattice(m=10, q=11, seed=42) + sage: B_dual = sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) + sage: B_dual_alt = transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True @@ -159,11 +169,11 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, Test some bad quotient polynomials:: - sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=cos(x)) + sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=cos(x)) # needs sage.symbolic Traceback (most recent call last): ... TypeError: self must be a numeric expression - sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=x^23-1) + sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=x^23-1) # needs sage.symbolic Traceback (most recent call last): ... ValueError: ideal basis requires n = quotient.degree() diff --git a/src/sage/crypto/lfsr.py b/src/sage/crypto/lfsr.py index b8aad014e27..869bdcf5990 100644 --- a/src/sage/crypto/lfsr.py +++ b/src/sage/crypto/lfsr.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.finite_rings r""" Linear feedback shift register (LFSR) sequence commands diff --git a/src/sage/crypto/lwe.py b/src/sage/crypto/lwe.py index e6928d00213..0be1b92d77e 100644 --- a/src/sage/crypto/lwe.py +++ b/src/sage/crypto/lwe.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# sage.doctest: needs scipy sage.symbolic """ (Ring-)LWE oracle generators @@ -52,6 +52,7 @@ Note that Ring-LWE samples are returned as vectors:: + sage: # needs sage.libs.pari sage: from sage.crypto.lwe import RingLWE sage: from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], euler_phi(16), 5) @@ -296,8 +297,8 @@ def __init__(self, n, q, D, secret_dist='uniform', m=None): fix the representation and recover the correct standard deviation of the noise:: - sage: from numpy import std - sage: while abs(std([e if e <= 200 else e-401 for e in S()]) - 3.0) > 0.01: + sage: from numpy import std # needs numpy + sage: while abs(std([e if e <= 200 else e-401 for e in S()]) - 3.0) > 0.01: # needs numpy ....: L = [] # reset L to avoid quadratic behaviour ....: add_samples() @@ -306,7 +307,7 @@ def __init__(self, n, q, D, secret_dist='uniform', m=None): sage: from sage.crypto.lwe import LWE sage: lwe = LWE(n=20, q=next_prime(400), D=D, m=30) sage: _ = [lwe() for _ in range(30)] - sage: lwe() # 31 + sage: lwe() # 31 Traceback (most recent call last): ... IndexError: Number of available samples exhausted. @@ -539,8 +540,8 @@ def __init__(self, N, q, D, poly=None, secret_dist='uniform', m=None): sage: from sage.crypto.lwe import RingLWE sage: from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler - sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n=euler_phi(20), sigma=3.0) - sage: RingLWE(N=20, q=next_prime(800), D=D) + sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n=euler_phi(20), sigma=3.0) # needs sage.libs.pari + sage: RingLWE(N=20, q=next_prime(800), D=D) # needs sage.libs.pari RingLWE(20, 809, Discrete Gaussian sampler for polynomials of degree < 8 with σ=3.000000 in each component, x^8 - x^6 + x^4 - x^2 + 1, 'uniform', None) """ self.N = ZZ(N) @@ -587,6 +588,7 @@ def __call__(self): """ EXAMPLES:: + sage: # needs sage.libs.pari sage: from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE sage: N = 16 sage: n = euler_phi(N) diff --git a/src/sage/crypto/mq/mpolynomialsystemgenerator.py b/src/sage/crypto/mq/mpolynomialsystemgenerator.py index 3c0bb6b349c..9028dab1d98 100644 --- a/src/sage/crypto/mq/mpolynomialsystemgenerator.py +++ b/src/sage/crypto/mq/mpolynomialsystemgenerator.py @@ -68,7 +68,7 @@ def varstrs(self, name, round): sage: from sage.crypto.mq.mpolynomialsystemgenerator import MPolynomialSystemGenerator sage: msg = MPolynomialSystemGenerator() - sage: msg.varstrs('K', i) + sage: msg.varstrs('K', i) # needs sage.all Traceback (most recent call last): ... NotImplementedError diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index dbf2e0dd5a0..ba24ed0627e 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" Rijndael-GF @@ -80,7 +81,7 @@ '3902dc1925dc116a8409850b1dfb9732'. We can use this example to demonstrate the correctness of this implementation:: - sage: rgf = RijndaelGF(4, 4) # change dimensions for this example + sage: rgf = RijndaelGF(4, 4) # change dimensions for this example sage: plain = '3243f6a8885a308d313198a2e0370734' sage: key = '2b7e151628aed2a6abf7158809cf4f3c' sage: expected_ciphertext = '3925841d02dc09fbdc118597196a0b32' @@ -160,8 +161,7 @@ finally perform the inversion step after the affine transformation polynomial has been evaluated. :: - sage: inv_affine = sb_pc(1, 2, algorithm='decrypt', - ....: no_inversion=True) + sage: inv_affine = sb_pc(1, 2, algorithm='decrypt', no_inversion=True) sage: state = rgf._hex_to_GF('ff87968431d86a51645151fa773ad009') sage: evaluated = inv_affine(state.list()) sage: result = evaluated * -1 @@ -252,7 +252,7 @@ ``apply_poly`` a dictionary mapping keywords to their values. :: sage: rgf.apply_poly(rgf.state_vrs, rgf.add_round_key_poly_constr(), - ....: poly_constr_attr={'round' : 5}) + ....: poly_constr_attr={'round': 5}) [a00 + k500 a01 + k501 a02 + k502 a03 + k503] [a10 + k510 a11 + k511 a12 + k512 a13 + k513] [a20 + k520 a21 + k521 a22 + k522 a23 + k523] @@ -269,10 +269,11 @@ when passed an index ``i,j`` will return `g(f(A))_{i,j}` in terms of the entries of `A`. :: + sage: # needs sage.libs.gap sage: rcpc = rgf.compose(rgf.shift_rows_poly_constr(), - ....: rgf.mix_columns_poly_constr()) - sage: rcpc - A polynomial constructor of a round component of Rijndael-GF block cipher with block length 4, key length 6, and 12 rounds. + ....: rgf.mix_columns_poly_constr()); rcpc + A polynomial constructor of a round component of Rijndael-GF block cipher + with block length 4, key length 6, and 12 rounds. sage: rcpc(2, 1) a01 + a12 + x*a23 + (x + 1)*a30 @@ -284,7 +285,7 @@ True sage: rcpc = rgf.compose(rgf.mix_columns_poly_constr(), - ....: rgf.shift_rows_poly_constr()) + ....: rgf.shift_rows_poly_constr()) sage: result = rgf.apply_poly(state, rcpc, algorithm='decrypt') sage: new_state = rgf.mix_columns(state, algorithm='decrypt') sage: new_state = rgf.shift_rows(new_state, algorithm='decrypt') @@ -301,8 +302,7 @@ ``compose`` will return a polynomial representing `g(f(A))_{i,j}` in terms of the entries of `A`. :: - sage: poly = rgf.mix_columns_poly_constr()(0, 3) - sage: poly + sage: poly = rgf.mix_columns_poly_constr()(0, 3); poly x*a03 + (x + 1)*a13 + a23 + a33 sage: rgf.compose(rgf.sub_bytes_poly_constr(), poly) (x^3 + x)*a03^254 + @@ -347,16 +347,18 @@ returned ``Round_Component_Poly_Constr`` object's ``__call__`` method must have its own ``algorithm`` keyword defaulted to 'encrypt'. :: + sage: # needs sage.libs.gap sage: poly = rgf.shift_rows_poly_constr()(2, 1) sage: rgf.compose(rgf.mix_columns_poly_constr(), poly, algorithm='decrypt') (x^3 + x^2 + 1)*a03 + (x^3 + 1)*a13 + (x^3 + x^2 + x)*a23 + (x^3 + x + 1)*a33 sage: state = rgf._hex_to_GF('80121e0776fd1d8a8d8c31bc965d1fee') sage: with_decrypt = rgf.compose(rgf.sub_bytes_poly_constr(), - ....: rgf.shift_rows_poly_constr(), algorithm='decrypt') + ....: rgf.shift_rows_poly_constr(), + ....: algorithm='decrypt') sage: result_wd = rgf.apply_poly(state, with_decrypt) sage: no_decrypt = rgf.compose(rgf.sub_bytes_poly_constr(), - ....: rgf.shift_rows_poly_constr()) + ....: rgf.shift_rows_poly_constr()) sage: result_nd = rgf.apply_poly(state, no_decrypt) sage: result_wd == result_nd True @@ -365,10 +367,10 @@ ``compose`` to make ``f`` and ``g`` use those keywords during polynomial creation. :: - sage: rcpc = rgf.compose(rgf.add_round_key_poly_constr(), - ....: rgf.add_round_key_poly_constr(), - ....: f_attr={'round' : 4}, g_attr={'round' : 7}) - sage: rcpc(1, 2) + sage: rcpc = rgf.compose(rgf.add_round_key_poly_constr(), # needs sage.libs.gap + ....: rgf.add_round_key_poly_constr(), + ....: f_attr={'round': 4}, g_attr={'round': 7}) + sage: rcpc(1, 2) # needs sage.libs.gap a12 + k412 + k712 In addition to building polynomial representations of state matrices, we can @@ -1118,14 +1120,16 @@ def _check_valid_PRmatrix(self, PRm, keyword): sage: rgf._check_valid_PRmatrix(5, 'state') Traceback (most recent call last): ... - TypeError: keyword 'state' must be a 4 x 4 matrix with entries from a multivariate PolynomialRing over Finite Field in x of size 2^8 + TypeError: keyword 'state' must be a 4 x 4 matrix with entries from + a multivariate PolynomialRing over Finite Field in x of size 2^8 sage: entries = [rgf._F.random_element() for i in range(24)] sage: wrong_dimensions = matrix(4, 6, entries) sage: rgf._check_valid_PRmatrix(wrong_dimensions, 'state') Traceback (most recent call last): ... - TypeError: keyword 'state' must be a 4 x 4 matrix with entries from a multivariate PolynomialRing over Finite Field in x of size 2^8 + TypeError: keyword 'state' must be a 4 x 4 matrix with entries from + a multivariate PolynomialRing over Finite Field in x of size 2^8 sage: F. = GF(3^4) sage: entries = [F.random_element() for i in range(16)] @@ -1133,7 +1137,8 @@ def _check_valid_PRmatrix(self, PRm, keyword): sage: rgf._check_valid_PRmatrix(wrong_base, 'state') Traceback (most recent call last): ... - TypeError: keyword 'state' must be a 4 x 4 matrix with entries from a multivariate PolynomialRing over Finite Field in x of size 2^8 + TypeError: keyword 'state' must be a 4 x 4 matrix with entries from + a multivariate PolynomialRing over Finite Field in x of size 2^8 """ from sage.rings.polynomial.multi_polynomial_ring_base import \ MPolynomialRing_base @@ -1265,7 +1270,8 @@ def expand_key_poly(self, row, col, round): sage: rgf.compose(rgf.sub_bytes_poly_constr(), rgf.expand_key_poly) Traceback (most recent call last): ... - TypeError: keyword 'g' must be a Round_Component_Poly_Constr or a polynomial over Finite Field in x of size 2^8 + TypeError: keyword 'g' must be a Round_Component_Poly_Constr or + a polynomial over Finite Field in x of size 2^8 sage: state = rgf._hex_to_GF('00000000000000000000000000000000') sage: rgf.apply_poly(state, rgf.expand_key_poly) @@ -1358,7 +1364,7 @@ def apply_poly(self, state, poly_constr, algorithm='encrypt', keys=None, sage: state = rgf._hex_to_GF('4915598f55e5d7a0daca94fa1f0a63f7') sage: apply_poly_result = rgf.apply_poly(state, - ....: rgf.sub_bytes_poly_constr()) + ....: rgf.sub_bytes_poly_constr()) sage: direct_result = rgf.sub_bytes(state) sage: direct_result == apply_poly_result True @@ -1373,7 +1379,8 @@ def apply_poly(self, state, poly_constr, algorithm='encrypt', keys=None, sage: key = rgf._hex_to_GF('54d990a16ba09ab596bbf40ea111702f') sage: keys = rgf.expand_key(key) sage: result = rgf.apply_poly(state, - ....: rgf.add_round_key_poly_constr(), keys=keys) + ....: rgf.add_round_key_poly_constr(), + ....: keys=keys) sage: result == rgf.add_round_key(state, key) True @@ -1385,8 +1392,8 @@ def apply_poly(self, state, poly_constr, algorithm='encrypt', keys=None, dictionary ``poly_constr_attr`` mapping keywords to their values. :: sage: rgf.apply_poly(rgf.state_vrs, - ....: rgf.add_round_key_poly_constr(), - ....: poly_constr_attr={'round' : 5}) + ....: rgf.add_round_key_poly_constr(), + ....: poly_constr_attr={'round': 5}) [a00 + k500 a01 + k501 a02 + k502 a03 + k503] [a10 + k510 a11 + k511 a12 + k512 a13 + k513] [a20 + k520 a21 + k521 a22 + k522 a23 + k523] @@ -1499,9 +1506,9 @@ def compose(self, f, g, algorithm='encrypt', f_attr=None, g_attr=None): ``Round_Component_Poly_Constr`` object corresponding to the composition of multiple round functions as such:: - sage: fn = rgf.compose(rgf.shift_rows_poly_constr(), - ....: rgf.mix_columns_poly_constr()) - sage: fn(1, 3) + sage: fn = rgf.compose(rgf.shift_rows_poly_constr(), # needs sage.libs.gap + ....: rgf.mix_columns_poly_constr()) + sage: fn(1, 3) # needs sage.libs.gap a03 + x*a10 + (x + 1)*a21 + a32 If we use ``compose`` to make a new ``Round_Component_Poly_Constr`` @@ -1509,7 +1516,7 @@ def compose(self, f, g, algorithm='encrypt', f_attr=None, g_attr=None): ``compose``:: sage: state = rgf._hex_to_GF('36400926f9336d2d9fb59d23c42c3950') - sage: result = rgf.apply_poly(state, fn) + sage: result = rgf.apply_poly(state, fn) # needs sage.libs.gap sage: rgf._GF_to_hex(result) 'f4bcd45432e554d075f1d6c51dd03b3c' @@ -1520,7 +1527,7 @@ def compose(self, f, g, algorithm='encrypt', f_attr=None, g_attr=None): :: - sage: fn2 = rgf.compose(rgf.sub_bytes_poly_constr(), fn) + sage: fn2 = rgf.compose(rgf.sub_bytes_poly_constr(), fn) # needs sage.libs.gap If the second argument is a polynomial, then the value of ``algorithm`` is passed directly to the first argument `f` during evaluation. @@ -1528,21 +1535,22 @@ def compose(self, f, g, algorithm='encrypt', f_attr=None, g_attr=None): object, changing ``algorithm`` does nothing since the returned object has its own ``algorithm='encrypt'`` keyword. :: - sage: f = rgf.compose(rgf.sub_bytes_poly_constr(), - ....: rgf.mix_columns_poly_constr(), algorithm='decrypt') - sage: g = rgf.compose(rgf.sub_bytes_poly_constr(), - ....: rgf.mix_columns_poly_constr()) - sage: all(f(i,j) == g(i,j) for i in range(4) for j in range(4)) + sage: f = rgf.compose(rgf.sub_bytes_poly_constr(), # needs sage.libs.gap + ....: rgf.mix_columns_poly_constr(), + ....: algorithm='decrypt') + sage: g = rgf.compose(rgf.sub_bytes_poly_constr(), # needs sage.libs.gap + ....: rgf.mix_columns_poly_constr()) + sage: all(f(i,j) == g(i,j) for i in range(4) for j in range(4)) # needs sage.libs.gap True We can change the keyword attributes of the ``__call__`` methods of ``f`` and ``g`` by passing dictionaries ``f_attr`` and ``g_attr`` to ``compose``. :: - sage: fn = rgf.compose(rgf.add_round_key_poly_constr(), - ....: rgf.add_round_key_poly_constr(), - ....: f_attr={'round' : 4}, g_attr={'round' : 7}) - sage: fn(1, 2) + sage: fn = rgf.compose(rgf.add_round_key_poly_constr(), # needs sage.libs.gap + ....: rgf.add_round_key_poly_constr(), + ....: f_attr={'round': 4}, g_attr={'round': 7}) + sage: fn(1, 2) # needs sage.libs.gap a12 + k412 + k712 """ if not isinstance(f, RijndaelGF.Round_Component_Poly_Constr): @@ -1572,10 +1580,10 @@ def compose(self, f, g, algorithm='encrypt', f_attr=None, g_attr=None): return g(f_vals + self.subkey_vrs_list) else: if isinstance(g_attr, dict): - lm = lambda i, j, alg='encrypt' : \ + lm = lambda i, j, alg='encrypt': \ self.compose(f, g(i, j, alg, **g_attr), alg, f_attr, g_attr) else: - lm = lambda i, j, alg='encrypt' : \ + lm = lambda i, j, alg='encrypt': \ self.compose(f, g(i, j, alg), alg, f_attr, g_attr) return RijndaelGF.Round_Component_Poly_Constr(lm, self) @@ -1590,7 +1598,8 @@ def add_round_key_poly_constr(self): sage: rgf = RijndaelGF(4, 4) sage: ark_pc = rgf.add_round_key_poly_constr() sage: ark_pc - A polynomial constructor for the function 'Add Round Key' of Rijndael-GF block cipher with block length 4, key length 4, and 10 rounds. + A polynomial constructor for the function 'Add Round Key' of Rijndael-GF + block cipher with block length 4, key length 4, and 10 rounds. sage: ark_pc(0, 1) a01 + k001 @@ -1616,7 +1625,7 @@ def add_round_key_poly_constr(self): value. :: sage: rgf.apply_poly(rgf.state_vrs, ark_pc, - ....: poly_constr_attr={'round' : 6}) + ....: poly_constr_attr={'round': 6}) [a00 + k600 a01 + k601 a02 + k602 a03 + k603] [a10 + k610 a11 + k611 a12 + k612 a13 + k613] [a20 + k620 a21 + k621 a22 + k622 a23 + k623] @@ -1624,9 +1633,9 @@ def add_round_key_poly_constr(self): :: - sage: rcpc = rgf.compose(ark_pc, ark_pc, - ....: f_attr={'round' : 3}, g_attr={'round' : 5}) - sage: rcpc(3, 1) + sage: rcpc = rgf.compose(ark_pc, ark_pc, # needs sage.libs.gap + ....: f_attr={'round': 3}, g_attr={'round': 5}) + sage: rcpc(3, 1) # needs sage.libs.gap a31 + k331 + k531 """ return self._add_round_key_rcpc @@ -1666,10 +1675,8 @@ def _add_round_key_pc(self, row, col, algorithm='encrypt', round=0): As expected, since the encryption and decryption transformations are identical, changing ``algorithm`` has no effect. - sage: with_encrypt = rgf._add_round_key_pc(3, 2, - ....: 'encrypt') - sage: with_decrypt = rgf._add_round_key_pc(3, 2, - ....: 'decrypt') + sage: with_encrypt = rgf._add_round_key_pc(3, 2, 'encrypt') + sage: with_decrypt = rgf._add_round_key_pc(3, 2, 'decrypt') sage: with_encrypt == with_decrypt True """ @@ -1722,9 +1729,9 @@ def sub_bytes_poly_constr(self): sage: from sage.crypto.mq.rijndael_gf import RijndaelGF sage: rgf = RijndaelGF(4, 4) - sage: sb_pc = rgf.sub_bytes_poly_constr() - sage: sb_pc - A polynomial constructor for the function 'SubBytes' of Rijndael-GF block cipher with block length 4, key length 4, and 10 rounds. + sage: sb_pc = rgf.sub_bytes_poly_constr(); sb_pc + A polynomial constructor for the function 'SubBytes' of Rijndael-GF + block cipher with block length 4, key length 4, and 10 rounds. sage: sb_pc(2, 3) (x^2 + 1)*a23^254 + (x^3 + 1)*a23^253 + @@ -1772,17 +1779,17 @@ def sub_bytes_poly_constr(self): When passing the returned object to ``apply_poly`` and ``compose``, we can make those methods change the keyword ``no_inversion`` of this object's ``__call__`` method by passing the dictionary - ``{'no_inversion' : True}`` to them. :: + ``{'no_inversion': True}`` to them. :: sage: result = rgf.apply_poly(state, sb_pc, - ....: poly_constr_attr={'no_inversion' : True}) + ....: poly_constr_attr={'no_inversion': True}) sage: rgf._GF_to_hex(result) '961c72894526f746aa85fc920adcc719' :: - sage: rcpc = rgf.compose(sb_pc, rgf.shift_rows_poly_constr(), - ....: f_attr={'no_inversion' : True}) + sage: rcpc = rgf.compose(sb_pc, rgf.shift_rows_poly_constr(), # needs sage.libs.gap + ....: f_attr={'no_inversion': True}) Note that if we set ``algorithm='decrypt'`` for ``apply_poly``, it will perform the necessary performance enhancement described above @@ -1847,7 +1854,7 @@ def _sub_bytes_pc(self, row, col, algorithm='encrypt', no_inversion=False): calculated. :: sage: poly = rgf._sub_bytes_pc(0, 0, - ....: algorithm='decrypt', no_inversion=True) + ....: algorithm='decrypt', no_inversion=True) sage: state = rgf._hex_to_GF('b415f8016858552e4bb6124c5f998a4c') sage: poly(state.list()) ^ -1 x^7 + x^6 + x^2 + x @@ -1955,7 +1962,8 @@ def mix_columns_poly_constr(self): sage: rgf = RijndaelGF(4, 4) sage: mc_pc = rgf.mix_columns_poly_constr() sage: mc_pc - A polynomial constructor for the function 'Mix Columns' of Rijndael-GF block cipher with block length 4, key length 4, and 10 rounds. + A polynomial constructor for the function 'Mix Columns' of Rijndael-GF + block cipher with block length 4, key length 4, and 10 rounds. sage: mc_pc(1, 2) a02 + x*a12 + (x + 1)*a22 + a32 sage: mc_pc(1, 0, algorithm='decrypt') @@ -2180,13 +2188,14 @@ def __init__(self, polynomial_constr, rgf, round_component_name=None): EXAMPLES:: - sage: from sage.crypto.mq.rijndael_gf import \ - ....: RijndaelGF + sage: from sage.crypto.mq.rijndael_gf import RijndaelGF sage: rgf = RijndaelGF(4, 4) sage: rcpc = RijndaelGF.Round_Component_Poly_Constr( - ....: rgf._shift_rows_pc, rgf, "Shift Rows") + ....: rgf._shift_rows_pc, rgf, "Shift Rows") sage: rcpc - A polynomial constructor for the function 'Shift Rows' of Rijndael-GF block cipher with block length 4, key length 4, and 10 rounds. + A polynomial constructor for the function 'Shift Rows' of + Rijndael-GF block cipher with block length 4, key length 4, + and 10 rounds. If `\phi` is the round component function to which this object corresponds to, then ``__call__(i,j)`` `= \phi(A)_{i,j}`, where @@ -2194,7 +2203,7 @@ def __init__(self, polynomial_constr, rgf, round_component_name=None): by ``__call__(i,j)`` will be in terms of the entries of `A`. :: sage: rcpc = RijndaelGF.Round_Component_Poly_Constr( - ....: rgf._mix_columns_pc, rgf, "Mix Columns") + ....: rgf._mix_columns_pc, rgf, "Mix Columns") sage: poly = rcpc(1, 2); poly a02 + x*a12 + (x + 1)*a22 + a32 sage: state = rgf._hex_to_GF('d1876c0f79c4300ab45594add66ff41f') @@ -2209,7 +2218,7 @@ def __init__(self, polynomial_constr, rgf, round_component_name=None): ``Round_Component_Poly_Constr`` object will act similarly. :: sage: all(rgf._mix_columns_pc(i, j) == rcpc(i, j) - ....: for i in range(4) for j in range(4)) + ....: for i in range(4) for j in range(4)) True Since all keyword arguments of ``polynomial_constr`` must have a @@ -2224,8 +2233,8 @@ def __init__(self, polynomial_constr, rgf, round_component_name=None): ``compose``. :: sage: rgf.apply_poly(rgf.state_vrs, - ....: rgf.add_round_key_poly_constr(), - ....: poly_constr_attr={'round' : 9}) + ....: rgf.add_round_key_poly_constr(), + ....: poly_constr_attr={'round': 9}) [a00 + k900 a01 + k901 a02 + k902 a03 + k903] [a10 + k910 a11 + k911 a12 + k912 a13 + k913] [a20 + k920 a21 + k921 a22 + k922 a23 + k923] @@ -2233,10 +2242,10 @@ def __init__(self, polynomial_constr, rgf, round_component_name=None): :: - sage: fn = rgf.compose(rgf.add_round_key_poly_constr(), - ....: rgf.add_round_key_poly_constr(), - ....: f_attr={'round' : 3}, g_attr={'round' : 7}) - sage: fn(2, 3) + sage: fn = rgf.compose(rgf.add_round_key_poly_constr(), # needs sage.libs.gap + ....: rgf.add_round_key_poly_constr(), + ....: f_attr={'round': 3}, g_attr={'round': 7}) + sage: fn(2, 3) # needs sage.libs.gap a23 + k323 + k723 Because all ``Round_Component_Poly_Constr`` objects are callable @@ -2245,9 +2254,9 @@ def __init__(self, polynomial_constr, rgf, round_component_name=None): keywords, however, must be checked in ``polynomial_constr``. :: sage: def my_poly_constr(row, col, algorithm='encrypt'): - ....: return x * rgf._F.one() # example body with no checks + ....: return x * rgf._F.one() # example body with no checks sage: rcpc = RijndaelGF.Round_Component_Poly_Constr( - ....: my_poly_constr, rgf, "My Poly Constr") + ....: my_poly_constr, rgf, "My Poly Constr") sage: rcpc(-1, 2) Traceback (most recent call last): ... @@ -2303,15 +2312,14 @@ def __call__(self, row, col, algorithm='encrypt', **kwargs): EXAMPLES:: - sage: from sage.crypto.mq.rijndael_gf import \ - ....: RijndaelGF + sage: from sage.crypto.mq.rijndael_gf import RijndaelGF sage: rgf = RijndaelGF(4, 4) sage: rcpc = RijndaelGF.Round_Component_Poly_Constr( - ....: rgf._shift_rows_pc, rgf, "Shift Rows") + ....: rgf._shift_rows_pc, rgf, "Shift Rows") sage: rcpc(1, 2) a13 sage: all(rcpc(i, j) == rgf._shift_rows_pc(i, j) - ....: for i in range(4) for j in range(4)) + ....: for i in range(4) for j in range(4)) True """ if row not in range(4): @@ -2332,15 +2340,17 @@ def __repr__(self): EXAMPLES:: - sage: from sage.crypto.mq.rijndael_gf import \ - ....: RijndaelGF + sage: from sage.crypto.mq.rijndael_gf import RijndaelGF sage: rgf = RijndaelGF(4, 4) sage: RijndaelGF.Round_Component_Poly_Constr( - ....: rgf._shift_rows_pc, rgf, "Shift Rows") - A polynomial constructor for the function 'Shift Rows' of Rijndael-GF block cipher with block length 4, key length 4, and 10 rounds. + ....: rgf._shift_rows_pc, rgf, "Shift Rows") + A polynomial constructor for the function 'Shift Rows' of + Rijndael-GF block cipher with block length 4, key length 4, + and 10 rounds. sage: RijndaelGF.Round_Component_Poly_Constr( - ....: rgf._shift_rows_pc, rgf) - A polynomial constructor of a round component of Rijndael-GF block cipher with block length 4, key length 4, and 10 rounds. + ....: rgf._shift_rows_pc, rgf) + A polynomial constructor of a round component of Rijndael-GF + block cipher with block length 4, key length 4, and 10 rounds. """ if self._rc_name is None: msg = "A polynomial constructor of a round component of {0}" diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index ee3fce334a4..091ff4a96fa 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" Small Scale Variants of the AES (SR) Polynomial System Generator @@ -103,8 +104,8 @@ sage: a = K.gen() sage: K = [a] sage: P = [1] - sage: F,s = sr.polynomial_system(P=P, K=K) - sage: F.groebner_basis() + sage: F,s = sr.polynomial_system(P=P, K=K) # needs sage.rings.polynomial.pbori + sage: F.groebner_basis() # needs sage.rings.polynomial.pbori [k100, k101 + 1, k102, k103 + k003, x100 + 1, x101 + k003 + 1, x102 + k003 + 1, x103 + k003, w100, w101, w102 + 1, w103 + k003 + 1, @@ -123,8 +124,8 @@ All solutions can easily be recovered using the variety function for ideals.:: - sage: I = F.ideal() - sage: for V in I.variety(): + sage: I = F.ideal() # needs sage.rings.polynomial.pbori + sage: for V in I.variety(): # needs sage.rings.polynomial.pbori sage.symbolic ....: for k,v in sorted(V.items()): ....: print("{} {}".format(k, v)) ....: print("\n") @@ -173,7 +174,7 @@ We can also verify the correctness of the variety by evaluating all ideal generators on all points.:: - sage: for V in I.variety(): + sage: for V in I.variety(): # needs sage.rings.polynomial.pbori sage.symbolic ....: for f in I.gens(): ....: if f.subs(V) != 0: ....: print("epic fail") @@ -206,7 +207,7 @@ or use ``S`` to find alternative polynomial representations for the S-Box.:: - sage: S.polynomials(degree=3) + sage: S.polynomials(degree=3) # needs sage.libs.singular [x0*x1 + x1*x2 + x0*x3 + x0*y2 + x1 + y0 + y1 + 1, x0*x1 + x0*x2 + x0*y0 + x0*y1 + x0*y2 + x1 + x2 + y0 + y1 + y2, x0*x1 + x0*x2 + x0*x3 + x1*x3 + x0*y0 + x1*y0 + x0*y1 + x0*y3, @@ -1651,7 +1652,7 @@ def variable_dict(self): 'x103': x103} sage: sr = mq.SR(1,1,1,4,gf2=True) - sage: sr.variable_dict() + sage: sr.variable_dict() # needs sage.rings.polynomial.pbori {'k000': k000, 'k001': k001, 'k002': k002, @@ -2003,16 +2004,16 @@ def polynomial_system(self, P=None, K=None, C=None): sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=True) sage: P = sr.vector([0, 0, 1, 0]) sage: K = sr.vector([1, 0, 0, 1]) - sage: F, s = sr.polynomial_system(P, K) + sage: F, s = sr.polynomial_system(P, K) # needs sage.rings.polynomial.pbori This returns a polynomial system:: - sage: F + sage: F # needs sage.rings.polynomial.pbori Polynomial Sequence with 36 Polynomials in 20 Variables and a solution:: - sage: s # random -- maybe we need a better doctest here? + sage: s # random -- maybe we need a better doctest here? # needs sage.rings.polynomial.pbori {k000: 1, k001: 0, k003: 1, k002: 0} This solution is not the only solution that we can learn from the @@ -2020,24 +2021,25 @@ def polynomial_system(self, P=None, K=None, C=None): :: - sage: F.groebner_basis()[-3:] + sage: F.groebner_basis()[-3:] # needs sage.rings.polynomial.pbori [k000 + 1, k001, k003 + 1] In particular we have two solutions:: - sage: len(F.ideal().variety()) + sage: len(F.ideal().variety()) # needs sage.rings.polynomial.pbori 2 In the following example we provide ``C`` explicitly:: sage: C = sr(P,K) - sage: F,s = sr.polynomial_system(P=P, C=C) - sage: F + sage: F,s = sr.polynomial_system(P=P, C=C) # needs sage.rings.polynomial.pbori + sage: F # needs sage.rings.polynomial.pbori Polynomial Sequence with 36 Polynomials in 20 Variables Alternatively, we can use symbols for the ``P`` and ``C``. First, we have to create a polynomial ring:: + sage: # needs sage.rings.polynomial.pbori sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=True) sage: R = sr.R sage: vn = sr.varstrs("P",0,1,4) + R.variable_names() + sr.varstrs("C",0,1,4) @@ -2047,6 +2049,7 @@ def polynomial_system(self, P=None, K=None, C=None): Now, we can construct the purely symbolic equation system:: + sage: # needs sage.rings.polynomial.pbori sage: C = sr.vars("C",0); C (C000, C001, C002, C003) sage: P = sr.vars("P",0) @@ -2061,13 +2064,13 @@ def polynomial_system(self, P=None, K=None, C=None): We show that the (returned) key is a solution to the returned system:: sage: sr = mq.SR(3,4,4,8, star=True, gf2=True, polybori=True) - sage: while True: # workaround (see :trac:`31891`) + sage: while True: # workaround (see :trac:`31891`) # needs sage.rings.polynomial.pbori ....: try: ....: F, s = sr.polynomial_system() ....: break ....: except ZeroDivisionError: ....: pass - sage: F.subs(s).groebner_basis() # long time + sage: F.subs(s).groebner_basis() # long time # needs sage.rings.polynomial.pbori Polynomial Sequence with 1248 Polynomials in 1248 Variables """ plaintext = P @@ -2588,7 +2591,7 @@ def phi(self, l, diffusion_matrix=False): sage: sr = mq.SR(2, 1, 2, 4, gf2=True) sage: k = sr.base_ring() sage: A = matrix(k, 1, 2, [k.gen(), 0] ) - sage: sr.phi(A) + sage: sr.phi(A) # needs sage.libs.gap [0 0] [0 0] [1 0] @@ -2642,7 +2645,7 @@ def antiphi(self, l): sage: sr = mq.SR(gf2=True) sage: A = sr.random_state_array() - sage: sr.antiphi(sr.phi(A)) == A + sage: sr.antiphi(sr.phi(A)) == A # needs sage.libs.gap True """ e = self.e @@ -3145,9 +3148,9 @@ def inversion_polynomials(self, xi, wi, length): EXAMPLES:: sage: sr = mq.SR(1, 1, 1, 8, gf2=True) - sage: xi = sr.vars('x', 1) - sage: wi = sr.vars('w', 1) - sage: sr.inversion_polynomials(xi, wi, len(xi))[:3] + sage: xi = sr.vars('x', 1) # needs sage.rings.polynomial.pbori + sage: wi = sr.vars('w', 1) # needs sage.rings.polynomial.pbori + sage: sr.inversion_polynomials(xi, wi, len(xi))[:3] # needs sage.rings.polynomial.pbori [x100*w100 + x100*w102 + x100*w103 + x100*w107 + x101*w101 + x101*w102 + x101*w106 + x102*w100 + x102*w101 + x102*w105 + x103*w100 + x103*w104 + x104*w103 + x105*w102 + x106*w101 + x107*w100, x100*w101 + x100*w103 + x100*w104 + x101*w100 + x101*w102 + x101*w103 + x101*w107 + x102*w101 + x102*w102 + x102*w106 + x103*w100 + x103*w101 + x103*w105 + x104*w100 + x104*w104 + x105*w103 + x106*w102 + x107*w101, x100*w102 + x100*w104 + x100*w105 + x101*w101 + x101*w103 + x101*w104 + x102*w100 + x102*w102 + x102*w103 + x102*w107 + x103*w101 + x103*w102 + x103*w106 + x104*w100 + x104*w101 + x104*w105 + x105*w100 + x105*w104 + x106*w103 + x107*w102] @@ -3227,7 +3230,7 @@ def inversion_polynomials_single_sbox(self, x=None, w=None, biaffine_only=None, sage: sr = SR_gf2_2(1, 1, 1, e) sage: P = PolynomialRing(GF(2),['x%d'%i for i in range(e)] + ['w%d'%i for i in range(e)],order='lex') sage: X,W = P.gens()[:e],P.gens()[e:] - sage: sr.inversion_polynomials_single_sbox(X, W, groebner=True) + sage: sr.inversion_polynomials_single_sbox(X, W, groebner=True) # needs sage.libs.singular [x0 + w0*w1*w2 + w0*w1 + w0*w2 + w0*w3 + w0 + w1 + w2, x1 + w0*w1*w3 + w0*w3 + w0 + w1*w3 + w1 + w2*w3, x2 + w0*w2*w3 + w0*w2 + w0 + w1*w2 + w1*w3 + w2*w3, @@ -3236,7 +3239,7 @@ def inversion_polynomials_single_sbox(self, x=None, w=None, biaffine_only=None, sage: from sage.crypto.mq.sr import SR_gf2_2 sage: e = 4 sage: sr = SR_gf2_2(1, 1, 1, e) - sage: sr.inversion_polynomials_single_sbox() + sage: sr.inversion_polynomials_single_sbox() # needs sage.libs.singular [w3*w1 + w3*w0 + w3*x2 + w3*x1 + w3 + w2*w1 + w1 + x3 + x2 + x1, w3*w2 + w3*w1 + w3*x3 + w2 + w1 + x3, w3*w2 + w3*w1 + w3*x2 + w3 + w2*x3 + x2 + x1, @@ -3269,8 +3272,8 @@ def inversion_polynomials_single_sbox(self, x=None, w=None, biaffine_only=None, sage: from sage.crypto.mq.sr import SR_gf2_2 sage: e = 4 sage: sr = SR_gf2_2(1, 1, 1, e) - sage: l = sr.inversion_polynomials_single_sbox() - sage: l == sr.inversion_polynomials_single_sbox(biaffine_only=True, correct_only=False) + sage: l = sr.inversion_polynomials_single_sbox() # needs sage.libs.singular + sage: l == sr.inversion_polynomials_single_sbox(biaffine_only=True, correct_only=False) # needs sage.libs.singular True """ diff --git a/src/sage/crypto/public_key/blum_goldwasser.py b/src/sage/crypto/public_key/blum_goldwasser.py index 8f076a4f70d..1d090673400 100644 --- a/src/sage/crypto/public_key/blum_goldwasser.py +++ b/src/sage/crypto/public_key/blum_goldwasser.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat r""" Blum-Goldwasser Probabilistic Encryption @@ -88,6 +89,7 @@ class BlumGoldwasser(PublicKeyCryptosystem): The following encryption/decryption example is taken from Example 8.57, pages 309--310 of [MvOV1996]_:: + sage: # needs sage.symbolic sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser(); bg The Blum-Goldwasser public-key encryption scheme. @@ -111,6 +113,7 @@ class BlumGoldwasser(PublicKeyCryptosystem): private key. Finally, compare the decrypted message with the original plaintext. :: + sage: # needs sage.libs.pari sage.symbolic sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import bin_to_ascii sage: bg = BlumGoldwasser() @@ -124,10 +127,10 @@ class BlumGoldwasser(PublicKeyCryptosystem): If `(p, q, a, b)` is a private key, then `n = pq` is the corresponding public key. Furthermore, we have `\gcd(p, q) = ap + bq = 1`. :: - sage: p, q, a, b = prikey - sage: pubkey == p * q + sage: p, q, a, b = prikey # needs sage.symbolic + sage: pubkey == p * q # needs sage.symbolic True - sage: gcd(p, q) == a*p + b*q == 1 + sage: gcd(p, q) == a*p + b*q == 1 # needs sage.symbolic True """ @@ -264,24 +267,25 @@ def decrypt(self, C, K): Decrypt a longer ciphertext and convert the resulting plaintext into an ASCII string:: + sage: # needs sage.libs.pari sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import bin_to_ascii sage: bg = BlumGoldwasser() sage: p = 78307; q = 412487 sage: K = bg.private_key(p, q) - sage: C = ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ - ....: [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ - ....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ - ....: [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \ - ....: [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \ - ....: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \ - ....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \ - ....: [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \ - ....: [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \ - ....: [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \ - ....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \ - ....: [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \ - ....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) + sage: C = ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], + ....: [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], + ....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], + ....: [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], + ....: [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], + ....: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], + ....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], + ....: [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], + ....: [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], + ....: [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], + ....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], + ....: [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], + ....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) sage: P = bg.decrypt(C, K) sage: bin_to_ascii(flatten(P)) 'Blum-Goldwasser encryption' @@ -415,13 +419,13 @@ def encrypt(self, P, K, seed=None): sage: bg = BlumGoldwasser() sage: p = 499; q = 547; n = p * q sage: P = "10011100000100001100" - sage: C = bg.encrypt(P, n, seed=159201); C + sage: C = bg.encrypt(P, n, seed=159201); C # needs sage.symbolic ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) Convert the ciphertext sub-blocks into a binary string:: sage: bin = BinaryStrings() - sage: bin(flatten(C[0])) + sage: bin(flatten(C[0])) # needs sage.symbolic 00100000110011100100 Now encrypt an ASCII string. The result is random; no seed is @@ -432,7 +436,7 @@ def encrypt(self, P, K, seed=None): sage: bg = BlumGoldwasser() sage: K = 32300619509 sage: P = "Blum-Goldwasser encryption" - sage: bg.encrypt(P, K) # random + sage: bg.encrypt(P, K) # random # needs sage.symbolic ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ @@ -554,9 +558,9 @@ def private_key(self, p, q): sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import is_blum_prime sage: bg = BlumGoldwasser() - sage: P = primes_first_n(10); P + sage: P = primes_first_n(10); P # needs sage.libs.pari [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] - sage: [is_blum_prime(_) for _ in P] + sage: [is_blum_prime(_) for _ in P] # needs sage.libs.pari [False, True, False, True, True, False, False, True, True, False] sage: bg.private_key(19, 23) (19, 23, -6, 5) @@ -566,6 +570,7 @@ def private_key(self, p, q): resulting private key `(p, q, a, b)` satisfies `\gcd(p, q) = ap + bq = 1`:: + sage: # needs sage.libs.pari sage: from sage.crypto.util import random_blum_prime sage: p = random_blum_prime(10**4, 10**5) sage: q = random_blum_prime(10**4, 10**5) @@ -628,9 +633,9 @@ def public_key(self, p, q): sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import is_blum_prime sage: bg = BlumGoldwasser() - sage: P = primes_first_n(10); P + sage: P = primes_first_n(10); P # needs sage.libs.pari [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] - sage: [is_blum_prime(_) for _ in P] + sage: [is_blum_prime(_) for _ in P] # needs sage.libs.pari [False, True, False, True, True, False, False, True, True, False] sage: bg.public_key(3, 7) 21 @@ -639,6 +644,7 @@ def public_key(self, p, q): public key corresponding to those two primes, and test that the public key factorizes into Blum primes:: + sage: # needs sage.libs.pari sage: from sage.crypto.util import random_blum_prime sage: p = random_blum_prime(10**4, 10**5) sage: q = random_blum_prime(10**4, 10**5) @@ -730,6 +736,7 @@ def random_key(self, lbound, ubound, ntries=100): Choosing a random pair of public and private keys. We then test to see if they satisfy the requirements of the Blum-Goldwasser scheme:: + sage: # needs sage.libs.pari sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import is_blum_prime sage: bg = BlumGoldwasser() @@ -754,7 +761,7 @@ def random_key(self, lbound, ubound, ntries=100): sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() - sage: pubkey, privkey = bg.random_key(24, 30) + sage: pubkey, privkey = bg.random_key(24, 30) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: No Blum primes within the specified closed interval. diff --git a/src/sage/crypto/sbox.pyx b/src/sage/crypto/sbox.pyx index 453ac488a88..e2a99aa718e 100644 --- a/src/sage/crypto/sbox.pyx +++ b/src/sage/crypto/sbox.pyx @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" S-Boxes and Their Algebraic Representations """ @@ -39,7 +40,7 @@ cdef Py_ssize_t _nterms(Py_ssize_t nvars, Py_ssize_t deg): sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) - sage: F = S.polynomials(degree=3) # indirect doctest + sage: F = S.polynomials(degree=3) # indirect doctest # needs sage.libs.singular """ cdef Py_ssize_t total = 1 cdef Py_ssize_t divisor = 1 @@ -58,9 +59,9 @@ cdef Py_ssize_t _nterms(Py_ssize_t nvars, Py_ssize_t deg): cdef class SBox(SageObject): r""" A substitution box or S-box is one of the basic components of - symmetric key cryptography. In general, an S-box takes ``m`` input - bits and transforms them into ``n`` output bits. This is called an - ``mxn`` S-box and is often implemented as a lookup table. These + symmetric key cryptography. In general, an S-box takes `m` input + bits and transforms them into `n` output bits. This is called an + `m \times n` S-box and is often implemented as a lookup table. These S-boxes are carefully chosen to resist linear and differential cryptanalysis [He2002]_. @@ -590,7 +591,7 @@ cdef class SBox(SageObject): ... IndexError: list index out of range sage: from sage.crypto.sboxes import PRESENT - sage: PRESENT.derivative(1).max_degree() < PRESENT.max_degree() + sage: PRESENT.derivative(1).max_degree() < PRESENT.max_degree() # needs sage.rings.polynomial.pbori True """ from sage.structure.element import is_Vector @@ -839,7 +840,8 @@ cdef class SBox(SageObject): sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) sage: S.ring() - Multivariate Polynomial Ring in x0, x1, x2, y0, y1, y2 over Finite Field of size 2 + Multivariate Polynomial Ring in x0, x1, x2, y0, y1, y2 over + Finite Field of size 2 """ return self._ring @@ -857,9 +859,9 @@ cdef class SBox(SageObject): sage: from sage.crypto.sbox import SBox sage: S = SBox([7,6,0,4,2,5,1,3]) - sage: F = S.polynomials() + sage: F = S.polynomials() # needs sage.libs.singular sage: s = S.solutions() - sage: any(f.subs(_s) for f in F for _s in s) + sage: any(f.subs(_s) for f in F for _s in s) # needs sage.libs.singular False """ if X is None and Y is None: @@ -907,7 +909,7 @@ cdef class SBox(SageObject): By default, this method returns an indirect representation:: - sage: S.polynomials() + sage: S.polynomials() # needs sage.libs.singular [x0*x2 + x1 + y1 + 1, x0*x1 + x1 + x2 + y0 + y1 + y2 + 1, x0*y1 + x0 + x2 + y0 + y2, @@ -929,7 +931,7 @@ cdef class SBox(SageObject): bits are greater than the input bits:: sage: P. = PolynomialRing(GF(2),6,order='lex') - sage: S.polynomials([x0,x1,x2],[y0,y1,y2], groebner=True) + sage: S.polynomials([x0,x1,x2],[y0,y1,y2], groebner=True) # needs sage.libs.singular [y0 + x0*x1 + x0*x2 + x0 + x1*x2 + x1 + 1, y1 + x0*x2 + x1 + 1, y2 + x0 + x1*x2 + x1 + x2 + 1] @@ -1270,11 +1272,11 @@ cdef class SBox(SageObject): sage: from sage.crypto.sbox import SBox sage: S = SBox([7,6,0,4,2,5,1,3]) sage: f3 = S.component_function(3) - sage: f3.algebraic_normal_form() + sage: f3.algebraic_normal_form() # needs sage.rings.polynomial.pbori x0*x1 + x0*x2 + x0 + x2 sage: f5 = S.component_function([1, 0, 1]) - sage: f5.algebraic_normal_form() + sage: f5.algebraic_normal_form() # needs sage.rings.polynomial.pbori x0*x2 + x0 + x1*x2 """ cdef Py_ssize_t m = self.m @@ -1449,7 +1451,7 @@ cdef class SBox(SageObject): sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) - sage: S.autocorrelation_table() + sage: S.autocorrelation_table() # needs sage.combinat [ 8 8 8 8 8 8 8 8] [ 8 0 0 0 0 0 0 -8] [ 8 0 -8 0 0 0 0 0] @@ -1573,7 +1575,7 @@ cdef class SBox(SageObject): sage: from sage.crypto.sbox import SBox sage: S = SBox([0,1,3,6,7,4,5,2]) - sage: S.linear_structures() + sage: S.linear_structures() # needs sage.combinat [(1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (5, 5, 1), (6, 6, 1), (7, 7, 1)] """ @@ -1651,7 +1653,7 @@ cdef class SBox(SageObject): sage: from sage.crypto.sbox import SBox sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2]) - sage: S.max_degree() + sage: S.max_degree() # needs sage.rings.polynomial.pbori 3 """ ret = ZZ.zero() @@ -1671,7 +1673,7 @@ cdef class SBox(SageObject): sage: from sage.crypto.sbox import SBox sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2]) - sage: S.min_degree() + sage: S.min_degree() # needs sage.rings.polynomial.pbori 2 """ ret = ZZ(self.m) diff --git a/src/sage/crypto/sboxes.py b/src/sage/crypto/sboxes.py index 056ce082b50..bfb3834d5aa 100644 --- a/src/sage/crypto/sboxes.py +++ b/src/sage/crypto/sboxes.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" S-Boxes used in cryptographic schemes diff --git a/src/sage/crypto/stream.py b/src/sage/crypto/stream.py index 71d8ffb9a21..636f588ad97 100644 --- a/src/sage/crypto/stream.py +++ b/src/sage/crypto/stream.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat sage.rings.finite_rings """ Stream Cryptosystems """ diff --git a/src/sage/crypto/stream_cipher.py b/src/sage/crypto/stream_cipher.py index 198e93d9216..c6f46c8cef0 100644 --- a/src/sage/crypto/stream_cipher.py +++ b/src/sage/crypto/stream_cipher.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat sage.rings.finite_rings """ Stream Ciphers """ diff --git a/src/sage/crypto/util.py b/src/sage/crypto/util.py index 637738e45d0..8590c83db5c 100644 --- a/src/sage/crypto/util.py +++ b/src/sage/crypto/util.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.combinat """ Utility Functions for Cryptography @@ -23,11 +24,11 @@ from sage.arith.functions import lcm from sage.arith.misc import is_prime, primes, random_prime from sage.misc.lazy_import import lazy_import -from sage.monoids.string_monoid import BinaryStrings from sage.rings.finite_rings.integer_mod import Mod as mod from sage.rings.integer import Integer lazy_import('sage.arith.misc', ('carmichael_lambda'), deprecation=34719) +lazy_import('sage.monoids.string_monoid', 'BinaryStrings') def ascii_integer(B): @@ -297,14 +298,14 @@ def has_blum_prime(lbound, ubound): sage: from sage.crypto.util import has_blum_prime sage: from sage.crypto.util import is_blum_prime - sage: has_blum_prime(4, 100) + sage: has_blum_prime(4, 100) # needs sage.libs.pari True sage: for n in range(4, 100): ....: if is_blum_prime(n): ....: print(n) ....: break 7 - sage: has_blum_prime(24, 28) + sage: has_blum_prime(24, 28) # needs sage.libs.pari False TESTS: @@ -377,8 +378,8 @@ def is_blum_prime(n): False sage: is_blum_prime(7) True - sage: p = random_blum_prime(10**3, 10**5) - sage: is_blum_prime(p) + sage: p = random_blum_prime(10**3, 10**5) # needs sage.libs.pari + sage: is_blum_prime(p) # needs sage.libs.pari True """ if n < 0: @@ -486,10 +487,10 @@ def random_blum_prime(lbound, ubound, ntries=100): Choose a random prime and check that it is a Blum prime:: sage: from sage.crypto.util import random_blum_prime - sage: p = random_blum_prime(10**4, 10**5) - sage: is_prime(p) + sage: p = random_blum_prime(10**4, 10**5) # needs sage.libs.pari + sage: is_prime(p) # needs sage.libs.pari True - sage: mod(p, 4) == 3 + sage: mod(p, 4) == 3 # needs sage.libs.pari True TESTS: @@ -500,11 +501,11 @@ def random_blum_prime(lbound, ubound, ntries=100): is not a Blum prime. :: sage: from sage.crypto.util import random_blum_prime - sage: random_blum_prime(24, 30, ntries=10) + sage: random_blum_prime(24, 30, ntries=10) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: No Blum primes within the specified closed interval. - sage: random_blum_prime(24, 28) + sage: random_blum_prime(24, 28) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: No Blum primes within the specified closed interval.