Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/crypto/block_cipher/sdes.py
4072 views
1
r"""
2
Simplified DES
3
4
A simplified variant of the Data Encryption Standard (DES). Note that
5
Simplified DES or S-DES is for educational purposes only. It is a
6
small-scale version of the DES designed to help beginners understand the
7
basic structure of DES.
8
9
AUTHORS:
10
11
- Minh Van Nguyen (2009-06): initial version
12
"""
13
14
###########################################################################
15
# Copyright (c) 2009 Minh Van Nguyen <[email protected]>
16
#
17
# This program is free software; you can redistribute it and/or modify
18
# it under the terms of the GNU General Public License as published by
19
# the Free Software Foundation; either version 2 of the License, or
20
# (at your option) any later version.
21
#
22
# This program is distributed in the hope that it will be useful,
23
# but WITHOUT ANY WARRANTY; without even the implied warranty of
24
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
# GNU General Public License for more details.
26
#
27
# http://www.gnu.org/licenses/
28
###########################################################################
29
30
from sage.monoids.string_monoid import BinaryStrings
31
from sage.structure.sage_object import SageObject
32
33
class SimplifiedDES(SageObject):
34
r"""
35
This class implements the Simplified Data Encryption Standard (S-DES)
36
described in [Sch96]_. Schaefer's S-DES is for educational purposes
37
only and is not secure for practical purposes. S-DES is a version of
38
the DES with all parameters significantly reduced, but at the same time
39
preserving the structure of DES. The goal of S-DES is to allow a
40
beginner to understand the structure of DES, thus laying a foundation
41
for a thorough study of DES. Its goal is as a teaching tool in the same
42
spirit as Phan's
43
:mod:`Mini-AES <sage.crypto.block_cipher.miniaes>` [Pha02]_.
44
45
EXAMPLES:
46
47
Encrypt a random block of 8-bit plaintext using a random key, decrypt
48
the ciphertext, and compare the result with the original plaintext::
49
50
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
51
sage: sdes = SimplifiedDES(); sdes
52
Simplified DES block cipher with 10-bit keys
53
sage: bin = BinaryStrings()
54
sage: P = [bin(str(randint(0, 1))) for i in xrange(8)]
55
sage: K = sdes.random_key()
56
sage: C = sdes.encrypt(P, K)
57
sage: plaintxt = sdes.decrypt(C, K)
58
sage: plaintxt == P
59
True
60
61
We can also encrypt binary strings that are larger than 8 bits in length.
62
However, the number of bits in that binary string must be positive
63
and a multiple of 8::
64
65
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
66
sage: sdes = SimplifiedDES()
67
sage: bin = BinaryStrings()
68
sage: P = bin.encoding("Encrypt this using S-DES!")
69
sage: Mod(len(P), 8) == 0
70
True
71
sage: K = sdes.list_to_string(sdes.random_key())
72
sage: C = sdes(P, K, algorithm="encrypt")
73
sage: plaintxt = sdes(C, K, algorithm="decrypt")
74
sage: plaintxt == P
75
True
76
77
REFERENCES:
78
79
.. [Pha02] R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a
80
testbed for cryptanalysis students. Cryptologia, 26(4):283--306, 2002.
81
82
.. [Sch96] E. Schaefer. A simplified data encryption algorithm.
83
Cryptologia, 20(1):77--84, 1996.
84
"""
85
86
def __init__(self):
87
r"""
88
A simplified variant of the Data Encryption Standard (DES).
89
90
EXAMPLES::
91
92
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
93
sage: sdes = SimplifiedDES(); sdes
94
Simplified DES block cipher with 10-bit keys
95
sage: B = BinaryStrings()
96
sage: P = [B(str(randint(0, 1))) for i in xrange(8)]
97
sage: K = sdes.random_key()
98
sage: C = sdes.encrypt(P, K)
99
sage: plaintxt = sdes.decrypt(C, K)
100
sage: plaintxt == P
101
True
102
"""
103
from sage.crypto.mq import SBox
104
# the number of bits in a secret key
105
self._key_size = 10
106
# the S-box S_0
107
self._sbox0 = SBox(1, 0, 3, 2, 3, 2, 1, 0, 0, 2, 1, 3, 3, 1, 3, 2)
108
# the S-box S_1
109
self._sbox1 = SBox(0, 1, 2, 3, 2, 0, 1, 3, 3, 0, 1, 0, 2, 1, 0, 3)
110
111
def __call__(self, B, K, algorithm="encrypt"):
112
r"""
113
Apply S-DES encryption or decryption on the binary string ``B``
114
using the key ``K``. The flag ``algorithm`` controls what action is
115
to be performed on ``B``.
116
117
INPUT:
118
119
- ``B`` -- a binary string, where the number of bits is positive and
120
a multiple of 8.
121
122
- ``K`` -- a secret key; this must be a 10-bit binary string
123
124
- ``algorithm`` -- (default: ``"encrypt"``) a string; a flag to signify
125
whether encryption or decryption is to be applied to the binary
126
string ``B``. The encryption flag is ``"encrypt"`` and the decryption
127
flag is ``"decrypt"``.
128
129
OUTPUT:
130
131
- The ciphertext (respectively plaintext) corresponding to the
132
binary string ``B``.
133
134
EXAMPLES:
135
136
Encrypt a plaintext, decrypt the ciphertext, and compare the
137
result with the original plaintext::
138
139
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
140
sage: sdes = SimplifiedDES()
141
sage: bin = BinaryStrings()
142
sage: P = bin.encoding("Encrypt this using DES!")
143
sage: K = sdes.random_key()
144
sage: K = sdes.list_to_string(K)
145
sage: C = sdes(P, K, algorithm="encrypt")
146
sage: plaintxt = sdes(C, K, algorithm="decrypt")
147
sage: plaintxt == P
148
True
149
150
TESTS:
151
152
The binary string ``B`` must be non-empty and the number of bits must
153
be a multiple of 8::
154
155
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
156
sage: sdes = SimplifiedDES()
157
sage: sdes("B", "K")
158
Traceback (most recent call last):
159
...
160
TypeError: input B must be a non-empty binary string with number of bits a multiple of 8
161
sage: bin = BinaryStrings()
162
sage: B = bin("101")
163
sage: sdes(B, "K")
164
Traceback (most recent call last):
165
...
166
ValueError: the number of bits in the binary string B must be positive and a multiple of 8
167
168
The secret key ``K`` must be a block of 10 bits::
169
170
sage: B = bin.encoding("abc")
171
sage: sdes(B, "K")
172
Traceback (most recent call last):
173
...
174
TypeError: secret key must be a 10-bit binary string
175
sage: K = bin("1010")
176
sage: sdes(B, K)
177
Traceback (most recent call last):
178
...
179
ValueError: secret key must be a 10-bit binary string
180
181
The value for ``algorithm`` must be either ``"encrypt"`` or
182
``"decrypt"``::
183
184
sage: B = bin.encoding("abc")
185
sage: K = sdes.list_to_string(sdes.random_key())
186
sage: sdes(B, K, algorithm="e")
187
Traceback (most recent call last):
188
...
189
ValueError: algorithm must be either 'encrypt' or 'decrypt'
190
sage: sdes(B, K, algorithm="d")
191
Traceback (most recent call last):
192
...
193
ValueError: algorithm must be either 'encrypt' or 'decrypt'
194
sage: sdes(B, K, algorithm="abc")
195
Traceback (most recent call last):
196
...
197
ValueError: algorithm must be either 'encrypt' or 'decrypt'
198
"""
199
from sage.monoids.string_monoid_element import StringMonoidElement
200
from sage.rings.finite_rings.integer_mod import Mod
201
# S-DES operates on 8-bit ciphertext/plaintext blocks
202
Blength = 8
203
204
if not isinstance(B, StringMonoidElement):
205
raise TypeError, "input B must be a non-empty binary string with number of bits a multiple of 8"
206
if (len(B) == 0) or (Mod(len(B), Blength).lift() != 0):
207
raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 8"
208
if not isinstance(K, StringMonoidElement):
209
raise TypeError, "secret key must be a 10-bit binary string"
210
if len(K) != self._key_size:
211
raise ValueError, "secret key must be a 10-bit binary string"
212
213
N = len(B) / Blength # the number of 8-bit blocks
214
S = ""
215
bin = BinaryStrings()
216
# encrypt each 8-bit block in succession
217
if algorithm == "encrypt":
218
for i in xrange(N):
219
# get an 8-bit block
220
block = B[i*Blength : (i+1)*Blength]
221
block = self.string_to_list(str(block))
222
key = self.string_to_list(str(K))
223
# encrypt the block using key
224
C = self.encrypt(block, key)
225
C = self.list_to_string(C)
226
# append encrypted block to ciphertext string
227
S = "".join([S, str(C)])
228
return bin(S)
229
# decrypt each 8-bit block in succession
230
elif algorithm == "decrypt":
231
for i in xrange(N):
232
# get an 8-bit block
233
block = B[i*Blength : (i+1)*Blength]
234
block = self.string_to_list(str(block))
235
key = self.string_to_list(str(K))
236
# decrypt the block using key
237
P = self.decrypt(block, key)
238
P = self.list_to_string(P)
239
# append decrypted block to plaintext string
240
S = "".join([S, str(P)])
241
return bin(S)
242
# invalid value for algorithm option
243
else:
244
raise ValueError, "algorithm must be either 'encrypt' or 'decrypt'"
245
246
def __eq__(self, other):
247
r"""
248
Compare ``self`` with ``other``.
249
250
Simplified DES objects are the same if they have the same key size
251
and S-boxes.
252
253
EXAMPLES::
254
255
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
256
sage: s = SimplifiedDES()
257
sage: s == loads(dumps(s))
258
True
259
"""
260
return ( (self._key_size == other._key_size) and
261
(self._sbox0 == other._sbox0) and
262
(self._sbox1 == other._sbox1) )
263
264
def __repr__(self):
265
r"""
266
A string representation of this Simplified DES.
267
268
EXAMPLES::
269
270
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
271
sage: SimplifiedDES()
272
Simplified DES block cipher with 10-bit keys
273
"""
274
return "Simplified DES block cipher with 10-bit keys"
275
276
def block_length(self):
277
r"""
278
Return the block length of Schaefer's S-DES block cipher. A key in
279
Schaefer's S-DES is a block of 10 bits.
280
281
OUTPUT:
282
283
- The block (or key) length in number of bits.
284
285
EXAMPLES::
286
287
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
288
sage: sdes = SimplifiedDES()
289
sage: sdes.block_length()
290
10
291
"""
292
return self._key_size
293
294
def decrypt(self, C, K):
295
r"""
296
Return an 8-bit plaintext corresponding to the ciphertext ``C``,
297
using S-DES decryption with key ``K``. The decryption process of
298
S-DES is as follows. Let `P` be the initial permutation function,
299
`P^{-1}` the corresponding inverse permutation, `\Pi_F` the
300
permutation/substitution function, and `\sigma` the switch function.
301
The ciphertext block ``C`` first goes through `P`, the output of
302
which goes through `\Pi_F` using the second subkey. Then we apply
303
the switch function to the output of the last function, and the
304
result is then fed into `\Pi_F` using the first subkey. Finally,
305
run the output through `P^{-1}` to get the plaintext.
306
307
INPUT:
308
309
- ``C`` -- an 8-bit ciphertext; a block of 8 bits
310
311
- ``K`` -- a 10-bit key; a block of 10 bits
312
313
OUTPUT:
314
315
The 8-bit plaintext corresponding to ``C``, obtained using the
316
key ``K``.
317
318
EXAMPLES:
319
320
Decrypt an 8-bit ciphertext block::
321
322
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
323
sage: sdes = SimplifiedDES()
324
sage: C = [0, 1, 0, 1, 0, 1, 0, 1]
325
sage: K = [1, 0, 1, 0, 0, 0, 0, 0, 1, 0]
326
sage: sdes.decrypt(C, K)
327
[0, 0, 0, 1, 0, 1, 0, 1]
328
329
We can also work with strings of bits::
330
331
sage: C = "01010101"
332
sage: K = "1010000010"
333
sage: sdes.decrypt(sdes.string_to_list(C), sdes.string_to_list(K))
334
[0, 0, 0, 1, 0, 1, 0, 1]
335
336
TESTS:
337
338
The ciphertext must be a block of 8 bits::
339
340
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
341
sage: sdes = SimplifiedDES()
342
sage: sdes.decrypt("C", "K")
343
Traceback (most recent call last):
344
...
345
TypeError: ciphertext must be a list of 8 bits
346
sage: sdes.decrypt([], "K")
347
Traceback (most recent call last):
348
...
349
ValueError: ciphertext must be a list of 8 bits
350
sage: sdes.decrypt([1, 2, 3, 4], "K")
351
Traceback (most recent call last):
352
...
353
ValueError: ciphertext must be a list of 8 bits
354
355
The key must be a block of 10 bits::
356
357
sage: sdes.decrypt([1, 0, 1, 0, 1, 1, 0, 1], "K")
358
Traceback (most recent call last):
359
...
360
TypeError: the key must be a list of 10 bits
361
sage: sdes.decrypt([1, 0, 1, 0, 1, 1, 0, 1], [])
362
Traceback (most recent call last):
363
...
364
TypeError: the key must be a list of 10 bits
365
sage: sdes.decrypt([1, 0, 1, 0, 1, 1, 0, 1], [1, 2, 3, 4, 5])
366
Traceback (most recent call last):
367
...
368
TypeError: the key must be a list of 10 bits
369
370
The value of each element of ``C`` or ``K`` must be either 0 or 1::
371
372
sage: C = [1, 2, 3, 4, 5, 6, 7, 8]
373
sage: K = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
374
sage: sdes.decrypt(C, K)
375
Traceback (most recent call last):
376
...
377
TypeError: Argument x (= 2) is not a valid string.
378
sage: C = [0, 1, 0, 0, 1, 1, 1, 0]
379
sage: K = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
380
sage: sdes.decrypt(C, K)
381
Traceback (most recent call last):
382
...
383
TypeError: Argument x (= 13) is not a valid string.
384
"""
385
# sanity check
386
if not isinstance(C, list):
387
raise TypeError, "ciphertext must be a list of 8 bits"
388
if len(C) != 8:
389
raise ValueError, "ciphertext must be a list of 8 bits"
390
if not isinstance(K, list):
391
raise TypeError, "the key must be a list of 10 bits"
392
if len(K) != 10:
393
raise TypeError, "the key must be a list of 10 bits"
394
395
# run through initial permutation
396
P = self.initial_permutation(C, inverse=False)
397
# run through Pi_F with subkey 2
398
P = self.permute_substitute(P, self.subkey(K, n=2))
399
# run through switch function
400
P = self.switch(P)
401
# run through Pi_F with subkey 1
402
P = self.permute_substitute(P, self.subkey(K, n=1))
403
# run through inverse permutation
404
P = self.initial_permutation(P, inverse=True)
405
# output the plaintext
406
return P
407
408
def encrypt(self, P, K):
409
r"""
410
Return an 8-bit ciphertext corresponding to the plaintext ``P``,
411
using S-DES encryption with key ``K``. The encryption process of
412
S-DES is as follows. Let `P` be the initial permutation function,
413
`P^{-1}` the corresponding inverse permutation, `\Pi_F` the
414
permutation/substitution function, and `\sigma` the switch function.
415
The plaintext block ``P`` first goes through `P`, the output of
416
which goes through `\Pi_F` using the first subkey. Then we apply
417
the switch function to the output of the last function, and the
418
result is then fed into `\Pi_F` using the second subkey. Finally,
419
run the output through `P^{-1}` to get the ciphertext.
420
421
INPUT:
422
423
- ``P`` -- an 8-bit plaintext; a block of 8 bits
424
425
- ``K`` -- a 10-bit key; a block of 10 bits
426
427
OUTPUT:
428
429
The 8-bit ciphertext corresponding to ``P``, obtained using the
430
key ``K``.
431
432
EXAMPLES:
433
434
Encrypt an 8-bit plaintext block::
435
436
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
437
sage: sdes = SimplifiedDES()
438
sage: P = [0, 1, 0, 1, 0, 1, 0, 1]
439
sage: K = [1, 0, 1, 0, 0, 0, 0, 0, 1, 0]
440
sage: sdes.encrypt(P, K)
441
[1, 1, 0, 0, 0, 0, 0, 1]
442
443
We can also work with strings of bits::
444
445
sage: P = "01010101"
446
sage: K = "1010000010"
447
sage: sdes.encrypt(sdes.string_to_list(P), sdes.string_to_list(K))
448
[1, 1, 0, 0, 0, 0, 0, 1]
449
450
TESTS:
451
452
The plaintext must be a block of 8 bits::
453
454
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
455
sage: sdes = SimplifiedDES()
456
sage: sdes.encrypt("P", "K")
457
Traceback (most recent call last):
458
...
459
TypeError: plaintext must be a list of 8 bits
460
sage: sdes.encrypt([], "K")
461
Traceback (most recent call last):
462
...
463
ValueError: plaintext must be a list of 8 bits
464
sage: sdes.encrypt([1, 2, 3, 4], "K")
465
Traceback (most recent call last):
466
...
467
ValueError: plaintext must be a list of 8 bits
468
469
The key must be a block of 10 bits::
470
471
sage: sdes.encrypt([1, 0, 1, 0, 1, 1, 0, 1], "K")
472
Traceback (most recent call last):
473
...
474
TypeError: the key must be a list of 10 bits
475
sage: sdes.encrypt([1, 0, 1, 0, 1, 1, 0, 1], [])
476
Traceback (most recent call last):
477
...
478
TypeError: the key must be a list of 10 bits
479
sage: sdes.encrypt([1, 0, 1, 0, 1, 1, 0, 1], [1, 2, 3, 4, 5])
480
Traceback (most recent call last):
481
...
482
TypeError: the key must be a list of 10 bits
483
484
The value of each element of ``P`` or ``K`` must be either 0 or 1::
485
486
sage: P = [1, 2, 3, 4, 5, 6, 7, 8]
487
sage: K = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
488
sage: sdes.encrypt(P, K)
489
Traceback (most recent call last):
490
...
491
TypeError: Argument x (= 2) is not a valid string.
492
sage: P = [0, 1, 0, 0, 1, 1, 1, 0]
493
sage: K = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
494
sage: sdes.encrypt(P, K)
495
Traceback (most recent call last):
496
...
497
TypeError: Argument x (= 13) is not a valid string.
498
"""
499
# sanity check
500
if not isinstance(P, list):
501
raise TypeError, "plaintext must be a list of 8 bits"
502
if len(P) != 8:
503
raise ValueError, "plaintext must be a list of 8 bits"
504
if not isinstance(K, list):
505
raise TypeError, "the key must be a list of 10 bits"
506
if len(K) != 10:
507
raise TypeError, "the key must be a list of 10 bits"
508
509
# run through initial permutation
510
C = self.initial_permutation(P, inverse=False)
511
# run through Pi_F with subkey 1
512
C = self.permute_substitute(C, self.subkey(K, n=1))
513
# run through switch function
514
C = self.switch(C)
515
# run through Pi_F with subkey 2
516
C = self.permute_substitute(C, self.subkey(K, n=2))
517
# run through inverse permutation
518
C = self.initial_permutation(C, inverse=True)
519
# output the ciphertext
520
return C
521
522
def initial_permutation(self, B, inverse=False):
523
r"""
524
Return the initial permutation of ``B``. Denote the initial
525
permutation function by `P` and let `(b_0, b_1, b_2, \dots, b_7)`
526
be a vector of 8 bits, where each `b_i \in \{ 0, 1 \}`. Then
527
528
.. MATH::
529
530
531
P(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7)
532
= (b_1, b_5, b_2, b_0, b_3, b_7, b_4, b_6)
533
534
The inverse permutation is `P^{-1}`:
535
536
.. MATH::
537
538
P^{-1}(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7)
539
= (b_3, b_0, b_2, b_4, b_6, b_1, b_7, b_5)
540
541
INPUT:
542
543
- ``B`` -- list; a block of 8 bits
544
545
- ``inverse`` -- (default: ``False``) if ``True`` then use the
546
inverse permutation `P^{-1}`; if ``False`` then use the initial
547
permutation `P`
548
549
OUTPUT:
550
551
The initial permutation of ``B`` if ``inverse=False``, or the
552
inverse permutation of ``B`` if ``inverse=True``.
553
554
EXAMPLES:
555
556
The initial permutation of a list of 8 bits::
557
558
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
559
sage: sdes = SimplifiedDES()
560
sage: B = [1, 0, 1, 1, 0, 1, 0, 0]
561
sage: P = sdes.initial_permutation(B); P
562
[0, 1, 1, 1, 1, 0, 0, 0]
563
564
Recovering the original list of 8 bits from the permutation::
565
566
sage: Pinv = sdes.initial_permutation(P, inverse=True)
567
sage: Pinv; B
568
[1, 0, 1, 1, 0, 1, 0, 0]
569
[1, 0, 1, 1, 0, 1, 0, 0]
570
571
We can also work with a string of bits::
572
573
sage: S = "10110100"
574
sage: L = sdes.string_to_list(S)
575
sage: P = sdes.initial_permutation(L); P
576
[0, 1, 1, 1, 1, 0, 0, 0]
577
sage: sdes.initial_permutation(sdes.string_to_list("01111000"), inverse=True)
578
[1, 0, 1, 1, 0, 1, 0, 0]
579
580
TESTS:
581
582
The input block must be a list::
583
584
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
585
sage: sdes = SimplifiedDES()
586
sage: sdes.initial_permutation("B")
587
Traceback (most recent call last):
588
...
589
TypeError: input block must be a list of 8 bits
590
sage: sdes.initial_permutation(())
591
Traceback (most recent call last):
592
...
593
TypeError: input block must be a list of 8 bits
594
595
The input block must be a list of 8 bits::
596
597
sage: sdes.initial_permutation([])
598
Traceback (most recent call last):
599
...
600
ValueError: input block must be a list of 8 bits
601
sage: sdes.initial_permutation([1, 2, 3, 4, 5, 6, 7, 8, 9])
602
Traceback (most recent call last):
603
...
604
ValueError: input block must be a list of 8 bits
605
606
The value of each element of the list must be either 0 or 1::
607
608
sage: sdes.initial_permutation([1, 2, 3, 4, 5, 6, 7, 8])
609
Traceback (most recent call last):
610
...
611
TypeError: Argument x (= 2) is not a valid string.
612
"""
613
# sanity check
614
if not isinstance(B, list):
615
raise TypeError, "input block must be a list of 8 bits"
616
if len(B) != 8:
617
raise ValueError, "input block must be a list of 8 bits"
618
619
bin = BinaryStrings()
620
621
# use the initial permutation P
622
if not inverse:
623
return [ bin(str(B[1])), bin(str(B[5])),
624
bin(str(B[2])), bin(str(B[0])),
625
bin(str(B[3])), bin(str(B[7])),
626
bin(str(B[4])), bin(str(B[6])) ]
627
628
# use the inverse permutation P^-1
629
if inverse:
630
return [ bin(str(B[3])), bin(str(B[0])),
631
bin(str(B[2])), bin(str(B[4])),
632
bin(str(B[6])), bin(str(B[1])),
633
bin(str(B[7])), bin(str(B[5])) ]
634
635
def left_shift(self, B, n=1):
636
r"""
637
Return a circular left shift of ``B`` by ``n`` positions. Let
638
`B = (b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)` be a vector
639
of 10 bits. Then the left shift operation `L_n` is performed on the
640
first 5 bits and the last 5 bits of `B` separately. That is, if the
641
number of shift positions is ``n=1``, then `L_1` is defined as
642
643
.. MATH::
644
645
L_1(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)
646
= (b_1, b_2, b_3, b_4, b_0, b_6, b_7, b_8, b_9, b_5)
647
648
If the number of shift positions is ``n=2``, then `L_2` is given by
649
650
.. MATH::
651
652
L_2(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)
653
= (b_2, b_3, b_4, b_0, b_1, b_7, b_8, b_9, b_5, b_6)
654
655
INPUT:
656
657
- ``B`` -- a list of 10 bits
658
659
- ``n`` -- (default: 1) if ``n=1`` then perform left shift by 1
660
position; if ``n=2`` then perform left shift by 2 positions. The
661
valid values for ``n`` are 1 and 2, since only up to 2 positions
662
are defined for this circular left shift operation.
663
664
OUTPUT:
665
666
The circular left shift of each half of ``B``.
667
668
EXAMPLES:
669
670
Circular left shift by 1 position of a 10-bit string::
671
672
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
673
sage: sdes = SimplifiedDES()
674
sage: B = [1, 0, 0, 0, 0, 0, 1, 1, 0, 0]
675
sage: sdes.left_shift(B)
676
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
677
sage: sdes.left_shift([1, 0, 1, 0, 0, 0, 0, 0, 1, 0])
678
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0]
679
680
Circular left shift by 2 positions of a 10-bit string::
681
682
sage: B = [0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
683
sage: sdes.left_shift(B, n=2)
684
[0, 0, 1, 0, 0, 0, 0, 0, 1, 1]
685
686
Here we work with a string of bits::
687
688
sage: S = "1000001100"
689
sage: L = sdes.string_to_list(S)
690
sage: sdes.left_shift(L)
691
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
692
sage: sdes.left_shift(sdes.string_to_list("1010000010"), n=2)
693
[1, 0, 0, 1, 0, 0, 1, 0, 0, 0]
694
695
TESTS:
696
697
The input block must be a list::
698
699
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
700
sage: sdes = SimplifiedDES()
701
sage: sdes.left_shift("B")
702
Traceback (most recent call last):
703
...
704
TypeError: input block must be a list of 10 bits
705
sage: sdes.left_shift(())
706
Traceback (most recent call last):
707
...
708
TypeError: input block must be a list of 10 bits
709
710
The input block must be a list of 10 bits::
711
712
sage: sdes.left_shift([])
713
Traceback (most recent call last):
714
...
715
ValueError: input block must be a list of 10 bits
716
sage: sdes.left_shift([1, 2, 3, 4, 5])
717
Traceback (most recent call last):
718
...
719
ValueError: input block must be a list of 10 bits
720
721
The value of each element of the list must be either 0 or 1::
722
723
sage: sdes.left_shift([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
724
Traceback (most recent call last):
725
...
726
TypeError: Argument x (= 2) is not a valid string.
727
728
The number of shift positions must be either 1 or 2::
729
730
sage: B = [0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
731
sage: sdes.left_shift(B, n=-1)
732
Traceback (most recent call last):
733
...
734
ValueError: input n must be either 1 or 2
735
sage: sdes.left_shift(B, n=3)
736
Traceback (most recent call last):
737
...
738
ValueError: input n must be either 1 or 2
739
"""
740
# sanity check
741
if not isinstance(B, list):
742
raise TypeError, "input block must be a list of 10 bits"
743
if len(B) != 10:
744
raise ValueError, "input block must be a list of 10 bits"
745
746
bin = BinaryStrings()
747
# circular left shift by 1 position
748
if n == 1:
749
return [ bin(str(B[1])), bin(str(B[2])),
750
bin(str(B[3])), bin(str(B[4])),
751
bin(str(B[0])), bin(str(B[6])),
752
bin(str(B[7])), bin(str(B[8])),
753
bin(str(B[9])), bin(str(B[5])) ]
754
# circular left shift by 2 positions
755
elif n == 2:
756
return [ bin(str(B[2])), bin(str(B[3])),
757
bin(str(B[4])), bin(str(B[0])),
758
bin(str(B[1])), bin(str(B[7])),
759
bin(str(B[8])), bin(str(B[9])),
760
bin(str(B[5])), bin(str(B[6])) ]
761
# an invalid number of shift positions
762
else:
763
raise ValueError, "input n must be either 1 or 2"
764
765
def list_to_string(self, B):
766
r"""
767
Return a binary string representation of the list ``B``.
768
769
INPUT:
770
771
- ``B`` -- a non-empty list of bits
772
773
OUTPUT:
774
775
The binary string representation of ``B``.
776
777
EXAMPLES:
778
779
A binary string representation of a list of bits::
780
781
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
782
sage: sdes = SimplifiedDES()
783
sage: L = [0, 0, 0, 0, 1, 1, 0, 1, 0, 0]
784
sage: sdes.list_to_string(L)
785
0000110100
786
787
TESTS:
788
789
Input ``B`` must be a non-empty list::
790
791
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
792
sage: sdes = SimplifiedDES()
793
sage: sdes.list_to_string("L")
794
Traceback (most recent call last):
795
...
796
TypeError: input B must be a non-empty list of bits
797
sage: sdes.list_to_string([])
798
Traceback (most recent call last):
799
...
800
ValueError: input B must be a non-empty list of bits
801
802
Input must be a non-empty list of bits::
803
804
sage: sdes.list_to_string([0, 1, 2])
805
Traceback (most recent call last):
806
...
807
IndexError: tuple index out of range
808
"""
809
# sanity check
810
if not isinstance(B, list):
811
raise TypeError, "input B must be a non-empty list of bits"
812
if len(B) == 0:
813
raise ValueError, "input B must be a non-empty list of bits"
814
815
# perform the conversion from list to binary string
816
from sage.rings.integer import Integer
817
bin = BinaryStrings()
818
return bin([Integer(str(b)) for b in B])
819
820
def permutation4(self, B):
821
r"""
822
Return a permutation of a 4-bit string. This permutation is called
823
`P_4` and is specified as follows. Let
824
`(b_0, b_1, b_2, b_3)` be a vector of 4 bits where each
825
`b_i \in \{ 0, 1 \}`. Then `P_4` is defined by
826
827
.. MATH::
828
829
P_4(b_0, b_1, b_2, b_3) = (b_1, b_3, b_2, b_0)
830
831
INPUT:
832
833
- ``B`` -- a block of 4-bit string
834
835
OUTPUT:
836
837
A permutation of ``B``.
838
839
EXAMPLES:
840
841
Permute a 4-bit string::
842
843
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
844
sage: sdes = SimplifiedDES()
845
sage: B = [1, 1, 0, 0]
846
sage: sdes.permutation4(B)
847
[1, 0, 0, 1]
848
sage: sdes.permutation4([0, 1, 0, 1])
849
[1, 1, 0, 0]
850
851
We can also work with a string of bits::
852
853
sage: S = "1100"
854
sage: L = sdes.string_to_list(S)
855
sage: sdes.permutation4(L)
856
[1, 0, 0, 1]
857
sage: sdes.permutation4(sdes.string_to_list("0101"))
858
[1, 1, 0, 0]
859
860
TESTS:
861
862
The input block must be a list::
863
864
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
865
sage: sdes = SimplifiedDES()
866
sage: sdes.permutation4("B")
867
Traceback (most recent call last):
868
...
869
TypeError: input block must be a list of 4 bits
870
sage: sdes.permutation4(())
871
Traceback (most recent call last):
872
...
873
TypeError: input block must be a list of 4 bits
874
875
The input block must be a list of 4 bits::
876
877
sage: sdes.permutation4([])
878
Traceback (most recent call last):
879
...
880
ValueError: input block must be a list of 4 bits
881
sage: sdes.permutation4([1, 2, 3, 4, 5])
882
Traceback (most recent call last):
883
...
884
ValueError: input block must be a list of 4 bits
885
886
The value of each element of the list must be either 0 or 1::
887
888
sage: sdes.permutation4([1, 2, 3, 4])
889
Traceback (most recent call last):
890
...
891
TypeError: Argument x (= 2) is not a valid string.
892
"""
893
# sanity check
894
if not isinstance(B, list):
895
raise TypeError, "input block must be a list of 4 bits"
896
if len(B) != 4:
897
raise ValueError, "input block must be a list of 4 bits"
898
899
# perform the permutation
900
bin = BinaryStrings()
901
return [ bin(str(B[1])), bin(str(B[3])),
902
bin(str(B[2])), bin(str(B[0])) ]
903
904
def permutation8(self, B):
905
r"""
906
Return a permutation of an 8-bit string. This permutation is called
907
`P_8` and is specified as follows. Let
908
`(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)` be a vector of
909
10 bits where each `b_i \in \{ 0, 1 \}`. Then `P_8` picks out 8 of
910
those 10 bits and permutes those 8 bits:
911
912
.. MATH::
913
914
P_8(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)
915
=
916
(b_5, b_2, b_6, b_3, b_7, b_4, b_9, b_8)
917
918
INPUT:
919
920
- ``B`` -- a block of 10-bit string
921
922
OUTPUT:
923
924
Pick out 8 of the 10 bits of ``B`` and permute those 8 bits.
925
926
EXAMPLES:
927
928
Permute a 10-bit string::
929
930
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
931
sage: sdes = SimplifiedDES()
932
sage: B = [1, 1, 0, 0, 1, 0, 0, 1, 0, 1]
933
sage: sdes.permutation8(B)
934
[0, 0, 0, 0, 1, 1, 1, 0]
935
sage: sdes.permutation8([0, 1, 1, 0, 1, 0, 0, 1, 0, 1])
936
[0, 1, 0, 0, 1, 1, 1, 0]
937
sage: sdes.permutation8([0, 0, 0, 0, 1, 1, 1, 0, 0, 0])
938
[1, 0, 1, 0, 0, 1, 0, 0]
939
940
We can also work with a string of bits::
941
942
sage: S = "1100100101"
943
sage: L = sdes.string_to_list(S)
944
sage: sdes.permutation8(L)
945
[0, 0, 0, 0, 1, 1, 1, 0]
946
sage: sdes.permutation8(sdes.string_to_list("0110100101"))
947
[0, 1, 0, 0, 1, 1, 1, 0]
948
949
TESTS:
950
951
The input block must be a list::
952
953
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
954
sage: sdes = SimplifiedDES()
955
sage: sdes.permutation8("B")
956
Traceback (most recent call last):
957
...
958
TypeError: input block must be a list of 10 bits
959
sage: sdes.permutation8(())
960
Traceback (most recent call last):
961
...
962
TypeError: input block must be a list of 10 bits
963
964
The input block must be a list of 10 bits::
965
966
sage: sdes.permutation8([])
967
Traceback (most recent call last):
968
...
969
ValueError: input block must be a list of 10 bits
970
sage: sdes.permutation8([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
971
Traceback (most recent call last):
972
...
973
ValueError: input block must be a list of 10 bits
974
975
The value of each element of the list must be either 0 or 1::
976
977
sage: sdes.permutation8([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
978
Traceback (most recent call last):
979
...
980
TypeError: Argument x (= 6) is not a valid string.
981
"""
982
# sanity check
983
if not isinstance(B, list):
984
raise TypeError, "input block must be a list of 10 bits"
985
if len(B) != 10:
986
raise ValueError, "input block must be a list of 10 bits"
987
988
# perform the permutation
989
bin = BinaryStrings()
990
return [ bin(str(B[5])), bin(str(B[2])),
991
bin(str(B[6])), bin(str(B[3])),
992
bin(str(B[7])), bin(str(B[4])),
993
bin(str(B[9])), bin(str(B[8])) ]
994
995
def permutation10(self, B):
996
r"""
997
Return a permutation of a 10-bit string. This permutation is called
998
`P_{10}` and is specified as follows. Let
999
`(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)` be a vector of
1000
10 bits where each `b_i \in \{ 0, 1 \}`. Then `P_{10}` is given by
1001
1002
.. MATH::
1003
1004
P_{10}(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)
1005
=
1006
(b_2, b_4, b_1, b_6, b_3, b_9, b_0, b_8, b_7, b_5)
1007
1008
INPUT:
1009
1010
- ``B`` -- a block of 10-bit string
1011
1012
OUTPUT:
1013
1014
A permutation of ``B``.
1015
1016
EXAMPLES:
1017
1018
Permute a 10-bit string::
1019
1020
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1021
sage: sdes = SimplifiedDES()
1022
sage: B = [1, 1, 0, 0, 1, 0, 0, 1, 0, 1]
1023
sage: sdes.permutation10(B)
1024
[0, 1, 1, 0, 0, 1, 1, 0, 1, 0]
1025
sage: sdes.permutation10([0, 1, 1, 0, 1, 0, 0, 1, 0, 1])
1026
[1, 1, 1, 0, 0, 1, 0, 0, 1, 0]
1027
sage: sdes.permutation10([1, 0, 1, 0, 0, 0, 0, 0, 1, 0])
1028
[1, 0, 0, 0, 0, 0, 1, 1, 0, 0]
1029
1030
Here we work with a string of bits::
1031
1032
sage: S = "1100100101"
1033
sage: L = sdes.string_to_list(S)
1034
sage: sdes.permutation10(L)
1035
[0, 1, 1, 0, 0, 1, 1, 0, 1, 0]
1036
sage: sdes.permutation10(sdes.string_to_list("0110100101"))
1037
[1, 1, 1, 0, 0, 1, 0, 0, 1, 0]
1038
1039
TESTS:
1040
1041
The input block must be a list::
1042
1043
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1044
sage: sdes = SimplifiedDES()
1045
sage: sdes.permutation10("B")
1046
Traceback (most recent call last):
1047
...
1048
TypeError: input block must be a list of 10 bits
1049
sage: sdes.permutation10(())
1050
Traceback (most recent call last):
1051
...
1052
TypeError: input block must be a list of 10 bits
1053
1054
The input block must be a list of 10 bits::
1055
1056
sage: sdes.permutation10([])
1057
Traceback (most recent call last):
1058
...
1059
ValueError: input block must be a list of 10 bits
1060
sage: sdes.permutation10([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
1061
Traceback (most recent call last):
1062
...
1063
ValueError: input block must be a list of 10 bits
1064
1065
The value of each element of the list must be either 0 or 1::
1066
1067
sage: sdes.permutation10([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
1068
Traceback (most recent call last):
1069
...
1070
TypeError: Argument x (= 3) is not a valid string.
1071
"""
1072
# sanity check
1073
if not isinstance(B, list):
1074
raise TypeError, "input block must be a list of 10 bits"
1075
if len(B) != 10:
1076
raise ValueError, "input block must be a list of 10 bits"
1077
1078
# perform the permutation
1079
bin = BinaryStrings()
1080
return [ bin(str(B[2])), bin(str(B[4])),
1081
bin(str(B[1])), bin(str(B[6])),
1082
bin(str(B[3])), bin(str(B[9])),
1083
bin(str(B[0])), bin(str(B[8])),
1084
bin(str(B[7])), bin(str(B[5])) ]
1085
1086
def permute_substitute(self, B, key):
1087
r"""
1088
Apply the function `\Pi_F` on the block ``B`` using subkey ``key``.
1089
Let `(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7)`
1090
be a vector of 8 bits where each `b_i \in \{ 0, 1 \}`, let `L` and
1091
`R` be the leftmost 4 bits and rightmost 4 bits of ``B``
1092
respectively, and let `F` be a function mapping 4-bit strings to
1093
4-bit strings. Then
1094
1095
.. MATH::
1096
1097
\Pi_F(L, R) = (L \oplus F(R, S), R)
1098
1099
where `S` is a subkey and `\oplus` denotes the bit-wise
1100
exclusive-OR function.
1101
1102
The function `F` can be described as follows. Its 4-bit input block
1103
`(n_0, n_1, n_2, n_3)` is first expanded into an 8-bit block
1104
to become `(n_3, n_0, n_1, n_2, n_1, n_2, n_3, n_0)`. This is
1105
usually represented as follows
1106
1107
.. MATH::
1108
1109
\begin{tabular}{c|cc|c}
1110
$n_3$ & $n_0$ & $n_1$ & $n_2$ \\
1111
$n_1$ & $n_2$ & $n_3$ & $n_0$
1112
\end{tabular}
1113
1114
Let `K = (k_0, k_1, k_2, k_3, k_4, k_5, k_6, k_7)` be an 8-bit
1115
subkey. Then `K` is added to the above expanded input block using
1116
exclusive-OR to produce
1117
1118
.. MATH::
1119
1120
\begin{tabular}{c|cc|c}
1121
$n_3 + k_0$ & $n_0 + k_1$ & $n_1 + k_2$ & $n_2 + k_3$ \\
1122
$n_1 + k_4$ & $n_2 + k_5$ & $n_3 + k_6$ & $n_0 + k_7$
1123
\end{tabular}
1124
=
1125
\begin{tabular}{c|cc|c}
1126
$p_{0,0}$ & $p_{0,1}$ & $p_{0,2}$ & $p_{0,3}$ \\
1127
$p_{1,0}$ & $p_{1,1}$ & $p_{1,2}$ & $p_{1,3}$
1128
\end{tabular}
1129
1130
Now read the first row as the 4-bit string
1131
`p_{0,0} p_{0,3} p_{0,1} p_{0,2}` and input this 4-bit string through
1132
S-box `S_0` to get a 2-bit output.
1133
1134
.. MATH::
1135
1136
S_0
1137
=
1138
\begin{tabular}{cc|cc} \hline
1139
Input & Output & Input & Output \\\hline
1140
0000 & 01 & 1000 & 00 \\
1141
0001 & 00 & 1001 & 10 \\
1142
0010 & 11 & 1010 & 01 \\
1143
0011 & 10 & 1011 & 11 \\
1144
0100 & 11 & 1100 & 11 \\
1145
0101 & 10 & 1101 & 01 \\
1146
0110 & 01 & 1110 & 11 \\
1147
0111 & 00 & 1111 & 10 \\\hline
1148
\end{tabular}
1149
1150
Next read the second row as the 4-bit string
1151
`p_{1,0} p_{1,3} p_{1,1} p_{1,2}` and input this 4-bit string through
1152
S-box `S_1` to get another 2-bit output.
1153
1154
.. MATH::
1155
1156
S_1
1157
=
1158
\begin{tabular}{cc|cc} \hline
1159
Input & Output & Input & Output \\\hline
1160
0000 & 00 & 1000 & 11 \\
1161
0001 & 01 & 1001 & 00 \\
1162
0010 & 10 & 1010 & 01 \\
1163
0011 & 11 & 1011 & 00 \\
1164
0100 & 10 & 1100 & 10 \\
1165
0101 & 00 & 1101 & 01 \\
1166
0110 & 01 & 1110 & 00 \\
1167
0111 & 11 & 1111 & 11 \\\hline
1168
\end{tabular}
1169
1170
Denote the 4 bits produced by `S_0` and `S_1` as `b_0 b_1 b_2 b_3`.
1171
This 4-bit string undergoes another permutation called `P_4` as
1172
follows:
1173
1174
.. MATH::
1175
1176
P_4(b_0, b_1, b_2, b_3) = (b_1, b_3, b_2, b_0)
1177
1178
The output of `P_4` is the output of the function `F`.
1179
1180
INPUT:
1181
1182
- ``B`` -- a list of 8 bits
1183
1184
- ``key`` -- an 8-bit subkey
1185
1186
OUTPUT:
1187
1188
The result of applying the function `\Pi_F` to ``B``.
1189
1190
EXAMPLES:
1191
1192
Applying the function `\Pi_F` to an 8-bit block and an 8-bit subkey::
1193
1194
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1195
sage: sdes = SimplifiedDES()
1196
sage: B = [1, 0, 1, 1, 1, 1, 0, 1]
1197
sage: K = [1, 1, 0, 1, 0, 1, 0, 1]
1198
sage: sdes.permute_substitute(B, K)
1199
[1, 0, 1, 0, 1, 1, 0, 1]
1200
1201
We can also work with strings of bits::
1202
1203
sage: B = "10111101"
1204
sage: K = "11010101"
1205
sage: B = sdes.string_to_list(B); K = sdes.string_to_list(K)
1206
sage: sdes.permute_substitute(B, K)
1207
[1, 0, 1, 0, 1, 1, 0, 1]
1208
1209
TESTS:
1210
1211
The input ``B`` must be a block of 8 bits::
1212
1213
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1214
sage: sdes = SimplifiedDES()
1215
sage: sdes.permute_substitute("B", "K")
1216
Traceback (most recent call last):
1217
...
1218
TypeError: input B must be an 8-bit string
1219
sage: sdes.permute_substitute([], "K")
1220
Traceback (most recent call last):
1221
...
1222
ValueError: input B must be an 8-bit string
1223
1224
The input ``key`` must be an 8-bit subkey::
1225
1226
sage: sdes.permute_substitute([0, 1, 0, 0, 1, 1, 1, 0], "K")
1227
Traceback (most recent call last):
1228
...
1229
TypeError: input key must be an 8-bit subkey
1230
sage: sdes.permute_substitute([0, 1, 0, 0, 1, 1, 1, 0], [])
1231
Traceback (most recent call last):
1232
...
1233
ValueError: input key must be an 8-bit subkey
1234
1235
The value of each element of ``B`` or ``key`` must be either 0 or 1::
1236
1237
sage: B = [1, 2, 3, 4, 5, 6, 7, 8]
1238
sage: K = [0, 1, 2, 3, 4, 5, 6, 7]
1239
sage: sdes.permute_substitute(B, K)
1240
Traceback (most recent call last):
1241
...
1242
TypeError: Argument x (= 2) is not a valid string.
1243
sage: B = [0, 1, 0, 0, 1, 1, 1, 0]
1244
sage: K = [1, 2, 3, 4, 5, 6, 7, 8]
1245
sage: sdes.permute_substitute(B, K)
1246
Traceback (most recent call last):
1247
...
1248
TypeError: Argument x (= 2) is not a valid string.
1249
"""
1250
# sanity check
1251
if not isinstance(B, list):
1252
raise TypeError, "input B must be an 8-bit string"
1253
if len(B) != 8:
1254
raise ValueError, "input B must be an 8-bit string"
1255
if not isinstance(key, list):
1256
raise TypeError, "input key must be an 8-bit subkey"
1257
if len(key) != 8:
1258
raise ValueError, "input key must be an 8-bit subkey"
1259
1260
from sage.rings.finite_rings.constructor import FiniteField
1261
GF = FiniteField(2, "x")
1262
bin = BinaryStrings()
1263
bin_to_GF2 = {bin("0"): GF(0), bin("1"): GF(1)}
1264
1265
# the leftmost 4 bits of B
1266
L = [ bin_to_GF2[bin(str(B[i]))] for i in xrange(4) ]
1267
# the rightmost 4 bits of B
1268
R = [ bin_to_GF2[bin(str(B[i]))] for i in xrange(4, len(B)) ]
1269
# get the GF(2) representation of the subkey
1270
K = [ bin_to_GF2[bin(str(key[i]))] for i in xrange(len(key)) ]
1271
# expand the rightmost 4 bits into an 8-bit block
1272
RX = [ R[3], R[0], R[1], R[2], R[1], R[2], R[3], R[0] ]
1273
# add the subkey to the expanded 8-bit block using exclusive-OR
1274
P = [ RX[i] + K[i] for i in xrange(len(K)) ]
1275
# run each half of P separately through the S-boxes
1276
left = self._sbox0([ P[0], P[3], P[1], P[2] ])
1277
right = self._sbox1([ P[4], P[7], P[5], P[6] ])
1278
# First concatenate the left and right parts, then get the
1279
# output of the function F.
1280
F = self.permutation4(left + right)
1281
F = [ bin_to_GF2[F[i]] for i in xrange(len(F)) ]
1282
# Add L to F using exclusive-OR. Then concatenate the result with
1283
# the rightmost 4 bits of B. This is the output of the function Pi_F.
1284
L = [ L[i] + F[i] for i in xrange(len(F)) ]
1285
return L + R
1286
1287
def random_key(self):
1288
r"""
1289
Return a random 10-bit key.
1290
1291
EXAMPLES:
1292
1293
The size of each key is the same as the block size::
1294
1295
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1296
sage: sdes = SimplifiedDES()
1297
sage: key = sdes.random_key()
1298
sage: len(key) == sdes.block_length()
1299
True
1300
"""
1301
from sage.misc.prandom import randint
1302
bin = BinaryStrings()
1303
return [bin(str(randint(0, 1))) for i in xrange(self._key_size)]
1304
1305
def sbox(self):
1306
r"""
1307
Return the S-boxes of simplified DES.
1308
1309
EXAMPLES::
1310
1311
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1312
sage: sdes = SimplifiedDES()
1313
sage: sbox = sdes.sbox()
1314
sage: sbox[0]; sbox[1]
1315
(1, 0, 3, 2, 3, 2, 1, 0, 0, 2, 1, 3, 3, 1, 3, 2)
1316
(0, 1, 2, 3, 2, 0, 1, 3, 3, 0, 1, 0, 2, 1, 0, 3)
1317
"""
1318
return [self._sbox0, self._sbox1]
1319
1320
def string_to_list(self, S):
1321
r"""
1322
Return a list representation of the binary string ``S``.
1323
1324
INPUT:
1325
1326
- ``S`` -- a string of bits
1327
1328
OUTPUT:
1329
1330
A list representation of the string ``S``.
1331
1332
EXAMPLES:
1333
1334
A list representation of a string of bits::
1335
1336
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1337
sage: sdes = SimplifiedDES()
1338
sage: S = "0101010110"
1339
sage: sdes.string_to_list(S)
1340
[0, 1, 0, 1, 0, 1, 0, 1, 1, 0]
1341
1342
TESTS:
1343
1344
Input must be a non-empty string::
1345
1346
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1347
sage: sdes = SimplifiedDES()
1348
sage: sdes.string_to_list("")
1349
Traceback (most recent call last):
1350
...
1351
ValueError: input S must be a non-empty string of bits
1352
sage: sdes.string_to_list(1)
1353
Traceback (most recent call last):
1354
...
1355
TypeError: input S must be a non-empty string of bits
1356
1357
Input must be a non-empty string of bits::
1358
1359
sage: sdes.string_to_list("0123")
1360
Traceback (most recent call last):
1361
...
1362
TypeError: Argument x (= 2) is not a valid string.
1363
"""
1364
# sanity check
1365
if not isinstance(S, str):
1366
raise TypeError, "input S must be a non-empty string of bits"
1367
if len(S) == 0:
1368
raise ValueError, "input S must be a non-empty string of bits"
1369
1370
# perform the conversion from string to list
1371
bin = BinaryStrings()
1372
return [bin(s) for s in S]
1373
1374
def subkey(self, K, n=1):
1375
r"""
1376
Return the ``n``-th subkey based on the key ``K``.
1377
1378
INPUT:
1379
1380
- ``K`` -- a 10-bit secret key of this Simplified DES
1381
1382
- ``n`` -- (default: 1) if ``n=1`` then return the first subkey based
1383
on ``K``; if ``n=2`` then return the second subkey. The valid
1384
values for ``n`` are 1 and 2, since only two subkeys are defined
1385
for each secret key in Schaefer's S-DES.
1386
1387
OUTPUT:
1388
1389
The ``n``-th subkey based on the secret key ``K``.
1390
1391
EXAMPLES:
1392
1393
Obtain the first subkey from a secret key::
1394
1395
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1396
sage: sdes = SimplifiedDES()
1397
sage: key = [1, 0, 1, 0, 0, 0, 0, 0, 1, 0]
1398
sage: sdes.subkey(key, n=1)
1399
[1, 0, 1, 0, 0, 1, 0, 0]
1400
1401
Obtain the second subkey from a secret key::
1402
1403
sage: key = [1, 0, 1, 0, 0, 0, 0, 0, 1, 0]
1404
sage: sdes.subkey(key, n=2)
1405
[0, 1, 0, 0, 0, 0, 1, 1]
1406
1407
We can also work with strings of bits::
1408
1409
sage: K = "1010010010"
1410
sage: L = sdes.string_to_list(K)
1411
sage: sdes.subkey(L, n=1)
1412
[1, 0, 1, 0, 0, 1, 0, 1]
1413
sage: sdes.subkey(sdes.string_to_list("0010010011"), n=2)
1414
[0, 1, 1, 0, 1, 0, 1, 0]
1415
1416
TESTS:
1417
1418
Input ``K`` must be a 10-bit key::
1419
1420
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1421
sage: sdes = SimplifiedDES()
1422
sage: sdes.subkey("K")
1423
Traceback (most recent call last):
1424
...
1425
TypeError: input K must be a 10-bit key
1426
sage: sdes.subkey([])
1427
Traceback (most recent call last):
1428
...
1429
ValueError: input K must be a 10-bit key
1430
1431
There are only two subkeys::
1432
1433
sage: key = [1, 0, 1, 0, 0, 0, 0, 0, 1, 0]
1434
sage: sdes.subkey(key, n=0)
1435
Traceback (most recent call last):
1436
...
1437
ValueError: input n must be either 1 or 2
1438
sage: sdes.subkey(key, n=3)
1439
Traceback (most recent call last):
1440
...
1441
ValueError: input n must be either 1 or 2
1442
"""
1443
# sanity check
1444
if not isinstance(K, list):
1445
raise TypeError, "input K must be a 10-bit key"
1446
if len(K) != self._key_size:
1447
raise ValueError, "input K must be a 10-bit key"
1448
1449
# get the first subkey
1450
if n == 1:
1451
key1 = self.permutation10(K)
1452
key1 = self.left_shift(key1, n=1)
1453
return self.permutation8(key1)
1454
# get the second subkey
1455
elif n == 2:
1456
key2 = self.permutation10(K)
1457
key2 = self.left_shift(key2, n=1)
1458
key2 = self.left_shift(key2, n=2)
1459
return self.permutation8(key2)
1460
# an invalid subkey number
1461
else:
1462
raise ValueError, "input n must be either 1 or 2"
1463
1464
def switch(self, B):
1465
r"""
1466
Interchange the first 4 bits with the last 4 bits in the list ``B``
1467
of 8 bits. Let `(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7)`
1468
be a vector of 8 bits, where each `b_i \in \{ 0, 1 \}`. Then the
1469
switch function `\sigma` is given by
1470
1471
.. MATH::
1472
1473
\sigma(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7)
1474
= (b_4, b_5, b_6, b_7, b_0, b_1, b_2, b_3)
1475
1476
INPUT:
1477
1478
- ``B`` -- list; a block of 8 bits
1479
1480
OUTPUT:
1481
1482
A block of the same dimension, but in which the first 4 bits from
1483
``B`` has been switched for the last 4 bits in ``B``.
1484
1485
EXAMPLES:
1486
1487
Interchange the first 4 bits with the last 4 bits::
1488
1489
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1490
sage: sdes = SimplifiedDES()
1491
sage: B = [1, 1, 1, 0, 1, 0, 0, 0]
1492
sage: sdes.switch(B)
1493
[1, 0, 0, 0, 1, 1, 1, 0]
1494
sage: sdes.switch([1, 1, 1, 1, 0, 0, 0, 0])
1495
[0, 0, 0, 0, 1, 1, 1, 1]
1496
1497
We can also work with a string of bits::
1498
1499
sage: S = "11101000"
1500
sage: L = sdes.string_to_list(S)
1501
sage: sdes.switch(L)
1502
[1, 0, 0, 0, 1, 1, 1, 0]
1503
sage: sdes.switch(sdes.string_to_list("11110000"))
1504
[0, 0, 0, 0, 1, 1, 1, 1]
1505
1506
TESTS:
1507
1508
The input block must be a list::
1509
1510
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
1511
sage: sdes = SimplifiedDES()
1512
sage: sdes.switch("B")
1513
Traceback (most recent call last):
1514
...
1515
TypeError: input block must be a list of 8 bits
1516
sage: sdes.switch(())
1517
Traceback (most recent call last):
1518
...
1519
TypeError: input block must be a list of 8 bits
1520
1521
The input block must be a list of 8 bits::
1522
1523
sage: sdes.switch([])
1524
Traceback (most recent call last):
1525
...
1526
ValueError: input block must be a list of 8 bits
1527
sage: sdes.switch([1, 2, 3, 4, 5, 6, 7, 8, 9])
1528
Traceback (most recent call last):
1529
...
1530
ValueError: input block must be a list of 8 bits
1531
1532
The value of each element of the list must be either 0 or 1::
1533
1534
sage: sdes.switch([1, 2, 3, 4, 5, 6, 7, 8])
1535
Traceback (most recent call last):
1536
...
1537
TypeError: Argument x (= 5) is not a valid string.
1538
"""
1539
# sanity check
1540
if not isinstance(B, list):
1541
raise TypeError, "input block must be a list of 8 bits"
1542
if len(B) != 8:
1543
raise ValueError, "input block must be a list of 8 bits"
1544
1545
# perform the switch
1546
bin = BinaryStrings()
1547
return [ bin(str(B[4])), bin(str(B[5])),
1548
bin(str(B[6])), bin(str(B[7])),
1549
bin(str(B[0])), bin(str(B[1])),
1550
bin(str(B[2])), bin(str(B[3])) ]
1551
1552