DES#

The Data Encryption Standard.

This file implements the Data Encryption Standard and the corresponding key schedule as described in [U.S1999].

This implementation is meant for experimental and educational usage only, do not use it in production code!

EXAMPLES:

Encrypt a message:

sage: from sage.crypto.block_cipher.des import DES
sage: des = DES()
sage: P = 0x01A1D6D039776742
sage: K = 0x7CA110454A1A6E57
sage: C = des.encrypt(plaintext=P, key=K); C.hex()
'690f5b0d9a26939b'
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> des = DES()
>>> P = Integer(0x01A1D6D039776742)
>>> K = Integer(0x7CA110454A1A6E57)
>>> C = des.encrypt(plaintext=P, key=K); C.hex()
'690f5b0d9a26939b'

And decrypt it again:

sage: des.decrypt(ciphertext=C, key=K).hex()
'1a1d6d039776742'
>>> from sage.all import *
>>> des.decrypt(ciphertext=C, key=K).hex()
'1a1d6d039776742'

Have a look at the used round keys:

sage: from sage.crypto.block_cipher.des import DES_KS
sage: ks = DES_KS()
sage: [k.hex() for k in ks(0x1F08260D1AC2465E)]
['103049bfb90e',
 '808d40f07bf',
  ...
 '231000f2dd97']
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES_KS
>>> ks = DES_KS()
>>> [k.hex() for k in ks(Integer(0x1F08260D1AC2465E))]
['103049bfb90e',
 '808d40f07bf',
  ...
 '231000f2dd97']

Validate the Sample Round Outputs for DES (cf. [KeSm1998] p. 124):

sage: from sage.crypto.block_cipher.des import DES
sage: P = 0
sage: K = 0x10316E028C8F3B4A
sage: for r in range(1, 17):
....:     DES(rounds=r, doFinalRound=False).encrypt(P, K).hex()
'47092b5b'
'47092b5b53f372af'
'53f372af9f1d158b'
...
'3f6c3efd5a1e5228'
sage: DES().encrypt(P, K).hex()
'82dcbafbdeab6602'
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> P = Integer(0)
>>> K = Integer(0x10316E028C8F3B4A)
>>> for r in range(Integer(1), Integer(17)):
...     DES(rounds=r, doFinalRound=False).encrypt(P, K).hex()
'47092b5b'
'47092b5b53f372af'
'53f372af9f1d158b'
...
'3f6c3efd5a1e5228'
>>> DES().encrypt(P, K).hex()
'82dcbafbdeab6602'

Change cipher internals:

sage: from sage.crypto.sbox import SBox
sage: cipher = DES(rounds=1, doFinalRound=False)
sage: cipher.sboxes = [[SBox(range(16))]*4]*8
sage: cipher.keySchedule = lambda x: [0]  # return the 0 key as round key
sage: cipher.encrypt(plaintext=0x1234, key=0x0).hex()
'80004000d08100'
>>> from sage.all import *
>>> from sage.crypto.sbox import SBox
>>> cipher = DES(rounds=Integer(1), doFinalRound=False)
>>> cipher.sboxes = [[SBox(range(Integer(16)))]*Integer(4)]*Integer(8)
>>> cipher.keySchedule = lambda x: [Integer(0)]  # return the 0 key as round key
>>> cipher.encrypt(plaintext=Integer(0x1234), key=Integer(0x0)).hex()
'80004000d08100'

AUTHORS:

  • Lukas Stennes (2019-03-29): initial version

class sage.crypto.block_cipher.des.DES(rounds=None, keySchedule='DES_KS', keySize=64, doFinalRound=True)[source]#

Bases: SageObject

This class implements DES described in [U.S1999].

EXAMPLES:

You can invoke DES encryption/decryption either by calling DES with an appropriate flag:

