commit e66893f
shrub
·
2026-07-31 15:03:58 +0000 UTC
parent 2e29696
do auth with bearssl and libtls
8 files changed,
+642,
-17
+10,
-0
1@@ -0,0 +1,10 @@
2+#ifndef MG_RANDOM_H
3+#define MG_RANDOM_H
4+
5+#include <stddef.h>
6+
7+#include "types.h"
8+
9+int mg_random_bytes(u8* data, size_t size);
10+
11+#endif /* MG_RANDOM_H */
+7,
-0
1@@ -11,6 +11,8 @@ cc = meson.get_compiler('c')
2 math_dep = cc.find_library('m', required: false)
3 sdl_dep = dependency('sdl2')
4 zlib_dep = dependency('zlib')
5+tls_dep = dependency('libtls')
6+bearssl_dep = cc.find_library('bearssl')
7 renderer_backend = get_option('renderer')
8
9 sources = [
10@@ -18,8 +20,10 @@ sources = [
11 'source/util.c',
12 'source/world/chunk.c',
13 'source/client/camera.c',
14+ 'source/mcproto/auth.c',
15 'source/mcproto/buffer.c',
16 'source/mcproto/client.c',
17+ 'source/mcproto/crypto.c',
18 'source/platform/platform.c',
19 'vendor/stb_image_c89.c',
20 ]
21@@ -99,6 +103,8 @@ renderer_dep = declare_dependency(
22 deps = [
23 sdl_dep,
24 zlib_dep,
25+ tls_dep,
26+ bearssl_dep,
27 mgmath_dep,
28 renderer_dep,
29 ]
30@@ -109,4 +115,5 @@ executable(
31 include_directories: include_dirs,
32 dependencies: deps,
33 c_args: renderer_c_args,
34+ override_options: [ 'c_std=c99' ],
35 )
+3,
-2
1@@ -1,5 +1,6 @@
2 #include <SDL2/SDL.h>
3 #include <stdio.h>
4+#include <stdlib.h>
5
6 #include "SDL_keyboard.h"
7 #include "SDL_timer.h"
8@@ -65,8 +66,8 @@ static int protocol_poc(const char* address, const char* username)
9 char error[1024];
10
11 account.username = username;
12- account.profile_uuid = NULL;
13- account.access_token = NULL;
14+ account.profile_uuid = getenv("MANGOLIA_PROFILE_UUID");
15+ account.access_token = getenv("MANGOLIA_ACCESS_TOKEN");
16 if (!mc_client_login(&client, address, &account, error, sizeof(error))) {
17 fprintf(stderr, "%s\n", error);
18 return MG_ERR_EXIT_FAILURE;
+201,
-0
1@@ -0,0 +1,201 @@
2+#define _POSIX_C_SOURCE 200112L
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+#include <tls.h>
9+
10+#include "internal.h"
11+
12+#define MC_JOIN_HOST "sessionserver.mojang.com"
13+#define MC_JOIN_PORT "443"
14+#define MC_JOIN_PATH "/session/minecraft/join"
15+
16+static int json_string(MCBuffer* buffer, const char* value)
17+{
18+ const unsigned char* p = (const unsigned char*)value;
19+ if (!bufwrite_u8(buffer, '"'))
20+ return 0;
21+ while (*p) {
22+ char escaped[7];
23+ size_t size = 1;
24+ switch (*p) {
25+ case '"': escaped[0] = '\\'; escaped[1] = '"'; size = 2; break;
26+ case '\\': escaped[0] = '\\'; escaped[1] = '\\'; size = 2; break;
27+ case '\b': escaped[0] = '\\'; escaped[1] = 'b'; size = 2; break;
28+ case '\f': escaped[0] = '\\'; escaped[1] = 'f'; size = 2; break;
29+ case '\n': escaped[0] = '\\'; escaped[1] = 'n'; size = 2; break;
30+ case '\r': escaped[0] = '\\'; escaped[1] = 'r'; size = 2; break;
31+ case '\t': escaped[0] = '\\'; escaped[1] = 't'; size = 2; break;
32+ default:
33+ if (*p < 0x20) {
34+ snprintf(escaped, sizeof(escaped), "\\u%04x", *p);
35+ size = 6;
36+ } else {
37+ escaped[0] = (char)*p;
38+ }
39+ }
40+ if (!bufwrite(buffer, escaped, size))
41+ return 0;
42+ ++p;
43+ }
44+ return bufwrite_u8(buffer, '"');
45+}
46+
47+static int tls_write_all(struct tls* tls, const void* data, size_t size)
48+{
49+ const u8* bytes = (const u8*)data;
50+ size_t sent = 0;
51+
52+ while (sent < size) {
53+ ssize_t count = tls_write(tls, bytes + sent, size - sent);
54+ if (count == TLS_WANT_POLLIN || count == TLS_WANT_POLLOUT)
55+ continue;
56+ if (count <= 0)
57+ return 0;
58+ sent += (size_t)count;
59+ }
60+ return 1;
61+}
62+
63+static int tls_read_response(struct tls* tls, MCBuffer* response)
64+{
65+ u8 data[2048];
66+
67+ for (;;) {
68+ ssize_t count = tls_read(tls, data, sizeof(data));
69+ if (count == TLS_WANT_POLLIN || count == TLS_WANT_POLLOUT)
70+ continue;
71+ if (count == 0)
72+ return 1;
73+ if (count < 0)
74+ return 0;
75+ if (!bufwrite(response, data, (size_t)count))
76+ return 0;
77+ if (response->size > 64 * 1024)
78+ return 0;
79+ }
80+}
81+
82+static long http_status(const MCBuffer* response)
83+{
84+ long status;
85+
86+ if (response->size < 12 || memcmp(response->data, "HTTP/", 5) != 0)
87+ return 0;
88+ status = strtol((const char*)response->data + 9, NULL, 10);
89+ return status;
90+}
91+
92+int mc_join_server(
93+ const MCAccount* account,
94+ const char* server_id,
95+ const u8 shared_secret[16],
96+ const u8* public_key,
97+ size_t public_key_size,
98+ char* error,
99+ size_t error_size)
100+{
101+ struct tls_config* config = NULL;
102+ struct tls* tls = NULL;
103+ MCBuffer json;
104+ MCBuffer request;
105+ MCBuffer response;
106+ char hash[42];
107+ char headers[512];
108+ long status;
109+ int ok = 0;
110+
111+ if (!account->access_token || !account->access_token[0] ||
112+ !account->profile_uuid || !account->profile_uuid[0]) {
113+ mc_set_error(error, error_size,
114+ "online-mode server requires MANGOLIA_ACCESS_TOKEN and MANGOLIA_PROFILE_UUID");
115+ return 0;
116+ }
117+
118+ bufinit(&json);
119+ bufinit(&request);
120+ bufinit(&response);
121+ mc_server_hash(server_id, shared_secret, public_key, public_key_size, hash);
122+ if (!bufwrite(&json, "{\"accessToken\":", 15) ||
123+ !json_string(&json, account->access_token) ||
124+ !bufwrite(&json, ",\"selectedProfile\":", 19) ||
125+ !json_string(&json, account->profile_uuid) ||
126+ !bufwrite(&json, ",\"serverId\":", 12) ||
127+ !json_string(&json, hash) ||
128+ !bufwrite_u8(&json, '}')) {
129+ mc_set_error(error, error_size, "out of memory building session request");
130+ goto cleanup;
131+ }
132+
133+ if (tls_init() != 0) {
134+ mc_set_error(error, error_size, "libtls initialization failed");
135+ goto cleanup;
136+ }
137+ config = tls_config_new();
138+ tls = tls_client();
139+ if (!config || !tls) {
140+ mc_set_error(error, error_size, "libtls request initialization failed");
141+ goto cleanup;
142+ }
143+ if (tls_configure(tls, config) != 0) {
144+ mc_set_error(error, error_size, "libtls configure failed: %s",
145+ tls_error(tls));
146+ goto cleanup;
147+ }
148+ if (tls_connect(tls, MC_JOIN_HOST, MC_JOIN_PORT) != 0) {
149+ mc_set_error(error, error_size, "session TLS connect failed: %s",
150+ tls_error(tls));
151+ goto cleanup;
152+ }
153+ snprintf(headers, sizeof(headers),
154+ "POST " MC_JOIN_PATH " HTTP/1.1\r\n"
155+ "Host: " MC_JOIN_HOST "\r\n"
156+ "User-Agent: Magnolia/1.8.9\r\n"
157+ "Content-Type: application/json\r\n"
158+ "Content-Length: %lu\r\n"
159+ "Connection: close\r\n"
160+ "\r\n",
161+ (unsigned long)json.size);
162+ if (!bufwrite(&request, headers, strlen(headers)) ||
163+ !bufwrite(&request, json.data, json.size)) {
164+ mc_set_error(error, error_size, "out of memory building session HTTP request");
165+ goto cleanup;
166+ }
167+ if (!tls_write_all(tls, request.data, request.size)) {
168+ mc_set_error(error, error_size, "session join request write failed: %s",
169+ tls_error(tls));
170+ goto cleanup;
171+ }
172+ if (!tls_read_response(tls, &response)) {
173+ mc_set_error(error, error_size, "session join response read failed: %s",
174+ tls_error(tls));
175+ goto cleanup;
176+ }
177+ status = http_status(&response);
178+ if (status != 204) {
179+ size_t show = response.size < 400 ? response.size : 400;
180+ mc_set_error(error, error_size,
181+ "session server rejected join (HTTP %ld): %.*s",
182+ status, (int)show, response.data ? (char*)response.data : "");
183+ goto cleanup;
184+ }
185+ ok = 1;
186+
187+cleanup:
188+ if (json.data)
189+ memset(json.data, 0, json.capacity);
190+ if (request.data)
191+ memset(request.data, 0, request.capacity);
192+ if (tls) {
193+ tls_close(tls);
194+ tls_free(tls);
195+ }
196+ if (config)
197+ tls_config_free(config);
198+ buffree(&response);
199+ buffree(&request);
200+ buffree(&json);
201+ return ok;
202+}
+98,
-15
1@@ -11,6 +11,7 @@
2 #include <zlib.h>
3
4 #include "internal.h"
5+#include "mg_random.h"
6
7 #ifndef MSG_NOSIGNAL
8 #define MSG_NOSIGNAL 0
9@@ -100,16 +101,28 @@ static int tcp_connect(MCClient* client, char* error, size_t error_size)
10
11 static int socket_write(MCClient* client, const u8* data, size_t size)
12 {
13- size_t sent = 0;
14- while (sent < size) {
15- ssize_t count = send(client->socket_fd, data + sent,
16- size - sent, MSG_NOSIGNAL);
17- if (count > 0)
18- sent += (size_t)count;
19- else if (count < 0 && errno == EINTR)
20- continue;
21- else
22- return 0;
23+ u8 encrypted[4096];
24+ size_t offset = 0;
25+
26+ while (offset < size) {
27+ size_t chunk = size - offset;
28+ size_t sent = 0;
29+ if (chunk > sizeof(encrypted))
30+ chunk = sizeof(encrypted);
31+ memcpy(encrypted, data + offset, chunk);
32+ if (client->encrypted)
33+ mc_cipher_encrypt(&client->write_cipher, encrypted, chunk);
34+ while (sent < chunk) {
35+ ssize_t count = send(client->socket_fd, encrypted + sent,
36+ chunk - sent, MSG_NOSIGNAL);
37+ if (count > 0)
38+ sent += (size_t)count;
39+ else if (count < 0 && errno == EINTR)
40+ continue;
41+ else
42+ return 0;
43+ }
44+ offset += chunk;
45 }
46 return 1;
47 }
48@@ -126,6 +139,8 @@ static int socket_read(MCClient* client, u8* data, size_t size)
49 else
50 return 0;
51 }
52+ if (client->encrypted)
53+ mc_cipher_decrypt(&client->read_cipher, data, size);
54 return 1;
55 }
56
57@@ -325,7 +340,74 @@ static int send_login_start(MCClient* client)
58 return ok;
59 }
60
61-static int login_loop(MCClient* client, char* error, size_t error_size)
62+static int handle_encryption(
63+ MCClient* client,
64+ const MCAccount* account,
65+ MCPacket* packet,
66+ char* error,
67+ size_t error_size)
68+{
69+ MCBuffer input = { packet->data, packet->size, packet->size, 0 };
70+ MCBuffer response;
71+ char server_id[256];
72+ const u8* public_key;
73+ const u8* verify_token;
74+ size_t public_key_size;
75+ size_t verify_token_size;
76+ u8 shared_secret[16];
77+ u8* encrypted_secret = NULL;
78+ u8* encrypted_token = NULL;
79+ size_t encrypted_secret_size = 0;
80+ size_t encrypted_token_size = 0;
81+ int ok = 0;
82+
83+ if (!bufread_string(&input, server_id, sizeof(server_id)) ||
84+ !bufread_bytes(&input, &public_key, &public_key_size) ||
85+ !bufread_bytes(&input, &verify_token, &verify_token_size)) {
86+ mc_set_error(error, error_size, "malformed encryption request");
87+ return 0;
88+ }
89+ if (!mg_random_bytes(shared_secret, sizeof(shared_secret))) {
90+ mc_set_error(error, error_size, "could not obtain secure random bytes");
91+ return 0;
92+ }
93+ if (!mc_join_server(account, server_id, shared_secret,
94+ public_key, public_key_size, error, error_size) ||
95+ !mc_rsa_encrypt(public_key, public_key_size,
96+ shared_secret, sizeof(shared_secret),
97+ &encrypted_secret, &encrypted_secret_size, error, error_size) ||
98+ !mc_rsa_encrypt(public_key, public_key_size,
99+ verify_token, verify_token_size,
100+ &encrypted_token, &encrypted_token_size, error, error_size))
101+ goto cleanup;
102+
103+ bufinit(&response);
104+ if (bufwrite_varint(&response, (i32)encrypted_secret_size) &&
105+ bufwrite(&response, encrypted_secret, encrypted_secret_size) &&
106+ bufwrite_varint(&response, (i32)encrypted_token_size) &&
107+ bufwrite(&response, encrypted_token, encrypted_token_size) &&
108+ mc_client_write(client, 0x01, response.data, response.size)) {
109+ mc_cipher_init(&client->read_cipher, shared_secret);
110+ mc_cipher_init(&client->write_cipher, shared_secret);
111+ client->encrypted = 1;
112+ ok = 1;
113+ } else {
114+ mc_set_error(error, error_size, "could not send encryption response");
115+ }
116+ buffree(&response);
117+
118+cleanup:
119+ memset(shared_secret, 0, sizeof(shared_secret));
120+ free(encrypted_secret);
121+ free(encrypted_token);
122+ return ok;
123+}
124+
125+static int login_loop(
126+ MCClient* client,
127+ const MCAccount* account,
128+ char* error,
129+ size_t error_size)
130 {
131 /*
132 * login state packet ids for protocol 47:
133@@ -354,10 +436,11 @@ static int login_loop(MCClient* client, char* error, size_t error_size)
134 return 0;
135 }
136 if (packet.id == 0x01) {
137- mc_set_error(error, error_size,
138- "server requested online-mode encryption, idfk know what to do");
139+ int ok = handle_encryption(client, account, &packet, error, error_size);
140 mc_packet_free(&packet);
141- return 0;
142+ if (!ok)
143+ return 0;
144+ continue;
145 }
146 if (packet.id == 0x02) {
147 char username[64];
148@@ -431,7 +514,7 @@ int mc_client_login(
149 return 0;
150 }
151 printf("sent handshake and login start\n");
152- if (!login_loop(client, error, error_size)) {
153+ if (!login_loop(client, account, error, error_size)) {
154 mc_client_destroy(client);
155 return 0;
156 }
+250,
-0
1@@ -0,0 +1,250 @@
2+#include <stdlib.h>
3+#include <string.h>
4+
5+#include "internal.h"
6+#include "mg_random.h"
7+
8+typedef struct {
9+ const u8* data;
10+ size_t size;
11+ size_t cursor;
12+} DERReader;
13+
14+static int der_length(DERReader* reader, size_t* length)
15+{
16+ u8 first;
17+ size_t value = 0;
18+ unsigned count;
19+ unsigned i;
20+
21+ if (reader->cursor >= reader->size)
22+ return 0;
23+ first = reader->data[reader->cursor++];
24+ if (!(first & 0x80)) {
25+ *length = first;
26+ return *length <= reader->size - reader->cursor;
27+ }
28+ count = first & 0x7f;
29+ if (count == 0 || count > sizeof(size_t) ||
30+ count > reader->size - reader->cursor)
31+ return 0;
32+ for (i = 0; i < count; ++i)
33+ value = (value << 8) | reader->data[reader->cursor++];
34+ if (value > reader->size - reader->cursor)
35+ return 0;
36+ *length = value;
37+ return 1;
38+}
39+
40+static int der_value(DERReader* reader, u8 tag, DERReader* value)
41+{
42+ size_t length;
43+ if (reader->cursor >= reader->size || reader->data[reader->cursor++] != tag ||
44+ !der_length(reader, &length))
45+ return 0;
46+ value->data = reader->data + reader->cursor;
47+ value->size = length;
48+ value->cursor = 0;
49+ reader->cursor += length;
50+ return 1;
51+}
52+
53+static int rsa_public_key(
54+ const u8* der,
55+ size_t der_size,
56+ br_rsa_public_key* key,
57+ char* error,
58+ size_t error_size)
59+{
60+ DERReader root = { der, der_size, 0 };
61+ DERReader spki;
62+ DERReader algorithm;
63+ DERReader bits;
64+ DERReader rsa;
65+ DERReader modulus;
66+ DERReader exponent;
67+
68+ if (!der_value(&root, 0x30, &spki) ||
69+ !der_value(&spki, 0x30, &algorithm) ||
70+ !der_value(&spki, 0x03, &bits) ||
71+ bits.size < 1 || bits.data[0] != 0) {
72+ mc_set_error(error, error_size, "invalid X.509 RSA public key");
73+ return 0;
74+ }
75+ bits.cursor = 1;
76+ if (!der_value(&bits, 0x30, &rsa) ||
77+ !der_value(&rsa, 0x02, &modulus) ||
78+ !der_value(&rsa, 0x02, &exponent)) {
79+ mc_set_error(error, error_size, "invalid RSA public key integers");
80+ return 0;
81+ }
82+ while (modulus.size > 1 && modulus.data[0] == 0) {
83+ ++modulus.data;
84+ --modulus.size;
85+ }
86+ while (exponent.size > 1 && exponent.data[0] == 0) {
87+ ++exponent.data;
88+ --exponent.size;
89+ }
90+ key->n = (unsigned char*)modulus.data;
91+ key->nlen = modulus.size;
92+ key->e = (unsigned char*)exponent.data;
93+ key->elen = exponent.size;
94+ return key->nlen >= 64 && key->elen > 0;
95+}
96+
97+void mc_cipher_init(MCCipher* cipher, const u8 key[16])
98+{
99+ br_aes_ct_cbcenc_init(&cipher->key, key, 16);
100+ memcpy(cipher->state, key, 16);
101+}
102+
103+static u8 cipher_byte(MCCipher* cipher)
104+{
105+ u8 zero_iv[16] = { 0 };
106+ u8 block[16];
107+ memcpy(block, cipher->state, 16);
108+ br_aes_ct_cbcenc_run(&cipher->key, zero_iv, block, sizeof(block));
109+ return block[0];
110+}
111+
112+void mc_cipher_encrypt(MCCipher* cipher, u8* data, size_t size)
113+{
114+ size_t i;
115+ for (i = 0; i < size; ++i) {
116+ u8 encrypted = data[i] ^ cipher_byte(cipher);
117+ memmove(cipher->state, cipher->state + 1, 15);
118+ cipher->state[15] = encrypted;
119+ data[i] = encrypted;
120+ }
121+}
122+
123+void mc_cipher_decrypt(MCCipher* cipher, u8* data, size_t size)
124+{
125+ size_t i;
126+ for (i = 0; i < size; ++i) {
127+ u8 encrypted = data[i];
128+ data[i] ^= cipher_byte(cipher);
129+ memmove(cipher->state, cipher->state + 1, 15);
130+ cipher->state[15] = encrypted;
131+ }
132+}
133+
134+int mc_rsa_encrypt(
135+ const u8* public_key,
136+ size_t public_key_size,
137+ const u8* message,
138+ size_t message_size,
139+ u8** encrypted,
140+ size_t* encrypted_size,
141+ char* error,
142+ size_t error_size)
143+{
144+ br_rsa_public_key key;
145+ br_rsa_public rsa;
146+ u8* output;
147+ size_t padding_size;
148+ size_t i;
149+
150+ if (!rsa_public_key(public_key, public_key_size, &key, error, error_size))
151+ return 0;
152+ if (message_size + 11 > key.nlen) {
153+ mc_set_error(error, error_size, "message is too large for server RSA key");
154+ return 0;
155+ }
156+ output = (u8*)malloc(key.nlen);
157+ if (!output) {
158+ mc_set_error(error, error_size, "out of memory encrypting login response");
159+ return 0;
160+ }
161+ padding_size = key.nlen - message_size - 3;
162+ output[0] = 0;
163+ output[1] = 2;
164+ /* PKCS#1 v1.5 encryption padding requires every padding byte to be nonzero */
165+ if (!mg_random_bytes(output + 2, padding_size)) {
166+ free(output);
167+ mc_set_error(error, error_size, "could not obtain secure random bytes");
168+ return 0;
169+ }
170+ for (i = 0; i < padding_size; ++i) {
171+ while (output[2 + i] == 0) {
172+ if (!mg_random_bytes(output + 2 + i, 1)) {
173+ free(output);
174+ mc_set_error(error, error_size, "could not obtain secure random bytes");
175+ return 0;
176+ }
177+ }
178+ }
179+ output[2 + padding_size] = 0;
180+ memcpy(output + 3 + padding_size, message, message_size);
181+
182+ rsa = br_rsa_public_get_default();
183+ if (!rsa || !rsa(output, key.nlen, &key)) {
184+ free(output);
185+ mc_set_error(error, error_size, "BearSSL RSA encryption failed");
186+ return 0;
187+ }
188+ *encrypted = output;
189+ *encrypted_size = key.nlen;
190+ return 1;
191+}
192+
193+static void twos_complement(u8* data, size_t size)
194+{
195+ size_t i = size;
196+ unsigned carry = 1;
197+ while (i > 0) {
198+ unsigned value;
199+ --i;
200+ value = (unsigned)(data[i] ^ 0xff) + carry;
201+ data[i] = (u8)value;
202+ carry = value >> 8;
203+ }
204+}
205+
206+void mc_server_hash(
207+ const char* server_id,
208+ const u8 shared_secret[16],
209+ const u8* public_key,
210+ size_t public_key_size,
211+ char output[42])
212+{
213+ static const char hex[] = "0123456789abcdef";
214+ br_sha1_context sha;
215+ u8 digest[20];
216+ size_t first = 0;
217+ size_t i;
218+ size_t out = 0;
219+ int negative;
220+
221+ br_sha1_init(&sha);
222+ br_sha1_update(&sha, server_id, strlen(server_id));
223+ br_sha1_update(&sha, shared_secret, 16);
224+ br_sha1_update(&sha, public_key, public_key_size);
225+ br_sha1_out(&sha, digest);
226+ /* mc puts sha-1 digest as a signed twos-complement int */
227+ negative = (digest[0] & 0x80) != 0;
228+ if (negative)
229+ twos_complement(digest, sizeof(digest));
230+ while (first < sizeof(digest) && digest[first] == 0)
231+ ++first;
232+ if (negative)
233+ output[out++] = '-';
234+ if (first == sizeof(digest)) {
235+ output[out++] = '0';
236+ } else {
237+ output[out++] = hex[digest[first] >> 4];
238+ output[out++] = hex[digest[first] & 15];
239+ for (i = first + 1; i < sizeof(digest); ++i) {
240+ output[out++] = hex[digest[i] >> 4];
241+ output[out++] = hex[digest[i] & 15];
242+ }
243+ if (output[negative ? 1 : 0] == '0') {
244+ memmove(output + (negative ? 1 : 0),
245+ output + (negative ? 2 : 1),
246+ out - (negative ? 2 : 1));
247+ --out;
248+ }
249+ }
250+ output[out] = '\0';
251+}
+37,
-0
1@@ -3,6 +3,8 @@
2
3 #include <stddef.h>
4
5+#include <bearssl.h>
6+
7 #include "mcproto/mcproto.h"
8
9 #define MC_MAX_PACKET_SIZE (16u * 1024u * 1024u)
10@@ -16,6 +18,11 @@ typedef struct {
11 size_t cursor;
12 } MCBuffer;
13
14+typedef struct {
15+ br_aes_ct_cbcenc_keys key;
16+ u8 state[16];
17+} MCCipher;
18+
19 struct MCClient {
20 int socket_fd;
21 char host[256];
22@@ -23,6 +30,9 @@ struct MCClient {
23 char username[17];
24 char uuid[37];
25 i32 compression_threshold;
26+ int encrypted;
27+ MCCipher read_cipher;
28+ MCCipher write_cipher;
29 };
30
31 void mc_set_error(char* error, size_t error_size, const char* fmt, ...);
32@@ -51,4 +61,31 @@ int bufread_varint(MCBuffer* buffer, i32* value);
33 int bufread_string(MCBuffer* buffer, char* value, size_t value_size);
34 int bufread_bytes(MCBuffer* buffer, const u8** value, size_t* size);
35
36+void mc_cipher_init(MCCipher* cipher, const u8 key[16]);
37+void mc_cipher_encrypt(MCCipher* cipher, u8* data, size_t size);
38+void mc_cipher_decrypt(MCCipher* cipher, u8* data, size_t size);
39+int mc_rsa_encrypt(
40+ const u8* public_key,
41+ size_t public_key_size,
42+ const u8* message,
43+ size_t message_size,
44+ u8** encrypted,
45+ size_t* encrypted_size,
46+ char* error,
47+ size_t error_size);
48+void mc_server_hash(
49+ const char* server_id,
50+ const u8 shared_secret[16],
51+ const u8* public_key,
52+ size_t public_key_size,
53+ char output[42]);
54+int mc_join_server(
55+ const MCAccount* account,
56+ const char* server_id,
57+ const u8 shared_secret[16],
58+ const u8* public_key,
59+ size_t public_key_size,
60+ char* error,
61+ size_t error_size);
62+
63 #endif /* MCPROTO_INTERNAL_H */
+36,
-0
1@@ -0,0 +1,36 @@
2+#include "mg_random.h"
3+
4+#include <errno.h>
5+#include <stddef.h>
6+
7+#if defined(__linux__)
8+#include <sys/random.h>
9+#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
10+ defined(__NetBSD__) || defined(__DragonFly__)
11+#include <stdlib.h>
12+#elif defined(_WIN32)
13+/* TODO: Windows shi*/
14+#else
15+#error "mg_random_bytes needs a platform CSPRNG implementation"
16+#endif
17+
18+int mg_random_bytes(u8* data, size_t size)
19+{
20+#if defined(__linux__)
21+ while (size > 0) {
22+ ssize_t count = getrandom(data, size, 0);
23+ if (count > 0) {
24+ data += (size_t)count;
25+ size -= (size_t)count;
26+ continue;
27+ }
28+ if (count < 0 && errno == EINTR)
29+ continue;
30+ return 0;
31+ }
32+ return 1;
33+#else
34+ arc4random_buf(data, size);
35+ return 1;
36+#endif
37+}