157 lines
5.3 KiB
Python
157 lines
5.3 KiB
Python
## Aron Gohr's SPECK implementation.
|
|
## https://github.com/agohr/deep_speck
|
|
|
|
import numpy as np
|
|
from os import urandom
|
|
|
|
def WORD_SIZE():
|
|
return(16);
|
|
|
|
def ALPHA():
|
|
return(7);
|
|
|
|
def BETA():
|
|
return(2);
|
|
|
|
MASK_VAL = 2 ** WORD_SIZE() - 1;
|
|
|
|
def shuffle_together(l):
|
|
state = np.random.get_state();
|
|
for x in l:
|
|
np.random.set_state(state);
|
|
np.random.shuffle(x);
|
|
|
|
def rol(x,k):
|
|
return(((x << k) & MASK_VAL) | (x >> (WORD_SIZE() - k)));
|
|
|
|
def ror(x,k):
|
|
return((x >> k) | ((x << (WORD_SIZE() - k)) & MASK_VAL));
|
|
|
|
def enc_one_round(p, k):
|
|
c0, c1 = p[0], p[1];
|
|
c0 = ror(c0, ALPHA());
|
|
c0 = (c0 + c1) & MASK_VAL;
|
|
c0 = c0 ^ k;
|
|
c1 = rol(c1, BETA());
|
|
c1 = c1 ^ c0;
|
|
return(c0,c1);
|
|
|
|
def dec_one_round(c,k):
|
|
c0, c1 = c[0], c[1];
|
|
c1 = c1 ^ c0;
|
|
c1 = ror(c1, BETA());
|
|
c0 = c0 ^ k;
|
|
c0 = (c0 - c1) & MASK_VAL;
|
|
c0 = rol(c0, ALPHA());
|
|
return(c0, c1);
|
|
|
|
def expand_key(k, t):
|
|
ks = [0 for i in range(t)];
|
|
ks[0] = k[len(k)-1];
|
|
l = list(reversed(k[:len(k)-1]));
|
|
for i in range(t-1):
|
|
l[i%3], ks[i+1] = enc_one_round((l[i%3], ks[i]), i);
|
|
return(ks);
|
|
|
|
def encrypt(p, ks):
|
|
x, y = p[0], p[1];
|
|
for k in ks:
|
|
x,y = enc_one_round((x,y), k);
|
|
return(x, y);
|
|
|
|
def decrypt(c, ks):
|
|
x, y = c[0], c[1];
|
|
for k in reversed(ks):
|
|
x, y = dec_one_round((x,y), k);
|
|
return(x,y);
|
|
|
|
def check_testvector():
|
|
key = (0x1918,0x1110,0x0908,0x0100)
|
|
pt = (0x6574, 0x694c)
|
|
ks = expand_key(key, 22)
|
|
ct = encrypt(pt, ks)
|
|
if (ct == (0xa868, 0x42f2)):
|
|
print("Testvector verified.")
|
|
return(True);
|
|
else:
|
|
print("Testvector not verified.")
|
|
return(False);
|
|
|
|
#convert_to_binary takes as input an array of ciphertext pairs
|
|
#where the first row of the array contains the lefthand side of the ciphertexts,
|
|
#the second row contains the righthand side of the ciphertexts,
|
|
#the third row contains the lefthand side of the second ciphertexts,
|
|
#and so on
|
|
#it returns an array of bit vectors containing the same data
|
|
def convert_to_binary(arr):
|
|
X = np.zeros((4 * WORD_SIZE(),len(arr[0])),dtype=np.uint8);
|
|
for i in range(4 * WORD_SIZE()):
|
|
index = i // WORD_SIZE();
|
|
offset = WORD_SIZE() - (i % WORD_SIZE()) - 1;
|
|
X[i] = (arr[index] >> offset) & 1;
|
|
X = X.transpose();
|
|
return(X);
|
|
|
|
#takes a text file that contains encrypted block0, block1, true diff prob, real or random
|
|
#data samples are line separated, the above items whitespace-separated
|
|
#returns train data, ground truth, optimal ddt prediction
|
|
def readcsv(datei):
|
|
data = np.genfromtxt(datei, delimiter=' ', converters={x: lambda s: int(s,16) for x in range(2)});
|
|
X0 = [data[i][0] for i in range(len(data))];
|
|
X1 = [data[i][1] for i in range(len(data))];
|
|
Y = [data[i][3] for i in range(len(data))];
|
|
Z = [data[i][2] for i in range(len(data))];
|
|
ct0a = [X0[i] >> 16 for i in range(len(data))];
|
|
ct1a = [X0[i] & MASK_VAL for i in range(len(data))];
|
|
ct0b = [X1[i] >> 16 for i in range(len(data))];
|
|
ct1b = [X1[i] & MASK_VAL for i in range(len(data))];
|
|
ct0a = np.array(ct0a, dtype=np.uint16); ct1a = np.array(ct1a,dtype=np.uint16);
|
|
ct0b = np.array(ct0b, dtype=np.uint16); ct1b = np.array(ct1b, dtype=np.uint16);
|
|
|
|
#X = [[X0[i] >> 16, X0[i] & 0xffff, X1[i] >> 16, X1[i] & 0xffff] for i in range(len(data))];
|
|
X = convert_to_binary([ct0a, ct1a, ct0b, ct1b]);
|
|
Y = np.array(Y, dtype=np.uint8); Z = np.array(Z);
|
|
return(X,Y,Z);
|
|
|
|
#baseline training data generator
|
|
def make_train_data(n, nr, diff=(0x0040,0)):
|
|
Y = np.frombuffer(urandom(n), dtype=np.uint8); Y = Y & 1;
|
|
keys = np.frombuffer(urandom(8*n),dtype=np.uint16).reshape(4,-1);
|
|
plain0l = np.frombuffer(urandom(2*n),dtype=np.uint16);
|
|
plain0r = np.frombuffer(urandom(2*n),dtype=np.uint16);
|
|
plain1l = plain0l ^ diff[0]; plain1r = plain0r ^ diff[1];
|
|
num_rand_samples = np.sum(Y==0);
|
|
plain1l[Y==0] = np.frombuffer(urandom(2*num_rand_samples),dtype=np.uint16);
|
|
plain1r[Y==0] = np.frombuffer(urandom(2*num_rand_samples),dtype=np.uint16);
|
|
ks = expand_key(keys, nr);
|
|
ctdata0l, ctdata0r = encrypt((plain0l, plain0r), ks);
|
|
ctdata1l, ctdata1r = encrypt((plain1l, plain1r), ks);
|
|
X = convert_to_binary([ctdata0l, ctdata0r, ctdata1l, ctdata1r]);
|
|
return(X,Y);
|
|
|
|
#real differences data generator
|
|
def real_differences_data(n, nr, diff=(0x0040,0)):
|
|
#generate labels
|
|
Y = np.frombuffer(urandom(n), dtype=np.uint8); Y = Y & 1;
|
|
#generate keys
|
|
keys = np.frombuffer(urandom(8*n),dtype=np.uint16).reshape(4,-1);
|
|
#generate plaintexts
|
|
plain0l = np.frombuffer(urandom(2*n),dtype=np.uint16);
|
|
plain0r = np.frombuffer(urandom(2*n),dtype=np.uint16);
|
|
#apply input difference
|
|
plain1l = plain0l ^ diff[0]; plain1r = plain0r ^ diff[1];
|
|
num_rand_samples = np.sum(Y==0);
|
|
#expand keys and encrypt
|
|
ks = expand_key(keys, nr);
|
|
ctdata0l, ctdata0r = encrypt((plain0l, plain0r), ks);
|
|
ctdata1l, ctdata1r = encrypt((plain1l, plain1r), ks);
|
|
#generate blinding values
|
|
k0 = np.frombuffer(urandom(2*num_rand_samples),dtype=np.uint16);
|
|
k1 = np.frombuffer(urandom(2*num_rand_samples),dtype=np.uint16);
|
|
#apply blinding to the samples labelled as random
|
|
ctdata0l[Y==0] = ctdata0l[Y==0] ^ k0; ctdata0r[Y==0] = ctdata0r[Y==0] ^ k1;
|
|
ctdata1l[Y==0] = ctdata1l[Y==0] ^ k0; ctdata1r[Y==0] = ctdata1r[Y==0] ^ k1;
|
|
#convert to input data for neural networks
|
|
X = convert_to_binary([ctdata0l, ctdata0r, ctdata1l, ctdata1r]);
|
|
return(X,Y);
|