-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
341 lines (287 loc) · 11 KB
/
main.go
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/*
********************************************************************************
Final implemententation of the Practical 1 out of 2 OT protocol for CS232
Goal: receiver can choose which message to receive without revealing their choice to the sender
and the sender cannot determine which message was read by receiver.
Prints are for a semi-honest party, showing what they could read if curious.
Terminal output was used over comments so we can see the flow of the protocol while running.
Used to run in around 100 ms before User Input
@todos:
make prints as debug
time metrics could be better without counting IO time
********************************************************************************
To run: go run main.go or main.exe
*/
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"fmt"
"math/big"
"sync"
"time"
)
/*
********************************************************************************
Encryption functions
********************************************************************************
*/
// Generate RSA Keys: public and private pair
// Rand.reader is a global, shared instance of a cryptographically secure random number generator
// 2048 is the key size
func GenerateRSAKeys() (*rsa.PrivateKey, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
return key, nil
}
/*
OAEP: Optimal Asymmetric Encryption Padding
In RSA-OAEP, the message is not directly encrypted with the RSA algorithm.
Instead, the message is first padded with some additional data.
This padding includes a hash of the message, some random data, and a hash of that random data.
The padded message is then encrypted with the RSA algorithm.
rsa.EncryptOAEP function works with byte slices, not strings, thus we have to convert msg.
"The message must be no longer than the length of the public modulus minus twice the
hash length, minus a further 2." - Function documentation
The public modulus is 2048 bits, or 256 bytes, so the message must be no longer than 256 - 2*32 - 2 = 190 bytes.
@todo: what if is bigger?
*/
func Encrypt(pubKey *rsa.PublicKey, msg string) ([]byte, error) {
hash := sha256.New()
ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pubKey, []byte(msg), nil)
if err != nil {
return nil, err
}
return ciphertext, err
}
/*
This function doesn't accept to be applied on values that are not
reconizable as a valid ciphertext. So, I've decided to return a random value in case it fails.
In this case, the user won't be able to notice an empty string returned by the function and thus see
which message was chosen by the receiver.
*/
func Decrypt(privKey *rsa.PrivateKey, ciphertext []byte) (string, error) {
hash := sha256.New()
plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, privKey, ciphertext, nil)
if err != nil {
fdata, _ := generateRandomBytes()
return string(fdata), nil
}
return string(plaintext), nil
}
// We need to generate random bytes to use as messages/placeholders
func generateRandomBytes() ([]byte, error) {
x := make([]byte, 150)
_, err := rand.Read(x)
if err != nil {
fmt.Println("Error generating random value:", err)
return nil, err
}
return x, nil
}
/*
********************************************************************************
Sender steps
********************************************************************************
*/
func senderRoutine(msgChan chan []byte, keyChan chan *rsa.PublicKey, receiveV chan []byte) {
// Sender inputs the messages
// Space is currently breaking the string, what is anoying for sending sets
// It is possible to send something like [alex,mateo,joao]
var m0, m1 string
fmt.Println("SENDER: Enter message 0 with maximum 120 chars:")
fmt.Scanln(&m0)
for len(m0) > 120 {
fmt.Println("Error: Message 0 exceeds maximum length of 120 characters")
fmt.Println("SENDER: Enter message 0 with maximum 120 chars:")
fmt.Scanln(&m0)
}
fmt.Println("SENDER: Enter message 1 with maximum 120 chars:")
fmt.Scanln(&m1)
for len(m1) > 120 {
fmt.Println("Error: Message 1 exceeds maximum length of 120 characters")
fmt.Println("SENDER: Enter message 1 with maximum 120 chars:")
fmt.Scanln(&m1)
}
senderKeys, err := GenerateRSAKeys()
if err != nil {
fmt.Println("Error generating keys:", err)
return
}
fmt.Println("SENDER STEP 1: generated RSA key-pair")
keyChan <- &senderKeys.PublicKey
x0, _ := generateRandomBytes()
x1, _ := generateRandomBytes()
msgChan <- x0
msgChan <- x1
fmt.Println("SENDER STEP 2: Generated random messages. Now sending random messages and Public Key to Receiver", x0, x1)
// Sender receives v and decrypts
v := <-receiveV
// fmt.Println("SENDER STEP 2.5: Received v from Receiver", v)
// v - x0 and v - x1
// values must be converted to big int to perform operations
incomingVBytes := new(big.Int).SetBytes(v)
preK0 := new(big.Int).Sub(incomingVBytes, new(big.Int).SetBytes(x0))
preK1 := new(big.Int).Sub(incomingVBytes, new(big.Int).SetBytes(x1))
// ciphertext should be byte slices
preK0bytes := preK0.Bytes()
preK1bytes := preK1.Bytes()
//fmt.Println("DEBUG: preK0:", len(preK0bytes))
//fmt.Println("DEBUG: preK1:", len(preK1bytes))
// Can't have error handling so we don't leak information
// k0^d and k1^d
k0, _ := Decrypt(senderKeys, preK0bytes)
k1, _ := Decrypt(senderKeys, preK1bytes)
//fmt.Println("DEBUG: k0:", k0)
//fmt.Println("DEBUG: k1:", k1)
// Again, we need to convert k0 and k1 to a big.Int to perform operations
k0Slice := []byte(k0)
k0BigInt := new(big.Int).SetBytes(k0Slice)
k1Slice := []byte(k1)
k1BigInt := new(big.Int).SetBytes(k1Slice)
// Now we need to mod k0 and k1 with the public key N and then convert them to string
// k0%N and k1%N
k0Str := new(big.Int).Mod(k0BigInt, new(big.Int).Set(senderKeys.PublicKey.N))
k1Str := new(big.Int).Mod(k1BigInt, new(big.Int).Set(senderKeys.PublicKey.N))
// fmt.Println("DEBUG: k0:", k0)
// fmt.Println("DEBUG: k1:", k1)
// fmt.Println("DEBUG: k0BIG:", k0BigInt)
// fmt.Println("DEBUG: k1BIG:", k1BigInt)
// fmt.Println("DEBUG: k0Str:", k0Str)
// fmt.Println("DEBUG: k1Str:", k1Str)
fmt.Printf("SENDER STEP 3: Decrypts the two possible ks %v and %v\n", k0Str, k1Str)
// messages zero and one prime
// Converting to byte slice and then big.Int to perform operations
m0Big := new(big.Int).SetBytes([]byte(m0))
m1Big := new(big.Int).SetBytes([]byte(m1))
m0p := new(big.Int).Add(m0Big, k0Str).Bytes()
m1p := new(big.Int).Add(m1Big, k1Str).Bytes()
fmt.Println("Message 0 Prime:", m0p)
fmt.Println("Message 1 Prime:", m1p)
msgChan <- m0p
msgChan <- m1p
fmt.Printf("SENDER STEP 4: hid the messages 0: %v and 1:%v now is sending them on the channel\n", m0p, m1p)
}
/*
********************************************************************************
Sender steps end
********************************************************************************
*/
/*
********************************************************************************
Receiver steps
********************************************************************************
*/
// Receiver v calculation
func calculateV(sigma int, x0, x1, encK []byte, senderPubKey rsa.PublicKey) []byte {
x0Int := new(big.Int).SetBytes(x0)
x1Int := new(big.Int).SetBytes(x1)
encKInt := new(big.Int).SetBytes(encK)
encKInt.Mod(encKInt, senderPubKey.N)
//fmt.Println("x0Int:", x0Int)
//fmt.Println("x1Int:", x1Int)
//fmt.Println("encKInt:", encKInt)
v := new(big.Int)
if sigma == 0 {
v = v.Add(x0Int, encKInt)
//fmt.Println("Performed the add to 0:", v)
} else {
v = v.Add(x1Int, encKInt)
//fmt.Println("Performed the add to 1:", v)
}
return v.Bytes()
}
func receiverRoutine(msgChan chan []byte, keyChan chan *rsa.PublicKey, sendV chan []byte) {
senderPubKey := <-keyChan
tx0 := <-msgChan
tx1 := <-msgChan
fmt.Printf("RECEIVER STEP 0: received random messages and Public Key\nRandom string 1: %v\nRandom string 2: %v\nSender's Public Key exponent: %v\nSender's Public Key N: %v\n", tx0, tx1, senderPubKey.E, senderPubKey.N)
// Receiver chooses which message wants to see
var sigma int
for {
fmt.Println("RECEIVER: Choose sigma 0 or 1:")
_, err := fmt.Scanln(&sigma)
if err != nil || (sigma != 0 && sigma != 1) {
fmt.Println("Invalid value for sigma.")
} else {
break
}
}
fmt.Println("Receiver chose sigma", sigma)
kBytes, _ := generateRandomBytes()
k := string(kBytes)
fmt.Println("\nRECEIVER STEP 1: Generated random message k:", k)
encK, err := Encrypt(senderPubKey, k)
if err != nil {
fmt.Println("Error encrypting message:", err)
return
}
fmt.Println("RECEIVER STEP 2: Encrypted random message k:", encK)
v := calculateV(sigma, tx0, tx1, encK, *senderPubKey)
sendV <- v
fmt.Printf("RECEIVER STEP 3: Calculated v %v and now is sending on the channel\n", v)
// Receiver receives the messages
m0r := <-msgChan
m1r := <-msgChan
kInt := new(big.Int)
kInt.SetBytes(kBytes)
// Receiver retrieves the messages
// m0r - k and m1r - k = msg0 and msg1
msg0 := new(big.Int)
msg1 := new(big.Int)
m0rInt := new(big.Int).SetBytes(m0r)
msg0.Sub(m0rInt, kInt)
m1rInt := new(big.Int).SetBytes(m1r)
msg1.Sub(m1rInt, kInt)
// fmt.Println("DEBUG: kInt:", kInt)
// fmt.Println("DEBUG: m0rInt:", m0rInt)
// fmt.Println("DEBUG: msg0:", msg0)
// fmt.Println("DEBUG: m1rInt:", m1rInt)
// fmt.Println("DEBUG: msg1:", msg1)
// Convert the messages to strings
msg0Bytes := big.NewInt(0).Set(msg0).Bytes()
msg0Str := string(msg0Bytes)
//msg0Str = strings.TrimRight(msg0Str, "0")
msg1Bytes := big.NewInt(0).Set(msg1).Bytes()
msg1Str := string(msg1Bytes)
// msg1Str = strings.TrimRight(msg1Str, "0")
if sigma == 0 {
fmt.Printf("RECEIVER STEP 4: Retrieved the message %v for sigma %v\n", msg0Str, sigma)
fmt.Printf("CURIOUS RECEIVER STEP 5: Retrieved the message %v for sigma %v\n", msg1Str, 1-sigma)
} else {
fmt.Printf("RECEIVER STEP 4: Retrieved the message %v for sigma %v\n", msg1Str, sigma)
fmt.Printf("CURIOUS RECEIVER STEP 5: Retrieved the message %v for sigma %v\n", msg0Str, 1-sigma)
}
}
/*
********************************************************************************
Receiver steps end
********************************************************************************
*/
func main() {
startTime := time.Now()
fmt.Println("Starting the protocol simulation")
// Create a channel to send the random numbers and key
// Channels are FIFO
msgChan := make(chan []byte)
keyChan := make(chan *rsa.PublicKey)
receiveV := make(chan []byte)
var wg sync.WaitGroup
wg.Add(2) // Add the number of goroutines to wait for
go func() {
senderRoutine(msgChan, keyChan, receiveV)
wg.Done() // Decrease the WaitGroup counter when the goroutine finishes
}()
go func() {
receiverRoutine(msgChan, keyChan, receiveV)
wg.Done() // Decrease the WaitGroup counter when the goroutine finishes
}()
wg.Wait() // Wait for all goroutines to finish
endTime := time.Now()
fmt.Println("Total execution time in milliseconds:", endTime.Sub(startTime).Milliseconds())
fmt.Println("Press Enter to finish...")
fmt.Scanln()
}