You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
6.4 KiB
118 lines
6.4 KiB
/* Независимые HKDF/GCM-векторы и проверка привязки записей к обоим Hello. */ |
|
#include "reality.h" |
|
#include "../lib/debug_config.h" |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <openssl/crypto.h> |
|
|
|
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_REALITY, "FAIL line=%d: %s", __LINE__, #x); return 1; } } while (0) |
|
static size_t unhex(const char *s, uint8_t *out) { |
|
size_t n = 0; |
|
while (s[0] && s[1]) { |
|
unsigned value; |
|
if (sscanf(s, "%2x", &value) != 1) break; |
|
out[n++] = (uint8_t)value; |
|
s += 2; |
|
} |
|
return n; |
|
} |
|
|
|
static int vectors(void) { |
|
struct reality_traffic send = {0}, recv = {0}; |
|
uint8_t secret[32], wire[REALITY_RECORD_MAX], plain[REALITY_INNER_MAX], expected[64], type; |
|
for (unsigned i = 0; i < 32; i++) secret[i] = i; |
|
CHECK(!reality_traffic_init(&send, secret)); |
|
CHECK(unhex("1519da96243cb6965fbfa3b28ae25a27", expected) == 16 && !memcmp(send.key, expected, 16)); |
|
CHECK(unhex("16e5473250aa12b61fbacec7", expected) == 12 && !memcmp(send.iv, expected, 12)); |
|
const char *golden[] = { |
|
"1703030018a128808be00c4bdf042cf5ae940649c8f0d4701075afa09b", |
|
"170303001811fa393d1f1db1a3ee840870bedcc6f5c9387a00c9b41c84" |
|
}; |
|
const uint8_t data[] = {1, 2, 3, 0}; |
|
size_t n, decoded; |
|
for (unsigned i = 0; i < 2; i++) { |
|
CHECK(!reality_traffic_init(&send, secret)); |
|
recv = send; |
|
send.sequence = recv.sequence = i ? 7 : 0; |
|
CHECK(!reality_record_seal(&send, 23, data, sizeof(data), 3, wire, sizeof(wire), &n)); |
|
CHECK(unhex(golden[i], expected) == n && !memcmp(wire, expected, n)); |
|
CHECK(!reality_record_open(&recv, wire, n, plain, sizeof(plain), &decoded, &type)); |
|
CHECK(decoded == sizeof(data) && type == 23 && !memcmp(plain, data, decoded)); |
|
CHECK(reality_record_open(&recv, wire, n, plain, sizeof(plain), &decoded, &type) < 0); // повтор sequence |
|
recv.sequence--; |
|
wire[n - 1] ^= 1; |
|
CHECK(reality_record_open(&recv, wire, n, plain, sizeof(plain), &decoded, &type) < 0); |
|
CHECK(recv.sequence == (i ? 7 : 0)); |
|
} |
|
CHECK(!reality_traffic_init(&send, secret)); recv = send; |
|
memset(plain, 0xaa, sizeof(plain)); |
|
CHECK(!reality_record_seal(&send, 23, plain, 16384, 0, wire, sizeof(wire), &n)); |
|
CHECK(n == REALITY_RECORD_MAX); |
|
CHECK(!reality_record_open(&recv, wire, n, plain, sizeof(plain), &decoded, &type) && decoded == 16384); |
|
CHECK(reality_record_seal(&send, 23, plain, 16384, 1, wire, sizeof(wire), &n) < 0); |
|
CHECK(reality_record_seal(&send, 22, NULL, 0, 0, wire, sizeof(wire), &n) < 0); |
|
CHECK(!reality_record_seal(&send, 23, NULL, 0, 0, wire, sizeof(wire), &n)); |
|
CHECK(!reality_record_open(&recv, wire, n, plain, sizeof(plain), &decoded, &type) && decoded == 0); |
|
send.sequence = REALITY_KEY_LIMIT; |
|
CHECK(reality_record_seal(&send, 23, data, sizeof(data), 0, wire, sizeof(wire), &n) < 0); |
|
uint8_t old_key[16]; memcpy(old_key, send.key, 16); |
|
CHECK(!reality_traffic_update(&send) && send.sequence == 0 && send.generation == 1 && memcmp(old_key, send.key, 16)); |
|
recv.sequence = REALITY_KEY_LIMIT; |
|
CHECK(!reality_traffic_update(&recv)); |
|
CHECK(!reality_record_seal(&send, 23, data, sizeof(data), 0, wire, sizeof(wire), &n)); |
|
CHECK(!reality_record_open(&recv, wire, n, plain, sizeof(plain), &decoded, &type)); |
|
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "HKDF, GCM nonce/AAD, replay, limits, empty DATA and KeyUpdate vectors passed"); |
|
return 0; |
|
} |
|
|
|
static int hello_session(void) { |
|
struct reality_session client = {0}, server = {0}; |
|
struct reality_client_config cc; |
|
struct reality_server_config sc = {0}; |
|
reality_client_config_set_defaults(&cc); |
|
CHECK(!reality_generate_keypair(sc.static_privkey, cc.server_static_pubkey)); |
|
strcpy(cc.server_name, "www.microsoft.com"); |
|
sc.version[0] = 2; sc.short_id_count = 1; sc.time_window_sec = 30; |
|
uint8_t ch[REALITY_MAX_CH_SIZE], sh[REALITY_MAX_SH_SIZE], finished[32], check[32]; |
|
size_t cn, sn; |
|
CHECK(!reality_session_client_start(&client, &cc, 1000, ch, sizeof(ch), &cn)); |
|
CHECK(!reality_session_server_start(&server, &sc, 1000, ch, cn, sh, sizeof(sh), &sn)); |
|
const size_t offsets[] = {0, 4, 5, 8, 10, 43, 44, 76, 77, 78, 80, 84, 86, 88, 92, 94}; |
|
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { |
|
sh[offsets[i]] ^= 1; |
|
CHECK(reality_session_client_accept(&client, sh, sn) != REALITY_OK); |
|
sh[offsets[i]] ^= 1; |
|
} |
|
CHECK(!reality_session_client_accept(&client, sh, sn)); |
|
CHECK(!memcmp(client.send.key, server.recv.key, 16) && !memcmp(client.recv.key, server.send.key, 16)); |
|
CHECK(memcmp(client.send.key, client.recv.key, 16)); |
|
CHECK(!reality_finished(&server, 1, finished) && !reality_finished(&client, 0, check) && !memcmp(finished, check, 32)); |
|
const uint8_t ee[] = {8, 0, 0, 2, 0, 0}; |
|
CHECK(!reality_transcript_add(&client, ee, sizeof(ee))); |
|
CHECK(!reality_finished(&client, 0, check) && memcmp(finished, check, 32)); |
|
CHECK(!reality_transcript_add(&server, ee, sizeof(ee))); |
|
CHECK(!reality_finished(&server, 1, finished) && !reality_finished(&client, 0, check) && !memcmp(finished, check, 32)); |
|
for (unsigned i = 0; i < 32; i++) CHECK(!client.auth_key[i] && !server.auth_key[i] && !client.ephemeral_private[i]); |
|
reality_session_cleanup(&client); reality_session_cleanup(&server); |
|
|
|
/* Все открытые байты совпадают с локальным эталоном OpenSSL; динамические поля исключены. */ |
|
FILE *f = fopen("fixtures/reality_openssl_hello.hex", "r"); |
|
if (!f) f = fopen("tests/fixtures/reality_openssl_hello.hex", "r"); |
|
CHECK(f); |
|
char line[2048]; uint8_t reference[1024]; size_t rn = 0; |
|
while (fgets(line, sizeof(line), f)) if (line[0] != '#') { rn = unhex(line, reference); break; } |
|
fclose(f); |
|
CHECK(rn == cn && cn == 263); |
|
memset(ch + 11, 0, 32); memset(reference + 11, 0, 32); |
|
memset(ch + 44, 0, 32); memset(reference + 44, 0, 32); |
|
memset(ch + cn - 41, 0, 32); memset(reference + cn - 41, 0, 32); |
|
CHECK(!memcmp(ch, reference, cn)); |
|
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "Hello validation, independent traffic keys, transcript Finished and OpenSSL profile passed"); |
|
return 0; |
|
} |
|
|
|
int main(void) { |
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); |
|
return vectors() || hello_session(); |
|
}
|
|
|