-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathbasic_SPN.py
More file actions
124 lines (97 loc) · 4.59 KB
/
basic_SPN.py
File metadata and controls
124 lines (97 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# A basic Substitution-Permutation Network cipher, implemented by following
# 'A Tutorial on Linear and Differential Cryptanalysis'
# by Howard M. Heys
#
# 02/12/16 Chris Hicks
#
# Basic SPN cipher which takes as input a 16-bit input block and has 4 rounds.
# Each round consists of (1) substitution (2) transposition (3) key mixing
import random
import hashlib
blockSize = 16
verboseState = False
# (1) Substitution: 4x4 bijective, one sbox used for all 4 sub-blocks of size 4. Nibble wise
sbox = {0:0xE, 1:0x4, 2:0xD, 3:0x1, 4:0x2, 5:0xF, 6:0xB, 7:0x8, 8:0x3, 9:0xA, 0xA:0x6, 0xB:0xC, 0xC:0x5, 0xD:0x9, 0xE:0x0, 0xF:0x7} #key:value
sbox_inv = {0xE:0, 0x4:1, 0xD:2, 0x1:3, 0x2:4, 0xF:5, 0xB:6, 0x8:7, 0x3:8, 0xA:9, 0x6:0xA, 0xC:0xB, 0x5:0xC, 0x9:0xD, 0x0:0xE, 0x7:0xF}
# Apply sbox (1) to a 16 bit state and return the result
def apply_sbox(state, sbox):
subStates = [state&0x000f, (state&0x00f0)>>4, (state&0x0f00)>>8, (state&0xf000)>>12]
for idx,subState in enumerate(subStates):
subStates[idx] = sbox[subState]
return subStates[0]|subStates[1]<<4|subStates[2]<<8|subStates[3]<<12
# (2) Permutation. Applied bit-wise
pbox = {0:0, 1:4, 2:8, 3:12, 4:1, 5:5, 6:9, 7:13, 8:2, 9:6, 10:10, 11:14, 12:3, 13:7, 14:11, 15:15}
pbox_inv = {0:0, 4:1, 8:2, 12:3, 1:4, 5:5, 9:6, 13:7, 2:8, 6:9, 10:10, 14:11, 3:12, 7:13, 11:14, 15:15}
# (3) Key mixing: bitwise XOR between round subkey and data block input to round
# Key schedule: independant random round keys.
# We take the sha-hash of a 128-bit 'random' seed and then take the first 80-bits
# of the output as out round keys K1-K5 (Each 16 bits long). (not secure, this is just a demo)
def keyGeneration():
k = hashlib.sha1( hex(random.getrandbits(128)).encode('utf-8') ).hexdigest()[2:2+20]
return k
# Simple SPN Cipher encrypt function
def encrypt(pt, k):
state = pt
if verboseState: print('**pt = {:04x}**'.format(state))
subKeys = [ int(subK,16) for subK in [ k[0:4],k[4:8], k[8:12], k[12:16], k[16:20] ] ]
#First three rounds of sinple SPN cipher
for roundN in range(0,3):
if verboseState: print(roundN, end = ' ')
#XOR state with round key (3, subkeys 1,..,4)
state = state^subKeys[roundN]
if verboseState: print (hex(state), end = ' ')
#Break state into nibbles, perform sbox on each nibble, write to state (1)
state = apply_sbox(state,sbox)
if verboseState: print (hex(state), end = ' ')
#Permute the state bitwise (2)
state_temp = 0
for bitIdx in range(0,blockSize):
if(state & (1 << bitIdx)):
state_temp |= (1 << pbox[bitIdx])
state = state_temp
if verboseState: print (hex(state))
# Final round of SPN cipher (k4, sbox, s5)
state = state^subKeys[-2] #penultimate subkey (key 4) mixing
if verboseState: print (str(3), hex(state), end = ' ')
state = apply_sbox(state,sbox)
if verboseState: print (hex(state), end = ' ')
state = state^subKeys[-1] #Final subkey (key 5) mixing
if verboseState: print (hex(state))
if verboseState: print('**ct = {:04x}**'.format(state))
return state
# Simple SPN Cipher decrypt function
def decrypt(ct, k):
state = ct
if verboseState: print('**ct = {:04x}**'.format(state))
#Derive round keys
subKeys = [ int(subK,16) for subK in [ k[0:4],k[4:8], k[8:12], k[12:16], k[16:20] ] ]
if verboseState: print (str(3), hex(state), end= ' ')
#Undo final round key
state = state^subKeys[4]
if verboseState: print (hex(state), end= ' ')
#Apply inverse s-box
state = apply_sbox(state,sbox_inv)
if verboseState: print (hex(state))
#Undo first 3 rounds of simple SPN cipher
for roundN in range(2, -1, -1):
if verboseState: print(roundN, end = ' ')
#XOR state with round key (3, subkeys 4,..,0)
state = state^subKeys[roundN+1]
if verboseState: print (hex(state), end=' ')
#Un-permute the state bitwise (2)
state_temp = 0
for bitIdx in range(0, blockSize):
if(state & (1 << bitIdx)):
state_temp |= (1 << pbox[bitIdx])
state = state_temp
if verboseState: print (hex(state), end = ' ')
#Apply inverse s-box
state = apply_sbox(state,sbox_inv)
if verboseState: print (hex(state))
if verboseState: print(roundN, end = ' ')
#XOR state with round key 0
state = state^subKeys[0]
if verboseState: print('**pt = {:04x}**'.format(state))
return state