| | 1 | r""" |
| | 2 | Small Scale Variants of the AES (SR) Polynomial System Generator. |
| | 3 | |
| | 4 | We support polynomial system generation over $GF(2)$ and |
| | 5 | $GF(2^e)$. Also, we support both the specification of SR as given in |
| | 6 | the paper and we support a variant of SR* which is AES. |
| | 7 | |
| | 8 | AUTHORS: |
| | 9 | -- Martin Albrecht <malb@informatik.uni-bremen.de> (2007-09) initial version |
| | 10 | |
| | 11 | EXAMPLES: |
| | 12 | sage: sr = mq.SR(1,1,1,4) |
| | 13 | |
| | 14 | $n$ is the number of rounds, $r$ the number of rows in the state |
| | 15 | array, $c$ the number of columns in the state array. $e$ the |
| | 16 | degree of the underlying field. |
| | 17 | |
| | 18 | sage: sr.n, sr.r, sr.c, sr.e |
| | 19 | (1, 1, 1, 4) |
| | 20 | |
| | 21 | By default variables are ordered reverse to as they appear., e.g.: |
| | 22 | |
| | 23 | sage: sr.R |
| | 24 | Polynomial Ring in k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003, k000, k001, k002, k003 over Finite Field in a of size 2^4 |
| | 25 | |
| | 26 | For SR(1,1,1,4) the ShiftRows matrix isn't that interresting: |
| | 27 | |
| | 28 | sage: sr.ShiftRows |
| | 29 | [1 0 0 0] |
| | 30 | [0 1 0 0] |
| | 31 | [0 0 1 0] |
| | 32 | [0 0 0 1] |
| | 33 | |
| | 34 | Also, the MixColumns matrix is the identity matrix: |
| | 35 | |
| | 36 | sage: sr.MixColumns |
| | 37 | [1 0 0 0] |
| | 38 | [0 1 0 0] |
| | 39 | [0 0 1 0] |
| | 40 | [0 0 0 1] |
| | 41 | |
| | 42 | Lin, however is not the identity matrix. |
| | 43 | |
| | 44 | sage: sr.Lin |
| | 45 | [ a^2 + 1 1 a^3 + a^2 a^2 + 1] |
| | 46 | [ a a 1 a^3 + a^2 + a + 1] |
| | 47 | [ a^3 + a a^2 a^2 1] |
| | 48 | [ 1 a^3 a + 1 a + 1] |
| | 49 | |
| | 50 | |
| | 51 | M and Mstar are identical for SR(1,1,1,4) |
| | 52 | |
| | 53 | sage: sr.M |
| | 54 | [ a^2 + 1 1 a^3 + a^2 a^2 + 1] |
| | 55 | [ a a 1 a^3 + a^2 + a + 1] |
| | 56 | [ a^3 + a a^2 a^2 1] |
| | 57 | [ 1 a^3 a + 1 a + 1] |
| | 58 | |
| | 59 | sage: sr.Mstar |
| | 60 | [ a^2 + 1 1 a^3 + a^2 a^2 + 1] |
| | 61 | [ a a 1 a^3 + a^2 + a + 1] |
| | 62 | [ a^3 + a a^2 a^2 1] |
| | 63 | [ 1 a^3 a + 1 a + 1] |
| | 64 | |
| | 65 | |
| | 66 | REFERENCES: |
| | 67 | C. Cid , S. Murphy, and M.J.B. Robshaw; Small Scale Variants of the |
| | 68 | AES; in Proceedings of Fast Software Encryption 2005, LNCS 3557; |
| | 69 | Springer 2005; available at http://www.isg.rhul.ac.uk/~sean/smallAES-fse05.pdf |
| | 70 | |
| | 71 | C. Cid , S. Murphy, and M.J.B. Robshaw; Algebraic Aspects of the |
| | 72 | Advanced Encryption Standard; Springer 2006; |
| | 73 | """ |
| | 74 | |
| | 75 | from sage.rings.finite_field import GF |
| | 76 | from sage.rings.integer_ring import ZZ |
| | 77 | from sage.rings.polynomial.polynomial_ring import PolynomialRing |
| | 78 | |
| | 79 | from sage.matrix.matrix import is_Matrix |
| | 80 | from sage.matrix.constructor import Matrix, random_matrix |
| | 81 | from sage.matrix.matrix_space import MatrixSpace |
| | 82 | |
| | 83 | from sage.misc.misc import get_verbose, set_verbose |
| | 84 | from sage.misc.flatten import flatten |
| | 85 | |
| | 86 | from sage.modules.vector_modn_dense import Vector_modn_dense |
| | 87 | |
| | 88 | from mpolynomialsystem import MPolynomialSystem, MPolynomialRoundSystem |
| | 89 | from mpolynomialsystemgenerator import MPolynomialSystemGenerator |
| | 90 | |
| | 91 | def SR(n=1,r=1,c=1,e=4, star=False, **kwargs): |
| | 92 | """ |
| | 93 | Return a small scale variant of the AES polynomial system |
| | 94 | constructor subject to the following conditions: |
| | 95 | |
| | 96 | INPUT: |
| | 97 | n -- the number of rounds (default: 1) |
| | 98 | r -- the number of rows in the state array (default: 1) |
| | 99 | c -- the number of columns in the state array (default: 1) |
| | 100 | e -- the exponent of the finite extension field (default: 4) |
| | 101 | star -- determines if SR* or SR should be constructed (default: False) |
| | 102 | aes_mode -- as the SR key schedule specification differs slightly from the AES |
| | 103 | key schedule this parameter controls which schedule to use (default: True) |
| | 104 | gf2 -- generate polynomial systems over GF(2) rather than over GF(2^n) (default: False) |
| | 105 | order -- a string to specify the term ordering of the variables |
| | 106 | postfix -- a string which is appended after the variable name (default: '') |
| | 107 | allow_zero_inversions -- a boolean to controll whether zero inversions raise |
| | 108 | an exception (default: False) |
| | 109 | |
| | 110 | ADDITIONAL INPUT FOR GF(2): |
| | 111 | correct_only -- only include correct inversion polynomials (default: False) |
| | 112 | biaffine_only -- only include bilinear and biaffine inversion polynomials (default: True) |
| | 113 | """ |
| | 114 | if not kwargs.get("gf2",False): |
| | 115 | return SR_gf2n(n,r,c,e,star,**kwargs) |
| | 116 | else: |
| | 117 | return SR_gf2(n,r,c,e,star,**kwargs) |
| | 118 | |
| | 119 | class SR_generic(MPolynomialSystemGenerator): |
| | 120 | """ |
| | 121 | Small Scale Variants of the AES. |
| | 122 | |
| | 123 | EXAMPLES: |
| | 124 | sage: sr = mq.SR(1,1,1,4) |
| | 125 | sage: ShiftRows = sr.shift_rows_matrix() |
| | 126 | sage: MixColumns = sr.mix_columns_matrix() |
| | 127 | sage: Lin = sr.lin_matrix() |
| | 128 | sage: M = MixColumns * ShiftRows * Lin |
| | 129 | sage: print sr.hex_str_matrix(M) |
| | 130 | 5 1 C 5 |
| | 131 | 2 2 1 F |
| | 132 | A 4 4 1 |
| | 133 | 1 8 3 3 |
| | 134 | |
| | 135 | TESTS: |
| | 136 | sage: sr = mq.SR(1,2,1,4) |
| | 137 | sage: ShiftRows = sr.shift_rows_matrix() |
| | 138 | sage: MixColumns = sr.mix_columns_matrix() |
| | 139 | sage: Lin = sr.lin_matrix() |
| | 140 | sage: M = MixColumns * ShiftRows * Lin |
| | 141 | sage: print sr.hex_str_matrix(M) |
| | 142 | F 3 7 F A 2 B A |
| | 143 | A A 5 6 8 8 4 9 |
| | 144 | 7 8 8 2 D C C 3 |
| | 145 | 4 6 C C 5 E F F |
| | 146 | A 2 B A F 3 7 F |
| | 147 | 8 8 4 9 A A 5 6 |
| | 148 | D C C 3 7 8 8 2 |
| | 149 | 5 E F F 4 6 C C |
| | 150 | |
| | 151 | sage: sr = mq.SR(1,2,2,4) |
| | 152 | sage: ShiftRows = sr.shift_rows_matrix() |
| | 153 | sage: MixColumns = sr.mix_columns_matrix() |
| | 154 | sage: Lin = sr.lin_matrix() |
| | 155 | sage: M = MixColumns * ShiftRows * Lin |
| | 156 | sage: print sr.hex_str_matrix(M) |
| | 157 | F 3 7 F 0 0 0 0 0 0 0 0 A 2 B A |
| | 158 | A A 5 6 0 0 0 0 0 0 0 0 8 8 4 9 |
| | 159 | 7 8 8 2 0 0 0 0 0 0 0 0 D C C 3 |
| | 160 | 4 6 C C 0 0 0 0 0 0 0 0 5 E F F |
| | 161 | A 2 B A 0 0 0 0 0 0 0 0 F 3 7 F |
| | 162 | 8 8 4 9 0 0 0 0 0 0 0 0 A A 5 6 |
| | 163 | D C C 3 0 0 0 0 0 0 0 0 7 8 8 2 |
| | 164 | 5 E F F 0 0 0 0 0 0 0 0 4 6 C C |
| | 165 | 0 0 0 0 A 2 B A F 3 7 F 0 0 0 0 |
| | 166 | 0 0 0 0 8 8 4 9 A A 5 6 0 0 0 0 |
| | 167 | 0 0 0 0 D C C 3 7 8 8 2 0 0 0 0 |
| | 168 | 0 0 0 0 5 E F F 4 6 C C 0 0 0 0 |
| | 169 | 0 0 0 0 F 3 7 F A 2 B A 0 0 0 0 |
| | 170 | 0 0 0 0 A A 5 6 8 8 4 9 0 0 0 0 |
| | 171 | 0 0 0 0 7 8 8 2 D C C 3 0 0 0 0 |
| | 172 | 0 0 0 0 4 6 C C 5 E F F 0 0 0 0 |
| | 173 | |
| | 174 | """ |
| | 175 | def __init__(self,n=1,r=1,c=1,e=4, star=False, **kwargs): |
| | 176 | """ |
| | 177 | See help for SR. |
| | 178 | """ |
| | 179 | if n-1 not in range(10): |
| | 180 | raise TypeError, "n must be between 1 and 10 (inclusive)" |
| | 181 | self._n = n |
| | 182 | |
| | 183 | if r not in (1,2,4): |
| | 184 | raise TypeError, "r must be in (1,2,4)" |
| | 185 | self._r = r |
| | 186 | |
| | 187 | if c not in (1,2,4): |
| | 188 | raise TypeError, "c must be in (1,2,4)" |
| | 189 | self._c = c |
| | 190 | |
| | 191 | if e not in (4,8): |
| | 192 | raise TypeError, "e must be either 4 or 8" |
| | 193 | self._e = e |
| | 194 | |
| | 195 | self._star = bool(star) |
| | 196 | |
| | 197 | self._base = self.base_ring() |
| | 198 | |
| | 199 | # to generate table, allow zero inversions |
| | 200 | self._allow_zero_inversions = True |
| | 201 | sub_byte_lookup = dict([(e,self.sub_byte(e)) for e in self._base]) |
| | 202 | self._sub_byte_lookup = sub_byte_lookup |
| | 203 | |
| | 204 | self._postfix = kwargs.get("postfix", "") |
| | 205 | self._order = kwargs.get("order", "degrevlex") |
| | 206 | self._allow_zero_inversions= bool(kwargs.get("allow_zero_inversions", False)) |
| | 207 | self._aes_mode = kwargs.get("aes_mode",True) |
| | 208 | self._gf2 = kwargs.get("gf2",False) |
| | 209 | |
| | 210 | def __getattr__(self, attr): |
| | 211 | if attr == "e": |
| | 212 | return self._e |
| | 213 | elif attr == "c": |
| | 214 | return self._c |
| | 215 | elif attr == "n": |
| | 216 | return self._n |
| | 217 | elif attr == "r": |
| | 218 | return self._r |
| | 219 | |
| | 220 | elif attr == "R": |
| | 221 | self.R = self.ring() |
| | 222 | return self.R |
| | 223 | elif attr == "k": |
| | 224 | self.k = self.base_ring() |
| | 225 | return self.k |
| | 226 | |
| | 227 | elif attr == "M": |
| | 228 | self.M = self.MixColumns * self.ShiftRows * self.Lin |
| | 229 | return self.M |
| | 230 | elif attr == "Mstar": |
| | 231 | self.Mstar = self.MixColumns * self.ShiftRows * self.Lin |
| | 232 | return self.Mstar |
| | 233 | elif attr == "ShiftRows": |
| | 234 | self.ShiftRows = self.shift_rows_matrix() |
| | 235 | return self.ShiftRows |
| | 236 | elif attr == "MixColumns": |
| | 237 | self.MixColumns = self.mix_columns_matrix() |
| | 238 | return self.MixColumns |
| | 239 | elif attr == "Lin": |
| | 240 | self.Lin = self.lin_matrix() |
| | 241 | return self.Lin |
| | 242 | |
| | 243 | raise AttributeError, "%s has no attribute %s"%(type(self),attr) |
| | 244 | |
| | 245 | def _repr_(self): |
| | 246 | if self._star: |
| | 247 | return "SR*(%d,%d,%d,%d)"%(self._n,self._r,self._c,self._e) |
| | 248 | else: |
| | 249 | return "SR(%d,%d,%d,%d)"%(self._n,self._r,self._c,self._e) |
| | 250 | |
| | 251 | def base_ring(self): |
| | 252 | """ |
| | 253 | Return the base field of self as determined through self.e. |
| | 254 | |
| | 255 | EXAMPLE: |
| | 256 | sage: sr = mq.SR(10,2,2,4) |
| | 257 | sage: sr.base_ring().polynomial() |
| | 258 | a^4 + a + 1 |
| | 259 | |
| | 260 | The Rijndael polynomial: |
| | 261 | |
| | 262 | sage: sr = mq.SR(10,4,4,8) |
| | 263 | sage: sr.base_ring().polynomial() |
| | 264 | a^8 + a^4 + a^3 + a + 1 |
| | 265 | """ |
| | 266 | try: |
| | 267 | return self._base |
| | 268 | except AttributeError: |
| | 269 | pass |
| | 270 | |
| | 271 | if self._e == 4: |
| | 272 | self._base = GF(2**4,'a',modulus=(1,1,0,0,1)) |
| | 273 | elif self._e == 8: |
| | 274 | self._base = GF(2**8,'a',modulus=(1,1,0,1,1,0,0,0,1)) |
| | 275 | |
| | 276 | return self._base |
| | 277 | |
| | 278 | def sub_bytes(self, d): |
| | 279 | """ |
| | 280 | Perform the non-linear transform on d. |
| | 281 | |
| | 282 | INPUT: |
| | 283 | d -- state array or something coercable to a state array |
| | 284 | """ |
| | 285 | d = self.state_array(d) |
| | 286 | return Matrix(self.base_ring(), d.nrows(), d.ncols(), [self.sub_byte(b) for b in d.list()]) |
| | 287 | |
| | 288 | def sub_byte(self, b): |
| | 289 | """ |
| | 290 | Perform SubByte on a single byte/halfbyte b. |
| | 291 | |
| | 292 | A ZeroDivision exception is raised if an attempt is made to |
| | 293 | perform an inversion on the zero element. This can be disabled |
| | 294 | by passing allow_zero_inversion=True to the constructor. A zero inversion |
| | 295 | will result in an inconsisten equation system. |
| | 296 | |
| | 297 | INPUT: |
| | 298 | b -- an element in self.base_ring() |
| | 299 | |
| | 300 | EXAMPLE: |
| | 301 | |
| | 302 | The S-Box table for GF(2^4): |
| | 303 | |
| | 304 | sage: sr = mq.SR(1,1,1,4, allow_zero_inversions=True) |
| | 305 | sage: for e in sr.base_ring(): |
| | 306 | ... print '% 20s % 20s'%(e, sr.sub_byte(e)) |
| | 307 | 0 a^2 + a |
| | 308 | a a^2 + 1 |
| | 309 | a^2 a |
| | 310 | a^3 a^3 + 1 |
| | 311 | a + 1 a^2 |
| | 312 | a^2 + a a^2 + a + 1 |
| | 313 | a^3 + a^2 a + 1 |
| | 314 | a^3 + a + 1 a^3 + a^2 |
| | 315 | a^2 + 1 a^3 + a^2 + a |
| | 316 | a^3 + a a^3 + a^2 + a + 1 |
| | 317 | a^2 + a + 1 a^3 + a |
| | 318 | a^3 + a^2 + a 0 |
| | 319 | a^3 + a^2 + a + 1 a^3 |
| | 320 | a^3 + a^2 + 1 1 |
| | 321 | a^3 + 1 a^3 + a^2 + 1 |
| | 322 | 1 a^3 + a + 1 |
| | 323 | |
| | 324 | """ |
| | 325 | if not b: |
| | 326 | if not self._allow_zero_inversions: |
| | 327 | raise ZeroDivisionError, "A zero inversion occurred during an encryption or key schedule." |
| | 328 | else: |
| | 329 | return self.sbox_constant() |
| | 330 | try: |
| | 331 | return self._sub_byte_lookup[b] |
| | 332 | except AttributeError: |
| | 333 | pass |
| | 334 | |
| | 335 | e = self.e |
| | 336 | k = self.k |
| | 337 | a = k.gen() |
| | 338 | |
| | 339 | # inversion |
| | 340 | b = b ** ( 2**e - 2 ) |
| | 341 | |
| | 342 | # GF(2) linear map |
| | 343 | if e == 4: |
| | 344 | if not hasattr(self,"_L"): |
| | 345 | self._L = Matrix(GF(2),4,4,[[1, 1, 1, 0], |
| | 346 | [0, 1, 1, 1], |
| | 347 | [1, 0, 1, 1], |
| | 348 | [1, 1, 0, 1]]) |
| | 349 | |
| | 350 | elif e==8: |
| | 351 | if not hasattr(self,"_L"): |
| | 352 | self._L = Matrix(GF(2),8,8,[[1, 0, 0, 0, 1, 1, 1, 1], |
| | 353 | [1, 1, 0, 0, 0, 1, 1, 1], |
| | 354 | [1, 1, 1, 0, 0, 0, 1, 1], |
| | 355 | [1, 1, 1, 1, 0, 0, 0, 1], |
| | 356 | [1, 1, 1, 1, 1, 0, 0, 0], |
| | 357 | [0, 1, 1, 1, 1, 1, 0, 0], |
| | 358 | [0, 0, 1, 1, 1, 1, 1, 0], |
| | 359 | [0, 0, 0, 1, 1, 1, 1, 1]]) |
| | 360 | |
| | 361 | b = k(self._L * b.vector()) |
| | 362 | |
| | 363 | # constant addition |
| | 364 | if e == 4: |
| | 365 | b = b + k.fetch_int(6) |
| | 366 | elif e == 8: |
| | 367 | b = b + k.fetch_int(99) |
| | 368 | |
| | 369 | return b |
| | 370 | |
| | 371 | def sbox_constant(self): |
| | 372 | """ |
| | 373 | Return the sbox constant which is added after $L(x^{-1})$ was |
| | 374 | performed. That is 0x63 if e == 8 or 0x6 if e == 4. |
| | 375 | |
| | 376 | EXAMPLE: |
| | 377 | sage: sr = mq.SR(10,1,1,8) |
| | 378 | sage: sr.sbox_constant() |
| | 379 | a^6 + a^5 + a + 1 |
| | 380 | """ |
| | 381 | k = self.k |
| | 382 | if self.e == 4: |
| | 383 | return k.fetch_int(6) |
| | 384 | elif self.e == 8: |
| | 385 | return k.fetch_int(99) |
| | 386 | else: |
| | 387 | raise TypeError, "sbox constant only defined for e in (4,8)" |
| | 388 | |
| | 389 | def shift_rows(self, d): |
| | 390 | """ |
| | 391 | Perform the ShiftRows operation on d. |
| | 392 | |
| | 393 | INPUT: |
| | 394 | d -- state arrary or something coercable to an state array |
| | 395 | |
| | 396 | EXAMPLES: |
| | 397 | sage: sr = mq.SR(10,4,4,4) |
| | 398 | sage: E = sr.state_array() + 1; E |
| | 399 | [1 0 0 0] |
| | 400 | [0 1 0 0] |
| | 401 | [0 0 1 0] |
| | 402 | [0 0 0 1] |
| | 403 | |
| | 404 | sage: sr.shift_rows(E) |
| | 405 | [1 0 0 0] |
| | 406 | [1 0 0 0] |
| | 407 | [1 0 0 0] |
| | 408 | [1 0 0 0] |
| | 409 | """ |
| | 410 | d = self.state_array(d) |
| | 411 | ret = [] |
| | 412 | for i in range(d.nrows()): |
| | 413 | ret += list(d.row(i)[i%d.ncols():]) + list(d.row(i)[:i%d.ncols()]) |
| | 414 | return Matrix(self.base_ring(), self._r, self._c, ret) |
| | 415 | |
| | 416 | def mix_columns(self, d): |
| | 417 | """ |
| | 418 | Perform the MixColumns operation on d. |
| | 419 | |
| | 420 | INPUT: |
| | 421 | d -- state arrary or something coercable to an state array |
| | 422 | |
| | 423 | EXAMPLES: |
| | 424 | sage: sr = mq.SR(10,4,4,4) |
| | 425 | sage: E = sr.state_array() + 1; E |
| | 426 | [1 0 0 0] |
| | 427 | [0 1 0 0] |
| | 428 | [0 0 1 0] |
| | 429 | [0 0 0 1] |
| | 430 | |
| | 431 | sage: sr.mix_columns(E) |
| | 432 | [ a a + 1 1 1] |
| | 433 | [ 1 a a + 1 1] |
| | 434 | [ 1 1 a a + 1] |
| | 435 | [a + 1 1 1 a] |
| | 436 | """ |
| | 437 | d = self.state_array(d) |
| | 438 | k = self.base_ring() |
| | 439 | a = k.gen() |
| | 440 | r = self._r |
| | 441 | if r == 1: |
| | 442 | M = Matrix(self.base_ring(),1,1, [[1]]) |
| | 443 | elif r == 2: |
| | 444 | M = Matrix(self.base_ring(),2,2, [[a + 1, a], |
| | 445 | [a, a + 1]]) |
| | 446 | |
| | 447 | elif r == 4: |
| | 448 | M = Matrix(self.base_ring(),4,4, [[a, a+1, 1, 1], |
| | 449 | [1, a, a+1, 1], |
| | 450 | [1, 1, a, a+1], |
| | 451 | [a+1, 1, 1, a]]) |
| | 452 | ret =[] |
| | 453 | for column in d.columns(): |
| | 454 | ret.append(M * column) |
| | 455 | # AES uses the column major ordering |
| | 456 | return Matrix(k, d.ncols(), d.nrows(), ret).transpose() |
| | 457 | |
| | 458 | |
| | 459 | def add_round_key(self, d, key): |
| | 460 | """ |
| | 461 | Perform the AddRoundKey operation on d using key. |
| | 462 | |
| | 463 | INPUT: |
| | 464 | d -- state arrary or something coercable to an state array |
| | 465 | key -- state arrary or something coercable to an state array |
| | 466 | |
| | 467 | EXAMPLE: |
| | 468 | sage: sr = mq.SR(10,4,4,4) |
| | 469 | sage: D = sr.random_state_array() |
| | 470 | sage: K = sr.random_state_array() |
| | 471 | sage: sr.add_round_key(D,K) == K + D |
| | 472 | True |
| | 473 | """ |
| | 474 | d = self.state_array(d) |
| | 475 | key = self.state_array(key) |
| | 476 | |
| | 477 | return d+key |
| | 478 | |
| | 479 | def state_array(self, d=None): |
| | 480 | """ |
| | 481 | Convert the parameter to a state array. |
| | 482 | |
| | 483 | INPUT: |
| | 484 | d -- None, a matrix, a list, or a tuple |
| | 485 | |
| | 486 | EXAMPLES: |
| | 487 | sage: sr = mq.SR(2,2,2,4) |
| | 488 | sage: k = sr.base_ring() |
| | 489 | sage: e1 = [k.fetch_int(e) for e in range(2*2)]; e1 |
| | 490 | [0, 1, a, a + 1] |
| | 491 | sage: e2 = sr.phi( Matrix(k,2*2,1,e1) ) |
| | 492 | sage: sr.state_array(e1) # note the column major ordering |
| | 493 | [ 0 a] |
| | 494 | [ 1 a + 1] |
| | 495 | sage: sr.state_array(e2) |
| | 496 | [ 0 a] |
| | 497 | [ 1 a + 1] |
| | 498 | |
| | 499 | sage: sr.state_array() |
| | 500 | [0 0] |
| | 501 | [0 0] |
| | 502 | |
| | 503 | """ |
| | 504 | r = self.r |
| | 505 | c = self.c |
| | 506 | e = self.e |
| | 507 | k = self.base_ring() |
| | 508 | |
| | 509 | if d is None: |
| | 510 | return Matrix(k, r, c) |
| | 511 | |
| | 512 | if is_Matrix(d): |
| | 513 | if d.nrows() == r*c*e: |
| | 514 | return Matrix(k, c, r, self.antiphi(d)).transpose() |
| | 515 | elif d.ncols() == c and d.nrows() == r and d.base_ring() == k: |
| | 516 | return d |
| | 517 | |
| | 518 | if isinstance(d, tuple([list,tuple])): |
| | 519 | return Matrix(k, c, r, d).transpose() |
| | 520 | |
| | 521 | def is_state_array(self, d): |
| | 522 | """ |
| | 523 | Return True if d is a state array, i.e. has the correct dimensions and base field. |
| | 524 | """ |
| | 525 | return is_Matrix(d) and \ |
| | 526 | d.nrows() == self.r and \ |
| | 527 | d.ncols() == self.c and \ |
| | 528 | d.base_ring() == self.base_ring() |
| | 529 | |
| | 530 | def random_state_array(self): |
| | 531 | """ |
| | 532 | Return a random element in MatrixSpace(self.base_ring(),self.r,self.c). |
| | 533 | """ |
| | 534 | return random_matrix(self.base_ring(),self._r,self._c) |
| | 535 | |
| | 536 | def random_vector(self): |
| | 537 | """ |
| | 538 | Return a random vector as it might appear in the algebraic |
| | 539 | expression of self. |
| | 540 | |
| | 541 | NOTE: Phi was already applied to the result. |
| | 542 | """ |
| | 543 | return self.vector( self.random_state_array() ) |
| | 544 | |
| | 545 | def random_element(self, type = "vector"): |
| | 546 | """ |
| | 547 | Return a random element for self. |
| | 548 | |
| | 549 | INPUT: |
| | 550 | type -- either 'vector' or 'state array' (default: 'vector') |
| | 551 | |
| | 552 | EXAMPLE: |
| | 553 | sage: sr = mq.SR() |
| | 554 | sage: sr.random_element() # random |
| | 555 | [ a] |
| | 556 | [ a^2] |
| | 557 | [ a + 1] |
| | 558 | [a^2 + 1] |
| | 559 | sage: sr.random_element('state_array') # random |
| | 560 | [a^3 + a + 1] |
| | 561 | """ |
| | 562 | if type == "vector": |
| | 563 | return self.random_vector() |
| | 564 | elif type == "state_array": |
| | 565 | return self.random_state_array() |
| | 566 | else: |
| | 567 | raise TypeError, "parameter type not understood" |
| | 568 | |
| | 569 | def key_schedule(self, kj, i): |
| | 570 | """ |
| | 571 | Return $k_i$ for a given $i$ and $k_j$ with $j = i-1$. |
| | 572 | |
| | 573 | TESTS: |
| | 574 | sage: sr = mq.SR(10,4,4,8, star=True, allow_zero_inversions=True) |
| | 575 | sage: ki = sr.state_array() |
| | 576 | sage: for i in range(10): |
| | 577 | ... ki = sr.key_schedule(ki,i+1) |
| | 578 | sage: print sr.hex_str_matrix(ki) |
| | 579 | B4 3E 23 6F |
| | 580 | EF 92 E9 8F |
| | 581 | 5B E2 51 18 |
| | 582 | CB 11 CF 8E |
| | 583 | """ |
| | 584 | if i < 0: |
| | 585 | raise TypeError, "i must be >= i" |
| | 586 | |
| | 587 | if i == 0: |
| | 588 | return kj |
| | 589 | |
| | 590 | r = self.r |
| | 591 | c = self.c |
| | 592 | e = self.e |
| | 593 | F = self.base_ring() |
| | 594 | a = F.gen() |
| | 595 | SubByte = self.sub_byte |
| | 596 | |
| | 597 | rc = Matrix(F,r,c, ([a**(i-1)] * c) + [F(0)]*((r-1)*c) ) |
| | 598 | ki = Matrix(F,r,c) |
| | 599 | |
| | 600 | if r == 1: |
| | 601 | s0 = SubByte(kj[0,c-1]) |
| | 602 | |
| | 603 | if c > 1: |
| | 604 | for q in range(c): |
| | 605 | ki[0,q] = s0 + sum([kj[0,t] for t in range(q+1) ]) |
| | 606 | else: |
| | 607 | ki[0,0] = s0 |
| | 608 | |
| | 609 | elif r == 2: |
| | 610 | s0 = SubByte(kj[1,c-1]) |
| | 611 | s1 = SubByte(kj[0,c-1]) |
| | 612 | |
| | 613 | if c > 1: |
| | 614 | for q in range(c): |
| | 615 | ki[0, q] = s0 + sum([ kj[0,t] for t in range(q+1) ]) |
| | 616 | ki[1, q] = s1 + sum([ kj[1,t] for t in range(q+1) ]) |
| | 617 | else: |
| | 618 | ki[0,0] = s0 |
| | 619 | ki[1,0] = s1 |
| | 620 | |
| | 621 | elif r == 4: |
| | 622 | |
| | 623 | if self._aes_mode: |
| | 624 | s0 = SubByte(kj[1,c-1]) |
| | 625 | s1 = SubByte(kj[2,c-1]) |
| | 626 | s2 = SubByte(kj[3,c-1]) |
| | 627 | s3 = SubByte(kj[0,c-1]) |
| | 628 | else: |
| | 629 | s0 = SubByte(kj[3,c-1]) |
| | 630 | s1 = SubByte(kj[2,c-1]) |
| | 631 | s2 = SubByte(kj[1,c-1]) |
| | 632 | s3 = SubByte(kj[0,c-1]) |
| | 633 | |
| | 634 | if c > 1: |
| | 635 | for q in range(c): |
| | 636 | ki[0,q] = s0 + sum([ kj[0,t] for t in range(q+1) ]) |
| | 637 | ki[1,q] = s1 + sum([ kj[1,t] for t in range(q+1) ]) |
| | 638 | ki[2,q] = s2 + sum([ kj[2,t] for t in range(q+1) ]) |
| | 639 | ki[3,q] = s3 + sum([ kj[3,t] for t in range(q+1) ]) |
| | 640 | |
| | 641 | else: |
| | 642 | ki[0,0] = s0 |
| | 643 | ki[1,0] = s1 |
| | 644 | ki[2,0] = s2 |
| | 645 | ki[3,0] = s3 |
| | 646 | ki += rc |
| | 647 | |
| | 648 | return ki |
| | 649 | |
| | 650 | def __call__(self, P, K): |
| | 651 | r""" |
| | 652 | Encrypts the plaintext $P$ using the key $K$. |
| | 653 | |
| | 654 | Both must be given as state arrays or coercable to state arrays. |
| | 655 | |
| | 656 | INPUTS: |
| | 657 | P -- plaintext as state array or something coercable to a state array |
| | 658 | K -- key as state array or something coercable to a state array |
| | 659 | |
| | 660 | TESTS: |
| | 661 | The official AES test vectors: |
| | 662 | |
| | 663 | sage: sr = mq.SR(10,4,4,8, star=True, allow_zero_inversions=True) |
| | 664 | sage: k = sr.base_ring() |
| | 665 | sage: plaintext = sr.state_array([k.fetch_int(e) for e in range(16)]) |
| | 666 | sage: key = sr.state_array([k.fetch_int(e) for e in range(16)]) |
| | 667 | sage: print sr.hex_str_matrix( sr(plaintext, key) ) |
| | 668 | 0A 41 F1 C6 |
| | 669 | 94 6E C3 53 |
| | 670 | 0B F0 94 EA |
| | 671 | B5 45 58 5A |
| | 672 | |
| | 673 | Brian Gladman's development vectors (dev_vec.txt): |
| | 674 | |
| | 675 | sage: sr = mq.SR(10,4,4,8,star=True,allow_zero_inversions=True, aes_mode=True) |
| | 676 | sage: k = sr.base_ring() |
| | 677 | sage: plain = '3243f6a8885a308d313198a2e0370734' |
| | 678 | sage: key = '2b7e151628aed2a6abf7158809cf4f3c' |
| | 679 | sage: set_verbose(2) |
| | 680 | sage: cipher = sr(plain,key) |
| | 681 | R[01].start 193DE3BEA0F4E22B9AC68D2AE9F84808 |
| | 682 | R[01].s_box D42711AEE0BF98F1B8B45DE51E415230 |
| | 683 | R[01].s_row D4BF5D30E0B452AEB84111F11E2798E5 |
| | 684 | R[01].m_col 046681E5E0CB199A48F8D37A2806264C |
| | 685 | R[01].k_sch A0FAFE1788542CB123A339392A6C7605 |
| | 686 | R[02].start A49C7FF2689F352B6B5BEA43026A5049 |
| | 687 | R[02].s_box 49DED28945DB96F17F39871A7702533B |
| | 688 | R[02].s_row 49DB873B453953897F02D2F177DE961A |
| | 689 | R[02].m_col 584DCAF11B4B5AACDBE7CAA81B6BB0E5 |
| | 690 | R[02].k_sch F2C295F27A96B9435935807A7359F67F |
| | 691 | R[03].start AA8F5F0361DDE3EF82D24AD26832469A |
| | 692 | R[03].s_box AC73CF7BEFC111DF13B5D6B545235AB8 |
| | 693 | R[03].s_row ACC1D6B8EFB55A7B1323CFDF457311B5 |
| | 694 | R[03].m_col 75EC0993200B633353C0CF7CBB25D0DC |
| | 695 | R[03].k_sch 3D80477D4716FE3E1E237E446D7A883B |
| | 696 | R[04].start 486C4EEE671D9D0D4DE3B138D65F58E7 |
| | 697 | R[04].s_box 52502F2885A45ED7E311C807F6CF6A94 |
| | 698 | R[04].s_row 52A4C89485116A28E3CF2FD7F6505E07 |
| | 699 | R[04].m_col 0FD6DAA9603138BF6FC0106B5EB31301 |
| | 700 | R[04].k_sch EF44A541A8525B7FB671253BDB0BAD00 |
| | 701 | R[05].start E0927FE8C86363C0D9B1355085B8BE01 |
| | 702 | R[05].s_box E14FD29BE8FBFBBA35C89653976CAE7C |
| | 703 | R[05].s_row E1FB967CE8C8AE9B356CD2BA974FFB53 |
| | 704 | R[05].m_col 25D1A9ADBD11D168B63A338E4C4CC0B0 |
| | 705 | R[05].k_sch D4D1C6F87C839D87CAF2B8BC11F915BC |
| | 706 | R[06].start F1006F55C1924CEF7CC88B325DB5D50C |
| | 707 | R[06].s_box A163A8FC784F29DF10E83D234CD503FE |
| | 708 | R[06].s_row A14F3DFE78E803FC10D5A8DF4C632923 |
| | 709 | R[06].m_col 4B868D6D2C4A8980339DF4E837D218D8 |
| | 710 | R[06].k_sch 6D88A37A110B3EFDDBF98641CA0093FD |
| | 711 | R[07].start 260E2E173D41B77DE86472A9FDD28B25 |
| | 712 | R[07].s_box F7AB31F02783A9FF9B4340D354B53D3F |
| | 713 | R[07].s_row F783403F27433DF09BB531FF54ABA9D3 |
| | 714 | R[07].m_col 1415B5BF461615EC274656D7342AD843 |
| | 715 | R[07].k_sch 4E54F70E5F5FC9F384A64FB24EA6DC4F |
| | 716 | R[08].start 5A4142B11949DC1FA3E019657A8C040C |
| | 717 | R[08].s_box BE832CC8D43B86C00AE1D44DDA64F2FE |
| | 718 | R[08].s_row BE3BD4FED4E1F2C80A642CC0DA83864D |
| | 719 | R[08].m_col 00512FD1B1C889FF54766DCDFA1B99EA |
| | 720 | R[08].k_sch EAD27321B58DBAD2312BF5607F8D292F |
| | 721 | R[09].start EA835CF00445332D655D98AD8596B0C5 |
| | 722 | R[09].s_box 87EC4A8CF26EC3D84D4C46959790E7A6 |
| | 723 | R[09].s_row 876E46A6F24CE78C4D904AD897ECC395 |
| | 724 | R[09].m_col 473794ED40D4E4A5A3703AA64C9F42BC |
| | 725 | R[09].k_sch AC7766F319FADC2128D12941575C006E |
| | 726 | R[10].s_box E9098972CB31075F3D327D94AF2E2CB5 |
| | 727 | R[10].s_row E9317DB5CB322C723D2E895FAF090794 |
| | 728 | R[10].k_sch D014F9A8C9EE2589E13F0CC8B6630CA6 |
| | 729 | R[10].output 3925841D02DC09FBDC118597196A0B32 |
| | 730 | sage: set_verbose(0) |
| | 731 | """ |
| | 732 | r = self.r |
| | 733 | c = self.c |
| | 734 | n = self.n |
| | 735 | e = self.e |
| | 736 | F = self.base_ring() |
| | 737 | |
| | 738 | _type = self.state_array |
| | 739 | |
| | 740 | if isinstance(P,str): |
| | 741 | P = self.state_array([F.fetch_int(ZZ(P[i:i+2],16)) for i in range(0,len(P),2)]) |
| | 742 | if isinstance(K,str): |
| | 743 | K = self.state_array([F.fetch_int(ZZ(K[i:i+2],16)) for i in range(0,len(K),2)]) |
| | 744 | |
| | 745 | if self.is_state_array(P) and self.is_state_array(K): |
| | 746 | _type = self.state_array |
| | 747 | elif self.is_vector(P) and self.is_vector(K): |
| | 748 | _type = self.vector |
| | 749 | else: |
| | 750 | raise TypeError, "plaintext or key parameter not understood" |
| | 751 | |
| | 752 | P = self.state_array(P) |
| | 753 | K = self.state_array(K) |
| | 754 | |
| | 755 | AddRoundKey = self.add_round_key |
| | 756 | SubBytes = self.sub_bytes |
| | 757 | MixColumns = self.mix_columns |
| | 758 | ShiftRows = self.shift_rows |
| | 759 | KeyExpansion = self.key_schedule |
| | 760 | |
| | 761 | P = AddRoundKey(P,K) |
| | 762 | |
| | 763 | for r in range(self._n-1): |
| | 764 | if get_verbose() >= 2: print "R[%02d].start %s"%(r+1,self.hex_str_vector(P)) |
| | 765 | |
| | 766 | P = SubBytes(P) |
| | 767 | if get_verbose() >= 2: print "R[%02d].s_box %s"%(r+1,self.hex_str_vector(P)) |
| | 768 | |
| | 769 | P = ShiftRows(P) |
| | 770 | if get_verbose() >= 2: print "R[%02d].s_row %s"%(r+1,self.hex_str_vector(P)) |
| | 771 | |
| | 772 | P = MixColumns(P) |
| | 773 | if get_verbose() >= 2: print "R[%02d].m_col %s"%(r+1,self.hex_str_vector(P)) |
| | 774 | |
| | 775 | K = KeyExpansion(K,r+1) |
| | 776 | if get_verbose() >= 2: print "R[%02d].k_sch %s"%(r+1,self.hex_str_vector(K)) |
| | 777 | |
| | 778 | P = AddRoundKey(P, K) |
| | 779 | |
| | 780 | P = SubBytes(P) |
| | 781 | if get_verbose() >= 2: print "R[%02d].s_box %s"%(self.n,self.hex_str_vector(P)) |
| | 782 | |
| | 783 | P = ShiftRows(P) |
| | 784 | if get_verbose() >= 2: print "R[%02d].s_row %s"%(self.n,self.hex_str_vector(P)) |
| | 785 | |
| | 786 | if not self._star: |
| | 787 | P = MixColumns(P) |
| | 788 | if get_verbose() >= 2: print "R[%02d].m_col %s"%(self.n,self.hex_str_vector(P)) |
| | 789 | |
| | 790 | K = KeyExpansion(K,self._n) |
| | 791 | if get_verbose() >= 2: print "R[%02d].k_sch %s"%(self.n,self.hex_str_vector(K)) |
| | 792 | |
| | 793 | P = AddRoundKey(P, K) |
| | 794 | if get_verbose() >= 2: print "R[%02d].output %s"%(self.n,self.hex_str_vector(P)) |
| | 795 | |
| | 796 | return _type(P) |
| | 797 | |
| | 798 | def hex_str(self, M, type="matrix"): |
| | 799 | """ |
| | 800 | Return a hex string for the provided AES state array/matrix. |
| | 801 | |
| | 802 | INPUT: |
| | 803 | M -- state array |
| | 804 | type -- controls what to return, either 'matrix' or 'vector' |
| | 805 | (default: 'matrix') |
| | 806 | """ |
| | 807 | if type == "matrix": |
| | 808 | return self._hex_str_matrix(M) |
| | 809 | elif type == "vector": |
| | 810 | return self._hex_str_vector(M) |
| | 811 | else: |
| | 812 | raise TypeError, "parameter type must either be 'matrix' or 'vector'" |
| | 813 | |
| | 814 | def hex_str_matrix(self, M): |
| | 815 | """ |
| | 816 | Return a two-dimensional AES like representation of the matrix M. |
| | 817 | |
| | 818 | That is, show the finite field elements as hex strings. |
| | 819 | |
| | 820 | INPUT: |
| | 821 | M -- an AES state array |
| | 822 | """ |
| | 823 | e = M.base_ring().degree() |
| | 824 | st = [""] |
| | 825 | for x in range(M.nrows()): |
| | 826 | for y in range(M.ncols()): |
| | 827 | if e == 8: |
| | 828 | st.append("%02X"%(int(str(M[x,y].int_repr())))) |
| | 829 | else: |
| | 830 | st.append("%X"%(int(str(M[x,y].int_repr())))) |
| | 831 | st.append("\n") |
| | 832 | return " ".join(st) |
| | 833 | |
| | 834 | def hex_str_vector(self, M): |
| | 835 | """ |
| | 836 | Return a one dimensional AES like representation of the matrix M. |
| | 837 | |
| | 838 | That is, show the finite field elements as hex strings. |
| | 839 | |
| | 840 | INPUT: |
| | 841 | M -- an AES state array |
| | 842 | |
| | 843 | """ |
| | 844 | e = M.base_ring().degree() |
| | 845 | st = [""] |
| | 846 | for y in range(M.ncols()): |
| | 847 | for x in range(M.nrows()): |
| | 848 | if e == 8: |
| | 849 | st.append("%02X"%(int(str(M[x,y].int_repr())))) |
| | 850 | else: |
| | 851 | st.append("%X"%(int(str(M[x,y].int_repr())))) |
| | 852 | #st.append("\n") |
| | 853 | return "".join(st) |
| | 854 | |
| | 855 | def _insert_matrix_into_matrix(self, dst, src, row, col): |
| | 856 | """ |
| | 857 | Insert matrix src into matrix dst starting at row and col. |
| | 858 | |
| | 859 | INPUT: |
| | 860 | dst -- a matrix |
| | 861 | src -- a matrix |
| | 862 | row -- offset row |
| | 863 | col -- offset columns |
| | 864 | |
| | 865 | EXAMPLE: |
| | 866 | sage: sr = mq.SR(10,4,4,4) |
| | 867 | sage: a = sr.k.gen() |
| | 868 | sage: A = sr.state_array() + 1; A |
| | 869 | [1 0 0 0] |
| | 870 | [0 1 0 0] |
| | 871 | [0 0 1 0] |
| | 872 | [0 0 0 1] |
| | 873 | sage: B = Matrix(sr.base_ring(),2,2,[0,a,a+1,a^2]); B |
| | 874 | [ 0 a] |
| | 875 | [a + 1 a^2] |
| | 876 | sage: sr._insert_matrix_into_matrix(A,B,1,1) |
| | 877 | [ 1 0 0 0] |
| | 878 | [ 0 0 a 0] |
| | 879 | [ 0 a + 1 a^2 0] |
| | 880 | [ 0 0 0 1] |
| | 881 | """ |
| | 882 | for i in range(src.nrows()): |
| | 883 | for j in range(src.ncols()): |
| | 884 | dst[row+i,col+j] = src[i,j] |
| | 885 | return dst |
| | 886 | |
| | 887 | |
| | 888 | def varformatstr(self, name, n=None, rc=None, e=None): |
| | 889 | """ |
| | 890 | Return a format string which is understood by print et al. |
| | 891 | |
| | 892 | If a numberical value is omitted the default value of self is |
| | 893 | used. The numerical values (n,rc,e) are used to determine the |
| | 894 | width of the respective fields in the format string. |
| | 895 | |
| | 896 | INPUT: |
| | 897 | name -- name of the variable |
| | 898 | n -- number of rounds (default: None) |
| | 899 | rc -- number of rows * number of cols (default: None) |
| | 900 | e -- exponent of base field (default: None) |
| | 901 | |
| | 902 | EXAMPLE: |
| | 903 | sage: sr = mq.SR(1,2,2,4) |
| | 904 | sage: sr.varformatstr('x') |
| | 905 | 'x%01d%01d%01d' |
| | 906 | sage: sr.varformatstr('x', n=1000) |
| | 907 | 'x%03d%03d%03d' |
| | 908 | """ |
| | 909 | if n is None: |
| | 910 | n = self.n |
| | 911 | if rc is None: |
| | 912 | rc = self.r * self.c |
| | 913 | if e is None: |
| | 914 | e = self.e |
| | 915 | |
| | 916 | l = str(max([ len(str(rc-1)), len(str(n-1)), len(str(e-1)) ] )) |
| | 917 | if name != "k": |
| | 918 | pf = self._postfix |
| | 919 | else: |
| | 920 | pf = "" |
| | 921 | format_string = name + pf + "%0" + l + "d" + "%0" + l + "d" + "%0" + l + "d" |
| | 922 | return format_string |
| | 923 | |
| | 924 | def varstrs(self, name, round, rc = None, e = None): |
| | 925 | """ |
| | 926 | Return a list of strings representing variables in self. |
| | 927 | |
| | 928 | INPUT: |
| | 929 | name -- variable name |
| | 930 | round -- number of round to create variable strings for |
| | 931 | rc -- number of rounds * number of columns in the state array |
| | 932 | e -- exponent of base field |
| | 933 | |
| | 934 | EXAMPLE: |
| | 935 | sr = mq.SR(10,1,2,4) |
| | 936 | sr._varstrs('x',2) |
| | 937 | ['x200', 'x201', 'x202', 'x203', 'x210', 'x211', 'x212', 'x213'] |
| | 938 | |
| | 939 | """ |
| | 940 | if rc is None: |
| | 941 | rc = self.r * self.c |
| | 942 | |
| | 943 | if e is None: |
| | 944 | e = self.e |
| | 945 | |
| | 946 | n = self._n |
| | 947 | |
| | 948 | format_string = self.varformatstr(name,n,rc,e) |
| | 949 | |
| | 950 | return [format_string%(round,rci,ei) for rci in range(rc) for ei in range(e)] |
| | 951 | |
| | 952 | def vars(self, name, round, rc=None, e=None): |
| | 953 | """ |
| | 954 | Return a list ofvariables in self. |
| | 955 | |
| | 956 | INPUT: |
| | 957 | name -- variable name |
| | 958 | round -- number of round to create variable strings for |
| | 959 | rc -- number of rounds * number of columns in the state array |
| | 960 | e -- exponent of base field |
| | 961 | |
| | 962 | EXAMPLE: |
| | 963 | sr = mq.SR(10,1,2,4) |
| | 964 | sr._vars('x',2) |
| | 965 | [x200, x201, x202, x203, x210, x211, x212, x213] |
| | 966 | |
| | 967 | """ |
| | 968 | return [self.R(e) for e in self.varstrs(name,round,rc,e)] |
| | 969 | |
| | 970 | def block_order(self): |
| | 971 | """ |
| | 972 | Return a block order for self where each round is a block. |
| | 973 | """ |
| | 974 | r = self.r |
| | 975 | c = self.c |
| | 976 | e = self.e |
| | 977 | n = self.n |
| | 978 | k = self.k |
| | 979 | |
| | 980 | T = None |
| | 981 | for _n in range(n): |
| | 982 | T = TermOrder('degrevlex', r*e + 3*r*c*e ) + T |
| | 983 | |
| | 984 | T += TermOrder('degrevlex',r*c*e); |
| | 985 | |
| | 986 | return T |
| | 987 | |
| | 988 | def ring(self, order=None): |
| | 989 | """ |
| | 990 | Construct a ring as a base ring for the polynomial system. |
| | 991 | |
| | 992 | Variables are ordered in the reverse of their natural |
| | 993 | ordering, i.e. the reverse of as they appear. |
| | 994 | """ |
| | 995 | r = self.r |
| | 996 | c = self.c |
| | 997 | e = self.e |
| | 998 | n = self.n |
| | 999 | if not self._gf2: |
| | 1000 | k = self.base_ring() |
| | 1001 | else: |
| | 1002 | k = GF(2) |
| | 1003 | |
| | 1004 | if order is not None: |
| | 1005 | self._order = order |
| | 1006 | if self._order == 'block': |
| | 1007 | self._order = self.block_order() |
| | 1008 | |
| | 1009 | names = [] |
| | 1010 | |
| | 1011 | for _n in reversed(xrange(n)): |
| | 1012 | names += self.varstrs("k",_n+1,r*c,e) |
| | 1013 | names += self.varstrs("x",_n+1,r*c,e) |
| | 1014 | names += self.varstrs("w",_n+1,r*c,e) |
| | 1015 | names += self.varstrs("s",_n,r,e) |
| | 1016 | |
| | 1017 | names += self.varstrs("k",0,r*c,e) |
| | 1018 | |
| | 1019 | |
| | 1020 | return PolynomialRing(k, 2*n*r*c*e + (n+1)*r*c*e + n*r*e, names, order=self._order) |
| | 1021 | |
| | 1022 | def round_polynomials(self, i, plaintext = None, ciphertext = None): |
| | 1023 | r""" |
| | 1024 | Return list of polynomials for a given round $i$. |
| | 1025 | |
| | 1026 | If $i == 0$ a plaintext must be provided, if $i == n$ a |
| | 1027 | ciphertext must be provided. |
| | 1028 | |
| | 1029 | INPUT: |
| | 1030 | i -- round number |
| | 1031 | plaintext -- optional plaintext (mandatory in first round) |
| | 1032 | ciphertext -- optional ciphertext (mandatory in last round) |
| | 1033 | |
| | 1034 | OUTPUT: |
| | 1035 | MPolynomialRoundSystem |
| | 1036 | |
| | 1037 | EXAMPLE: |
| | 1038 | sage: sr = mq.SR(1,1,1,4) |
| | 1039 | sage: k = sr.base_ring() |
| | 1040 | sage: p = [k.random_element() for _ in range(sr.r*sr.c)] |
| | 1041 | sage: sr.round_polynomials(0,plaintext=p) # random |
| | 1042 | [w100 + k000 + (a^2), w101 + k001 + (a + 1), w102 + k002 + (a^2 + 1), w103 + k003 + (a)] |
| | 1043 | """ |
| | 1044 | r = self._r |
| | 1045 | c = self._c |
| | 1046 | e = self._e |
| | 1047 | n = self._n |
| | 1048 | R = self.R |
| | 1049 | |
| | 1050 | M = self.M |
| | 1051 | |
| | 1052 | _vars = self.vars |
| | 1053 | |
| | 1054 | if i == 0: |
| | 1055 | w1 = Matrix(R, r*c*e, 1, _vars("w",1,r*c,e)) |
| | 1056 | k0 = Matrix(R, r*c*e, 1, _vars("k",0,r*c,e)) |
| | 1057 | if isinstance(plaintext,(tuple,list)) and len(plaintext) == r*c: |
| | 1058 | plaintext = Matrix(R, r*c*e, 1, self.phi(plaintext)) |
| | 1059 | return MPolynomialRoundSystem(R, w1 + k0 + plaintext) |
| | 1060 | |
| | 1061 | elif i>0 and i<=n: |
| | 1062 | xj = Matrix(R, r*c*e, 1, _vars("x",i,r*c,e)) |
| | 1063 | ki = Matrix(R, r*c*e, 1, _vars("k",i,r*c,e)) |
| | 1064 | rcon = Matrix(R, r*c*e, 1, self.phi([self.sbox_constant()]*r*c)) |
| | 1065 | |
| | 1066 | if i < n: |
| | 1067 | wj = Matrix(R, r*c*e, 1, _vars("w",i+1,r*c,e)) |
| | 1068 | if i == n: |
| | 1069 | if isinstance(ciphertext,(tuple,list)) and len(ciphertext) == r*c: |
| | 1070 | ciphertext = Matrix(R, r*c*e, 1, self.phi(ciphertext)) |
| | 1071 | wj = ciphertext |
| | 1072 | |
| | 1073 | lin = (wj + ki + M * xj + rcon).list() |
| | 1074 | |
| | 1075 | |
| | 1076 | wi = Matrix(R, r*c*e, 1, _vars("w",i,r*c,e)) |
| | 1077 | xi = Matrix(R, r*c*e, 1, _vars("x",i,r*c,e)) |
| | 1078 | sbox = [] |
| | 1079 | sbox += self.inversion_polynomials(xi,wi,r*c*e) |
| | 1080 | sbox += self.field_polynomials("x",i) |
| | 1081 | sbox += self.field_polynomials("w",i) |
| | 1082 | return MPolynomialRoundSystem(R, lin + sbox) |
| | 1083 | |
| | 1084 | def key_schedule_polynomials(self, i): |
| | 1085 | """ |
| | 1086 | Return polynomials for the $i$-th round of the key schedule. |
| | 1087 | |
| | 1088 | INPUT: |
| | 1089 | i -- round (0 <= i <= n) |
| | 1090 | """ |
| | 1091 | R = self.R |
| | 1092 | r = self.r |
| | 1093 | e = self.e |
| | 1094 | c = self.c |
| | 1095 | k = self.k |
| | 1096 | a = k.gen() |
| | 1097 | |
| | 1098 | if i < 0: |
| | 1099 | raise TypeError, "i must by >= 0" |
| | 1100 | |
| | 1101 | if i == 0: |
| | 1102 | return MPolynomialRoundSystem(R, self.field_polynomials("k",i, r*c)) |
| | 1103 | else: |
| | 1104 | L = self.lin_matrix(r) |
| | 1105 | ki = Matrix(R, r*c*e, 1, self.vars("k", i ,r*c,e)) |
| | 1106 | kj = Matrix(R, r*c*e, 1, self.vars("k", i-1,r*c,e)) |
| | 1107 | si = Matrix(R, r*e, 1, self.vars("s",i-1,r,e)) |
| | 1108 | |
| | 1109 | rc = Matrix(R, r*e, 1, self.phi([a**(i-1)] + [k(0)]*(r-1)) ) |
| | 1110 | d = Matrix(R, r*e, 1, self.phi([self.sbox_constant()]*r) ) |
| | 1111 | |
| | 1112 | sbox = [] |
| | 1113 | |
| | 1114 | sbox += self.field_polynomials("k",i) |
| | 1115 | sbox += self.field_polynomials("s",i-1, r) |
| | 1116 | |
| | 1117 | if r == 1: |
| | 1118 | sbox += self.inversion_polynomials(kj[(c - 1)*e:(c - 1)*e + e], si[0:e], e) |
| | 1119 | if r == 2: |
| | 1120 | sbox += self.inversion_polynomials( kj[(2*c -1)*e : (2*c -1)*e + e] , si[0:1*e], e ) |
| | 1121 | sbox += self.inversion_polynomials( kj[(2*c -2)*e : (2*c -2)*e + e] , si[e:2*e], e ) |
| | 1122 | if r == 4: |
| | 1123 | if self._aes_mode: |
| | 1124 | sbox += self.inversion_polynomials( kj[(4*c-3)*e : (4*c-3)*e + e] , si[0*e : 1*e] , e ) |
| | 1125 | sbox += self.inversion_polynomials( kj[(4*c-2)*e : (4*c-2)*e + e] , si[1*e : 2*e] , e ) |
| | 1126 | sbox += self.inversion_polynomials( kj[(4*c-1)*e : (4*c-1)*e + e] , si[2*e : 3*e] , e ) |
| | 1127 | sbox += self.inversion_polynomials( kj[(4*c-4)*e : (4*c-4)*e + e] , si[3*e : 4*e] , e ) |
| | 1128 | else: |
| | 1129 | sbox += self.inversion_polynomials( kj[(4*c-1)*e : (4*c-1)*e + e] , si[0*e : 1*e] , e ) |
| | 1130 | sbox += self.inversion_polynomials( kj[(4*c-2)*e : (4*c-2)*e + e] , si[1*e : 2*e] , e ) |
| | 1131 | sbox += self.inversion_polynomials( kj[(4*c-3)*e : (4*c-3)*e + e] , si[2*e : 3*e] , e ) |
| | 1132 | sbox += self.inversion_polynomials( kj[(4*c-4)*e : (4*c-4)*e + e] , si[3*e : 4*e] , e ) |
| | 1133 | |
| | 1134 | si = L * si + d + rc |
| | 1135 | Sum = Matrix(R, r*e, 1) |
| | 1136 | lin = [] |
| | 1137 | if c>1: |
| | 1138 | for q in range(c): |
| | 1139 | t = range(r*e*(q) , r*e*(q+1) ) |
| | 1140 | Sum += kj.matrix_from_rows(t) |
| | 1141 | lin += (ki.matrix_from_rows(t) + si + Sum).list() |
| | 1142 | |
| | 1143 | else: |
| | 1144 | lin += (ki + si).list() |
| | 1145 | return MPolynomialRoundSystem(R, lin + sbox ) |
| | 1146 | |
| | 1147 | def polynomial_system(self, P=None, K=None): |
| | 1148 | """ |
| | 1149 | Return a MPolynomialSystem for self for a given plaintext-key pair. |
| | 1150 | |
| | 1151 | If none are provided a random pair will be generated. |
| | 1152 | |
| | 1153 | INPUT: |
| | 1154 | P -- vector, list, or tuple (default: None) |
| | 1155 | K -- vector, list, or tuple (default: None) |
| | 1156 | """ |
| | 1157 | plaintext = P |
| | 1158 | key = K |
| | 1159 | |
| | 1160 | system = [] |
| | 1161 | n = self._n |
| | 1162 | |
| | 1163 | data = [] |
| | 1164 | |
| | 1165 | for d in (plaintext, key): |
| | 1166 | if d is None: |
| | 1167 | data.append(self.random_element("vector")) |
| | 1168 | elif isinstance(d, (tuple,list)): |
| | 1169 | data.append( self.phi(self.state_array(d)) ) |
| | 1170 | elif self.is_state_array(d): |
| | 1171 | data.append( self.phi(d) ) |
| | 1172 | elif self.is_vector(d): |
| | 1173 | data.append( d ) |
| | 1174 | else: |
| | 1175 | raise TypeError, "type %s of %s not understood"%(type(d),d) |
| | 1176 | |
| | 1177 | plaintext, key = data |
| | 1178 | |
| | 1179 | ciphertext = self(plaintext, key) |
| | 1180 | |
| | 1181 | for i in range(n+1): |
| | 1182 | system.append( self.round_polynomials(i,plaintext, ciphertext) ) |
| | 1183 | system.append( self.key_schedule_polynomials(i) ) |
| | 1184 | |
| | 1185 | return MPolynomialSystem(self.R, system), dict(zip(self.vars("k",0),key.list())) |
| | 1186 | |
| | 1187 | |
| | 1188 | class SR_gf2n(SR_generic): |
| | 1189 | r""" |
| | 1190 | Small Scale Variants of the AES polynomial system constructor over $GF(2^n)$. |
| | 1191 | """ |
| | 1192 | def vector(self, d=None): |
| | 1193 | """ |
| | 1194 | Constructs a vector suitable for the algebraic representation of SR, i.e. BES. |
| | 1195 | |
| | 1196 | INPUT: |
| | 1197 | d -- values for vector, must be understood by self.phi (default:None) |
| | 1198 | """ |
| | 1199 | r = self.r |
| | 1200 | c = self.c |
| | 1201 | e = self.e |
| | 1202 | k = self.base_ring() |
| | 1203 | |
| | 1204 | if d is None: |
| | 1205 | return Matrix(k, r*c*e, 1) |
| | 1206 | elif d.ncols() == c and d.nrows() == r and d.base_ring() == k: |
| | 1207 | return Matrix(k, r*c*e, 1, self.phi(d).transpose()) |
| | 1208 | |
| | 1209 | def is_vector(self, d): |
| | 1210 | """ |
| | 1211 | Return True if d can be used as a vector for self. |
| | 1212 | """ |
| | 1213 | return is_Matrix(d) and \ |
| | 1214 | d.nrows() == self.r*self.c*self.e and \ |
| | 1215 | d.ncols() == 1 and \ |
| | 1216 | d.base_ring() == self.base_ring() |
| | 1217 | |
| | 1218 | def phi(self, l): |
| | 1219 | r""" |
| | 1220 | Projects state arrays to their algebraic representation. |
| | 1221 | """ |
| | 1222 | ret = [] |
| | 1223 | if is_Matrix(l): |
| | 1224 | for e in l.transpose().list(): |
| | 1225 | ret += [e**(2**i) for i in range(self.e)] |
| | 1226 | else: |
| | 1227 | for e in l: |
| | 1228 | ret += [e**(2**i) for i in range(self.e)] |
| | 1229 | if isinstance(l,list): |
| | 1230 | return ret |
| | 1231 | elif isinstance(l,tuple): |
| | 1232 | return tuple(ret) |
| | 1233 | elif is_Matrix(l): |
| | 1234 | return Matrix(l.base_ring(),l.ncols(), l.nrows()*self.e, ret).transpose() |
| | 1235 | else: |
| | 1236 | raise TypeError |
| | 1237 | |
| | 1238 | def antiphi(self,l): |
| | 1239 | """ |
| | 1240 | Inverse of self.phi. |
| | 1241 | """ |
| | 1242 | if is_Matrix(l): |
| | 1243 | ret = [e for e in l.transpose().list()[0:-1:self.e]] |
| | 1244 | else: |
| | 1245 | ret = [e for e in l[0:-1:self.e]] |
| | 1246 | |
| | 1247 | if isinstance(l,list): |
| | 1248 | return ret |
| | 1249 | elif isinstance(l,tuple): |
| | 1250 | return tuple(ret) |
| | 1251 | elif is_Matrix(l): |
| | 1252 | return Matrix(self.base_ring(),l.ncols(), l.nrows()/self.e, ret).transpose() |
| | 1253 | else: |
| | 1254 | raise TypeError |
| | 1255 | |
| | 1256 | def shift_rows_matrix(self): |
| | 1257 | """ |
| | 1258 | Return the ShiftRows matrix. |
| | 1259 | |
| | 1260 | EXAMPLE: |
| | 1261 | sage: sr = mq.SR(1,2,2,4) |
| | 1262 | sage: s = sr.random_state_array() |
| | 1263 | sage: r1 = sr.shift_rows(s) |
| | 1264 | sage: r2 = sr.state_array( sr.ShiftRows * sr.vector(s) ) |
| | 1265 | sage: r1 == r2 |
| | 1266 | True |
| | 1267 | """ |
| | 1268 | e = self.e |
| | 1269 | r = self.r |
| | 1270 | c = self.c |
| | 1271 | k = self.base_ring() |
| | 1272 | bs = r*c*e |
| | 1273 | shift_rows = Matrix(k,bs,bs) |
| | 1274 | I = MatrixSpace(k,e,e)(1) |
| | 1275 | for x in range(0, c): |
| | 1276 | for y in range(0,r): |
| | 1277 | _r = ((x*r)+y) * e |
| | 1278 | _c = (((x*r)+((r+1)*y)) * e) % bs |
| | 1279 | self._insert_matrix_into_matrix(shift_rows,I, _r, _c) |
| | 1280 | |
| | 1281 | return shift_rows |
| | 1282 | |
| | 1283 | def lin_matrix(self, length = None): |
| | 1284 | """ |
| | 1285 | Return the Lin matrix. |
| | 1286 | |
| | 1287 | If no length is provided the standard state space size is |
| | 1288 | used. The key schedule calls this method with an explicit |
| | 1289 | length argument because only self.r S-Box applications are |
| | 1290 | performed in the key schedule. |
| | 1291 | |
| | 1292 | INPUT: |
| | 1293 | length -- length of state space. (default: None) |
| | 1294 | """ |
| | 1295 | r = self.r |
| | 1296 | c = self.c |
| | 1297 | e = self.e |
| | 1298 | k = self.k |
| | 1299 | |
| | 1300 | if length is None: |
| | 1301 | length = r*c |
| | 1302 | |
| | 1303 | lin = Matrix(self.base_ring(), length*e, length*e) |
| | 1304 | if e == 4: |
| | 1305 | l = [ k.fetch_int(x) for x in (5, 1, 12, 5) ] |
| | 1306 | for k in range( 0, length ): |
| | 1307 | for i in range(0,4): |
| | 1308 | for j in range(0,4): |
| | 1309 | lin[k*4+j,k*4+i]= l[(i-j)%4] ** (2**j) |
| | 1310 | elif e == 8: |
| | 1311 | l = [ k.fetch_int(x) for x in (5, 9, 249, 37, 244, 1, 181, 143) ] |
| | 1312 | for k in range( 0, length ): |
| | 1313 | for i in range(0,8): |
| | 1314 | for j in range(0,8): |
| | 1315 | lin[k*8+j,k*8+i]= l[(i-j)%8] ** (2**j) |
| | 1316 | |
| | 1317 | return lin |
| | 1318 | |
| | 1319 | def mix_columns_matrix(self): |
| | 1320 | """ |
| | 1321 | Return the MixColumns matrix. |
| | 1322 | |
| | 1323 | EXAMPLE: |
| | 1324 | sage: sr = mq.SR(1,2,2,4) |
| | 1325 | sage: s = sr.random_state_array() |
| | 1326 | sage: r1 = sr.mix_columns(s) |
| | 1327 | sage: r2 = sr.state_array(sr.MixColumns * sr.vector(s)) |
| | 1328 | sage: r1 == r2 |
| | 1329 | True |
| | 1330 | """ |
| | 1331 | |
| | 1332 | def D(b): |
| | 1333 | D = Matrix(self.base_ring(), self._e, self._e) |
| | 1334 | for i in range(self._e): |
| | 1335 | D[i,i] = b**(2**i) |
| | 1336 | return D |
| | 1337 | |
| | 1338 | r = self.r |
| | 1339 | c = self.c |
| | 1340 | e = self.e |
| | 1341 | k = self.k |
| | 1342 | a = k.gen() |
| | 1343 | |
| | 1344 | M = Matrix(k,r*e,r*e) |
| | 1345 | |
| | 1346 | if r == 1: |
| | 1347 | self._insert_matrix_into_matrix(M, D(1), 0, 0) |
| | 1348 | |
| | 1349 | elif r == 2: |
| | 1350 | self._insert_matrix_into_matrix(M, D(a+1), 0, 0) |
| | 1351 | self._insert_matrix_into_matrix(M, D(a+1), e, e) |
| | 1352 | self._insert_matrix_into_matrix(M, D(a), e, 0) |
| | 1353 | self._insert_matrix_into_matrix(M, D(a), 0, e) |
| | 1354 | |
| | 1355 | elif r == 4: |
| | 1356 | self._insert_matrix_into_matrix(M, D(a), 0, 0) |
| | 1357 | self._insert_matrix_into_matrix(M, D(a), e, e) |
| | 1358 | self._insert_matrix_into_matrix(M, D(a), 2*e, 2*e) |
| | 1359 | self._insert_matrix_into_matrix(M, D(a), 3*e, 3*e) |
| | 1360 | |
| | 1361 | self._insert_matrix_into_matrix(M, D(a+1), 0, e) |
| | 1362 | self._insert_matrix_into_matrix(M, D(a+1), e, 2*e) |
| | 1363 | self._insert_matrix_into_matrix(M, D(a+1), 2*e, 3*e) |
| | 1364 | self._insert_matrix_into_matrix(M, D(a+1), 3*e, 0) |
| | 1365 | |
| | 1366 | self._insert_matrix_into_matrix(M, D(1), 0, 2*e) |
| | 1367 | self._insert_matrix_into_matrix(M, D(1), e, 3*e) |
| | 1368 | self._insert_matrix_into_matrix(M, D(1), 2*e, 0) |
| | 1369 | self._insert_matrix_into_matrix(M, D(1), 3*e, 1*e) |
| | 1370 | |
| | 1371 | self._insert_matrix_into_matrix(M, D(1), 0, 3*e) |
| | 1372 | self._insert_matrix_into_matrix(M, D(1), e, 0) |
| | 1373 | self._insert_matrix_into_matrix(M, D(1), 2*e, 1*e) |
| | 1374 | self._insert_matrix_into_matrix(M, D(1), 3*e, 2*e) |
| | 1375 | |
| | 1376 | mix_columns = Matrix(k,r*c*e,r*c*e) |
| | 1377 | |
| | 1378 | for i in range(c): |
| | 1379 | self._insert_matrix_into_matrix(mix_columns, M, r*e*i, r*e*i) |
| | 1380 | |
| | 1381 | return mix_columns |
| | 1382 | |
| | 1383 | def inversion_polynomials(self, xi, wi, length): |
| | 1384 | """ |
| | 1385 | Return polynomials to represent the inversion in the AES S-Box. |
| | 1386 | |
| | 1387 | INPUT: |
| | 1388 | xi -- output variables |
| | 1389 | wi -- input variables |
| | 1390 | length -- length of both lists |
| | 1391 | """ |
| | 1392 | return [xi[j,0]*wi[j,0] + 1 for j in range(length)] |
| | 1393 | |
| | 1394 | def field_polynomials(self, name, i, l=None): |
| | 1395 | """ |
| | 1396 | Return list of conjugacy polynomials for a given round and name. |
| | 1397 | |
| | 1398 | INPUT: |
| | 1399 | name -- variable name |
| | 1400 | i -- round number |
| | 1401 | l -- r*c (default:None) |
| | 1402 | |
| | 1403 | EXAMPLE: |
| | 1404 | sage: sr = mq.SR(3,1,1,8) |
| | 1405 | sage: sr.field_polynomials('x',2) |
| | 1406 | [x200^2 + x201, |
| | 1407 | x201^2 + x202, |
| | 1408 | x202^2 + x203, |
| | 1409 | x203^2 + x204, |
| | 1410 | x204^2 + x205, |
| | 1411 | x205^2 + x206, |
| | 1412 | x206^2 + x207, |
| | 1413 | x207^2 + x200] |
| | 1414 | """ |
| | 1415 | r = self._r |
| | 1416 | c = self._c |
| | 1417 | e = self._e |
| | 1418 | n = self._n |
| | 1419 | |
| | 1420 | if l is None: |
| | 1421 | l = r*c |
| | 1422 | |
| | 1423 | fms = self.varformatstr(name,n,l,e) |
| | 1424 | |
| | 1425 | return [self.R( fms%(i,rci,ei) + "**2 + " + fms%(i, rci,(ei+1)%e) ) for rci in range(l) for ei in range(e) ] |
| | 1426 | |
| | 1427 | |
| | 1428 | |
| | 1429 | class SR_gf2(SR_generic): |
| | 1430 | r""" |
| | 1431 | Small Scale Variants of the AES polynomial system constructor over $GF(2)$. |
| | 1432 | """ |
| | 1433 | def __init__(self,n=1,r=1,c=1,e=4, star=False, **kwargs): |
| | 1434 | """ |
| | 1435 | See help for SR. |
| | 1436 | |
| | 1437 | """ |
| | 1438 | SR_generic.__init__(self,n,r,c,e,star,**kwargs) |
| | 1439 | self._correct_only = kwargs.get("correct_only",False) |
| | 1440 | self._biaffine_only = kwargs.get("biaffine_only",True) |
| | 1441 | |
| | 1442 | def vector(self, d=None): |
| | 1443 | """ |
| | 1444 | Constructs a vector suitable for the algebraic representation of SR. |
| | 1445 | |
| | 1446 | INPUT: |
| | 1447 | d -- values for vector(default:None) |
| | 1448 | """ |
| | 1449 | r = self.r |
| | 1450 | c = self.c |
| | 1451 | e = self.e |
| | 1452 | k = GF(2) |
| | 1453 | |
| | 1454 | if d is None: |
| | 1455 | return Matrix(k, r*c*e, 1) |
| | 1456 | elif is_Matrix(d) and d.ncols() == c and d.nrows() == r and d.base_ring() == self.k: |
| | 1457 | l = flatten([self.phi(x) for x in d.transpose().list()], Vector_modn_dense) |
| | 1458 | return Matrix(k, r*c*e, 1,l) |
| | 1459 | elif isinstance(d,(list,tuple)): |
| | 1460 | if len(d) == self.r*self.c: |
| | 1461 | l = flatten([self.phi(x) for x in d], Vector_modn_dense) |
| | 1462 | return Matrix(k, r*c*e, 1,l) |
| | 1463 | elif len(d) == self.r*self.c*self.e: |
| | 1464 | return Matrix(k, r*c*e, 1, d) |
| | 1465 | else: |
| | 1466 | raise TypeError |
| | 1467 | else: |
| | 1468 | raise TypeError |
| | 1469 | |
| | 1470 | def is_vector(self, d): |
| | 1471 | """ |
| | 1472 | Return True if the given matrix satisfies the conditions for a vector |
| | 1473 | as it appears in the algebraic expression of self. |
| | 1474 | |
| | 1475 | INPUT: |
| | 1476 | d -- matrix |
| | 1477 | """ |
| | 1478 | return is_Matrix(d) and \ |
| | 1479 | d.nrows() == self.r*self.c*self.e and \ |
| | 1480 | d.ncols() == 1 and \ |
| | 1481 | d.base_ring() == GF(2) |
| | 1482 | |
| | 1483 | def phi(self, l, diffusion_matrix=False): |
| | 1484 | r""" |
| | 1485 | Given a list/matrix of elements in $GF(2^n)$ return a matching |
| | 1486 | list/matrix of elements in $GF(2)$. |
| | 1487 | |
| | 1488 | INPUT: |
| | 1489 | l -- element to perform phi on. |
| | 1490 | diffusion_matrix -- if True the given matrix $l$ is transformed |
| | 1491 | to a matrix which performs the same operation |
| | 1492 | over GF(2) as $l$ over $GF(2^n)$ (default: False). |
| | 1493 | """ |
| | 1494 | ret = [] |
| | 1495 | r,c,e = self.r,self.c,self.e |
| | 1496 | |
| | 1497 | # handle diffusion layer matrices first |
| | 1498 | if is_Matrix(l) and diffusion_matrix and \ |
| | 1499 | l.nrows() == r*c and l.ncols() == r*c and \ |
| | 1500 | l.base_ring() == self.k: |
| | 1501 | B = Matrix(GF(2), r*c*e, r*c*e) |
| | 1502 | for x in range(r*c): |
| | 1503 | for y in range(r*c): |
| | 1504 | T = self._mul_matrix(l[x,y]) |
| | 1505 | self._insert_matrix_into_matrix(B,T,x*e, y*e) |
| | 1506 | return B |
| | 1507 | |
| | 1508 | # ground field elements |
| | 1509 | if l in self.k: |
| | 1510 | return list(reversed(l.vector())) |
| | 1511 | |
| | 1512 | # remaining matrices |
| | 1513 | if is_Matrix(l): |
| | 1514 | for x in l.transpose().list(): |
| | 1515 | ret += list(reversed(x.vector())) |
| | 1516 | # or lists |
| | 1517 | else: |
| | 1518 | for x in l: |
| | 1519 | ret += list(reversed(x.vector())) |
| | 1520 | |
| | 1521 | if isinstance(l,list): return ret |
| | 1522 | elif isinstance(l,tuple): return tuple(ret) |
| | 1523 | elif is_Matrix(l): return Matrix(GF(2), l.ncols(), l.nrows()*self.e, ret).transpose() |
| | 1524 | else: raise TypeError |
| | 1525 | |
| | 1526 | def antiphi(self,l): |
| | 1527 | """ |
| | 1528 | Inverse of self.phi. |
| | 1529 | """ |
| | 1530 | e = self.e |
| | 1531 | V = self.k.vector_space() |
| | 1532 | |
| | 1533 | if is_Matrix(l): |
| | 1534 | l2 = l.transpose().list() |
| | 1535 | else: |
| | 1536 | l2 = l |
| | 1537 | |
| | 1538 | ret = [] |
| | 1539 | for i in range(0,len(l2),e): |
| | 1540 | ret.append( self.k(V(list(reversed(l2[i:i+e])))) ) |
| | 1541 | |
| | 1542 | if isinstance(l, list): |
| | 1543 | return ret |
| | 1544 | elif isinstance(l,tuple): |
| | 1545 | return tuple(ret) |
| | 1546 | elif is_Matrix(l): |
| | 1547 | return Matrix(self.base_ring(), self.r *self.c, 1, ret) |
| | 1548 | else: |
| | 1549 | raise TypeError |
| | 1550 | |
| | 1551 | def shift_rows_matrix(self): |
| | 1552 | """ |
| | 1553 | Return the ShiftRows matrix. |
| | 1554 | |
| | 1555 | EXAMPLE: |
| | 1556 | sage: sr = mq.SR(1,2,2,4,gf2=True) |
| | 1557 | sage: s = sr.random_state_array() |
| | 1558 | sage: r1 = sr.shift_rows(s) |
| | 1559 | sage: r2 = sr.state_array( sr.ShiftRows * sr.vector(s) ) |
| | 1560 | sage: r1 == r2 |
| | 1561 | True |
| | 1562 | """ |
| | 1563 | r = self.r |
| | 1564 | c = self.c |
| | 1565 | k = self.k |
| | 1566 | bs = r*c |
| | 1567 | shift_rows = Matrix(k,r*c,r*c) |
| | 1568 | for x in range(0, c): |
| | 1569 | for y in range(0,r): |
| | 1570 | _r = ((x*r)+y) |
| | 1571 | _c = ((x*r)+((r+1)*y)) % bs |
| | 1572 | shift_rows[_r,_c] = 1 |
| | 1573 | return self.phi(shift_rows, diffusion_matrix=True) |
| | 1574 | |
| | 1575 | def mix_columns_matrix(self): |
| | 1576 | """ |
| | 1577 | Return the MixColumns matrix. |
| | 1578 | |
| | 1579 | EXAMPLE: |
| | 1580 | sage: sr = mq.SR(1,2,2,4,gf2=True) |
| | 1581 | sage: s = sr.random_state_array() |
| | 1582 | sage: r1 = sr.mix_columns(s) |
| | 1583 | sage: r2 = sr.state_array(sr.MixColumns * sr.vector(s)) |
| | 1584 | sage: r1 == r2 |
| | 1585 | True |
| | 1586 | """ |
| | 1587 | r = self.r |
| | 1588 | c = self.c |
| | 1589 | k = self.k |
| | 1590 | a = k.gen() |
| | 1591 | |
| | 1592 | |
| | 1593 | |
| | 1594 | if r == 1: |
| | 1595 | M = Matrix(k,r,r,1) |
| | 1596 | |
| | 1597 | elif r == 2: |
| | 1598 | M = Matrix(k,r,r,[a+1,a,a,a+1]) |
| | 1599 | |
| | 1600 | elif r == 4: |
| | 1601 | M = Matrix(k,r, [a,a+1,1,1,\ |
| | 1602 | 1,a,a+1,1,\ |
| | 1603 | 1,1,a,a+1,\ |
| | 1604 | a+1,1,1,a]) |
| | 1605 | |
| | 1606 | mix_columns = Matrix(k,r*c,r*c) |
| | 1607 | |
| | 1608 | for i in range(c): |
| | 1609 | self._insert_matrix_into_matrix(mix_columns, M, r*i, r*i) |
| | 1610 | |
| | 1611 | return self.phi(mix_columns, diffusion_matrix=True) |
| | 1612 | |
| | 1613 | def lin_matrix(self, length=None): |
| | 1614 | """ |
| | 1615 | Return the Lin matrix. |
| | 1616 | |
| | 1617 | If no length is provided the standard state space size is |
| | 1618 | used. The key schedule calls this method with an explicit |
| | 1619 | length argument because only self.r S-Box applications are |
| | 1620 | performed in the key schedule. |
| | 1621 | |
| | 1622 | INPUT: |
| | 1623 | length -- length of state space. (default: None) |
| | 1624 | """ |
| | 1625 | r,c,e = self.r, self.c,self.e |
| | 1626 | |
| | 1627 | if length is None: |
| | 1628 | length = r*c |
| | 1629 | |
| | 1630 | if e == 8: |
| | 1631 | Z = Matrix(GF(2),8,8,[1,0,0,0,1,1,1,1,\ |
| | 1632 | 1,1,0,0,0,1,1,1,\ |
| | 1633 | 1,1,1,0,0,0,1,1,\ |
| | 1634 | 1,1,1,1,0,0,0,1,\ |
| | 1635 | 1,1,1,1,1,0,0,0,\ |
| | 1636 | 0,1,1,1,1,1,0,0,\ |
| | 1637 | 0,0,1,1,1,1,1,0,\ |
| | 1638 | 0,0,0,1,1,1,1,1]) |
| | 1639 | else: |
| | 1640 | Z = Matrix(GF(2),4,4,[1,1,1,0,\ |
| | 1641 | 0,1,1,1,\ |
| | 1642 | 1,0,1,1,\ |
| | 1643 | 1,1,0,1]) |
| | 1644 | |
| | 1645 | |
| | 1646 | Z = Z.transpose() # account for endianess mismatch |
| | 1647 | |
| | 1648 | lin = Matrix(GF(2),length*e,length*e) |
| | 1649 | |
| | 1650 | for i in range(length): |
| | 1651 | self._insert_matrix_into_matrix(lin,Z,i*e,i*e) |
| | 1652 | return lin |
| | 1653 | |
| | 1654 | def _mul_matrix(self, x): |
| | 1655 | """ |
| | 1656 | Given an element $x$ in self.base_ring() return a matrix which |
| | 1657 | performs the same operation on a when interpreted over |
| | 1658 | $GF(2)^e$ as $x$ over $GF(2^e)$. |
| | 1659 | |
| | 1660 | INPUT: |
| | 1661 | x -- an element in self.base_ring() |
| | 1662 | |
| | 1663 | EXAMPLE: |
| | 1664 | sage: sr = mq.SR(gf2=True) |
| | 1665 | sage: a = sr.k.gen() |
| | 1666 | sage: A = sr._mul_matrix(a^2+1) |
| | 1667 | sage: sr.antiphi( A * sr.vector([a+1]) ) |
| | 1668 | [a^3 + a^2 + a + 1] |
| | 1669 | |
| | 1670 | sage: (a^2 + 1)*(a+1) |
| | 1671 | a^3 + a^2 + a + 1 |
| | 1672 | """ |
| | 1673 | a = self.k.gen() |
| | 1674 | k = self.k |
| | 1675 | e = self.e |
| | 1676 | a = k.gen() |
| | 1677 | |
| | 1678 | columns = [] |
| | 1679 | for i in reversed(range(e)): |
| | 1680 | columns.append( list(reversed((x * a**i).vector())) ) |
| | 1681 | return Matrix(GF(2), e, e, columns).transpose() |
| | 1682 | |
| | 1683 | def _square_matrix(self): |
| | 1684 | """ |
| | 1685 | Return a matrix of dimension self.e x self.e which performs |
| | 1686 | the squaring operation over GF(2^n) on vectors of length e. |
| | 1687 | |
| | 1688 | EXAMPLE: |
| | 1689 | sage: sr = mq.SR(gf2=True) |
| | 1690 | sage: a = sr.k.gen() |
| | 1691 | sage: S = sr._square_matrix() |
| | 1692 | sage: sr.antiphi( S * sr.vector([a^3+1]) ) |
| | 1693 | [a^3 + a^2 + 1] |
| | 1694 | |
| | 1695 | sage: (a^3 + 1)^2 |
| | 1696 | a^3 + a^2 + 1 |
| | 1697 | |
| | 1698 | """ |
| | 1699 | a = self.k.gen() |
| | 1700 | e = self.e |
| | 1701 | |
| | 1702 | columns = [] |
| | 1703 | for i in reversed(range(e)): |
| | 1704 | columns.append( list(reversed(((a**i)**2).vector())) ) |
| | 1705 | return Matrix(GF(2), e ,e, columns).transpose() |
| | 1706 | |
| | 1707 | def inversion_polynomials_single_sbox(self, x= None, w=None, biaffine_only=None, correct_only=None): |
| | 1708 | """ |
| | 1709 | Generator for S-Box inversion polynomials of a single sbox. |
| | 1710 | |
| | 1711 | INPUT: |
| | 1712 | x -- output variables (default: None) |
| | 1713 | w -- input variables (default: None) |
| | 1714 | biaffine_only -- only include biaffine polynomials (default: object default) |
| | 1715 | correct_only -- only include correct polynomials (default: object default) |
| | 1716 | |
| | 1717 | EXAMPLES: |
| | 1718 | sage: sr = mq.SR(1,1,1,8,gf2=True) |
| | 1719 | sage: len(sr.inversion_polynomials_single_sbox()) |
| | 1720 | 24 |
| | 1721 | sage: len(sr.inversion_polynomials_single_sbox(correct_only=True)) |
| | 1722 | 23 |
| | 1723 | sage: len(sr.inversion_polynomials_single_sbox(biaffine_only=False)) |
| | 1724 | 40 |
| | 1725 | sage: len(sr.inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True)) |
| | 1726 | 39 |
| | 1727 | """ |
| | 1728 | e = self.e |
| | 1729 | |
| | 1730 | if biaffine_only is None: |
| | 1731 | biaffine_only = self._biaffine_only |
| | 1732 | if correct_only is None: |
| | 1733 | correct_only = self._correct_only |
| | 1734 | |
| | 1735 | if x is None and w is None: |
| | 1736 | # make sure it prints like in the book. |
| | 1737 | names = ["w%d"%i for i in reversed(range(e))]+ ["x%d"%i for i in reversed(range(e))] |
| | 1738 | P = PolynomialRing(GF(2),e*2, names, order='lex') |
| | 1739 | x = Matrix(P,e,1,P.gens()[e:]) |
| | 1740 | w = Matrix(P,e,1,P.gens()[:e]) |
| | 1741 | else: |
| | 1742 | if isinstance(x,(tuple,list)): P = x[0].parent() |
| | 1743 | elif is_Matrix(x): P = x.base_ring() |
| | 1744 | else: raise TypeError, "x not understood" |
| | 1745 | |
| | 1746 | if isinstance(x, (tuple,list)): |
| | 1747 | x = Matrix(P,e,1, x) |
| | 1748 | if isinstance(w, (tuple,list)): |
| | 1749 | w = Matrix(P,e,1, w) |
| | 1750 | |
| | 1751 | T = self._mul_matrix(self.k.gen()) |
| | 1752 | o = Matrix(P,e,1,[0]*(e-1) + [1]) |
| | 1753 | |
| | 1754 | columns = [] |
| | 1755 | for i in reversed(range(e)): |
| | 1756 | columns.append((T**i * w).list()) |
| | 1757 | Cw = Matrix(P,e,e, columns).transpose() |
| | 1758 | |
| | 1759 | columns = [] |
| | 1760 | for i in reversed(range(e)): |
| | 1761 | columns.append((T**i * x).list()) |
| | 1762 | Cx = Matrix(P,e,e, columns).transpose() |
| | 1763 | |
| | 1764 | S = self._square_matrix() |
| | 1765 | |
| | 1766 | l = [] |
| | 1767 | if correct_only: |
| | 1768 | l.append( (Cw * x + o).list()[:-1] ) |
| | 1769 | else: |
| | 1770 | l.append( (Cw * x + o).list() ) |
| | 1771 | l.append( (Cw * S *x + x).list() ) |
| | 1772 | l.append( (Cx * S *w + w).list() ) |
| | 1773 | if not biaffine_only: |
| | 1774 | l.append( ((Cw * S**2 + Cx*S)*x).list() ) |
| | 1775 | l.append( ((Cx * S**2 + Cw*S)*w).list() ) |
| | 1776 | |
| | 1777 | return sum(l,[]) |
| | 1778 | |
| | 1779 | def inversion_polynomials(self, xi, wi, length): |
| | 1780 | """ |
| | 1781 | Return polynomials to represent the inversion in the AES S-Box. |
| | 1782 | |
| | 1783 | INPUT: |
| | 1784 | xi -- output variables |
| | 1785 | wi -- input variables |
| | 1786 | length -- length of both lists |
| | 1787 | """ |
| | 1788 | if is_Matrix(xi): |
| | 1789 | xi = xi.list() |
| | 1790 | if is_Matrix(wi): |
| | 1791 | wi = wi.list() |
| | 1792 | |
| | 1793 | e = self.e |
| | 1794 | l = [] |
| | 1795 | for j in range(0,length, e): |
| | 1796 | l += self.inversion_polynomials_single_sbox(xi[j:j+e], wi[j:j+e]) |
| | 1797 | return l |
| | 1798 | |
| | 1799 | |
| | 1800 | def field_polynomials(self, name, i, l=None): |
| | 1801 | """ |
| | 1802 | Return list of field polynomials for a given round -- given by its number $i$ -- and name. |
| | 1803 | |
| | 1804 | INPUT: |
| | 1805 | name -- variable name |
| | 1806 | i -- round number |
| | 1807 | l -- length of variable list (default:None => r*c) |
| | 1808 | |
| | 1809 | EXAMPLE: |
| | 1810 | sage: sr = mq.SR(3,1,1,8) |
| | 1811 | sage: sr.field_polynomials('x',2) |
| | 1812 | [x200^2 + x201, |
| | 1813 | x201^2 + x202, |
| | 1814 | x202^2 + x203, |
| | 1815 | x203^2 + x204, |
| | 1816 | x204^2 + x205, |
| | 1817 | x205^2 + x206, |
| | 1818 | x206^2 + x207, |
| | 1819 | x207^2 + x200] |
| | 1820 | """ |
| | 1821 | r = self._r |
| | 1822 | c = self._c |
| | 1823 | e = self._e |
| | 1824 | n = self._n |
| | 1825 | |
| | 1826 | if l is None: |
| | 1827 | l = r*c |
| | 1828 | |
| | 1829 | fms = self.varformatstr(name,n,l,e) |
| | 1830 | return [self.R( fms%(i,rci,ei) + "**2 + " + fms%(i, rci,ei) ) for rci in range(l) for ei in range(e) ] |
| | 1831 | |
| | 1832 | |
| | 1833 | def test_consistency(max_n=2, **kwargs): |
| | 1834 | r""" |
| | 1835 | Test all combinations of r,c,e and n in (1,2) for consistency of |
| | 1836 | random encryptions and their polynomial systems. $GF(2)$ and $GF(2^e)$ |
| | 1837 | systems are tested. This test takes a while. |
| | 1838 | |
| | 1839 | INPUT: |
| | 1840 | max_n -- maximal number of rounds to consider. |
| | 1841 | kwargs -- are passed to the SR constructor |
| | 1842 | |
| | 1843 | TESTS: |
| | 1844 | sage: from sage.crypto.mq.sr import test_consistency |
| | 1845 | sage: test_consistency(2) # long time |
| | 1846 | True |
| | 1847 | """ |
| | 1848 | consistent = True |
| | 1849 | for r in (1,2,4): |
| | 1850 | for c in (1,2,4): |
| | 1851 | for e in (4,8): |
| | 1852 | for n in range(1,max_n+1): |
| | 1853 | for gf2 in (True,False): |
| | 1854 | zero_division = True |
| | 1855 | while zero_division: |
| | 1856 | sr = SR(n,r,c,e,gf2=gf2, **kwargs) |
| | 1857 | try: |
| | 1858 | F, s = sr.polynomial_system() |
| | 1859 | F.subs(s) |
| | 1860 | consistent &= (F.groebner_basis('libsingular:slimgb')[0] != 1) |
| | 1861 | if not consistent: |
| | 1862 | print sr, " is not consistent" |
| | 1863 | zero_division = False |
| | 1864 | |
| | 1865 | except ZeroDivisionError: |
| | 1866 | pass |
| | 1867 | return consistent |