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'
See also
- __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. IfNone
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 be56
of64
. In the latter case the key contains 8 parity bits.doFinalRound
– boolean (default:True
); ifFalse
a swap takes places but the inverse initial permutation is omitted (i.e. you can get the state afterrounds
). 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
usingkey
. The flagalgorithm
controls what action is to be performed onblock
.INPUT:
block
– integer or bit list-like; the plaintext or ciphertextkey
– integer or bit list-like; the keyalgorithm
– string (default:'encrypt'
); a flag to signify whether encryption or decryption is to be applied toblock
. The encryption flag is'encrypt'
and the decryption flag is'decrypt'
OUTPUT:
The plaintext or ciphertext corresponding to
block
, obtained usingkey
. Ifblock
is an integer the output will be too. Ifblock
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 withkey
.INPUT:
ciphertext
– integer or bit list-like; the ciphertext that will be decryptedkey
– integer or bit list-like; the key
OUTPUT:
The plaintext corresponding to
ciphertext
, obtained usingkey
. Ifciphertext
is an integer the output will be too. Ifciphertext
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 withkey
.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 usingkey
. Ifplaintext
is an integer the output will be too. Ifplaintext
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)
See also
- 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
- __init__(rounds=16, masterKey=None)[source]#
Construct an instance of DES_KS.
INPUT:
rounds
– integer (default:16
); the number of roundsself
can create keys formasterKey
– 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 omitmasterKey
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. Ifkey
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 omitmasterKey
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 lengthL
.INPUT:
I
– integer or bit list-likeL
– integer; the desired bit length of the ouput
OUTPUT:
the
L
-bit vector representation ofI
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)