Skip to content

Commit c182fdb

Browse files
committed
feat(a1): implement proof of work
1 parent 7f2b1e1 commit c182fdb

File tree

5 files changed

+594
-5
lines changed

5 files changed

+594
-5
lines changed

README.md

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,50 @@ Go to `a1` directory and use command `make` to compile
88

99
### Run
1010

11+
#### Proof of Work
12+
13+
By default, the server runs at 127.0.0.1 on port 17777, and the client listens to it.
14+
15+
`./server.out <p_bits>`
16+
17+
- `p_bits`: optional, the length of P in bits, e.g. `12`, default `8`
18+
19+
`./client.out`
20+
21+
##### PoW Implementation Details
22+
23+
**server.cpp**:
24+
25+
This file contains the server code.
26+
27+
1. Listen to port 17777.
28+
2. Accept the client.
29+
3. Generate random 128-bit R and P of length `p_bits`.
30+
4. Transmit the hex-encoded challenge to the client.
31+
5. Wait for the response. Close the connection if it takes too long.
32+
6. Verify the response from the client.
33+
7. Transmit `welcome` if the response is valid. Otherwise, close the connection.
34+
35+
**client.cpp**:
36+
37+
This file contains the client code.
38+
39+
1. Connect to server at 127.0.0.1 on port 17777.
40+
2. Get challenge.
41+
3. Do proof of work.
42+
4. Give up if it takes too long.
43+
5. Transmit answer to the server.
44+
6. Wait for the response.
45+
7. Close the connection.
46+
47+
**custom_utils.h**:
48+
49+
This file contains shared helper functions.
50+
51+
##### PoW Disclaimer
52+
53+
The processing time varies, especially when `p_bits` is large, such as `16`.
54+
1155
#### Timing Attack
1256

1357
`./timing_attack.out <username> <number_of_trials> <password>`
@@ -16,15 +60,15 @@ Go to `a1` directory and use command `make` to compile
1660
- `number_of_trials`: optional, the number of trials for each letter, default `15000`
1761
- `password`: optional, the prefix of the password, should be empty at the beginning
1862

19-
##### Usage Examples
63+
##### TA Usage Examples
2064

2165
`./timing_attack.out user1`
2266

2367
`./timing_attack.out user1 15000 f`
2468

2569
`./timing_attack.out user1 15000 fi`
2670

27-
##### Implementation Details
71+
##### TA Implementation Details
2872

2973
1. Connect to the server at 127.0.0.1 on port 10458.
3074
2. Transmit the username.
@@ -35,7 +79,7 @@ Go to `a1` directory and use command `make` to compile
3579
7. If there is overlapping between the chosen letter and any other letter, stop and report. Otherwise, append the chosen letter to the password.
3680
8. Go back to step 4.
3781

38-
##### Disclaimer
82+
##### TA Disclaimer
3983

4084
Since 95% confidence intervals are used to determine the letter, there is uncertainty especially when cracking the last letter of the password.
4185

a1/Makefile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
default: server client timing_attack
2+
3+
server: custom_utils.h server.cpp
4+
rm -f server.out
5+
g++ -o server.out server.cpp -lcrypto
6+
7+
client: custom_utils.h client.cpp
8+
rm -f client.out
9+
g++ -o client.out client.cpp -lcrypto
10+
111
timing_attack:
212
rm -f timing_attack.out
313
g++ -o timing_attack.out timing_attack.cpp
4-
default:
5-
timing_attack

a1/client.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#include <iostream>
2+
#include <sstream>
3+
#include <map>
4+
#include <algorithm>
5+
#include <cstdlib>
6+
#include <ctime>
7+
#include <cstring>
8+
#include <cerrno>
9+
using namespace std;
10+
11+
#include <sys/types.h>
12+
#include <sys/socket.h>
13+
#include <netinet/in.h>
14+
#include <arpa/inet.h>
15+
#include <sys/time.h>
16+
#include <wait.h>
17+
#include <unistd.h>
18+
19+
#include "custom_utils.h"
20+
21+
bool process_challenge(const string &R, const string &PH, string &answer);
22+
int socket_to_server(const char *IP, int port);
23+
24+
int main(int argc, char *argv[])
25+
{
26+
int server_socket = socket_to_server("127.0.0.1", 17777);
27+
28+
if (server_socket != -1)
29+
{
30+
struct timeval tv;
31+
32+
tv.tv_sec = 10;
33+
tv.tv_usec = 0;
34+
35+
setsockopt(server_socket, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
36+
37+
// read challenge
38+
const string &challenge = read_packet(server_socket);
39+
int slash_pos = challenge.find('/');
40+
41+
if (slash_pos != challenge.npos)
42+
{
43+
const string &R = hex_to_string(challenge.substr(0, slash_pos));
44+
const string &PH = challenge.substr(slash_pos + 1, challenge.length());
45+
46+
string answer;
47+
48+
if (process_challenge(R, PH, answer))
49+
{
50+
cout << "Answer: " << answer << endl;
51+
52+
send(server_socket, (answer + "\n").c_str(), strlen((answer + "\n").c_str()), MSG_NOSIGNAL);
53+
54+
const string &result = read_packet(server_socket);
55+
cout << "Result: " << result << endl;
56+
57+
close(server_socket);
58+
}
59+
else
60+
{
61+
close(server_socket);
62+
}
63+
}
64+
else
65+
{
66+
cerr << "Wrong challenge format!" << endl;
67+
close(server_socket);
68+
}
69+
}
70+
71+
return 0;
72+
}
73+
74+
bool process_challenge(const string &R, const string &PH, string &answer)
75+
{
76+
int min_processing_time = get_min_processing_time();
77+
int max_processing_time = get_max_processing_time(PH.length() * 4);
78+
79+
// start timer
80+
clock_t start = clock();
81+
82+
string hash;
83+
double processing_time;
84+
85+
// do PoW
86+
do
87+
{
88+
answer = R + generate_random_string(128) + R;
89+
90+
if (!get_sha256(answer, hash))
91+
{
92+
cerr << "Cannot generate hash!" << endl;
93+
return false;
94+
}
95+
96+
// stop timer
97+
clock_t end = clock();
98+
99+
processing_time = (double)(end - start) / CLOCKS_PER_SEC;
100+
101+
if (processing_time > max_processing_time)
102+
{
103+
cerr << "It takes too long!" << endl;
104+
return false;
105+
}
106+
} while (hash.find(PH) == hash.npos || hash.find(PH) != 0);
107+
108+
cout << "Processing time: " << processing_time << "s" << endl;
109+
110+
while (processing_time < min_processing_time)
111+
{
112+
usleep(500000);
113+
processing_time += 0.5;
114+
}
115+
116+
answer = string_to_hex(answer);
117+
118+
return true;
119+
}
120+
121+
int socket_to_server(const char *IP, int port)
122+
{
123+
struct sockaddr_in address;
124+
125+
address.sin_family = AF_INET;
126+
address.sin_addr.s_addr = inet_addr(IP);
127+
address.sin_port = htons(port);
128+
129+
int sock = socket(AF_INET, SOCK_STREAM, 0);
130+
131+
if (connect(sock, (struct sockaddr *)&address, sizeof(address)) == -1)
132+
{
133+
return -1;
134+
}
135+
136+
return sock;
137+
}

0 commit comments

Comments
 (0)