116 {
117 if (!ctx || ctx->state != CRYPTO_HANDSHAKE_KEY_EXCHANGE) {
118 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, state=%d", ctx, ctx ? ctx->state : -1);
119 }
120 if (!transport) {
121 return SET_ERRNO(ERROR_INVALID_PARAM, "transport is NULL");
122 }
123
124
125 int result;
126
127
128 if (packet_type == PACKET_TYPE_CRYPTO_NO_ENCRYPTION) {
129
130
131 auth_failure_packet_t failure = {0};
132 failure.reason_flags = 0;
133 int send_result =
135 if (send_result != 0) {
136 return SET_ERRNO(ERROR_NETWORK, "Failed to send AUTH_FAILED packet");
137 }
138
139 return SET_ERRNO(ERROR_CRYPTO, "SECURITY: Client sent NO_ENCRYPTION response - encryption mode "
140 "mismatch. Server requires encryption, but "
141 "client has --no-encrypt. Use matching encryption settings on "
142 "both client and server");
143 }
144
145
146 if (packet_type != PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP) {
147 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Expected KEY_EXCHANGE_RESPONSE, got packet type %d", packet_type);
148 }
149
150
151
152
153 size_t simple_size = ctx->crypto_ctx.public_key_size;
154
155
156 size_t authenticated_size = ctx->crypto_ctx.public_key_size + ED25519_PUBLIC_KEY_SIZE + ED25519_SIGNATURE_SIZE;
157
158 bool client_sent_identity = false;
159 uint8_t *client_ephemeral_key = SAFE_MALLOC(ctx->crypto_ctx.public_key_size, uint8_t *);
160 uint8_t *client_identity_key = SAFE_MALLOC(ED25519_PUBLIC_KEY_SIZE, uint8_t *);
161 uint8_t *client_signature = SAFE_MALLOC(ED25519_SIGNATURE_SIZE, uint8_t *);
162
163 if (!client_ephemeral_key || !client_identity_key || !client_signature) {
164 if (client_ephemeral_key)
165 SAFE_FREE(client_ephemeral_key);
166 if (client_identity_key)
167 SAFE_FREE(client_identity_key);
168 if (client_signature)
169 SAFE_FREE(client_signature);
170 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate memory for client keys");
171 }
172
173
174 asciichat_error_t validation_result =
176 if (validation_result != ASCIICHAT_OK) {
177
178 SAFE_FREE(client_ephemeral_key);
179 SAFE_FREE(client_identity_key);
180 SAFE_FREE(client_signature);
181 return validation_result;
182 }
183
184
185
186 if (payload_len >= authenticated_size) {
187
188
189 memcpy(client_ephemeral_key, payload, ctx->crypto_ctx.public_key_size);
190 memcpy(client_identity_key, payload + ctx->crypto_ctx.public_key_size, ED25519_PUBLIC_KEY_SIZE);
191 memcpy(client_signature, payload + ctx->crypto_ctx.public_key_size + ED25519_PUBLIC_KEY_SIZE,
192 ED25519_SIGNATURE_SIZE);
193 client_sent_identity = true;
194 ctx->client_sent_identity = true;
195
196
197 const char *client_gpg_key_id = NULL;
198 char gpg_key_id_buffer[41] = {0};
199 size_t gpg_offset = ctx->crypto_ctx.public_key_size + ED25519_PUBLIC_KEY_SIZE + ED25519_SIGNATURE_SIZE;
200 if (payload_len > gpg_offset) {
201 uint8_t gpg_key_id_len = payload[gpg_offset];
202 if (gpg_key_id_len > 0 && gpg_key_id_len <= 40 && payload_len >= gpg_offset + 1 + gpg_key_id_len) {
203 memcpy(gpg_key_id_buffer, payload + gpg_offset + 1, gpg_key_id_len);
204 gpg_key_id_buffer[gpg_key_id_len] = '\0';
205 client_gpg_key_id = gpg_key_id_buffer;
206 log_debug("Extracted client GPG key ID from KEY_EXCHANGE_RESPONSE: %s", client_gpg_key_id);
207 }
208 }
209
210
211 bool client_has_null_identity = true;
212 for (size_t i = 0; i < ED25519_PUBLIC_KEY_SIZE; i++) {
213 if (client_identity_key[i] != 0) {
214 client_has_null_identity = false;
215 break;
216 }
217 }
218
219 if (client_has_null_identity) {
220
221 log_debug("Client sent null identity key - no client authentication required");
222 client_sent_identity = false;
223 ctx->client_sent_identity = false;
224 log_warn("Client connected without identity authentication");
225 } else {
226
227
228
229 if (!ctx->require_client_auth) {
230 log_info("Skipping client signature verification (no --client-keys specified)");
231 log_warn("Connection is encrypted but client identity is NOT verified");
232 } else {
233
234 log_debug("Verifying client's signature");
235
237 client_signature, client_gpg_key_id) != 0) {
238
239 auth_failure_packet_t failure = {0};
240 failure.reason_flags = AUTH_FAIL_SIGNATURE_INVALID;
241 int send_result =
243 if (send_result != 0) {
244 return SET_ERRNO(ERROR_NETWORK, "Failed to send AUTH_FAILED packet");
245 }
246
247 return SET_ERRNO(ERROR_CRYPTO, "Client signature verification FAILED - rejecting connection");
248 }
249 }
250 }
251
252
253 if (client_sent_identity) {
254 ctx->client_ed25519_key.type = KEY_TYPE_ED25519;
255 memcpy(ctx->client_ed25519_key.key, client_identity_key, ctx->crypto_ctx.public_key_size);
256 }
257 } else if (payload_len == simple_size) {
258
259 log_debug("Client sent non-authenticated response (%zu bytes)", payload_len);
260 memcpy(client_ephemeral_key, payload, ctx->crypto_ctx.public_key_size);
261 client_sent_identity = false;
262 ctx->client_sent_identity = false;
263 log_warn("Client connected without identity authentication");
264 } else {
265
266 SAFE_FREE(client_ephemeral_key);
267 SAFE_FREE(client_identity_key);
268 SAFE_FREE(client_signature);
269 return SET_ERRNO(ERROR_NETWORK_PROTOCOL,
270 "Invalid client key response size: %zu bytes (expected %zu for "
271 "authenticated or %zu for simple)",
272 payload_len, authenticated_size, simple_size);
273 }
274
275
276 const uint8_t *client_x25519 = client_ephemeral_key;
277 const uint8_t *client_ed25519 = client_sent_identity ? client_identity_key : NULL;
278
279
280
281 if (client_sent_identity && ctx->require_client_auth && ctx->client_whitelist && ctx->num_whitelisted_clients > 0) {
282 bool key_found = false;
283
284
285 char client_ed25519_hex[HEX_STRING_SIZE_32];
286 for (int i = 0; i < ED25519_PUBLIC_KEY_SIZE; i++) {
287 safe_snprintf(client_ed25519_hex + i * 2, 3,
"%02x", client_ed25519[i]);
288 }
289 log_debug("Client Ed25519 identity key: %s", client_ed25519_hex);
290
291
292 for (size_t i = 0; i < ctx->num_whitelisted_clients; i++) {
293
294 char whitelist_ed25519_hex[HEX_STRING_SIZE_32];
295 for (int j = 0; j < ED25519_PUBLIC_KEY_SIZE; j++) {
296 safe_snprintf(whitelist_ed25519_hex + j * 2, 3,
"%02x", ctx->client_whitelist[i].key[j]);
297 }
298 log_debug("Whitelist[%zu] Ed25519 key: %s", i, whitelist_ed25519_hex);
299
300
301
302 if (sodium_memcmp(client_ed25519, ctx->client_whitelist[i].key, ED25519_PUBLIC_KEY_SIZE) == 0) {
303 key_found = true;
304 ctx->client_ed25519_key_verified = true;
305
306
307 memcpy(&ctx->client_ed25519_key, &ctx->client_whitelist[i], sizeof(public_key_t));
308
309 log_debug("Client Ed25519 key authorized (whitelist entry %zu)", i);
310 if (strlen(ctx->client_whitelist[i].comment) > 0) {
311 log_info("Client identity: %s", ctx->client_whitelist[i].comment);
312 }
313 break;
314 }
315 }
316
317 if (!key_found) {
318 SET_ERRNO(ERROR_CRYPTO_AUTH, "Client Ed25519 key not in whitelist - rejecting connection");
319
320
321 ctx->client_ed25519_key_verified = false;
322 }
323 } else if (client_sent_identity) {
324
325
326 ctx->client_ed25519_key_verified = false;
327 }
328
329
331 if (crypto_result != CRYPTO_OK) {
332 return SET_ERRNO(ERROR_CRYPTO, "Failed to set peer public key and derive shared secret: %s",
334 }
335
336
337 SAFE_FREE(client_ephemeral_key);
338 SAFE_FREE(client_identity_key);
339 SAFE_FREE(client_signature);
340
341
342
343 if (client_sent_identity || ctx->crypto_ctx.has_password || ctx->require_client_auth) {
344
346 if (crypto_result != CRYPTO_OK) {
348 }
349
350
351 size_t challenge_packet_size = AUTH_CHALLENGE_FLAGS_SIZE + ctx->crypto_ctx.auth_challenge_size;
352 uint8_t challenge_packet[1 + HMAC_SHA256_SIZE];
353 uint8_t auth_flags = 0;
354
355
356 if (ctx->crypto_ctx.has_password) {
357 auth_flags |= AUTH_REQUIRE_PASSWORD;
358 }
359 if (ctx->require_client_auth) {
360 auth_flags |= AUTH_REQUIRE_CLIENT_KEY;
361 }
362
363 challenge_packet[0] = auth_flags;
364 memcpy(challenge_packet + 1, ctx->crypto_ctx.auth_nonce, ctx->crypto_ctx.auth_challenge_size);
365
366
368 challenge_packet_size, 0);
369 if (result != 0) {
370 return SET_ERRNO(ERROR_NETWORK, "Failed to send AUTH_CHALLENGE packet");
371 }
372
373 ctx->state = CRYPTO_HANDSHAKE_AUTHENTICATING;
374 } else {
375
376 log_debug("Skipping authentication (no password and client has no identity key)");
377
378
380 if (result != 0) {
381 return SET_ERRNO(ERROR_NETWORK, "Failed to send HANDSHAKE_COMPLETE packet");
382 }
383
384 ctx->state = CRYPTO_HANDSHAKE_READY;
385 ctx->crypto_ctx.handshake_complete = true;
386 log_debug("Crypto handshake completed successfully (no authentication)");
387 }
388
389 return ASCIICHAT_OK;
390}
asciichat_error_t crypto_handshake_validate_packet_size(const crypto_handshake_context_t *ctx, uint16_t packet_type, size_t packet_size)
crypto_result_t crypto_set_peer_public_key(crypto_context_t *ctx, const uint8_t *peer_public_key)
crypto_result_t crypto_generate_nonce(uint8_t nonce[32])
const char * crypto_result_to_string(crypto_result_t result)
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
asciichat_error_t ed25519_verify_signature(const uint8_t public_key[32], const uint8_t *message, size_t message_len, const uint8_t signature[64], const char *gpg_key_id)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.