sage: from sage.crypto.block_cipher.des import DES
sage: des = DES()
sage: P = 0x8000000000000000
sage: K = 0x0
sage: C = des(P, K, 'encrypt'); C.hex()
'95f8a5e5dd31d900'
sage: des(C, K, 'decrypt').hex()
'8000000000000000'
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> des = DES()
>>> P = Integer(0x8000000000000000)
>>> K = Integer(0x0)
>>> C = des(P, K, 'encrypt'); C.hex()
'95f8a5e5dd31d900'
>>> des(C, K, 'decrypt').hex()
'8000000000000000'

Or by calling encryption/decryption methods directly:

sage: C = des.encrypt(P, K)
sage: P == des.decrypt(C, K)
True
>>> from sage.all import *
>>> C = des.encrypt(P, K)
>>> P == des.decrypt(C, K)
True

The number of rounds can be reduced easily:

sage: des = DES(rounds=15)
sage: des(des(P, K, 'encrypt'), K, 'decrypt') == P
True
>>> from sage.all import *
>>> des = DES(rounds=Integer(15))
>>> des(des(P, K, 'encrypt'), K, 'decrypt') == P
True

You can use hex (i.e. integers) or a list-like bit representation for the inputs. If the input is an integer the output will be too. If it is list-like the output will be a bit vector:

sage: des = DES()
sage: P = vector(GF(2), 64, [1] + [0]*63)
sage: K = vector(GF(2), 64, [0,0,0,0,0,0,0,1]*8)
sage: des.encrypt(P, K)
(1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0,
 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0)
sage: P = 0x8000000000000000
sage: K = 0x0101010101010101
sage: C = des.encrypt(P, K); C; C.hex()
10806569712552630528
'95f8a5e5dd31d900'
>>> from sage.all import *
>>> des = DES()
>>> P = vector(GF(Integer(2)), Integer(64), [Integer(1)] + [Integer(0)]*Integer(63))
>>> K = vector(GF(Integer(2)), Integer(64), [Integer(0),Integer(0),Integer(0),Integer(0),Integer(0),Integer(0),Integer(0),Integer(1)]*Integer(8))
>>> des.encrypt(P, K)
(1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0,
 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0)
>>> P = Integer(0x8000000000000000)
>>> K = Integer(0x0101010101010101)
>>> C = des.encrypt(P, K); C; C.hex()
10806569712552630528
'95f8a5e5dd31d900'
__init__(rounds=None, keySchedule='DES_KS', keySize=64, doFinalRound=True)[source]#

Construct an instance of DES.

INPUT:

  • rounds – integer (default: None); the number of rounds. If None the number of rounds of the key schedule is used.

  • keySchedule – (default: 'DES_KS'); the key schedule that will be used for encryption and decryption. If 'DES_KS' the default DES key schedule is used.

  • keySize – (default: 64); the key length in bits. Must be 56 of 64. In the latter case the key contains 8 parity bits.

  • doFinalRound – boolean (default: True); if False a swap takes places but the inverse initial permutation is omitted (i.e. you can get the state after rounds). This only effects encryption.

EXAMPLES:

sage: from sage.crypto.block_cipher.des import DES
sage: DES() # indirect doctest
DES block cipher with 16 rounds and the following key schedule:
Original DES key schedule with 16 rounds
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> DES() # indirect doctest
DES block cipher with 16 rounds and the following key schedule:
Original DES key schedule with 16 rounds

Reducing the number of rounds is simple. But increasing it is only possible if the key schedule can produce enough round keys:

sage: DES(rounds=11) # indirect doctest
DES block cipher with 11 rounds and the following key schedule:
Original DES key schedule with 16 rounds
sage: DES(rounds=42) # indirect doctest
Traceback (most recent call last):
...
ValueError: number of rounds must be less or equal to the number
of rounds of the key schedule
>>> from sage.all import *
>>> DES(rounds=Integer(11)) # indirect doctest
DES block cipher with 11 rounds and the following key schedule:
Original DES key schedule with 16 rounds
>>> DES(rounds=Integer(42)) # indirect doctest
Traceback (most recent call last):
...
ValueError: number of rounds must be less or equal to the number
of rounds of the key schedule

