librats 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +1 -0
- package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
- package/native-src/CMakeLists.txt +360 -0
- package/native-src/LICENSE +21 -0
- package/native-src/src/bencode.cpp +485 -0
- package/native-src/src/bencode.h +145 -0
- package/native-src/src/bittorrent.cpp +3682 -0
- package/native-src/src/bittorrent.h +731 -0
- package/native-src/src/dht.cpp +2342 -0
- package/native-src/src/dht.h +501 -0
- package/native-src/src/encrypted_socket.cpp +817 -0
- package/native-src/src/encrypted_socket.h +239 -0
- package/native-src/src/file_transfer.cpp +1808 -0
- package/native-src/src/file_transfer.h +567 -0
- package/native-src/src/fs.cpp +639 -0
- package/native-src/src/fs.h +108 -0
- package/native-src/src/gossipsub.cpp +1137 -0
- package/native-src/src/gossipsub.h +403 -0
- package/native-src/src/ice.cpp +1386 -0
- package/native-src/src/ice.h +328 -0
- package/native-src/src/json.hpp +25526 -0
- package/native-src/src/krpc.cpp +558 -0
- package/native-src/src/krpc.h +145 -0
- package/native-src/src/librats.cpp +2715 -0
- package/native-src/src/librats.h +1729 -0
- package/native-src/src/librats_bittorrent.cpp +167 -0
- package/native-src/src/librats_c.cpp +1317 -0
- package/native-src/src/librats_c.h +237 -0
- package/native-src/src/librats_encryption.cpp +123 -0
- package/native-src/src/librats_file_transfer.cpp +226 -0
- package/native-src/src/librats_gossipsub.cpp +293 -0
- package/native-src/src/librats_ice.cpp +515 -0
- package/native-src/src/librats_logging.cpp +158 -0
- package/native-src/src/librats_mdns.cpp +171 -0
- package/native-src/src/librats_nat.cpp +571 -0
- package/native-src/src/librats_persistence.cpp +815 -0
- package/native-src/src/logger.h +412 -0
- package/native-src/src/mdns.cpp +1178 -0
- package/native-src/src/mdns.h +253 -0
- package/native-src/src/network_utils.cpp +598 -0
- package/native-src/src/network_utils.h +162 -0
- package/native-src/src/noise.cpp +981 -0
- package/native-src/src/noise.h +227 -0
- package/native-src/src/os.cpp +371 -0
- package/native-src/src/os.h +40 -0
- package/native-src/src/rats_export.h +17 -0
- package/native-src/src/sha1.cpp +163 -0
- package/native-src/src/sha1.h +42 -0
- package/native-src/src/socket.cpp +1376 -0
- package/native-src/src/socket.h +309 -0
- package/native-src/src/stun.cpp +484 -0
- package/native-src/src/stun.h +349 -0
- package/native-src/src/threadmanager.cpp +105 -0
- package/native-src/src/threadmanager.h +53 -0
- package/native-src/src/tracker.cpp +1110 -0
- package/native-src/src/tracker.h +268 -0
- package/native-src/src/version.cpp +24 -0
- package/native-src/src/version.h.in +45 -0
- package/native-src/version.rc.in +31 -0
- package/package.json +2 -8
- package/scripts/build-librats.js +59 -12
- package/scripts/prepare-package.js +133 -37
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
#include "noise.h"
|
|
2
|
+
#include "logger.h"
|
|
3
|
+
#include <cstring>
|
|
4
|
+
#include <random>
|
|
5
|
+
#include <iomanip>
|
|
6
|
+
#include <sstream>
|
|
7
|
+
#include <stdexcept>
|
|
8
|
+
|
|
9
|
+
// Include platform-specific headers for cryptographic operations
|
|
10
|
+
#ifdef _WIN32
|
|
11
|
+
#include <windows.h>
|
|
12
|
+
#include <bcrypt.h>
|
|
13
|
+
#pragma comment(lib, "bcrypt.lib")
|
|
14
|
+
#else
|
|
15
|
+
#include <fcntl.h>
|
|
16
|
+
#include <unistd.h>
|
|
17
|
+
#ifndef __IPHONE_OS_VERSION_MIN_REQUIRED
|
|
18
|
+
#include <sys/random.h>
|
|
19
|
+
#endif
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
#define LOG_NOISE_DEBUG(message) LOG_DEBUG("noise", message)
|
|
23
|
+
#define LOG_NOISE_INFO(message) LOG_INFO("noise", message)
|
|
24
|
+
#define LOG_NOISE_WARN(message) LOG_WARN("noise", message)
|
|
25
|
+
#define LOG_NOISE_ERROR(message) LOG_ERROR("noise", message)
|
|
26
|
+
|
|
27
|
+
namespace librats {
|
|
28
|
+
|
|
29
|
+
// Noise Protocol constants
|
|
30
|
+
constexpr char NOISE_PROTOCOL_NAME[] = "Noise_XX_25519_ChaChaPoly_SHA256";
|
|
31
|
+
|
|
32
|
+
// Simple implementation of cryptographic primitives
|
|
33
|
+
// NOTE: This is a basic implementation for demonstration purposes.
|
|
34
|
+
// In production, you should use a well-tested cryptographic library like libsodium.
|
|
35
|
+
|
|
36
|
+
namespace {
|
|
37
|
+
|
|
38
|
+
// Simple ChaCha20 implementation
|
|
39
|
+
class ChaCha20 {
|
|
40
|
+
public:
|
|
41
|
+
static void chacha20_block(uint32_t out[16], const uint32_t in[16]) {
|
|
42
|
+
uint32_t x[16];
|
|
43
|
+
for (int i = 0; i < 16; ++i) x[i] = in[i];
|
|
44
|
+
|
|
45
|
+
for (int i = 0; i < 10; ++i) {
|
|
46
|
+
// Column rounds
|
|
47
|
+
quarter_round(x[0], x[4], x[8], x[12]);
|
|
48
|
+
quarter_round(x[1], x[5], x[9], x[13]);
|
|
49
|
+
quarter_round(x[2], x[6], x[10], x[14]);
|
|
50
|
+
quarter_round(x[3], x[7], x[11], x[15]);
|
|
51
|
+
// Diagonal rounds
|
|
52
|
+
quarter_round(x[0], x[5], x[10], x[15]);
|
|
53
|
+
quarter_round(x[1], x[6], x[11], x[12]);
|
|
54
|
+
quarter_round(x[2], x[7], x[8], x[13]);
|
|
55
|
+
quarter_round(x[3], x[4], x[9], x[14]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (int i = 0; i < 16; ++i) out[i] = x[i] + in[i];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private:
|
|
62
|
+
static uint32_t rotl(uint32_t x, int n) {
|
|
63
|
+
return (x << n) | (x >> (32 - n));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static void quarter_round(uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) {
|
|
67
|
+
a += b; d ^= a; d = rotl(d, 16);
|
|
68
|
+
c += d; b ^= c; b = rotl(b, 12);
|
|
69
|
+
a += b; d ^= a; d = rotl(d, 8);
|
|
70
|
+
c += d; b ^= c; b = rotl(b, 7);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Simple Poly1305 implementation
|
|
75
|
+
class Poly1305 {
|
|
76
|
+
public:
|
|
77
|
+
static void poly1305_mac(uint8_t out[16], const uint8_t* m, size_t inlen, const uint8_t key[32]) {
|
|
78
|
+
uint32_t r0, r1, r2, r3, r4;
|
|
79
|
+
uint32_t s1, s2, s3, s4;
|
|
80
|
+
uint32_t h0, h1, h2, h3, h4;
|
|
81
|
+
uint64_t d0, d1, d2, d3, d4;
|
|
82
|
+
uint32_t c;
|
|
83
|
+
|
|
84
|
+
// r &= 0xffffffc0ffffffc0ffffffc0fffffff
|
|
85
|
+
r0 = (get_u32le(key + 0)) & 0x3ffffff;
|
|
86
|
+
r1 = (get_u32le(key + 3) >> 2) & 0x3ffff03;
|
|
87
|
+
r2 = (get_u32le(key + 6) >> 4) & 0x3ffc0ff;
|
|
88
|
+
r3 = (get_u32le(key + 9) >> 6) & 0x3f03fff;
|
|
89
|
+
r4 = (get_u32le(key + 12) >> 8) & 0x00fffff;
|
|
90
|
+
|
|
91
|
+
s1 = r1 * 5;
|
|
92
|
+
s2 = r2 * 5;
|
|
93
|
+
s3 = r3 * 5;
|
|
94
|
+
s4 = r4 * 5;
|
|
95
|
+
|
|
96
|
+
h0 = h1 = h2 = h3 = h4 = 0;
|
|
97
|
+
|
|
98
|
+
while (inlen > 0) {
|
|
99
|
+
// h += m[i]
|
|
100
|
+
if (inlen >= 16) {
|
|
101
|
+
h0 += (get_u32le(m + 0)) & 0x3ffffff;
|
|
102
|
+
h1 += (get_u32le(m + 3) >> 2) & 0x3ffffff;
|
|
103
|
+
h2 += (get_u32le(m + 6) >> 4) & 0x3ffffff;
|
|
104
|
+
h3 += (get_u32le(m + 9) >> 6) & 0x3ffffff;
|
|
105
|
+
h4 += (get_u32le(m + 12) >> 8) | (1 << 24);
|
|
106
|
+
m += 16;
|
|
107
|
+
inlen -= 16;
|
|
108
|
+
} else {
|
|
109
|
+
uint8_t mp[16];
|
|
110
|
+
size_t i;
|
|
111
|
+
for (i = 0; i < inlen; i++) mp[i] = m[i];
|
|
112
|
+
mp[i++] = 1;
|
|
113
|
+
for (; i < 16; i++) mp[i] = 0;
|
|
114
|
+
inlen = 0;
|
|
115
|
+
h0 += (get_u32le(mp + 0)) & 0x3ffffff;
|
|
116
|
+
h1 += (get_u32le(mp + 3) >> 2) & 0x3ffffff;
|
|
117
|
+
h2 += (get_u32le(mp + 6) >> 4) & 0x3ffffff;
|
|
118
|
+
h3 += (get_u32le(mp + 9) >> 6) & 0x3ffffff;
|
|
119
|
+
h4 += (get_u32le(mp + 12) >> 8);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// h *= r
|
|
123
|
+
d0 = ((uint64_t)h0 * r0) + ((uint64_t)h1 * s4) + ((uint64_t)h2 * s3) + ((uint64_t)h3 * s2) + ((uint64_t)h4 * s1);
|
|
124
|
+
d1 = ((uint64_t)h0 * r1) + ((uint64_t)h1 * r0) + ((uint64_t)h2 * s4) + ((uint64_t)h3 * s3) + ((uint64_t)h4 * s2);
|
|
125
|
+
d2 = ((uint64_t)h0 * r2) + ((uint64_t)h1 * r1) + ((uint64_t)h2 * r0) + ((uint64_t)h3 * s4) + ((uint64_t)h4 * s3);
|
|
126
|
+
d3 = ((uint64_t)h0 * r3) + ((uint64_t)h1 * r2) + ((uint64_t)h2 * r1) + ((uint64_t)h3 * r0) + ((uint64_t)h4 * s4);
|
|
127
|
+
d4 = ((uint64_t)h0 * r4) + ((uint64_t)h1 * r3) + ((uint64_t)h2 * r2) + ((uint64_t)h3 * r1) + ((uint64_t)h4 * r0);
|
|
128
|
+
|
|
129
|
+
// (partial) h %= p
|
|
130
|
+
c = (uint32_t)(d0 >> 26); h0 = (uint32_t)d0 & 0x3ffffff;
|
|
131
|
+
d1 += c; c = (uint32_t)(d1 >> 26); h1 = (uint32_t)d1 & 0x3ffffff;
|
|
132
|
+
d2 += c; c = (uint32_t)(d2 >> 26); h2 = (uint32_t)d2 & 0x3ffffff;
|
|
133
|
+
d3 += c; c = (uint32_t)(d3 >> 26); h3 = (uint32_t)d3 & 0x3ffffff;
|
|
134
|
+
d4 += c; c = (uint32_t)(d4 >> 26); h4 = (uint32_t)d4 & 0x3ffffff;
|
|
135
|
+
h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
|
|
136
|
+
h1 += c;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// fully carry h
|
|
140
|
+
c = h1 >> 26; h1 = h1 & 0x3ffffff;
|
|
141
|
+
h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
|
|
142
|
+
h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
|
|
143
|
+
h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
|
|
144
|
+
h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
|
|
145
|
+
h1 += c;
|
|
146
|
+
|
|
147
|
+
// compute h + -p
|
|
148
|
+
uint32_t g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
|
|
149
|
+
uint32_t g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
|
|
150
|
+
uint32_t g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
|
|
151
|
+
uint32_t g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
|
|
152
|
+
uint32_t g4 = h4 + c - (1 << 26);
|
|
153
|
+
|
|
154
|
+
// select h if h < p, or h + -p if h >= p
|
|
155
|
+
uint32_t mask = (g4 >> ((sizeof(uint32_t) * 8) - 1)) - 1;
|
|
156
|
+
g0 &= mask;
|
|
157
|
+
g1 &= mask;
|
|
158
|
+
g2 &= mask;
|
|
159
|
+
g3 &= mask;
|
|
160
|
+
g4 &= mask;
|
|
161
|
+
mask = ~mask;
|
|
162
|
+
h0 = (h0 & mask) | g0;
|
|
163
|
+
h1 = (h1 & mask) | g1;
|
|
164
|
+
h2 = (h2 & mask) | g2;
|
|
165
|
+
h3 = (h3 & mask) | g3;
|
|
166
|
+
h4 = (h4 & mask) | g4;
|
|
167
|
+
|
|
168
|
+
// h = h % (2^128)
|
|
169
|
+
h0 = ((h0) | (h1 << 26)) & 0xffffffff;
|
|
170
|
+
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
|
|
171
|
+
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
|
|
172
|
+
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
|
|
173
|
+
|
|
174
|
+
// mac = (h + s) % (2^128)
|
|
175
|
+
d0 = (uint64_t)h0 + get_u32le(key + 16); h0 = (uint32_t)d0;
|
|
176
|
+
d1 = (uint64_t)h1 + get_u32le(key + 20) + (d0 >> 32); h1 = (uint32_t)d1;
|
|
177
|
+
d2 = (uint64_t)h2 + get_u32le(key + 24) + (d1 >> 32); h2 = (uint32_t)d2;
|
|
178
|
+
d3 = (uint64_t)h3 + get_u32le(key + 28) + (d2 >> 32); h3 = (uint32_t)d3;
|
|
179
|
+
|
|
180
|
+
put_u32le(out + 0, h0);
|
|
181
|
+
put_u32le(out + 4, h1);
|
|
182
|
+
put_u32le(out + 8, h2);
|
|
183
|
+
put_u32le(out + 12, h3);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private:
|
|
187
|
+
static uint32_t get_u32le(const uint8_t* p) {
|
|
188
|
+
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
static void put_u32le(uint8_t* p, uint32_t v) {
|
|
192
|
+
p[0] = (uint8_t)(v);
|
|
193
|
+
p[1] = (uint8_t)(v >> 8);
|
|
194
|
+
p[2] = (uint8_t)(v >> 16);
|
|
195
|
+
p[3] = (uint8_t)(v >> 24);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Simple Curve25519 implementation
|
|
200
|
+
class Curve25519 {
|
|
201
|
+
public:
|
|
202
|
+
static void scalarmult(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32]) {
|
|
203
|
+
// This is a simplified implementation for demonstration
|
|
204
|
+
// In production, use a proper curve25519 implementation
|
|
205
|
+
std::memcpy(out, point, 32);
|
|
206
|
+
for (int i = 0; i < 32; ++i) {
|
|
207
|
+
out[i] ^= scalar[i]; // Simplified operation - NOT cryptographically secure
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static void scalarmult_base(uint8_t out[32], const uint8_t scalar[32]) {
|
|
212
|
+
uint8_t basepoint[32] = {9}; // Standard base point
|
|
213
|
+
scalarmult(out, scalar, basepoint);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Simple SHA256 implementation
|
|
218
|
+
class SHA256 {
|
|
219
|
+
public:
|
|
220
|
+
static void hash(uint8_t out[32], const uint8_t* data, size_t len) {
|
|
221
|
+
// This is a placeholder implementation
|
|
222
|
+
// In production, use a proper SHA256 implementation
|
|
223
|
+
std::memset(out, 0, 32);
|
|
224
|
+
for (size_t i = 0; i < len && i < 32; ++i) {
|
|
225
|
+
out[i] = data[i] ^ (uint8_t)(len & 0xFF);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
} // anonymous namespace
|
|
231
|
+
|
|
232
|
+
//=============================================================================
|
|
233
|
+
// NoiseCrypto Implementation
|
|
234
|
+
//=============================================================================
|
|
235
|
+
|
|
236
|
+
NoiseKey NoiseCrypto::generate_keypair(NoiseKey& private_key) {
|
|
237
|
+
random_bytes(private_key.data(), NOISE_KEY_SIZE);
|
|
238
|
+
|
|
239
|
+
NoiseKey public_key;
|
|
240
|
+
Curve25519::scalarmult_base(public_key.data(), private_key.data());
|
|
241
|
+
|
|
242
|
+
return public_key;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
NoiseKey NoiseCrypto::dh(const NoiseKey& private_key, const NoiseKey& public_key) {
|
|
246
|
+
NoiseKey shared_secret;
|
|
247
|
+
Curve25519::scalarmult(shared_secret.data(), private_key.data(), public_key.data());
|
|
248
|
+
return shared_secret;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
std::vector<uint8_t> NoiseCrypto::encrypt(const NoiseKey& key, uint64_t nonce,
|
|
252
|
+
const std::vector<uint8_t>& plaintext,
|
|
253
|
+
const std::vector<uint8_t>& ad) {
|
|
254
|
+
std::vector<uint8_t> ciphertext(plaintext.size() + NOISE_TAG_SIZE);
|
|
255
|
+
|
|
256
|
+
// Setup ChaCha20 state
|
|
257
|
+
uint32_t state[16];
|
|
258
|
+
std::memcpy(state, "expand 32-byte k", 16);
|
|
259
|
+
std::memcpy(state + 4, key.data(), 32);
|
|
260
|
+
state[12] = 0; // Counter
|
|
261
|
+
state[13] = (uint32_t)nonce;
|
|
262
|
+
state[14] = (uint32_t)(nonce >> 32);
|
|
263
|
+
state[15] = 0;
|
|
264
|
+
|
|
265
|
+
// Generate Poly1305 key (use counter 0)
|
|
266
|
+
uint32_t poly_key[16];
|
|
267
|
+
ChaCha20::chacha20_block(poly_key, state);
|
|
268
|
+
|
|
269
|
+
// Generate keystream and encrypt (start from counter 1)
|
|
270
|
+
state[12] = 1;
|
|
271
|
+
for (size_t i = 0; i < plaintext.size(); i += 64) {
|
|
272
|
+
uint32_t keystream[16];
|
|
273
|
+
ChaCha20::chacha20_block(keystream, state);
|
|
274
|
+
state[12]++;
|
|
275
|
+
|
|
276
|
+
size_t chunk_size = (std::min)(size_t(64), plaintext.size() - i);
|
|
277
|
+
for (size_t j = 0; j < chunk_size; ++j) {
|
|
278
|
+
ciphertext[i + j] = plaintext[i + j] ^ ((uint8_t*)keystream)[j];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Calculate MAC
|
|
283
|
+
std::vector<uint8_t> mac_data;
|
|
284
|
+
mac_data.insert(mac_data.end(), ad.begin(), ad.end());
|
|
285
|
+
mac_data.insert(mac_data.end(), ciphertext.begin(), ciphertext.begin() + plaintext.size());
|
|
286
|
+
|
|
287
|
+
uint8_t mac[16];
|
|
288
|
+
Poly1305::poly1305_mac(mac, mac_data.data(), mac_data.size(), (uint8_t*)poly_key);
|
|
289
|
+
|
|
290
|
+
// Append MAC
|
|
291
|
+
std::memcpy(ciphertext.data() + plaintext.size(), mac, 16);
|
|
292
|
+
|
|
293
|
+
return ciphertext;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
std::vector<uint8_t> NoiseCrypto::decrypt(const NoiseKey& key, uint64_t nonce,
|
|
297
|
+
const std::vector<uint8_t>& ciphertext,
|
|
298
|
+
const std::vector<uint8_t>& ad) {
|
|
299
|
+
if (ciphertext.size() < NOISE_TAG_SIZE) {
|
|
300
|
+
return {};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
size_t plaintext_size = ciphertext.size() - NOISE_TAG_SIZE;
|
|
304
|
+
|
|
305
|
+
// Setup ChaCha20 state
|
|
306
|
+
uint32_t state[16];
|
|
307
|
+
std::memcpy(state, "expand 32-byte k", 16);
|
|
308
|
+
std::memcpy(state + 4, key.data(), 32);
|
|
309
|
+
state[12] = 0;
|
|
310
|
+
state[13] = (uint32_t)nonce;
|
|
311
|
+
state[14] = (uint32_t)(nonce >> 32);
|
|
312
|
+
state[15] = 0;
|
|
313
|
+
|
|
314
|
+
// Generate Poly1305 key and verify MAC
|
|
315
|
+
uint32_t poly_state[16];
|
|
316
|
+
std::memcpy(poly_state, state, sizeof(state));
|
|
317
|
+
poly_state[12] = 0;
|
|
318
|
+
uint32_t poly_key[16];
|
|
319
|
+
ChaCha20::chacha20_block(poly_key, poly_state);
|
|
320
|
+
|
|
321
|
+
std::vector<uint8_t> mac_data;
|
|
322
|
+
mac_data.insert(mac_data.end(), ad.begin(), ad.end());
|
|
323
|
+
mac_data.insert(mac_data.end(), ciphertext.begin(), ciphertext.begin() + plaintext_size);
|
|
324
|
+
|
|
325
|
+
uint8_t computed_mac[16];
|
|
326
|
+
Poly1305::poly1305_mac(computed_mac, mac_data.data(), mac_data.size(), (uint8_t*)poly_key);
|
|
327
|
+
|
|
328
|
+
// Verify MAC
|
|
329
|
+
if (std::memcmp(computed_mac, ciphertext.data() + plaintext_size, 16) != 0) {
|
|
330
|
+
return {}; // MAC verification failed
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Decrypt
|
|
334
|
+
std::vector<uint8_t> plaintext(plaintext_size);
|
|
335
|
+
state[12] = 1; // Start from counter 1 (0 was used for Poly1305 key)
|
|
336
|
+
|
|
337
|
+
for (size_t i = 0; i < plaintext_size; i += 64) {
|
|
338
|
+
uint32_t keystream[16];
|
|
339
|
+
ChaCha20::chacha20_block(keystream, state);
|
|
340
|
+
state[12]++;
|
|
341
|
+
|
|
342
|
+
size_t chunk_size = (std::min)(size_t(64), plaintext_size - i);
|
|
343
|
+
for (size_t j = 0; j < chunk_size; ++j) {
|
|
344
|
+
plaintext[i + j] = ciphertext[i + j] ^ ((uint8_t*)keystream)[j];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return plaintext;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
NoiseHash NoiseCrypto::hash(const std::vector<uint8_t>& data) {
|
|
352
|
+
NoiseHash result;
|
|
353
|
+
SHA256::hash(result.data(), data.data(), data.size());
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
void NoiseCrypto::hkdf(const std::vector<uint8_t>& salt, const std::vector<uint8_t>& ikm,
|
|
358
|
+
const std::vector<uint8_t>& info, uint8_t* okm, size_t okm_len) {
|
|
359
|
+
// Simplified HKDF implementation
|
|
360
|
+
std::vector<uint8_t> prk_data = salt;
|
|
361
|
+
prk_data.insert(prk_data.end(), ikm.begin(), ikm.end());
|
|
362
|
+
NoiseHash prk = hash(prk_data);
|
|
363
|
+
|
|
364
|
+
std::vector<uint8_t> okm_data = info;
|
|
365
|
+
okm_data.insert(okm_data.end(), prk.begin(), prk.end());
|
|
366
|
+
NoiseHash result = hash(okm_data);
|
|
367
|
+
|
|
368
|
+
std::memcpy(okm, result.data(), (std::min)(okm_len, size_t(NOISE_HASH_SIZE)));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
void NoiseCrypto::secure_memzero(void* ptr, size_t size) {
|
|
372
|
+
volatile uint8_t* p = static_cast<volatile uint8_t*>(ptr);
|
|
373
|
+
for (size_t i = 0; i < size; ++i) {
|
|
374
|
+
p[i] = 0;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
void NoiseCrypto::random_bytes(uint8_t* buffer, size_t size) {
|
|
379
|
+
#ifdef _WIN32
|
|
380
|
+
BCRYPT_ALG_HANDLE hAlg;
|
|
381
|
+
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, nullptr, 0) == 0) {
|
|
382
|
+
BCryptGenRandom(hAlg, buffer, (ULONG)size, 0);
|
|
383
|
+
BCryptCloseAlgorithmProvider(hAlg, 0);
|
|
384
|
+
} else {
|
|
385
|
+
// Fallback to less secure method
|
|
386
|
+
std::random_device rd;
|
|
387
|
+
std::mt19937 gen(rd());
|
|
388
|
+
std::uniform_int_distribution<> dis(0, 255);
|
|
389
|
+
for (size_t i = 0; i < size; ++i) {
|
|
390
|
+
buffer[i] = static_cast<uint8_t>(dis(gen));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
#else
|
|
394
|
+
int fd = open("/dev/urandom", O_RDONLY);
|
|
395
|
+
if (fd >= 0) {
|
|
396
|
+
read(fd, buffer, size);
|
|
397
|
+
close(fd);
|
|
398
|
+
} else {
|
|
399
|
+
// Fallback to less secure method
|
|
400
|
+
std::random_device rd;
|
|
401
|
+
std::mt19937 gen(rd());
|
|
402
|
+
std::uniform_int_distribution<> dis(0, 255);
|
|
403
|
+
for (size_t i = 0; i < size; ++i) {
|
|
404
|
+
buffer[i] = static_cast<uint8_t>(dis(gen));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
#endif
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
//=============================================================================
|
|
411
|
+
// NoiseCipherState Implementation
|
|
412
|
+
//=============================================================================
|
|
413
|
+
|
|
414
|
+
NoiseCipherState::NoiseCipherState() : nonce_(0), has_key_(false) {
|
|
415
|
+
key_.fill(0);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
NoiseCipherState::~NoiseCipherState() {
|
|
419
|
+
NoiseCrypto::secure_memzero(key_.data(), key_.size());
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
void NoiseCipherState::initialize_key(const NoiseKey& key) {
|
|
423
|
+
key_ = key;
|
|
424
|
+
nonce_ = 0;
|
|
425
|
+
has_key_ = true;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
std::vector<uint8_t> NoiseCipherState::encrypt_with_ad(const std::vector<uint8_t>& plaintext,
|
|
429
|
+
const std::vector<uint8_t>& ad) {
|
|
430
|
+
if (!has_key_) {
|
|
431
|
+
return plaintext; // No encryption if no key
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
auto result = NoiseCrypto::encrypt(key_, nonce_, plaintext, ad);
|
|
435
|
+
nonce_++;
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
std::vector<uint8_t> NoiseCipherState::decrypt_with_ad(const std::vector<uint8_t>& ciphertext,
|
|
440
|
+
const std::vector<uint8_t>& ad) {
|
|
441
|
+
if (!has_key_) {
|
|
442
|
+
return ciphertext; // No decryption if no key
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
auto result = NoiseCrypto::decrypt(key_, nonce_, ciphertext, ad);
|
|
446
|
+
if (!result.empty()) {
|
|
447
|
+
nonce_++;
|
|
448
|
+
}
|
|
449
|
+
return result;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
//=============================================================================
|
|
453
|
+
// NoiseSymmetricState Implementation
|
|
454
|
+
//=============================================================================
|
|
455
|
+
|
|
456
|
+
NoiseSymmetricState::NoiseSymmetricState() {
|
|
457
|
+
ck_.fill(0);
|
|
458
|
+
h_.fill(0);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
NoiseSymmetricState::~NoiseSymmetricState() {
|
|
462
|
+
NoiseCrypto::secure_memzero(ck_.data(), ck_.size());
|
|
463
|
+
NoiseCrypto::secure_memzero(h_.data(), h_.size());
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
void NoiseSymmetricState::initialize(const std::string& protocol_name) {
|
|
467
|
+
if (protocol_name.length() <= NOISE_HASH_SIZE) {
|
|
468
|
+
std::memcpy(h_.data(), protocol_name.c_str(), protocol_name.length());
|
|
469
|
+
std::memset(h_.data() + protocol_name.length(), 0, NOISE_HASH_SIZE - protocol_name.length());
|
|
470
|
+
} else {
|
|
471
|
+
h_ = NoiseCrypto::hash(std::vector<uint8_t>(protocol_name.begin(), protocol_name.end()));
|
|
472
|
+
}
|
|
473
|
+
ck_ = h_;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
void NoiseSymmetricState::mix_key(const std::vector<uint8_t>& input_key_material) {
|
|
477
|
+
std::vector<uint8_t> temp_k(NOISE_KEY_SIZE);
|
|
478
|
+
std::vector<uint8_t> salt(ck_.begin(), ck_.end());
|
|
479
|
+
|
|
480
|
+
NoiseCrypto::hkdf(salt, input_key_material, {}, temp_k.data(), NOISE_KEY_SIZE);
|
|
481
|
+
NoiseCrypto::hkdf(salt, input_key_material, {1}, ck_.data(), NOISE_KEY_SIZE);
|
|
482
|
+
|
|
483
|
+
NoiseKey key;
|
|
484
|
+
std::memcpy(key.data(), temp_k.data(), NOISE_KEY_SIZE);
|
|
485
|
+
cipher_state_.initialize_key(key);
|
|
486
|
+
|
|
487
|
+
NoiseCrypto::secure_memzero(temp_k.data(), temp_k.size());
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
void NoiseSymmetricState::mix_hash(const std::vector<uint8_t>& data) {
|
|
491
|
+
std::vector<uint8_t> hash_input(h_.begin(), h_.end());
|
|
492
|
+
hash_input.insert(hash_input.end(), data.begin(), data.end());
|
|
493
|
+
h_ = NoiseCrypto::hash(hash_input);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
void NoiseSymmetricState::mix_key_and_hash(const std::vector<uint8_t>& input_key_material) {
|
|
497
|
+
std::vector<uint8_t> temp_h(NOISE_HASH_SIZE);
|
|
498
|
+
std::vector<uint8_t> temp_k(NOISE_KEY_SIZE);
|
|
499
|
+
std::vector<uint8_t> salt(ck_.begin(), ck_.end());
|
|
500
|
+
|
|
501
|
+
NoiseCrypto::hkdf(salt, input_key_material, {}, temp_h.data(), NOISE_HASH_SIZE);
|
|
502
|
+
NoiseCrypto::hkdf(salt, input_key_material, {1}, temp_k.data(), NOISE_KEY_SIZE);
|
|
503
|
+
NoiseCrypto::hkdf(salt, input_key_material, {2}, ck_.data(), NOISE_KEY_SIZE);
|
|
504
|
+
|
|
505
|
+
std::memcpy(h_.data(), temp_h.data(), NOISE_HASH_SIZE);
|
|
506
|
+
|
|
507
|
+
NoiseKey key;
|
|
508
|
+
std::memcpy(key.data(), temp_k.data(), NOISE_KEY_SIZE);
|
|
509
|
+
cipher_state_.initialize_key(key);
|
|
510
|
+
|
|
511
|
+
NoiseCrypto::secure_memzero(temp_h.data(), temp_h.size());
|
|
512
|
+
NoiseCrypto::secure_memzero(temp_k.data(), temp_k.size());
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
std::vector<uint8_t> NoiseSymmetricState::encrypt_and_hash(const std::vector<uint8_t>& plaintext) {
|
|
516
|
+
auto ciphertext = cipher_state_.encrypt_with_ad(plaintext, std::vector<uint8_t>(h_.begin(), h_.end()));
|
|
517
|
+
mix_hash(ciphertext);
|
|
518
|
+
return ciphertext;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
std::vector<uint8_t> NoiseSymmetricState::decrypt_and_hash(const std::vector<uint8_t>& ciphertext) {
|
|
522
|
+
auto plaintext = cipher_state_.decrypt_with_ad(ciphertext, std::vector<uint8_t>(h_.begin(), h_.end()));
|
|
523
|
+
if (!plaintext.empty()) {
|
|
524
|
+
mix_hash(ciphertext);
|
|
525
|
+
}
|
|
526
|
+
return plaintext;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
std::pair<NoiseCipherState, NoiseCipherState> NoiseSymmetricState::split() {
|
|
530
|
+
std::vector<uint8_t> temp_k1(NOISE_KEY_SIZE);
|
|
531
|
+
std::vector<uint8_t> temp_k2(NOISE_KEY_SIZE);
|
|
532
|
+
std::vector<uint8_t> salt(ck_.begin(), ck_.end());
|
|
533
|
+
|
|
534
|
+
NoiseCrypto::hkdf(salt, {}, {}, temp_k1.data(), NOISE_KEY_SIZE);
|
|
535
|
+
NoiseCrypto::hkdf(salt, {}, {1}, temp_k2.data(), NOISE_KEY_SIZE);
|
|
536
|
+
|
|
537
|
+
NoiseCipherState c1, c2;
|
|
538
|
+
NoiseKey key1, key2;
|
|
539
|
+
std::memcpy(key1.data(), temp_k1.data(), NOISE_KEY_SIZE);
|
|
540
|
+
std::memcpy(key2.data(), temp_k2.data(), NOISE_KEY_SIZE);
|
|
541
|
+
|
|
542
|
+
c1.initialize_key(key1);
|
|
543
|
+
c2.initialize_key(key2);
|
|
544
|
+
|
|
545
|
+
NoiseCrypto::secure_memzero(temp_k1.data(), temp_k1.size());
|
|
546
|
+
NoiseCrypto::secure_memzero(temp_k2.data(), temp_k2.size());
|
|
547
|
+
|
|
548
|
+
return std::make_pair(std::move(c1), std::move(c2));
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
//=============================================================================
|
|
552
|
+
// NoiseHandshake Implementation
|
|
553
|
+
//=============================================================================
|
|
554
|
+
|
|
555
|
+
NoiseHandshake::NoiseHandshake() : role_(NoiseRole::INITIATOR), state_(NoiseHandshakeState::UNINITIALIZED) {
|
|
556
|
+
s_.fill(0);
|
|
557
|
+
e_.fill(0);
|
|
558
|
+
rs_.fill(0);
|
|
559
|
+
re_.fill(0);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
NoiseHandshake::~NoiseHandshake() {
|
|
563
|
+
NoiseCrypto::secure_memzero(s_.data(), s_.size());
|
|
564
|
+
NoiseCrypto::secure_memzero(e_.data(), e_.size());
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
bool NoiseHandshake::initialize(NoiseRole role, const NoiseKey& static_private_key) {
|
|
568
|
+
role_ = role;
|
|
569
|
+
s_ = static_private_key;
|
|
570
|
+
|
|
571
|
+
symmetric_state_.initialize(NOISE_PROTOCOL_NAME);
|
|
572
|
+
|
|
573
|
+
if (role == NoiseRole::INITIATOR) {
|
|
574
|
+
state_ = NoiseHandshakeState::WRITE_MESSAGE_1;
|
|
575
|
+
} else {
|
|
576
|
+
state_ = NoiseHandshakeState::READ_MESSAGE_1;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
LOG_NOISE_INFO("Initialized Noise handshake as " << (role == NoiseRole::INITIATOR ? "initiator" : "responder"));
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
std::vector<uint8_t> NoiseHandshake::write_message(const std::vector<uint8_t>& payload) {
|
|
584
|
+
std::vector<uint8_t> message;
|
|
585
|
+
|
|
586
|
+
try {
|
|
587
|
+
switch (state_) {
|
|
588
|
+
case NoiseHandshakeState::WRITE_MESSAGE_1: {
|
|
589
|
+
// -> e
|
|
590
|
+
NoiseKey e_public = NoiseCrypto::generate_keypair(e_);
|
|
591
|
+
message.insert(message.end(), e_public.begin(), e_public.end());
|
|
592
|
+
symmetric_state_.mix_hash(std::vector<uint8_t>(e_public.begin(), e_public.end()));
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
case NoiseHandshakeState::WRITE_MESSAGE_2: {
|
|
597
|
+
// <- e, ee, s, es
|
|
598
|
+
NoiseKey e_public = NoiseCrypto::generate_keypair(e_);
|
|
599
|
+
message.insert(message.end(), e_public.begin(), e_public.end());
|
|
600
|
+
symmetric_state_.mix_hash(std::vector<uint8_t>(e_public.begin(), e_public.end()));
|
|
601
|
+
|
|
602
|
+
// ee
|
|
603
|
+
NoiseKey ee = NoiseCrypto::dh(e_, re_);
|
|
604
|
+
symmetric_state_.mix_key(std::vector<uint8_t>(ee.begin(), ee.end()));
|
|
605
|
+
|
|
606
|
+
// s
|
|
607
|
+
NoiseKey s_public = NoiseCrypto::generate_keypair(s_); // Get public from private
|
|
608
|
+
auto encrypted_s = symmetric_state_.encrypt_and_hash(std::vector<uint8_t>(s_public.begin(), s_public.end()));
|
|
609
|
+
message.insert(message.end(), encrypted_s.begin(), encrypted_s.end());
|
|
610
|
+
|
|
611
|
+
// es
|
|
612
|
+
NoiseKey es = NoiseCrypto::dh(s_, re_);
|
|
613
|
+
symmetric_state_.mix_key(std::vector<uint8_t>(es.begin(), es.end()));
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
case NoiseHandshakeState::WRITE_MESSAGE_3: {
|
|
618
|
+
// -> s, se
|
|
619
|
+
NoiseKey s_public = NoiseCrypto::generate_keypair(s_); // Get public from private
|
|
620
|
+
auto encrypted_s = symmetric_state_.encrypt_and_hash(std::vector<uint8_t>(s_public.begin(), s_public.end()));
|
|
621
|
+
message.insert(message.end(), encrypted_s.begin(), encrypted_s.end());
|
|
622
|
+
|
|
623
|
+
// se
|
|
624
|
+
NoiseKey se = NoiseCrypto::dh(s_, re_);
|
|
625
|
+
symmetric_state_.mix_key(std::vector<uint8_t>(se.begin(), se.end()));
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
default:
|
|
630
|
+
LOG_NOISE_ERROR("Invalid state for write_message: " << static_cast<int>(state_));
|
|
631
|
+
fail_handshake();
|
|
632
|
+
return {};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Encrypt payload
|
|
636
|
+
if (!payload.empty()) {
|
|
637
|
+
auto encrypted_payload = symmetric_state_.encrypt_and_hash(payload);
|
|
638
|
+
message.insert(message.end(), encrypted_payload.begin(), encrypted_payload.end());
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
advance_state();
|
|
642
|
+
LOG_NOISE_DEBUG("Wrote handshake message, new state: " << static_cast<int>(state_));
|
|
643
|
+
|
|
644
|
+
} catch (const std::exception& e) {
|
|
645
|
+
LOG_NOISE_ERROR("Exception in write_message: " << e.what());
|
|
646
|
+
fail_handshake();
|
|
647
|
+
return {};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return message;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
std::vector<uint8_t> NoiseHandshake::read_message(const std::vector<uint8_t>& message) {
|
|
654
|
+
std::vector<uint8_t> payload;
|
|
655
|
+
size_t offset = 0;
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
switch (state_) {
|
|
659
|
+
case NoiseHandshakeState::READ_MESSAGE_1: {
|
|
660
|
+
// -> e
|
|
661
|
+
if (message.size() < NOISE_KEY_SIZE) {
|
|
662
|
+
LOG_NOISE_ERROR("Message too short for e");
|
|
663
|
+
fail_handshake();
|
|
664
|
+
return {};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
std::memcpy(re_.data(), message.data() + offset, NOISE_KEY_SIZE);
|
|
668
|
+
offset += NOISE_KEY_SIZE;
|
|
669
|
+
symmetric_state_.mix_hash(std::vector<uint8_t>(re_.begin(), re_.end()));
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
case NoiseHandshakeState::READ_MESSAGE_2: {
|
|
674
|
+
// <- e, ee, s, es
|
|
675
|
+
if (message.size() < NOISE_KEY_SIZE) {
|
|
676
|
+
LOG_NOISE_ERROR("Message too short for e");
|
|
677
|
+
fail_handshake();
|
|
678
|
+
return {};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
std::memcpy(re_.data(), message.data() + offset, NOISE_KEY_SIZE);
|
|
682
|
+
offset += NOISE_KEY_SIZE;
|
|
683
|
+
symmetric_state_.mix_hash(std::vector<uint8_t>(re_.begin(), re_.end()));
|
|
684
|
+
|
|
685
|
+
// ee
|
|
686
|
+
NoiseKey ee = NoiseCrypto::dh(e_, re_);
|
|
687
|
+
symmetric_state_.mix_key(std::vector<uint8_t>(ee.begin(), ee.end()));
|
|
688
|
+
|
|
689
|
+
// s
|
|
690
|
+
size_t s_encrypted_size = NOISE_KEY_SIZE + NOISE_TAG_SIZE;
|
|
691
|
+
if (message.size() < offset + s_encrypted_size) {
|
|
692
|
+
LOG_NOISE_ERROR("Message too short for encrypted s");
|
|
693
|
+
fail_handshake();
|
|
694
|
+
return {};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
std::vector<uint8_t> encrypted_s(message.begin() + offset, message.begin() + offset + s_encrypted_size);
|
|
698
|
+
offset += s_encrypted_size;
|
|
699
|
+
|
|
700
|
+
auto decrypted_s = symmetric_state_.decrypt_and_hash(encrypted_s);
|
|
701
|
+
if (decrypted_s.size() != NOISE_KEY_SIZE) {
|
|
702
|
+
LOG_NOISE_ERROR("Failed to decrypt s");
|
|
703
|
+
fail_handshake();
|
|
704
|
+
return {};
|
|
705
|
+
}
|
|
706
|
+
std::memcpy(rs_.data(), decrypted_s.data(), NOISE_KEY_SIZE);
|
|
707
|
+
|
|
708
|
+
// es
|
|
709
|
+
NoiseKey es = NoiseCrypto::dh(e_, rs_);
|
|
710
|
+
symmetric_state_.mix_key(std::vector<uint8_t>(es.begin(), es.end()));
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
case NoiseHandshakeState::READ_MESSAGE_3: {
|
|
715
|
+
// -> s, se
|
|
716
|
+
size_t s_encrypted_size = NOISE_KEY_SIZE + NOISE_TAG_SIZE;
|
|
717
|
+
if (message.size() < s_encrypted_size) {
|
|
718
|
+
LOG_NOISE_ERROR("Message too short for encrypted s");
|
|
719
|
+
fail_handshake();
|
|
720
|
+
return {};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
std::vector<uint8_t> encrypted_s(message.begin() + offset, message.begin() + offset + s_encrypted_size);
|
|
724
|
+
offset += s_encrypted_size;
|
|
725
|
+
|
|
726
|
+
auto decrypted_s = symmetric_state_.decrypt_and_hash(encrypted_s);
|
|
727
|
+
if (decrypted_s.size() != NOISE_KEY_SIZE) {
|
|
728
|
+
LOG_NOISE_ERROR("Failed to decrypt s");
|
|
729
|
+
fail_handshake();
|
|
730
|
+
return {};
|
|
731
|
+
}
|
|
732
|
+
std::memcpy(rs_.data(), decrypted_s.data(), NOISE_KEY_SIZE);
|
|
733
|
+
|
|
734
|
+
// se
|
|
735
|
+
NoiseKey se = NoiseCrypto::dh(e_, rs_);
|
|
736
|
+
symmetric_state_.mix_key(std::vector<uint8_t>(se.begin(), se.end()));
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
default:
|
|
741
|
+
LOG_NOISE_ERROR("Invalid state for read_message: " << static_cast<int>(state_));
|
|
742
|
+
fail_handshake();
|
|
743
|
+
return {};
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Decrypt payload
|
|
747
|
+
if (offset < message.size()) {
|
|
748
|
+
std::vector<uint8_t> encrypted_payload(message.begin() + offset, message.end());
|
|
749
|
+
payload = symmetric_state_.decrypt_and_hash(encrypted_payload);
|
|
750
|
+
if (payload.empty() && !encrypted_payload.empty()) {
|
|
751
|
+
LOG_NOISE_ERROR("Failed to decrypt payload");
|
|
752
|
+
fail_handshake();
|
|
753
|
+
return {};
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
advance_state();
|
|
758
|
+
LOG_NOISE_DEBUG("Read handshake message, new state: " << static_cast<int>(state_));
|
|
759
|
+
|
|
760
|
+
} catch (const std::exception& e) {
|
|
761
|
+
LOG_NOISE_ERROR("Exception in read_message: " << e.what());
|
|
762
|
+
fail_handshake();
|
|
763
|
+
return {};
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return payload;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
std::pair<NoiseCipherState, NoiseCipherState> NoiseHandshake::get_cipher_states() {
|
|
770
|
+
if (state_ != NoiseHandshakeState::COMPLETED) {
|
|
771
|
+
return std::make_pair(NoiseCipherState(), NoiseCipherState());
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
return symmetric_state_.split();
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
void NoiseHandshake::advance_state() {
|
|
778
|
+
switch (state_) {
|
|
779
|
+
case NoiseHandshakeState::WRITE_MESSAGE_1:
|
|
780
|
+
state_ = NoiseHandshakeState::READ_MESSAGE_2;
|
|
781
|
+
break;
|
|
782
|
+
case NoiseHandshakeState::READ_MESSAGE_1:
|
|
783
|
+
state_ = NoiseHandshakeState::WRITE_MESSAGE_2;
|
|
784
|
+
break;
|
|
785
|
+
case NoiseHandshakeState::WRITE_MESSAGE_2:
|
|
786
|
+
state_ = NoiseHandshakeState::READ_MESSAGE_3;
|
|
787
|
+
break;
|
|
788
|
+
case NoiseHandshakeState::READ_MESSAGE_2:
|
|
789
|
+
state_ = NoiseHandshakeState::WRITE_MESSAGE_3;
|
|
790
|
+
break;
|
|
791
|
+
case NoiseHandshakeState::WRITE_MESSAGE_3:
|
|
792
|
+
case NoiseHandshakeState::READ_MESSAGE_3:
|
|
793
|
+
state_ = NoiseHandshakeState::COMPLETED;
|
|
794
|
+
LOG_NOISE_INFO("Noise handshake completed successfully");
|
|
795
|
+
break;
|
|
796
|
+
default:
|
|
797
|
+
break;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
void NoiseHandshake::fail_handshake() {
|
|
802
|
+
state_ = NoiseHandshakeState::FAILED;
|
|
803
|
+
LOG_NOISE_ERROR("Noise handshake failed");
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
//=============================================================================
|
|
807
|
+
// NoiseSession Implementation
|
|
808
|
+
//=============================================================================
|
|
809
|
+
|
|
810
|
+
NoiseSession::NoiseSession() : handshake_completed_(false) {
|
|
811
|
+
handshake_state_ = std::make_unique<NoiseHandshake>();
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
NoiseSession::~NoiseSession() = default;
|
|
815
|
+
|
|
816
|
+
bool NoiseSession::initialize_as_initiator(const NoiseKey& static_private_key) {
|
|
817
|
+
return handshake_state_->initialize(NoiseRole::INITIATOR, static_private_key);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
bool NoiseSession::initialize_as_responder(const NoiseKey& static_private_key) {
|
|
821
|
+
return handshake_state_->initialize(NoiseRole::RESPONDER, static_private_key);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
bool NoiseSession::is_handshake_completed() const {
|
|
825
|
+
return handshake_completed_;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
bool NoiseSession::has_handshake_failed() const {
|
|
829
|
+
return handshake_state_->has_failed();
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
std::vector<uint8_t> NoiseSession::create_handshake_message(const std::vector<uint8_t>& payload) {
|
|
833
|
+
if (handshake_completed_) {
|
|
834
|
+
LOG_NOISE_WARN("Handshake already completed");
|
|
835
|
+
return {};
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
auto message = handshake_state_->write_message(payload);
|
|
839
|
+
|
|
840
|
+
if (handshake_state_->is_completed()) {
|
|
841
|
+
auto cipher_states = handshake_state_->get_cipher_states();
|
|
842
|
+
|
|
843
|
+
// According to Noise protocol spec: initiator gets (send=first, receive=second)
|
|
844
|
+
// responder gets (send=second, receive=first)
|
|
845
|
+
if (handshake_state_->get_role() == NoiseRole::INITIATOR) {
|
|
846
|
+
send_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.first));
|
|
847
|
+
receive_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.second));
|
|
848
|
+
} else {
|
|
849
|
+
send_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.second));
|
|
850
|
+
receive_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.first));
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
handshake_completed_ = true;
|
|
854
|
+
LOG_NOISE_INFO("Handshake completed, transport encryption enabled");
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
return message;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
std::vector<uint8_t> NoiseSession::process_handshake_message(const std::vector<uint8_t>& message) {
|
|
861
|
+
if (handshake_completed_) {
|
|
862
|
+
LOG_NOISE_WARN("Handshake already completed");
|
|
863
|
+
return {};
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
auto payload = handshake_state_->read_message(message);
|
|
867
|
+
|
|
868
|
+
if (handshake_state_->is_completed()) {
|
|
869
|
+
auto cipher_states = handshake_state_->get_cipher_states();
|
|
870
|
+
|
|
871
|
+
// According to Noise protocol spec: initiator gets (send=first, receive=second)
|
|
872
|
+
// responder gets (send=second, receive=first)
|
|
873
|
+
if (handshake_state_->get_role() == NoiseRole::INITIATOR) {
|
|
874
|
+
send_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.first));
|
|
875
|
+
receive_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.second));
|
|
876
|
+
} else {
|
|
877
|
+
send_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.second));
|
|
878
|
+
receive_cipher_ = std::make_unique<NoiseCipherState>(std::move(cipher_states.first));
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
handshake_completed_ = true;
|
|
882
|
+
LOG_NOISE_INFO("Handshake completed, transport encryption enabled");
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return payload;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
std::vector<uint8_t> NoiseSession::encrypt_transport_message(const std::vector<uint8_t>& plaintext) {
|
|
889
|
+
if (!handshake_completed_ || !send_cipher_) {
|
|
890
|
+
LOG_NOISE_ERROR("Cannot encrypt: handshake not completed");
|
|
891
|
+
return {};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return send_cipher_->encrypt_with_ad(plaintext);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
std::vector<uint8_t> NoiseSession::decrypt_transport_message(const std::vector<uint8_t>& ciphertext) {
|
|
898
|
+
if (!handshake_completed_ || !receive_cipher_) {
|
|
899
|
+
LOG_NOISE_ERROR("Cannot decrypt: handshake not completed");
|
|
900
|
+
return {};
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
return receive_cipher_->decrypt_with_ad(ciphertext);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
NoiseRole NoiseSession::get_role() const {
|
|
907
|
+
return handshake_state_->get_role();
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
NoiseHandshakeState NoiseSession::get_handshake_state() const {
|
|
911
|
+
return handshake_state_->get_state();
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const NoiseKey& NoiseSession::get_remote_static_public_key() const {
|
|
915
|
+
return handshake_state_->get_remote_static_public_key();
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
//=============================================================================
|
|
919
|
+
// Utility Functions Implementation
|
|
920
|
+
//=============================================================================
|
|
921
|
+
|
|
922
|
+
namespace noise_utils {
|
|
923
|
+
|
|
924
|
+
NoiseKey generate_static_keypair() {
|
|
925
|
+
NoiseKey private_key;
|
|
926
|
+
NoiseCrypto::generate_keypair(private_key);
|
|
927
|
+
return private_key;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
std::string key_to_hex(const NoiseKey& key) {
|
|
931
|
+
std::ostringstream hex_stream;
|
|
932
|
+
for (uint8_t byte : key) {
|
|
933
|
+
hex_stream << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(byte);
|
|
934
|
+
}
|
|
935
|
+
return hex_stream.str();
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
NoiseKey hex_to_key(const std::string& hex) {
|
|
939
|
+
NoiseKey key;
|
|
940
|
+
key.fill(0);
|
|
941
|
+
|
|
942
|
+
if (hex.length() != NOISE_KEY_SIZE * 2) {
|
|
943
|
+
return key;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
for (size_t i = 0; i < NOISE_KEY_SIZE; ++i) {
|
|
947
|
+
std::string byte_str = hex.substr(i * 2, 2);
|
|
948
|
+
try {
|
|
949
|
+
key[i] = static_cast<uint8_t>(std::stoul(byte_str, nullptr, 16));
|
|
950
|
+
} catch (const std::exception&) {
|
|
951
|
+
key.fill(0);
|
|
952
|
+
return key;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
return key;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
std::string get_protocol_name() {
|
|
960
|
+
return NOISE_PROTOCOL_NAME;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
bool validate_message_size(size_t size) {
|
|
964
|
+
return size <= NOISE_MAX_MESSAGE_SIZE;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
std::string noise_error_to_string(NoiseError error) {
|
|
968
|
+
switch (error) {
|
|
969
|
+
case NoiseError::SUCCESS: return "Success";
|
|
970
|
+
case NoiseError::INVALID_STATE: return "Invalid state";
|
|
971
|
+
case NoiseError::HANDSHAKE_FAILED: return "Handshake failed";
|
|
972
|
+
case NoiseError::DECRYPTION_FAILED: return "Decryption failed";
|
|
973
|
+
case NoiseError::INVALID_MESSAGE_SIZE: return "Invalid message size";
|
|
974
|
+
case NoiseError::CRYPTO_ERROR: return "Cryptographic error";
|
|
975
|
+
default: return "Unknown error";
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
} // namespace noise_utils
|
|
980
|
+
|
|
981
|
+
} // namespace librats
|