-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathATM.cpp
396 lines (312 loc) · 11.6 KB
/
ATM.cpp
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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include "crypto/SHA-1.h"
#include "crypto/RSAPublicKey.h"
#include <fstream>
#include "crypto/RSA.h"
#include "proto/messages.pb.h"
#include <boost/multiprecision/cpp_int.hpp>
#include "crypto/DH.h"
#include "crypto/DESKey.h"
#include "Utils.h"
#include "crypto/DES.h"
#include "crypto/HMAC.h"
// ATM Client Instance
class ATM {
private:
int port = 1500;
std::string ip = "127.0.0.1";
int client;
RSAPublicKey public_key;
DESKey des_key;
std::string hmac_key;
HMAC hmac;
unsigned int seq_num;
std::string encrypt_msg(const std::string& msg) {
seq_num++; // must increase
UnencryptedMessage um;
um.set_inner_msg(msg);
um.set_seq_num(seq_num);
auto um_str = um.SerializeAsString();
std::vector<uint8_t> bytes(um_str.begin(), um_str.end());
auto iv = random_iv();
auto enc_longs = DES::encrypt3_cbc(
longs_from_int(int_from_bytes(bytes)),
des_key,
iv
);
auto enc_bytes = bytes_from_int(int_from_longs(enc_longs));
EncryptedMessage* em = new EncryptedMessage;
em->set_iv(iv);
em->set_inner_msg(enc_bytes.data(), enc_bytes.size());
MACMessage mm;
mm.set_allocated_inner_msg(em);
mm.set_mac(hmac(hmac_key, mm.inner_msg().SerializeAsString()));
return mm.SerializeAsString();
}
std::pair<bool, std::string> decrypt_msg(void* msg, int len) {
MACMessage mm;
if (!mm.ParseFromArray(msg, len)) {
return std::make_pair(false, "");
}
if (hmac(hmac_key, mm.inner_msg().SerializeAsString()) != mm.mac()) {
return std::make_pair(false, "");
}
EncryptedMessage em = mm.inner_msg();
std::vector<uint8_t> enc_bytes(em.inner_msg().begin(), em.inner_msg().end());
auto dec_longs = DES::decrypt3_cbc(
longs_from_int(int_from_bytes(enc_bytes)),
des_key,
em.iv()
);
auto dec_bytes = bytes_from_int(int_from_longs(dec_longs));
UnencryptedMessage um;
if (!um.ParseFromArray(dec_bytes.data(), dec_bytes.size())) {
return std::make_pair(false, "");
}
if (um.seq_num() < seq_num) {
// repeat of previous message is not allowed!
return std::make_pair(false, "");
}
seq_num = um.seq_num();
return std::make_pair(true, um.inner_msg());
}
std::string hash_msg(const std::string& msg) {
SHA1 sha1;
return sha1(msg);
}
void start_connection() {
// Create socket
client = socket(AF_INET, SOCK_STREAM, 0);
if (client < 0) {
std::cerr << "Error creating socket.\n";
exit(1);
}
// Address setup
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = INADDR_ANY;
// Connect to server_fd
if (connect(client, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
std::cerr << "Error connecting to server_fd.\n";
exit(1);
}
std::cout << "Connected to server_fd.\n";
// Start "TLS" negotiation
DH diffie_hellman;
// receive DH value from server
char buffer[8192] = {0};
int len = recv(client, buffer, sizeof(buffer), 0);
ServerHello sh;
if (!sh.ParseFromArray(buffer, len)) {
std::cerr << "Failed to parse ServerHello from Bank. Terminating..." << std::endl;
return;
}
std::vector<uint8_t> sig_bytes(sh.signature().begin(), sh.signature().end());
boost::multiprecision::cpp_int signature;
boost::multiprecision::import_bits(signature, sig_bytes.begin(), sig_bytes.end());
boost::multiprecision::cpp_int val;
boost::multiprecision::import_bits(val, sh.inner_msg().begin(), sh.inner_msg().end());
RSA rsa;
// verify msg is signed by RSA
if (val != rsa.decrypt(signature, public_key.getE(), public_key.getN())) {
std::cerr << "Wrong signature on ServerHello message from Bank. Terminating..." << std::endl;
return;
}
ServerHelloContent shc;
shc.ParseFromString(sh.inner_msg());
std::vector<uint8_t> dh_val_bytes(shc.diffie_hellman_value().begin(), shc.diffie_hellman_value().end());
boost::multiprecision::cpp_int imported_dh_value;
boost::multiprecision::import_bits(imported_dh_value, dh_val_bytes.begin(), dh_val_bytes.end());
diffie_hellman.build_shared_secret(imported_dh_value);
std::cout << "INFO: Received successful ServerHello from Bank" << std::endl;
// send "our" DH value to server
std::vector<uint8_t> dh_send_bytes;
boost::multiprecision::export_bits(diffie_hellman.get_send_value(), std::back_inserter(dh_send_bytes), 8);
ClientHello ch;
ch.set_diffie_hellman_value(dh_send_bytes.data(), dh_send_bytes.size());
std::string msg = ch.SerializeAsString();
send(client, msg.data(), msg.size(), 0);
std::cout << "INFO: Sent ClientHello to Bank" << std::endl;
auto shared_secret = diffie_hellman.get_shared_secret();
boost::multiprecision::cpp_int one = 1;
boost::multiprecision::cpp_int tmp = shared_secret & ((one << 64) - 1);
unsigned long key1 = tmp.convert_to<unsigned long>();
tmp = shared_secret >> 64;
tmp = tmp & ((one << 64) - 1);
unsigned long key2 = tmp.convert_to<unsigned long>();
tmp = shared_secret >> 128;
tmp = tmp & ((one << 64) - 1);
unsigned long key3 = tmp.convert_to<unsigned long>();
des_key = DESKey(key1, key2, key3);
std::fill_n(buffer, 8192, 0);
len = recv(client, buffer, sizeof(buffer), 0);
HMACSend hs;
if (!hs.ParseFromArray(buffer, len)) {
std::cerr << "Failed to parse HMACSend message. Terminating..." << std::endl;
return;
}
std::vector<uint8_t> enc_bytes(hs.encrypted_hmac().begin(), hs.encrypted_hmac().end());
auto decrypted_bytes = bytes_from_int(
int_from_longs(
DES::decrypt3_cbc(
longs_from_int(int_from_bytes(enc_bytes)),
des_key,
hs.iv()
)
)
);
hmac_key = std::string((char*)decrypted_bytes.data(), decrypted_bytes.size());
}
int login(const std::string& bank_card, const std::string& pass) {
std::string message = "LOGIN " + bank_card + " " + pass;
sendBankMsg(message.c_str());
std::string response = recvBankMsg();
if (response == "APPROVED") {
return 1;
}else{
return 0;
}
}
int deposit(const std::string& amount) {
std::string message = "DEPOSIT " + amount;
sendBankMsg(message.c_str());
std::string response = recvBankMsg();
if (response == "APPROVED") {
std::cout << "Deposit Approved" << std::endl;
return 1;
}else if (response == "LIMIT") {
std::cout << "Bank's balance limit reached" << std::endl;
return 0;
}else if (response == "INVALID") {
std::cout << "Invalid Amount" << std::endl;
return 0;
}else{
std::cerr << "Deposit Not Approved" << std::endl;
return 0;
}
}
int withdraw(const std::string& amount) {
sendBankMsg("WITHDRAW "+amount);
std::string response = recvBankMsg();
if (response == "APPROVED") {
std::cout << "Withdraw Approved" << std::endl;
return 1;
}else if (response == "INVALID") {
std::cout << "Invalid Amount" << std::endl;
return 0;
}else if (response == "INSUFFICIENT") {
std::cout << "Indufficient Funds" << std::endl;
return 0;
}else{
std::cerr << "Withdraw Not Approved" << std::endl;
return 0;
}
}
std::string balance() {
sendBankMsg("BALANCE");
std::string response = recvBankMsg();
return response;
}
int sendBankMsg (const std::string& msg) {
std::string encryptedMsg = encrypt_msg(msg);
int bytes_sent = send(client, encryptedMsg.c_str(), encryptedMsg.length(), 0);
if (bytes_sent == -1) {
std::cerr << "Error sending message.\n";
return -1;
}
return 1;
}
std::string recvBankMsg () {
char buffer[8192] = {0};
int len = recv(client, buffer, sizeof(buffer), 0);
auto msg = decrypt_msg(buffer, len);
return std::string(msg.second);
}
public:
ATM(int in_port) {
std::ifstream in("keys/public.txt");
public_key = RSA::parsePublicKey(in);
seq_num = 0;
port = in_port;
}
~ATM() {
close(client);
}
void displayMenu() {
std::cout << "\nATM" << std::endl;
std::cout << "1. Deposit" << std::endl;
std::cout << "2. Withdraw Money" << std::endl;
std::cout << "3. Check Balance" << std::endl;
std::cout << "4. Exit" << std::endl;
}
void run() {
start_connection();
std::string bankCardNumber, pin;
int authenticated=0;
int attempts = 0;
while(authenticated==0){
std::cout << "\nEnter your bank card number: ";
std::cin >> bankCardNumber;
std::cout << "Enter your PIN: ";
std::cin >> pin;
authenticated = login(bankCardNumber, pin);
if(authenticated){
break;
}
std::cerr << "Incorrect Credentials, Try Again.\n";
if(attempts>=3){
std::cerr << "Too Many Attempts. Exiting...\n";
exit(1);
}
attempts++;
}
std::cout << "Logged In.\n";
std::string amount;
while (authenticated) {
displayMenu();
std::cout << "Enter your choice: ";
int choice;
std::cin >> choice;
switch (choice) {
case 1: {
std::cout << "\nEnter amount to deposit: $";
std::cin >> amount;
std::cout << std::endl;
deposit(amount);
break;
}
case 2:
std::cout << "\nEnter amount to withdraw: $";
std::cin >> amount;
std::cout << std::endl;
withdraw(amount);
break;
case 3:
amount = balance();
std::cout << "\nYour Current Balance is: $" << amount << std::endl;
break;
case 4:
std::cout << "Exiting ATM.\n" << std::endl;
exit(1);
default:
std::cout << "Invalid choice, please try again.\n" << std::endl;
}
}
}
};
int main(int argc, char** argv) {
if (argc != 2) {
std::cout << "Provide port number!" << std::endl;
return 1;
}
int port = atoi(argv[1]);
ATM atm(port);
atm.run();
return 0;
}