You can use arbitrary key schedules. Since it is the only one implemented here the original key schedule is used for demonstration:

sage: from sage.crypto.block_cipher.des import DES_KS
sage: DES(keySchedule=DES_KS(11)) # indirect doctest
DES block cipher with 11 rounds and the following key schedule:
Original DES key schedule with 11 rounds
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES_KS
>>> DES(keySchedule=DES_KS(Integer(11))) # indirect doctest
DES block cipher with 11 rounds and the following key schedule:
Original DES key schedule with 11 rounds
__call__(block, key, algorithm='encrypt')[source]#

Apply DES encryption or decryption on block using key. The flag algorithm controls what action is to be performed on block.

INPUT:

  • block – integer or bit list-like; the plaintext or ciphertext

  • key – integer or bit list-like; the key

  • algorithm – string (default: 'encrypt'); a flag to signify whether encryption or decryption is to be applied to block. The encryption flag is 'encrypt' and the decryption flag is 'decrypt'

OUTPUT:

  • The plaintext or ciphertext corresponding to block, obtained using key. If block is an integer the output will be too. If block is list-like the output will be a bit vector.

EXAMPLES:

sage: from sage.crypto.block_cipher.des import DES
sage: des = DES()
sage: P = 0x480D39006EE762F2
sage: K = 0x025816164629B007
sage: des(P, K, 'encrypt').hex()
'a1f9915541020b56'
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> des = DES()
>>> P = Integer(0x480D39006EE762F2)
>>> K = Integer(0x025816164629B007)
>>> des(P, K, 'encrypt').hex()
'a1f9915541020b56'
decrypt(ciphertext, key)[source]#

Return the plaintext corresponding to ciphertext, using DES decryption with key.

INPUT:

  • ciphertext – integer or bit list-like; the ciphertext that will be decrypted

  • key – integer or bit list-like; the key

OUTPUT:

  • The plaintext corresponding to ciphertext, obtained using key. If ciphertext is an integer the output will be too. If ciphertext is list-like the output will be a bit vector.

EXAMPLES:

Decrypt a message:

sage: from sage.crypto.block_cipher.des import DES
sage: des = DES()
sage: K64 = 0x7CA110454A1A6E57
sage: C = 0x690F5B0D9A26939B
sage: P = des.decrypt(C, K64).hex(); P
'1a1d6d039776742'
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> des = DES()
>>> K64 = Integer(0x7CA110454A1A6E57)
>>> C = Integer(0x690F5B0D9A26939B)
>>> P = des.decrypt(C, K64).hex(); P
'1a1d6d039776742'

You can also use 56 bit keys i.e. you can leave out the parity bits:

sage: K56 = 0x7D404224A35BAB
sage: des = DES(keySize=56)
sage: des.decrypt(C, K56).hex() == P
True
>>> from sage.all import *
>>> K56 = Integer(0x7D404224A35BAB)
>>> des = DES(keySize=Integer(56))
>>> des.decrypt(C, K56).hex() == P
True
encrypt(plaintext, key)[source]#

Return the ciphertext corresponding to plaintext, using DES encryption with key.

INPUT:

  • plaintext – integer or bit list-like; the plaintext that will be encrypted.

  • key – integer or bit list-like; the key

OUTPUT:

  • The ciphertext corresponding to plaintext, obtained using key. If plaintext is an integer the output will be too. If plaintext is list-like the output will be a bit vector.

EXAMPLES:

Encrypt a message:

sage: from sage.crypto.block_cipher.des import DES
sage: des = DES()
sage: K64 = 0x133457799BBCDFF1
sage: P = 0x0123456789ABCDEF
sage: C = des.encrypt(P, K64); C.hex()
'85e813540f0ab405'
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> des = DES()
>>> K64 = Integer(0x133457799BBCDFF1)
>>> P = Integer(0x0123456789ABCDEF)
>>> C = des.encrypt(P, K64); C.hex()
'85e813540f0ab405'

You can also use 56 bit keys i.e. you can leave out the parity bits:

