-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathLFR.py
More file actions
144 lines (116 loc) · 5.15 KB
/
LFR.py
File metadata and controls
144 lines (116 loc) · 5.15 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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
####################################Defining the code correcion scheme####################################
q=2 #Finite Field size
r=5 #Number of randomness
n=5 #Plaintext size
s=50 #s>=n+r, Encoded plaintext size, s-(n+r) is the redundancy
A=MatrixSpace(GF(q),n+r,s).random_element() #Matrix A of the Error Correction Code
PCM=A.right_kernel_matrix() #Parity Check Matrix of A: if c in Fq^s is an element of the code, then PCM*c=0
#Encode: Fq^n->Fq^s, the encoding function of the Error Correction Code
def Encode(p):
Rnd=VectorSpace(GF(q),r).random_element()
x=vector(GF(q),list(p)+list(Rnd))*A
return(x)
#Decode: We did note define the real decode function that would consist of retrieving the corresponding plaintext
# of a cipher text if it exists, or return false otherwise. Instead, we simply verify that a given ciphertext
# is a element of the code or not.
# If Decode(F)=True for F a forged ciphertext, then the forgery attack succeeded
def Decode(x):
return(PCM*vector(GF(q),x)==0) #If True then x is a codeword, else not and we return bot
####################################Defining the Haystack-Code cipher:####################################
ShuffleElement=Permutations(range(s),s).random_element() #Lookup table shuffling function
#computing the reverse shuffle function:
UnshuffleElement=[]
for i in range(s):
j=0
while ShuffleElement[j]!=i:
j+=1
UnshuffleElement.append(j)
#We have for any input i UnshuffleElement[ShuffleElement[i]]=ShuffleElement[UnshuffleElement[i]]=i
#The Shuffle function of the Haystack cipher
def Shuffle(x):
x=list(x)
c=[0 for _ in range(len(x))]
for i in range(len(x)):
c[ShuffleElement[i]]=x[i]
return(c)
#The Unshuffle function of the Haystack cipher, we have Shuffle(Unshuffle(a))=Unshuffle(Shuffle(a))=a
def Unshuffle(c):
c=list(c)
x=[0 for _ in range(len(c))]
for i in range(len(c)):
x[UnshuffleElement[i]]=c[i]
return(c)
#The function Ek of the Haystack-Code cipher
def EncCode(p):
x=Encode(p)
c=Shuffle(x)
return(x)
#The Decoding function that returns if a given ciphertext is correct or not.
def DecCode(c):
x=Unshuffle(c)
p=Decode(x)
return(p)
#The CCA1 function that an attacker can call as much as he wants to receive the ciphertext of a random plaintext
def RandEncQuery():
p=VectorSpace(GF(q),n).random_element()
return(EncCode(p))
#Verification of the validity of the Haystack-Code cipher
for _ in range(100):
assert(DecCode(RandEncQuery()))
####################################Linear Fault Attack forgery on the CCA1 game:####################################
#A function that returns the number of zeroes of a given list
def numOfNonZeroes(L):
ctr=0
for l in L:
if l!=0:
ctr+=1
return ctr
#The core LFR function that perform an algebraic fault on a ciphertext
def OneLinearFault(ListIndexes, Forgery, X):
#Choose a random index to perform the fault on, that hasn't been already faulted
#Verify that the vector is not linearly independent from the other elements of the matrix
rcol=randrange(len(ListIndexes))
while numOfNonZeroes(X.solve_right(X[:,rcol]))==1:
rcol=randrange(len(ListIndexes))
ListIndexes.pop(rcol)
#Compute the vectors that sums to the selected vector at index rcol
LinEq=X.solve_right(X[:,rcol])
#Compute the algebraic fault
temp=GF(q)(0)
for i in range(s):
temp+=(LinEq[i]*Forgery[i])[0]
#Fault the ciphertext
ForgeryFinal=list(Forgery)
ForgeryFinal[rcol]=temp
return(ListIndexes, ForgeryFinal)
#The Linear Fault Recoding (LFR) attack presentedn in the paper.
#T: number of traces, the more the less chances we avoid false positives, T is in O(s) queries and O(s^omega) time complexity
#Notice that we do not use the information of the matrix A of the Error Correction Code to perform our attack.
def LinearFaultRecoding(T=s+20):
#In the CCA1 model, we can ask as many random encryption queries as we want before asking for the decryption
Queries=[]
for _ in range(T):
Queries.append(RandEncQuery())
#We store all of these ciphertexts in a matrix X
X=Matrix(GF(q),[list(Queries[i]) for i in range(T)])
#We select one of the ciphertext as a basis to construct our forgery
Forgery=list((X[0,:])[0])
ListIndexes=list(range(s))
#We perform algebraic fault until finding a valid ciphertext that was not previously queried
#Usually, one linear fault is enough
while len(ListIndexes)>0:
(ListIndexes,Forgery)=OneLinearFault(ListIndexes, Forgery, X)
# print("Fault %d: " % (s-len(ListIndexes)), end="")
# for e in Forgery:
# print(e,end="")
# print()
if (Forgery not in Queries):
if DecCode(vector(GF(q),Forgery)):
return(Forgery)
return(False)
#Verify that we can perform 100 successfull forgery attacks
Successful=True
for _ in range(100):
Successful= Successful and LinearFaultRecoding()
assert Successful
print("LFR is successful.")