sage: K56 = 0x12695BC9B7B7F8
sage: des = DES(keySize=56)
sage: des.encrypt(P, K56) == C
True
>>> from sage.all import *
>>> K56 = Integer(0x12695BC9B7B7F8)
>>> des = DES(keySize=Integer(56))
>>> des.encrypt(P, K56) == C
True
round(state, round_key)[source]#

Apply one round of DES to state and return the result.

EXAMPLES:

sage: from sage.crypto.block_cipher.des import DES
sage: from sage.crypto.block_cipher.des import convert_to_vector
sage: des = DES()
sage: k1 = convert_to_vector(0xFFFFFFFFFFFF, 48)
sage: state = convert_to_vector(0xFFFFFFFF11111111, 64)
sage: ZZ(list(des.round(state, k1))[::-1], 2).hex()
'11111111b29684b8'
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> from sage.crypto.block_cipher.des import convert_to_vector
>>> des = DES()
>>> k1 = convert_to_vector(Integer(0xFFFFFFFFFFFF), Integer(48))
>>> state = convert_to_vector(Integer(0xFFFFFFFF11111111), Integer(64))
>>> ZZ(list(des.round(state, k1))[::-Integer(1)], Integer(2)).hex()
'11111111b29684b8'
sbox_layer(block)[source]#

Apply the Sboxes to block.

EXAMPLES:

sage: from sage.crypto.block_cipher.des import DES
sage: des = DES()
sage: B = vector(GF(2), 48, [0,1,1,0,0,0,0,1,0,0,0,1,0,1,1,1,1,0,1,
....:                        1,1,0,1,0,1,0,0,0,0,1,1,0,0,1,1,0,0,1,
....:                        0,1,0,0,1,0,0,1,1,1])
sage: des.sbox_layer(B)
(0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
 0, 1, 1, 0, 0, 1, 0, 1, 1, 1)
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES
>>> des = DES()
>>> B = vector(GF(Integer(2)), Integer(48), [Integer(0),Integer(1),Integer(1),Integer(0),Integer(0),Integer(0),Integer(0),Integer(1),Integer(0),Integer(0),Integer(0),Integer(1),Integer(0),Integer(1),Integer(1),Integer(1),Integer(1),Integer(0),Integer(1),
...                        Integer(1),Integer(1),Integer(0),Integer(1),Integer(0),Integer(1),Integer(0),Integer(0),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0),Integer(0),Integer(1),
...                        Integer(0),Integer(1),Integer(0),Integer(0),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(1)])
>>> des.sbox_layer(B)
(0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
 0, 1, 1, 0, 0, 1, 0, 1, 1, 1)
class sage.crypto.block_cipher.des.DES_KS(rounds=16, masterKey=None)[source]#

Bases: SageObject

This class implements the DES key schedules described in [U.S1999].

EXAMPLES:

Initialise the key schedule with a \(masterKey\) to use it as an iterable:

sage: from sage.crypto.block_cipher.des import DES_KS
sage: ks = DES_KS(masterKey=0)
sage: ks[0]
0
sage: ks[15]
0
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES_KS
>>> ks = DES_KS(masterKey=Integer(0))
>>> ks[Integer(0)]
0
>>> ks[Integer(15)]
0

Or omit the \(masterKey\) and pass a key when calling the key schedule:

sage: ks = DES_KS()
sage: K = ks(0x584023641ABA6176)
sage: K[0].hex()
'd0a2ed2fa124'
sage: K[15].hex()
'43b42af81183'
>>> from sage.all import *
>>> ks = DES_KS()
>>> K = ks(Integer(0x584023641ABA6176))
>>> K[Integer(0)].hex()
'd0a2ed2fa124'
>>> K[Integer(15)].hex()
'43b42af81183'

See also

DES

__init__(rounds=16, masterKey=None)[source]#

Construct an instance of DES_KS.

INPUT:

  • rounds – integer (default: 16); the number of rounds self can create keys for

  • masterKey – integer or bit list-like (default: None); the 64-bit key that will be used

EXAMPLES:

sage: from sage.crypto.block_cipher.des import DES_KS
sage: DES_KS()
Original DES key schedule with 16 rounds
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES_KS
>>> DES_KS()
Original DES key schedule with 16 rounds

Note

If you want to use a DES_KS object as an iterable you have to pass a masterKey value on initialisation. Otherwise you can omit masterKey and pass a key when you call the object.

__call__(key)[source]#

Return all round keys in a list.

INPUT:

  • key – integer or bit list-like; the 64-bit key

OUTPUT:

  • A list containing the round keys. If key is an integer the elements of the output list will be too. If key is list-like the element of the output list will be bit vectors.

EXAMPLES:

This implementation is using bit vectors for all internal representations. So you can invoke the key schedule with a bit vector:

sage: from sage.crypto.block_cipher.des import DES_KS
sage: K = vector(GF(2),[0,0,0,1,0,0,1,1,0,0,1,1,0,1,0,0,0,1,0,1,0,
....:                   1,1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,
....:                   1,1,1,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,1])
sage: ks = DES_KS(16, K)
sage: [k for k in ks]
[(0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
  0, 0, 1, 0),
 (0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0,
  0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0,
  0, 1, 0, 1),
 ...
 (1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0,
  1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
  0, 1, 0, 1)]
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import DES_KS
>>> K = vector(GF(Integer(2)),[Integer(0),Integer(0),Integer(0),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0),Integer(1),Integer(0),Integer(0),Integer(0),Integer(1),Integer(0),Integer(1),Integer(0),
...                   Integer(1),Integer(1),Integer(1),Integer(0),Integer(1),Integer(1),Integer(1),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0),Integer(1),Integer(1),Integer(1),Integer(0),
...                   Integer(1),Integer(1),Integer(1),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0),Integer(1),Integer(1),Integer(1),Integer(1),Integer(1),Integer(1),Integer(1),Integer(1),Integer(1),Integer(0),Integer(0),Integer(0),Integer(1)])
>>> ks = DES_KS(Integer(16), K)
>>> [k for k in ks]
[(0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
  0, 0, 1, 0),
 (0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0,
  0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0,
  0, 1, 0, 1),
 ...
 (1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0,
  1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
  0, 1, 0, 1)]

But of course you can invoke it with hex representation as well:

sage: K = 0x133457799bbcdff1
sage: ks = DES_KS(16, K)
sage: [k.hex() for k in ks]
['1b02effc7072',
 '79aed9dbc9e5',
 ...
 'cb3d8b0e17f5']
>>> from sage.all import *
>>> K = Integer(0x133457799bbcdff1)
>>> ks = DES_KS(Integer(16), K)
>>> [k.hex() for k in ks]
['1b02effc7072',
 '79aed9dbc9e5',
 ...
 'cb3d8b0e17f5']

Note

If you want to use a DES_KS object as an iterable you have to pass a masterKey value on initialisation. Otherwise you can omit masterKey and pass a key when you call the object.

sage.crypto.block_cipher.des.convert_to_vector(I, L)[source]#

Convert I to a bit vector of length L.

INPUT:

  • I – integer or bit list-like

  • L – integer; the desired bit length of the ouput

OUTPUT:

  • the L-bit vector representation of I

EXAMPLES:

sage: from sage.crypto.block_cipher.des import convert_to_vector
sage: convert_to_vector(0x1F, 8)
(0, 0, 0, 1, 1, 1, 1, 1)
sage: v = vector(GF(2), 4, [1,0,1,0])
sage: convert_to_vector(v, 4)
(1, 0, 1, 0)
>>> from sage.all import *
>>> from sage.crypto.block_cipher.des import convert_to_vector
>>> convert_to_vector(Integer(0x1F), Integer(8))
(0, 0, 0, 1, 1, 1, 1, 1)
>>> v = vector(GF(Integer(2)), Integer(4), [Integer(1),Integer(0),Integer(1),Integer(0)])
>>> convert_to_vector(v, Integer(4))
(1, 0, 1, 0)