Files
Phantom/release/src/path.c

2112 lines
58 KiB
C

#include "path.h"
#include "server.h"
static int
add_routing_table_entry(SetupPackage *s, struct in6_addr *ap_adress, char *const *ips, int nips)
{
int i;
s->rte = malloc(sizeof (RoutingTableEntry));
if (s->rte == NULL) {
return -1;
}
routing_table_entry__init(s->rte);
s->rte->ap_adress.data = ap_adress->s6_addr;
assert(sizeof (ap_adress->s6_addr) == 16);
s->rte->ap_adress.len = sizeof (ap_adress->s6_addr);
s->rte->n_ip_adresses = nips;
s->rte->ip_adresses = malloc (nips * sizeof (char *));
if (s->rte->ip_adresses == NULL) {
free(s->rte);
return -1;
}
if (s->rte->ip_adresses == NULL) {
free(s->rte->ip_adresses);
free(s->rte);
return -1;
}
for (i = 0; i < nips; i++) {
s->rte->ip_adresses[i] = ips[i];
}
return 0;
}
static void
pad_key(const uint8_t *key, int len, uint8_t *out, int wantlen)
{
int i;
assert(wantlen > len);
for (i = 0; i < wantlen / len; i++) {
memcpy(out + len * i, key, len);
}
memcpy(out + i * len, key, wantlen - i * len);
}
static struct xkeys *
generate_conn_keys(int nkeys, const uint8_t *basekey, const uint8_t *salt)
{
int i;
uint8_t tmp[SYMMETRIC_CIPHER_KEY_LEN];
struct xkeys *keys;
cleanup_stack_init;
assert(SYMMETRIC_CIPHER_IV_LEN <= SYMMETRIC_CIPHER_KEY_LEN);
keys = malloc(sizeof (struct xkeys));
if (keys == NULL) {
return NULL;
}
cleanup_stack_push(free, keys);
keys->nkeys = nkeys;
keys->ivs = malloc(nkeys * SYMMETRIC_CIPHER_IV_LEN);
if (keys->ivs == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, keys->ivs);
keys->keys = malloc(nkeys * SYMMETRIC_CIPHER_KEY_LEN);
if (keys->keys == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, keys->keys);
memcpy(tmp, basekey, SYMMETRIC_CIPHER_KEY_LEN);
for (i = 0; i < nkeys; i++) {
PKCS5_PBKDF2_HMAC_SHA1((char *) tmp, SYMMETRIC_CIPHER_KEY_LEN, salt, SYMMETRIC_CIPHER_KEY_LEN, PBKDF2_STEPS, SYMMETRIC_CIPHER_KEY_LEN, keys->keys + SYMMETRIC_CIPHER_KEY_LEN * i);
memcpy(tmp, keys->keys + SYMMETRIC_CIPHER_KEY_LEN * i, SYMMETRIC_CIPHER_KEY_LEN);
}
for (i = 0; i < nkeys; i++) {
PKCS5_PBKDF2_HMAC_SHA1((char *) tmp, SYMMETRIC_CIPHER_IV_LEN, salt, SYMMETRIC_CIPHER_IV_LEN, PBKDF2_STEPS, SYMMETRIC_CIPHER_IV_LEN, keys->ivs + SYMMETRIC_CIPHER_IV_LEN * i);
memcpy(tmp, keys->ivs + SYMMETRIC_CIPHER_IV_LEN * i, SYMMETRIC_CIPHER_IV_LEN);
}
/* keep allocs in case of success */
return keys;
}
static int
contains(const uint32_t *haystack, uint32_t needle, size_t nmemb)
{
int cnt = 0;
while (--nmemb) {
if (haystack[nmemb] == needle) {
cnt++;
}
}
return cnt;
}
static void
delete_nodes(struct node_info *array, int len)
{
int i;
for (i = 0; i < len; i++) {
if (array[i].construction_certificate != NULL) {
X509_free(array[i].construction_certificate);
}
if (array[i].communication_certificate != NULL) {
X509_free(array[i].communication_certificate);
}
if (array[i].construction_certificate_flat != NULL) {
free_X509_flat(array[i].construction_certificate_flat);
}
if (array[i].communication_certificate_flat != NULL) {
free_X509_flat(array[i].communication_certificate_flat);
}
if (array[i].ip != NULL) {
free(array[i].ip);
}
}
free(array);
}
static struct path *
new_path(void)
{
struct path *p = calloc(1, sizeof (struct path));
if (p == NULL) {
return NULL;
}
return p;
}
static void
delete_struct_setup_path2(struct setup_path *path, int save_conn)
{
int i;
if (path->nodes != NULL) {
delete_nodes(path->nodes, path->nnodes);
}
if (path->construction_certificate != NULL) {
RSA_free(path->construction_certificate);
}
if (path->sizes != NULL) {
free(path->sizes);
}
if (path->contents != NULL) {
for (i = 0; i < path->nnodes; i++) {
if (path->contents[i] != NULL) {
free(path->contents[i]);
}
}
free(path->contents);
}
for (i = 0; i < path->nnodes; i++) {
if (path->sps[i].dummies != NULL) {
free(path->sps[i].dummies);
}
}
if (path->sps != NULL) {
free(path->sps);
}
if (path->ssl_conn != NULL && (! save_conn)) {
free_ssl_connection(path->ssl_conn);
}
if (path->construction_certificate_data != NULL) {
free(path->construction_certificate_data);
}
free(path);
}
static void
delete_struct_setup_path(struct setup_path *path)
{
delete_struct_setup_path2(path, 0);
}
static struct setup_path *
create_struct_setup_path(const struct config *config, int want_entrypath, int reserve_ap)
{
int i, j, ret;
struct setup_path *p = calloc(1, sizeof (struct setup_path));
if (p == NULL) {
free(p);
return NULL;
}
p->reserve_ap_adress = reserve_ap;
p->nxnodes = config->nxnodes;
p->nynodes = config->nynodes;
p->nnodes = config->nynodes + config->nxnodes;
p->entrypath = want_entrypath;
if (! reserve_ap) {
p->routing_certificate = config->routing_certificate;
p->routing_certificate_flat = config->routing_certificate_flat;
bzero(p->ap.s6_addr, 16);
ret = reserve_new_ap_adress(config, &p->ap);
if (ret != 0) {
free(p);
return NULL;
}
}
p->nodes = calloc(p->nnodes, sizeof (struct node_info));
if (p->nodes == NULL) {
delete_struct_setup_path(p);
return NULL;
}
p->sizes = calloc(p->nnodes, 4);
if (p->sizes == NULL) {
delete_struct_setup_path(p);
return NULL;
}
p->contents = calloc(p->nnodes, sizeof (uint8_t *));
if (p->contents == NULL) {
delete_struct_setup_path(p);
return NULL;
}
p->sps = calloc(p->nnodes, sizeof (struct setup_package));
if (p->sps == NULL) {
delete_struct_setup_path(p);
return NULL;
}
assert(MIN_DUMMIES <= MAX_DUMMIES);
for (i = 0; i < p->nnodes; i++) {
p->sps[i].ndummies = rand_range(MIN_DUMMIES, MAX_DUMMIES);
p->sps[i].dummies = malloc(p->sps[i].ndummies * sizeof (struct dummy_package));
if (p->sps[i].dummies == NULL) {
delete_struct_setup_path(p);
return NULL;
}
for (j = 0; j < p->sps[i].ndummies; j++) {
rand_bytes(p->sps[i].dummies[j].seed, SYMMETRIC_CIPHER_KEY_LEN);
}
}
return p;
}
static int
get_nodes_from_db(struct node_info *nodes, int num)
{
int i, ret;
char **ips;
uint16_t *ports;
X509 **ccs, **pbcs;
cleanup_stack_init;
ips = malloc(num * sizeof (char *));
if (ips == NULL) {
return -1;
}
cleanup_stack_push(free, ips);
ports = malloc(num * sizeof (uint16_t));
if (ports == NULL) {
cleanup_stack_free_all();
return -1;
}
cleanup_stack_push(free, ports);
ccs = malloc(num * sizeof (X509 *));
if (ccs == NULL) {
cleanup_stack_free_all();
return -1;
}
cleanup_stack_push(free, ccs);
pbcs = malloc(num * sizeof(X509 *));
if (pbcs == NULL) {
cleanup_stack_free_all();
return -1;
}
cleanup_stack_push(free, pbcs);
ret = get_random_node_ip_adresses(ips, ports, ccs, pbcs, num);
assert(ret == num);
for (i = 0; i < num; i++) {
nodes[i].ip = ips[i];
nodes[i].communication_certificate = ccs[i];
nodes[i].construction_certificate = pbcs[i];
nodes[i].communication_certificate_flat = flatten_X509(ccs[i]);
nodes[i].construction_certificate_flat = flatten_X509(pbcs[i]);
nodes[i].port = ports[i];
}
cleanup_stack_free_all();
return 0;
}
static void
build_xy_path(struct setup_path *path)
{
int i, x, occurences, cnt, excessynodes, *indices;
excessynodes = path->nynodes - 2 * path->nxnodes;
indices = alloca(excessynodes * sizeof (int));
randomize_array(path->nodes, path->nnodes, sizeof (struct node_info));
for (i = 0; i < excessynodes; i++) {
indices[i] = rand_range(0, path->nxnodes);
}
cnt = 0;
/* begin with a single y node */
path->nodes[cnt++].flags |= Y_NODE;
x = 0;
/* intermix x and y nodes in the middle */
while(x < path->nxnodes) {
occurences = contains((const uint32_t *) indices, (uint32_t) x, excessynodes);
if (occurences) {
/* insert randomly many excess ynodes */
for (i = 0; i < occurences; i++) {
path->nodes[cnt++].flags |= Y_NODE;
}
}
/* insert one xnode followed by one ynode*/
path->nodes[cnt++].flags |= X_NODE;
x++;
path->nodes[cnt++].flags |= Y_NODE;
}
/* end with the rest of the ynodes */
while (cnt < path->nnodes) {
path->nodes[cnt++].flags |= Y_NODE;
}
assert(x == path->nxnodes && cnt == path->nnodes);
/* reverse whole path with a 50% chance */
path->is_reverse_path = rand_range(0, 2);
if (path->is_reverse_path) {
reverse_array(path->nodes, path->nnodes, sizeof (struct node_info));
for (i = 0; i < path->nnodes; i++) {
if (path->nodes[i].flags & X_NODE) {
path->nodes[i].flags |= T_NODE;
if (path->entrypath) {
path->entry_ip = path->nodes[i].ip;
path->nodes[i].flags |= ENTRY_NODE;
} else if (path->reserve_ap_adress) {
path->nodes[i].flags |= RESERVE_AP;
}
break;
}
}
} else {
for (i = 0; i < path->nnodes; i++) {
if (path->nodes[path->nnodes - 1 - i].flags & X_NODE) {
path->nodes[path->nnodes - 1 - i].flags |= T_NODE;
if (path->entrypath) {
path->nodes[path-> nnodes - 1 - i].flags |= ENTRY_NODE;
path->entry_ip = path->nodes[path->nnodes - 1 - i].ip;
} else if (path->reserve_ap_adress) {
path->nodes[path-> nnodes - 1 - i].flags |= RESERVE_AP;
}
break;
}
}
}
}
static void __attribute__((unused))
printpath(const struct setup_path *path)
{
int i;
char c;
printf("start %s -> ", path->sps[0].prev_ip);
printf("%s ", path->sps[1].prev_ip);
for (i = 0; i < path->nnodes - 1; i++) {
c = (path->nodes[i].flags & X_NODE)? 'x' : 'y';
putchar(c);
if (path->nodes[i].flags & T_NODE) {
putchar('t');
}
printf(" -> %s ", path->sps[i].next_ip);
}
putchar('y');
printf(" -> %s end ", path->sps[path->nnodes - 1].next_ip);
printf("(%s)\n", (path->is_reverse_path)? "reverse" : "not reverse");
}
static int
generate_path_construction_keys(struct setup_path *path)
{
BUF_MEM *bptr;
BIO *bio;
int ret;
path->construction_certificate = RSA_generate_key(RSA_KEY_LEN, 65537, NULL, NULL);
if (path->construction_certificate == NULL) {
return -1;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
return -1;
}
ret = PEM_write_bio_RSAPublicKey(bio, path->construction_certificate);
if (ret == 0) {
BIO_free(bio);
return -1;
}
BIO_get_mem_ptr(bio, &bptr);
assert(BIO_set_close(bio, BIO_NOCLOSE) == 1);
BIO_free(bio);
path->construction_certificate_len = bptr->length;
path->construction_certificate_data = malloc(bptr->length);
if (path->construction_certificate_data == NULL) {
BUF_MEM_free(bptr);
return -1;
}
memcpy(path->construction_certificate_data, bptr->data, bptr->length);
BUF_MEM_free(bptr);
return 0;
}
static void
generate_setup_packages(const struct config *config, struct setup_path *path, int nround)
{
struct setup_package **sps;
struct node_info **nodes;
int i, nnodes;
sps = alloca(path->nnodes * sizeof (struct setup_package *));
nodes = alloca(path->nnodes * sizeof (struct node_info *));
assert(nround == 1 || nround == 2);
assert(path->nnodes > 1);
assert(path->nxnodes > 1);
if (nround == 1) {
for (i = 0; i < path->nnodes; i++) {
sps[i] = &(path->sps[i]);
nodes[i] = &(path->nodes[i]);
}
nnodes = path->nnodes;
} else {
/* tell all nodes round 1 was successfull */
/* and throw away the old contents of the nodes */
/* but save old prev_id for correct symm encryption */
for (i = 0; i < path->nnodes; i++) {
memcpy(path->sps[i].old_prev_id, path->sps[i].prev_id, SHA_DIGEST_LENGTH);
path->sps[i].flags |= SUCCESS_FLAG;
free(path->contents[i]);
}
nnodes = 0;
/* collect x nodes and packages */
for (i = 0; i < path->nnodes; i++) {
if (path->sps[i].flags & X_NODE) {
sps[nnodes] = &(path->sps[i]);
nodes[nnodes] = &(path->nodes[i]);
nnodes++;
}
}
assert(nnodes == path->nxnodes);
/* now repack the x packages */
}
/* first node */
sps[0]->prev_ip = config->ip;
sps[0]->prev_port = config->port;
sps[0]->next_ip = nodes[1]->ip;
sps[0]->next_port = nodes[1]->port;
rand_bytes(sps[0]->prev_id, SHA_DIGEST_LENGTH);
rand_bytes(sps[0]->next_id, SHA_DIGEST_LENGTH);
rand_bytes(sps[0]->replaceseed, SYMMETRIC_CIPHER_KEY_LEN);
sps[0]->prev_communication_certificate_flat = config->communication_certificate_flat;
sps[0]->next_communication_certificate_flat = nodes[1]->communication_certificate_flat;
sps[0]->flags |= nodes[0]->flags;
/* intermediary nodes */
for (i = 1; i < nnodes - 1; i++) {
sps[i]->prev_ip = nodes[i - 1]->ip;
sps[i]->prev_port = nodes[i - 1]->port;
sps[i]->next_ip = nodes[i + 1]->ip;
sps[i]->next_port = nodes[i + 1]->port;
rand_bytes(sps[i]->next_id, SHA_DIGEST_LENGTH);
rand_bytes(sps[i]->replaceseed, SYMMETRIC_CIPHER_KEY_LEN);
memcpy(sps[i]->prev_id, sps[i - 1]->next_id, SHA_DIGEST_LENGTH);
sps[i]->prev_communication_certificate_flat = nodes[i - 1]->communication_certificate_flat;
sps[i]->next_communication_certificate_flat = nodes[i + 1]->communication_certificate_flat;
sps[i]->flags |= nodes[i]->flags;
}
/* last node */
sps[nnodes - 1]->prev_ip = nodes[nnodes - 2]->ip;
sps[nnodes - 1]->prev_port = nodes[nnodes - 2]->port;
sps[nnodes - 1]->next_ip = config->ip;
sps[nnodes - 1]->next_port = config->port;
memcpy(sps[nnodes - 1]->prev_id, sps[nnodes - 2]->next_id, SHA_DIGEST_LENGTH);
memcpy(sps[nnodes - 1]->next_id, sps[0]->prev_id, SHA_DIGEST_LENGTH);
rand_bytes(sps[nnodes - 1]->replaceseed, SYMMETRIC_CIPHER_KEY_LEN);
sps[nnodes - 1]->prev_communication_certificate_flat = nodes[nnodes - 2]->communication_certificate_flat;
sps[nnodes - 1]->next_communication_certificate_flat = config->communication_certificate_flat;
if (nround == 2) {
for (i = 0; i < nnodes; i++) {
sps[i]->nkeys = config->nkeys;
rand_bytes(sps[i]->startkey, SYMMETRIC_CIPHER_KEY_LEN);
rand_bytes(sps[i]->salt, SYMMETRIC_CIPHER_KEY_LEN);
rand_bytes(sps[i]->replaceseed, SYMMETRIC_CIPHER_KEY_LEN);
}
}
}
static uint8_t *
encrypt_setup_package_asymmetric(const uint8_t *serialized, uint32_t len, EVP_PKEY *pubkey, int *outlen)
{
int ret, tmp, privkeylen;
uint32_t crypted;
uint8_t *out, *p;
/* Модернизировано для OpenSSL 3.0+ */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
const EVP_CIPHER *type = EVP_aes_256_cbc();
out = malloc((len / SYMMETRIC_CIPHER_BLOCK_SIZE) * SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_BLOCK_SIZE + EVP_PKEY_size(pubkey) + SYMMETRIC_CIPHER_IV_LEN + 2 * 4);
if (out == NULL) {
return NULL;
}
/* EVP_CIPHER_CTX_init не нужен - ctx уже инициализирован через _new() */
p = out + SYMMETRIC_CIPHER_IV_LEN + 2 * 4;
ret = EVP_SealInit(ctx, type, &p, &privkeylen, out + 2 * 4, &pubkey, 1);
assert (privkeylen == EVP_PKEY_size(pubkey));
if (ret == 0) {
EVP_CIPHER_CTX_cleanup(ctx);
free(out);
return NULL;
}
ret = EVP_SealUpdate(ctx, out + EVP_PKEY_size(pubkey) + SYMMETRIC_CIPHER_IV_LEN + 2 * 4, &tmp, serialized, len);
crypted = tmp;
if (ret == 0) {
EVP_CIPHER_CTX_cleanup(ctx);
free(out);
return NULL;
}
ret = EVP_SealFinal(ctx, out + crypted + EVP_PKEY_size(pubkey) + SYMMETRIC_CIPHER_IV_LEN + 2 * 4, &tmp);
crypted += tmp;
if (ret == 0) {
EVP_CIPHER_CTX_cleanup(ctx);
free(out);
return NULL;
}
EVP_CIPHER_CTX_cleanup(ctx);
assert(crypted == (len / SYMMETRIC_CIPHER_BLOCK_SIZE) * SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_BLOCK_SIZE);
serialize_32_t(crypted, out);
serialize_32_t(EVP_PKEY_size(pubkey), out + 4);
*outlen = crypted + EVP_PKEY_size(pubkey) + SYMMETRIC_CIPHER_IV_LEN + 2 * 4;
return out;
}
static uint8_t *
encrypt_symmetric(const uint8_t *serialized, uint32_t len, const uint8_t *key, int *outlen)
{
int ret, tmp;
uint32_t crypted;
uint8_t *out;
/* Модернизировано для OpenSSL 3.0+ */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
uint8_t sizedkey[SYMMETRIC_CIPHER_KEY_LEN];
const EVP_CIPHER *type = EVP_aes_256_cbc();
out = calloc((len / SYMMETRIC_CIPHER_BLOCK_SIZE) * SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_IV_LEN, 1);
if (out == NULL) {
return NULL;
}
pad_key(key, SHA_DIGEST_LENGTH, sizedkey, SYMMETRIC_CIPHER_KEY_LEN);
rand_bytes(out, EVP_CIPHER_iv_length(type));
/* EVP_CIPHER_CTX_init не нужен - ctx уже инициализирован через _new() */
ret = EVP_EncryptInit(ctx, type, sizedkey, out);
if (ret == 0) {
EVP_CIPHER_CTX_cleanup(ctx);
free(out);
return NULL;
}
ret = EVP_EncryptUpdate(ctx, out + SYMMETRIC_CIPHER_IV_LEN, &tmp, serialized, len);
crypted = tmp;
if (ret == 0) {
EVP_CIPHER_CTX_cleanup(ctx);
free(out);
return NULL;
}
ret = EVP_EncryptFinal(ctx, out + crypted + SYMMETRIC_CIPHER_IV_LEN, &tmp);
crypted += tmp;
if (ret == 0) {
EVP_CIPHER_CTX_cleanup(ctx);
free(out);
return NULL;
}
EVP_CIPHER_CTX_cleanup(ctx);
assert(crypted == (len / SYMMETRIC_CIPHER_BLOCK_SIZE) * SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_BLOCK_SIZE);
*outlen = crypted + SYMMETRIC_CIPHER_IV_LEN;
return out;
}
static int
sign_data(uint8_t *sig, const uint8_t *data, uint32_t len, EVP_PKEY *key)
{
int ret;
uint32_t written;
/* Модернизировано для OpenSSL 3.0+ */
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
const EVP_MD *type = EVP_sha1();
/* EVP_MD_CTX_init не нужен - ctx уже инициализирован через _new() */
EVP_SignInit(ctx, type);
ret = EVP_SignUpdate(ctx, data, len);
if (ret != 1) {
return -1;
}
ret = EVP_SignFinal(ctx, sig, &written, key);
if (ret != 1) {
return -1;
}
EVP_MD_CTX_free(ctx); /* Модернизировано: cleanup -> free */
assert(written == RSA_SIGN_LEN);
return 0;
}
static int
check_signed_data(const uint8_t *sig, uint32_t siglen, const uint8_t *data, uint32_t len, EVP_PKEY *key)
{
int ret;
/* Модернизировано для OpenSSL 3.0+ */
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
const EVP_MD *type = EVP_sha1();
/* EVP_MD_CTX_init не нужен - ctx уже инициализирован через _new() */
EVP_VerifyInit(ctx, type);
ret = EVP_VerifyUpdate(ctx, data, len);
if (ret != 1) {
return -1;
}
ret = EVP_VerifyFinal(ctx, sig, siglen, key);
if (ret != 1) {
return -1;
}
EVP_MD_CTX_free(ctx); /* Модернизировано: cleanup -> free */
return 0;
}
static DummySetupPackage *
create_dummy(struct dummy_package *dp)
{
DummySetupPackage *d;
d = malloc(sizeof (DummySetupPackage));
if (d == NULL) {
return NULL;
}
dummy_setup_package__init(d);
/*ProtobufCBinaryData seed;*/
/*fixed32 size;*/
/*fixed32 flags;*/
assert(SYMMETRIC_CIPHER_KEY_LEN >= SHA_DIGEST_LENGTH);
d->seed.len = SYMMETRIC_CIPHER_KEY_LEN;
d->seed.data = dp->seed;
/* d->size will be set later on */
/* d->flags will be set later on */
return d;
}
static SetupPackage *
sps_to_SetupPackage(struct setup_path *path, int package_index, int nround)
{
SetupPackage *sp;
struct setup_package *ssp;
uint32_t i;
cleanup_stack_init;
assert(nround == 1 || nround == 2);
ssp = &path->sps[package_index];
sp = calloc(1, sizeof (SetupPackage));
if (sp == NULL) {
return NULL;
}
cleanup_stack_push(free, sp);
setup_package__init(sp);
sp->dummies = calloc(ssp->ndummies, sizeof (DummySetupPackage *));
if (sp->dummies == NULL) {
cleanup_stack_free_all();
return NULL;
}
/*required string prev_ip = 1;*/
/*required string next_ip = 2;*/
/*required uint32 prev_port = 3;*/
/*required uint32 next_port = 4;*/
/*required bytes prev_id = 5;*/
/*required bytes next_id = 6;*/
/*required bytes prev_communication_certificate_flat = 7;*/
/*required bytes next_communication_certificate_flat = 8;*/
/*required bytes construction_certificate_flat = 9;*/
/*repeated dummy_setup_package dummies = 10;*/
/*required uint32 nkeys = 11;*/
/*required bytes key_seed = 12;*/
/*required bytes replacement_seed = 13;*/
/*required bytes key_salt = 14;*/
/*required uint32 flags = 15;*/
/*required bytes hash = 16;*/
/*required bytes external_hash = 17;*/
/*optional string ap_adress = 18;*/
/*optional bytes routing_table_entry = 19;*/
sp->prev_ip = ssp->prev_ip;
if ((ssp->flags & T_NODE) && (ssp->flags & X_NODE) && ! path->is_reverse_path && nround == 2) {
static char emptystring[] = {'"', '"', '\0'};
sp->next_ip = emptystring; /*set next_ip to 0 */
} else {
sp->next_ip = ssp->next_ip;
}
sp->prev_port = ssp->prev_port;
sp->next_port = ssp->next_port;
sp->prev_id.len = SHA_DIGEST_LENGTH;
sp->prev_id.data = ssp->prev_id;
sp->next_id.len = SHA_DIGEST_LENGTH;
if ((ssp->flags & T_NODE) && (ssp->flags & X_NODE) && ! path->is_reverse_path && nround == 2) {
bzero(ssp->next_id, SHA_DIGEST_LENGTH); /* set next_id to all zero */
}
sp->next_id.data = ssp->next_id;
sp->next_communication_certificate_flat.len = ssp->next_communication_certificate_flat->len;
sp->next_communication_certificate_flat.data = ssp->next_communication_certificate_flat->data;
sp->prev_communication_certificate_flat.len = ssp->prev_communication_certificate_flat->len;
sp->prev_communication_certificate_flat.data = ssp->prev_communication_certificate_flat->data;
sp->construction_certificate_flat.len = path->construction_certificate_len;
sp->construction_certificate_flat.data = path->construction_certificate_data;
sp->n_dummies = ssp->ndummies;
for (i = 0; i < sp->n_dummies; i++) {
sp->dummies[i] = create_dummy(&ssp->dummies[i]);
if (sp->dummies[i] == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, sp->dummies[i]);
}
sp->nkeys = ssp->nkeys;
sp->key_seed.len = SYMMETRIC_CIPHER_KEY_LEN;
sp->key_seed.data = ssp->startkey;
sp->replacement_seed.len = SYMMETRIC_CIPHER_KEY_LEN;
sp->replacement_seed.data = ssp->replaceseed;
sp->key_salt.len = SYMMETRIC_CIPHER_KEY_LEN;
sp->key_salt.data = ssp->salt;
sp->flags = ssp->flags;
sp->hash.len = SHA_DIGEST_LENGTH;
/* hash will be set later */
sp->external_hash.len = SHA_DIGEST_LENGTH;
/* external_hash will be set later */
if ((ssp->flags & T_NODE) && path->entrypath && nround == 2) {
int ret;
/* add routing table entry and own ap adress */
assert(sizeof (path->ap.s6_addr) == 16);
sp->has_ap_adress = 1;
sp->ap_adress.data = path->ap.s6_addr;
sp->ap_adress.len = 16;
ret = add_routing_table_entry(sp, &path->ap, &path->entry_ip, 1);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
}
/* no free all, since we want to keep all our allocs in case of success */
return sp;
}
static void
setup_package_free(SetupPackage *s)
{
uint32_t i;
if (s->dummies != NULL) {
for (i = 0; i < s->n_dummies; i++) {
if (s->dummies[i] != NULL) {
free(s->dummies[i]);
}
}
free(s->dummies);
}
if (s->hash.data != NULL) {
free(s->hash.data);
}
if (s->external_hash.data != NULL) {
free(s->external_hash.data);
}
if (s->rte != NULL) {
if (s->rte->ip_adresses != NULL) {
free(s->rte->ip_adresses);
}
if (s->rte->ports != NULL) {
free(s->rte->ports);
}
free(s->rte);
}
free(s);
}
static int
calculate_expected_size(uint32_t size, EVP_PKEY *pubkey)
{
return (((size / SYMMETRIC_CIPHER_BLOCK_SIZE) *
SYMMETRIC_CIPHER_BLOCK_SIZE + EVP_PKEY_size(pubkey) +
SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_IV_LEN + 2 * 4) /
SYMMETRIC_CIPHER_BLOCK_SIZE) * SYMMETRIC_CIPHER_BLOCK_SIZE +
SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_IV_LEN +
RSA_SIGN_LEN;
}
struct tracking_info {
int nentries;
uint8_t endhash[SHA_DIGEST_LENGTH];
struct tracking_entry *entries;
};
struct tracking_entry {
struct tracking_entry *next;
struct tracking_entry *prev;
uint8_t hash[SHA_DIGEST_LENGTH];
};
static void
free_tracking_info(struct tracking_info *t)
{
if (t->entries != NULL) {
free(t->entries);
}
free(t);
}
static struct tracking_info *
new_tracking_info(int nnodes)
{
struct tracking_info *t = calloc(1, sizeof (struct tracking_info));
if (t == NULL) {
return NULL;
}
t->nentries = nnodes;
t->entries = calloc(nnodes, sizeof (struct tracking_entry));
if (t->entries == NULL) {
free(t);
return NULL;
}
return t;
}
static uint8_t *
generate_dummy_payload(uint8_t *seed, int size)
{
uint8_t *buf;
struct rc4_rand *r;
buf = malloc(size);
if (buf == NULL) {
return NULL;
}
r = rc4_rand_init(seed, SYMMETRIC_CIPHER_KEY_LEN);
if (r == NULL) {
free(buf);
return NULL;
}
rc4_rand_bytes(r, buf, size);
rc4_rand_free(r);
return buf;
}
/*create and destroy dummy packages keep track of their hashes + which one are valid for which node */
static struct tracking_info *
create_dummy_package_information(SetupPackage **sps, int nnodes)
{
struct tracking_info *t;
struct tracking_entry *help1, *help2, *tmp, curlist, addlist;
int i, cur_dummies, add, delete;
uint32_t j;
uint8_t *generated;
LIST_init(&curlist);
LIST_init(&addlist);
t = new_tracking_info(nnodes);
if (t == NULL) {
return NULL;
}
cur_dummies = 0;
for (i = 0; i < nnodes; i++) {
bzero(t->entries[i].hash, SHA_DIGEST_LENGTH);
LIST_for_all(&curlist, help1, help2){
xor(t->entries[i].hash, help1->hash, SHA_DIGEST_LENGTH);
}
for (j = 0; j < sps[i]->n_dummies; j++) {
if (LIST_is_empty(&curlist)) {
add = 1;
} else {
add = rand_range(0, 2);
}
if (add) {
generated = generate_dummy_payload(sps[i]->dummies[j]->seed.data, sps[i]->dummies[j]->size);
if (generated == NULL) {
LIST_clear(&addlist, help1);
LIST_clear(&curlist, help1);
free_tracking_info(t);
return NULL;
}
tmp = calloc(1, sizeof (struct tracking_entry));
if (tmp == NULL) {
free(generated);
LIST_clear(&addlist, help1);
LIST_clear(&curlist, help1);
free_tracking_info(t);
return NULL;
}
sps[i]->dummies[j]->flags = DUMMY_INSERT;
SHA1(generated, sps[i]->dummies[j]->size, tmp->hash);
free(generated);
LIST_insert(&addlist, tmp);
} else {
assert(cur_dummies);
delete = rand_range(0, cur_dummies);
LIST_for_all(&curlist, help1, help2) {
if (!delete) {
break;
}
delete--;
}
assert(sps[i]->dummies[j]->seed.len <= SYMMETRIC_CIPHER_KEY_LEN);
memcpy(sps[i]->dummies[j]->seed.data, help1->hash, SHA_DIGEST_LENGTH);
sps[i]->dummies[j]->flags = DUMMY_DELETE;
LIST_remove(help1);
free(help1);
cur_dummies--;
}
}
LIST_for_all(&addlist, help1, help2) {
LIST_remove(help1);
LIST_insert(&curlist, help1);
cur_dummies++;
}
assert(LIST_is_empty(&addlist));
assert(cur_dummies >= 0);
}
assert(LIST_is_empty(&addlist));
LIST_for_all(&curlist, help1, help2){
xor(t->endhash, help1->hash, SHA_DIGEST_LENGTH);
}
LIST_clear(&curlist, help1);
return t;
}
static void
add_external_hash(SetupPackage *sp, const uint8_t *replaced_hashes, const uint8_t *original_hashes, int idx, const struct tracking_info *tracking, int nnodes)
{
int i;
bzero(sp->external_hash.data, SHA_DIGEST_LENGTH);
for (i = nnodes - 1; i > idx; i--) {
xor(sp->external_hash.data, original_hashes + i * SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH);
}
for (i = 0; i < idx; i++) {
xor(sp->external_hash.data, replaced_hashes + i * SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH);
}
xor(sp->external_hash.data, tracking->entries[idx].hash, SHA_DIGEST_LENGTH);
}
static uint8_t *
generate_replaced_hashes(SetupPackage **sps, const uint32_t *sizes, int nnodes)
{
int i;
uint8_t *h, *generated;
h = malloc(nnodes * SHA_DIGEST_LENGTH);
if (h == NULL) {
return NULL;
}
for (i = 0; i < nnodes ; i++) {
generated = generate_dummy_payload(sps[i]->replacement_seed.data, sizes[i]);
if (generated == NULL) {
free(h);
return NULL;
}
SHA1(generated, sizes[i], h + i * SHA_DIGEST_LENGTH);
free(generated);
}
return h;
}
static uint8_t *
pack_setup_array(const uint8_t *id, uint8_t **slots, const uint32_t *sizes, int num_slots, uint32_t *outsize, int randomize)
{
uint32_t *mixed_sizes, size_tmp, ret, total;
uint8_t **mixed_slots, *slot_tmp, *out;
int i, pos;
SetupArray a = SETUP_ARRAY__INIT;
cleanup_stack_init;
mixed_sizes = malloc(num_slots * sizeof (uint32_t));
if (mixed_sizes == NULL) {
return NULL;
}
cleanup_stack_push(free, mixed_sizes);
mixed_slots = malloc(num_slots * sizeof (uint8_t *));
if (mixed_slots== NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, mixed_slots);
memcpy(mixed_slots, slots, num_slots * sizeof(uint8_t *));
memcpy(mixed_sizes, sizes, num_slots * sizeof(uint32_t));
if (randomize) {
for (i = 0; i < num_slots; i++) {
pos = rand_range(0, num_slots);
if (pos == i) {
continue;
}
size_tmp = mixed_sizes[i];
slot_tmp = mixed_slots[i];
mixed_sizes[i] = mixed_sizes[pos];
mixed_slots[i] = mixed_slots[pos];
mixed_sizes[pos] = size_tmp;
mixed_slots[pos] = slot_tmp;
}
}
a.n_slots = num_slots;
a.slots = malloc (num_slots * sizeof (ProtobufCBinaryData));
if (a.slots == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, a.slots);
for (i = 0; i < num_slots; i++) {
a.slots[i].len = mixed_sizes[i];
a.slots[i].data = mixed_slots[i];
}
total = setup_array__get_packed_size(&a);
out = malloc(total + SHA_DIGEST_LENGTH);
if (out == NULL) {
cleanup_stack_free_all();
return NULL;
}
ret = setup_array__pack(&a, out + SHA_DIGEST_LENGTH);
assert(ret == total);
memcpy(out, id, SHA_DIGEST_LENGTH);
*outsize = total + SHA_DIGEST_LENGTH;
cleanup_stack_free_all();
return out;
}
static uint8_t *
create_setup_array(struct setup_path *path, SetupPackage **sps, const uint32_t *expected_sizes, uint32_t *sizes, const struct tracking_info *tracking_info, uint32_t *outsize, int nround)
{
EVP_PKEY *privkey;
uint8_t *replaced_hashes, *original_hashes, **packed, *out;
int ret, i;
cleanup_stack_init;
privkey = EVP_PKEY_new();
if (privkey == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(EVP_PKEY_free, privkey);
/* Модернизировано для OpenSSL 3.0+ */
ret = EVP_PKEY_assign_RSA(privkey, path->construction_certificate);
if (ret != 1) {
cleanup_stack_free_all();
return NULL;
}
packed = malloc(path->nnodes * sizeof(uint8_t *));
if (packed == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, packed);
replaced_hashes = generate_replaced_hashes(sps, expected_sizes, path->nnodes);
if (replaced_hashes == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, replaced_hashes);
original_hashes = malloc(path->nnodes * SHA_DIGEST_LENGTH);
if (original_hashes == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, original_hashes);
/* add external hashes to package, pack it and encrypt asymmetrically
* with receiving nodes public key
* loop is reverse */
for (i = path->nnodes - 1; i >= 0; i--) {
EVP_PKEY *pubkey;
int outlen;
uint8_t sig[RSA_SIGN_LEN], *oldcontents;
pubkey = X509_get_pubkey(path->nodes[i].construction_certificate);
if (pubkey == NULL) {
cleanup_stack_free_all();
return NULL;
}
add_external_hash(sps[i], replaced_hashes, original_hashes, i, tracking_info, path->nnodes);
packed[i] = calloc(1, sizes[i]);
if (packed[i] == NULL) {
EVP_PKEY_free(pubkey);
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, packed[i]);
bzero(sps[i]->hash.data, SHA_DIGEST_LENGTH);
ret = setup_package__pack(sps[i], packed[i]);
if (ret == 0) {
cleanup_stack_free_all();
EVP_PKEY_free(pubkey);
return NULL;
}
SHA1(packed[i], sizes[i], sps[i]->hash.data);
ret = setup_package__pack(sps[i], packed[i]);
if (ret == 0) {
EVP_PKEY_free(pubkey);
cleanup_stack_free_all();
return NULL;
}
/* encrypt asymmetrically (iv and symm key are prepended) */
path->contents[i] = encrypt_setup_package_asymmetric(packed[i], sizes[i], pubkey, &outlen);
sizes[i] = outlen;
EVP_PKEY_free(pubkey);
if (path->contents[i] == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, path->contents[i]);
if (nround == 1) {
path->contents[i] = encrypt_symmetric(path->contents[i], sizes[i], path->sps[i].prev_id, &outlen);
} else {
path->contents[i] = encrypt_symmetric(path->contents[i], sizes[i], path->sps[i].old_prev_id, &outlen);
}
if (path->contents[i] == NULL) {
cleanup_stack_free_all();
return NULL;
}
sizes[i] = outlen;
ret = sign_data(sig, path->contents[i], sizes[i], privkey);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
sizes[i] += RSA_SIGN_LEN;
assert(expected_sizes[i] == sizes[i]);
oldcontents = path->contents[i];
path->contents[i] = malloc(sizes[i]);
if (path->contents[i] == NULL) {
cleanup_stack_free_all();
return NULL;
}
/* prepend signature */
memcpy(path->contents[i], sig, RSA_SIGN_LEN);
memcpy(path->contents[i] + RSA_SIGN_LEN, oldcontents, sizes[i] - RSA_SIGN_LEN);
free(oldcontents);
SHA1(path->contents[i], sizes[i], original_hashes + i * SHA_DIGEST_LENGTH);
}
bzero(path->endhash, SHA_DIGEST_LENGTH);
memcpy(path->endhash, tracking_info->endhash, SHA_DIGEST_LENGTH);
for (i = 0; i < path->nnodes; i++) {
xor(path->endhash, replaced_hashes + i * SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH);
}
if (nround == 1) {
out = pack_setup_array(path->sps[0].prev_id, path->contents, sizes, path->nnodes, outsize, 1);
} else {
out = pack_setup_array(path->sps[0].old_prev_id, path->contents, sizes, path->nnodes, outsize, 1);
}
cleanup_stack_free_all();
return out;
}
static uint8_t *
pack_setup_packages(struct setup_path *path, uint32_t *outsize, int nround)
{
int i;
SetupPackage **sps;
uint32_t *sizes, *expected_sizes;
uint8_t *out;
struct tracking_info *tracking;
EVP_PKEY *pubkey;
cleanup_stack_init;
assert(nround == 1 || nround == 2);
sps = malloc(path->nnodes * sizeof (SetupPackage *));
if (sps == NULL) {
return NULL;
}
cleanup_stack_push(free, sps);
sizes = malloc(path->nnodes * sizeof (uint32_t));
if (sizes == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, sizes);
expected_sizes = malloc(path->nnodes * sizeof (uint32_t));
if (expected_sizes == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, expected_sizes);
for (i = 0; i < path->nnodes; i++) {
pubkey = X509_get_pubkey(path->nodes[i].construction_certificate);
if (pubkey == NULL) {
cleanup_stack_free_all();
return NULL;
}
sps[i] = sps_to_SetupPackage(path, i, nround);
if (sps[i] == NULL) {
EVP_PKEY_free(pubkey);
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(setup_package_free, sps[i]);
sizes[i] = setup_package__get_packed_size(sps[i]);
expected_sizes[i] = calculate_expected_size(sizes[i], pubkey);
EVP_PKEY_free(pubkey);
}
for (i = 0; i < path->nnodes; i++) {
uint32_t j;
for (j = 0; j < sps[i]->n_dummies; j++) {
sps[i]->dummies[j]->size = expected_sizes[rand_range(0, path->nnodes)];
}
assert(sps[i]->hash.data == NULL);
sps[i]->hash.data = malloc(SHA_DIGEST_LENGTH);
if (sps[i]->hash.data == NULL) {
cleanup_stack_free_all();
return NULL;
} /* do notfree hashes -> sps_delete will do it */
assert(sps[i]->external_hash.data == NULL);
sps[i]->external_hash.data = malloc(SHA_DIGEST_LENGTH);
if (sps[i]->external_hash.data == NULL) {
cleanup_stack_free_all();
return NULL;
} /* do not free external_hashes -> sps_delete will do it */
}
tracking = create_dummy_package_information(sps, path->nnodes);
if (tracking == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free_tracking_info, tracking);
out = create_setup_array(path, sps, expected_sizes, sizes, tracking, outsize, nround);
cleanup_stack_free_all();
return out;
}
static int
decide_about_participation(uint32_t flags)
{
(void) flags;
return 0;
}
static void
sa_array_cleanup_helper(SetupArray *a)
{
setup_array__free_unpacked(a, NULL);
}
static void
setup_package_cleanup_helper(SetupPackage *s)
{
setup_package__free_unpacked(s, NULL);
}
static int
validate_routing_table_entry(RoutingTableEntry *r)
{
uint32_t i;
if (r->ap_adress.data == NULL) {
return -1;
}
if (r->ap_adress.len != 16) {
return -1;
}
if (r->n_ip_adresses < 1) {
return -1;
}
for (i = 0; i < r->n_ip_adresses; i++) {
if (r->ip_adresses[i] == NULL) {
return -1;
}
}
return 0;
}
static int
validate_sp(const SetupPackage *sp)
{
uint32_t i;
if (sp->prev_id.len != SHA_DIGEST_LENGTH) {
return -1;
}
if (sp->next_id.len != SHA_DIGEST_LENGTH) {
return -1;
}
if (sp->key_seed.len != SYMMETRIC_CIPHER_KEY_LEN) {
return -1;
}
if (sp->key_salt.len != SYMMETRIC_CIPHER_KEY_LEN) {
return -1;
}
if (sp->replacement_seed.len != SYMMETRIC_CIPHER_KEY_LEN) {
return -1;
}
if (sp->hash.len != SHA_DIGEST_LENGTH) {
return -1;
}
if (sp->external_hash.len != SHA_DIGEST_LENGTH) {
return -1;
}
if (sp->prev_port>>16) {
return -1;
}
if (sp->next_port>>16) {
return -1;
}
if (sp->prev_communication_certificate_flat.len < 4) {
return -1;
}
if (sp->next_communication_certificate_flat.len < 4) {
return -1;
}
if (sp->construction_certificate_flat.len < 4) {
return -1;
}
for (i = 0; i < sp->n_dummies; i++) {
if (!(sp->dummies[i]->flags & (DUMMY_INSERT | DUMMY_DELETE))) {
return -1;
}
if (sp->dummies[i]->flags & DUMMY_INSERT && sp->dummies[i]->flags & DUMMY_DELETE) {
return -1;
}
if (sp->dummies[i]->seed.len != SYMMETRIC_CIPHER_KEY_LEN) {
return -1;
}
}
if (sp->flags & SUCCESS_FLAG && sp->flags & X_NODE) {
if (!sp->nkeys) {
return -1;
}
if (sp->key_seed.len != SYMMETRIC_CIPHER_KEY_LEN) {
return -1;
}
if (sp->key_salt.len != SYMMETRIC_CIPHER_KEY_LEN) {
return -1;
}
if (sp->flags & ENTRY_NODE) {
if (sp->ap_adress.len != 16) {
return -1;
}
if (sp->rte == NULL) {
return -1;
}
if (validate_routing_table_entry(sp->rte) != 0) {
return -1;
}
}
}
return 0;
}
static int
extract_rte_information(const RoutingTableEntry *r, struct conn_ctx *conn)
{
uint32_t ret;
conn->rte.len = routing_table_entry__get_packed_size(r);
conn->rte.data = malloc(conn->rte.len);
if (conn->rte.data == NULL) {
return -1;
}
ret = routing_table_entry__pack(r, conn->rte.data);
if (ret != conn->rte.len) {
return -1;
}
return 0;
}
static int
extract_slot_information(const SetupPackage *sp, struct conn_ctx *conn, int nround)
{
/* all allocs will be freed by conn_ctx_free in case of failure */
struct X509_flat x;
BIO *mem;
BUF_MEM bptr;
assert(nround == 1 || nround == 2);
memcpy(conn->prev_id, sp->prev_id.data, SHA_DIGEST_LENGTH);
memcpy(conn->next_id, sp->next_id.data, SHA_DIGEST_LENGTH);
conn->prev_ip = strdup(sp->prev_ip);
if (conn->prev_ip == NULL) {
return -1;
}
conn->next_ip = strdup(sp->next_ip);
if (conn->next_ip == NULL) {
return -1;
}
conn->prev_port = sp->prev_port;
conn->next_port = sp->next_port;
conn->flags = sp->flags;
x.len = sp->prev_communication_certificate_flat.len;
x.data = sp->prev_communication_certificate_flat.data;
conn->prev_communication_certificate = read_x509_from_x509_flat(&x);
x.len = sp->next_communication_certificate_flat.len;
x.data = sp->next_communication_certificate_flat.data;
conn->next_communication_certificate = read_x509_from_x509_flat(&x);
mem = BIO_new(BIO_s_mem());
if (mem == NULL) {
return -1;
}
bptr.data = (char *) sp->construction_certificate_flat.data;
bptr.length = sp->construction_certificate_flat.len;
BIO_set_mem_buf(mem, &bptr, BIO_NOCLOSE);
conn->construction_certificate = PEM_read_bio_RSAPublicKey(mem, NULL, NULL, NULL);
BIO_free(mem);
if (conn->construction_certificate == NULL) {
return -1;
}
if (nround == 2 && sp->flags & X_NODE) {
conn->keys = generate_conn_keys(sp->nkeys, sp->key_seed.data, sp->key_salt.data);
if (conn->keys == NULL) {
return -1;
}
if (sp->flags & T_NODE) {
if (sp->flags & X_NODE && sp->flags & ENTRY_NODE) {
assert(sp->ap_adress.len == 16);
memcpy(conn->ap.s6_addr, sp->ap_adress.data, 16);
if (extract_rte_information(sp->rte, conn) != 0) {
return -1;
}
}
}
}
return 0;
}
static int
validate_setup_array(const SetupArray *a)
{
uint32_t i;
if (a->n_slots < 1) {
return -1;
}
for (i = 0; i < a->n_slots; i++) {
if (a->slots[i].len < RSA_SIGN_LEN + SYMMETRIC_CIPHER_IV_LEN + 8) {
return -1;
}
}
return 0;
}
static int
check_external_hash(const SetupArray *a, const uint8_t *external_hash, uint32_t own_idx)
{
uint32_t i;
uint8_t hash[SHA_DIGEST_LENGTH], result[SHA_DIGEST_LENGTH];
bzero(result, SHA_DIGEST_LENGTH);
for (i = 0; i < a->n_slots; i++) {
if (i == own_idx) {
continue;
}
SHA1(a->slots[i].data, a->slots[i].len, hash);
xor(result, hash, SHA_DIGEST_LENGTH);
}
return memcmp(result, external_hash, SHA_DIGEST_LENGTH);
}
static int
contains_hash_of(const uint8_t *hashes, const uint8_t *data, int len, int nhashes)
{
int i;
uint8_t hash[SHA_DIGEST_LENGTH];
SHA1(data, len, hash);
for (i = 0; i < nhashes; i++) {
if (!memcmp(hashes + i * SHA_DIGEST_LENGTH, hash, SHA_DIGEST_LENGTH)) {
return 1;
}
}
return 0;
}
static uint8_t *
modify_setup_array(const SetupArray *a, const SetupPackage *sp, const uint8_t *id, uint32_t own_idx, uint32_t *outsize)
{
uint8_t *new, **slots, *delete_hashes;
uint32_t nslots, *sizes, i, adds, deletes, cnt;
cleanup_stack_init;
nslots = a->n_slots;
deletes = 0;
adds = 0;
for (i = 0; i < sp->n_dummies; i++) {
if (sp->dummies[i]->flags & DUMMY_INSERT) {
nslots++;
adds++;
} else {
nslots--;
deletes++;
}
}
slots = malloc(nslots * sizeof (uint8_t *));
if (slots == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, slots);
sizes = malloc(nslots * sizeof (uint32_t));
if (sizes == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, sizes);
delete_hashes = NULL;
if (deletes) {
delete_hashes = malloc(deletes * SHA_DIGEST_LENGTH);
if (delete_hashes == NULL) {
cleanup_stack_free_all();
return NULL;
}
cnt = 0;
cleanup_stack_push(free, delete_hashes);
for (i = 0; i < sp->n_dummies; i++) {
if (sp->dummies[i]->flags & DUMMY_DELETE) {
memcpy(delete_hashes + cnt * SHA_DIGEST_LENGTH, sp->dummies[i]->seed.data, SHA_DIGEST_LENGTH);
cnt++;
}
}
}
cnt = 0;
for (i = 0; i < a->n_slots; i++) {
if (own_idx == i) {
slots[cnt] = generate_dummy_payload(sp->replacement_seed.data, a->slots[own_idx].len);
if (slots[cnt] == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, slots[cnt]);
sizes[cnt] = a->slots[own_idx].len;
cnt++;
} else if (delete_hashes && contains_hash_of(delete_hashes, a->slots[i].data, a->slots[i].len, deletes)) {
continue;
} else {
slots[cnt] = a->slots[i].data;
sizes[cnt] = a->slots[i].len;
cnt++;
}
}
for (i = 0; i < sp->n_dummies; i++) {
if (sp->dummies[i]->flags & DUMMY_INSERT) {
slots[cnt] = generate_dummy_payload(sp->dummies[i]->seed.data, sp->dummies[i]->size);
if (slots[cnt] == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, slots[cnt]);
sizes[cnt] = sp->dummies[i]->size;
cnt++;
}
}
assert(cnt == nslots);
new = pack_setup_array(id, slots, sizes, nslots, outsize, 1);
cleanup_stack_free_all();
return new;
}
static SetupArray *
extract_setup_array(const uint8_t *sa, int sa_len, const uint8_t *id, const struct config *config, int *own_slot, SetupPackage **own_sp)
{
SetupArray *a;
int outlen, written1, written2, ret, own_idx;
uint32_t i, package_size, pubkey_size;
uint8_t *out, hash[SHA_DIGEST_LENGTH], *oldout, received_hash[SHA_DIGEST_LENGTH], sizedkey[SYMMETRIC_CIPHER_KEY_LEN];
SetupPackage *sp;
/* Модернизировано для OpenSSL 3.0+ */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
cleanup_stack_init;
pad_key(id, SHA_DIGEST_LENGTH, sizedkey, SYMMETRIC_CIPHER_KEY_LEN);
a = setup_array__unpack(NULL, sa_len, sa);
if (a == NULL) {
return NULL;
}
cleanup_stack_push(sa_array_cleanup_helper, a);
ret = validate_setup_array(a);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
sp = NULL;
own_idx = -1;
for (i = 0; i < a->n_slots; i++) {
package_size = a->slots[i].len - RSA_SIGN_LEN - SYMMETRIC_CIPHER_IV_LEN;
if (package_size % SYMMETRIC_CIPHER_BLOCK_SIZE || package_size - (int32_t) package_size) {
cleanup_stack_free_all();
return NULL;
}
out = malloc(package_size);
if (out == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, out);
/* EVP_CIPHER_CTX_init не нужен - ctx уже инициализирован через _new() */
ret = EVP_DecryptInit(ctx, EVP_aes_256_cbc(), sizedkey, a->slots[i].data + RSA_SIGN_LEN);
if (ret != 1) {
EVP_CIPHER_CTX_cleanup(ctx);
cleanup_stack_free_all();
return NULL;
}
ret = EVP_DecryptUpdate(ctx, out, &written1, a->slots[i].data + RSA_SIGN_LEN + SYMMETRIC_CIPHER_IV_LEN, package_size);
if (ret != 1) {
EVP_CIPHER_CTX_cleanup(ctx);
cleanup_stack_free_all();
return NULL;
}
ret = EVP_DecryptFinal(ctx, out + written1, &written2);
EVP_CIPHER_CTX_cleanup(ctx);
if (ret != 1) {
/* wrong package - padding mismatch */
cleanup_stack_pop(); /* out */
continue;
}
outlen = written1 + written2;
assert((outlen / SYMMETRIC_CIPHER_BLOCK_SIZE * SYMMETRIC_CIPHER_BLOCK_SIZE) + SYMMETRIC_CIPHER_BLOCK_SIZE == (int32_t) package_size);
if (outlen < SYMMETRIC_CIPHER_IV_LEN + SYMMETRIC_CIPHER_KEY_LEN) {
cleanup_stack_free_all();
return NULL;
}
/* EVP_CIPHER_CTX_init не нужен - ctx уже инициализирован через _new() */ /* reinit cipher for asymmetric decryption */
if (ret != 1) {
cleanup_stack_free_all();
return NULL;
}
pubkey_size = deserialize_32_t(out + 4);
ret = EVP_OpenInit(ctx, EVP_aes_256_cbc(), out + 2 * 4 + SYMMETRIC_CIPHER_IV_LEN, pubkey_size, out + 2 * 4, config->private_construction_key);
if (ret != 1) {
/* wrong package - key not revoverable */
/* bug in openssl doc - it is not the number of
* keybytes recovered returned if successful but
* constant 1 */
EVP_CIPHER_CTX_cleanup(ctx);
cleanup_stack_pop(); /* out */
continue;
}
oldout = out;
out = malloc((package_size / SYMMETRIC_CIPHER_BLOCK_SIZE) * SYMMETRIC_CIPHER_BLOCK_SIZE + SYMMETRIC_CIPHER_BLOCK_SIZE);
if (out == NULL) {
EVP_CIPHER_CTX_cleanup(ctx);
cleanup_stack_free_all();
return NULL;
}
ret = EVP_OpenUpdate(ctx, out, &written1, oldout + 2 * 4 + SYMMETRIC_CIPHER_IV_LEN + pubkey_size, deserialize_32_t(oldout));
cleanup_stack_pop(); /* old_out */
cleanup_stack_push(free, out);
if (ret != 1) {
EVP_CIPHER_CTX_cleanup(ctx);
cleanup_stack_free_all();
return NULL;
}
ret = EVP_OpenFinal(ctx, out + written1, &written2);
EVP_CIPHER_CTX_cleanup(ctx);
if (ret != 1) {
cleanup_stack_pop(); /* out */
continue; /* wrong package */
}
outlen = written1 + written2;
/* candidate for the right package - try to unpack it and check the hash */
sp = setup_package__unpack(NULL, outlen, out);
if (sp == NULL) {
cleanup_stack_pop(); /* out */
continue; /* wrong package */
}
ret = validate_sp(sp);
if (ret != 0) {
setup_package_cleanup_helper(sp);
cleanup_stack_free_all();
return NULL;
}
memcpy(received_hash, sp->hash.data, SHA_DIGEST_LENGTH);
bzero(out, outlen);
bzero(sp->hash.data, SHA_DIGEST_LENGTH);
assert(setup_package__get_packed_size(sp) == (uint32_t) outlen);
setup_package__pack(sp, out);
SHA1(out, outlen, hash);
if (memcmp(hash, received_hash, SHA_DIGEST_LENGTH)) {
setup_package_cleanup_helper(sp);
cleanup_stack_pop(); /* out */
continue; /* wrong package */
}
memcpy(sp->hash.data, received_hash, SHA_DIGEST_LENGTH);
cleanup_stack_pop(); /* out */
own_idx = i;
break;
}
if (own_idx == -1) {
cleanup_stack_free_all();
return NULL;
}
*own_slot = own_idx;
*own_sp = sp;
cleanup_stack_save_bottom(1); /* save array */
return a;
}
uint8_t *
handle_first_round_setup_array(const struct config *config, const uint8_t *sa, int sa_len, const uint8_t *id, const char *from_ip, struct conn_ctx *conn, uint32_t *outsize)
{
SetupArray *a;
SetupPackage *sp;
int own_idx, ret;
uint8_t *new;
cleanup_stack_init;
a = extract_setup_array(sa, sa_len, id, config, &own_idx, &sp);
if (a == NULL) {
return NULL;
}
cleanup_stack_push(setup_package_cleanup_helper, sp);
cleanup_stack_push(sa_array_cleanup_helper, a);
if (memcmp(sp->prev_id.data, id, SHA_DIGEST_LENGTH)) {
cleanup_stack_free_all();
return NULL;
}
if (strcmp(from_ip, sp->prev_ip)) {
cleanup_stack_free_all();
return NULL;
}
ret = check_external_hash(a, sp->external_hash.data, own_idx);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
ret = extract_slot_information(sp, conn, 1);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
ret = decide_about_participation(conn->flags);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
new = modify_setup_array(a, sp, sp->next_id.data, own_idx, outsize);
cleanup_stack_free_all();
return new;
}
uint8_t *
handle_second_round_setup_array(const struct config *config, const uint8_t *sa, int sa_len, const uint8_t *id, const struct conn_ctx *oldconn, struct conn_ctx *conn, uint32_t *outsize)
{
SetupArray *a;
SetupPackage *sp;
int own_idx, ret;
uint8_t *new;
EVP_PKEY *key;
cleanup_stack_init;
if (memcmp(oldconn->prev_id, id, SHA_DIGEST_LENGTH)) {
return NULL;
}
a = extract_setup_array(sa, sa_len, id, config, &own_idx, &sp);
if (a == NULL) {
return NULL;
}
cleanup_stack_push(setup_package_cleanup_helper, sp);
cleanup_stack_push(sa_array_cleanup_helper, a);
ret = check_external_hash(a, sp->external_hash.data, own_idx);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
key = EVP_PKEY_new();
if (key == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(EVP_PKEY_free, key);
/* Модернизировано для OpenSSL 3.0+ */
ret = EVP_PKEY_assign_RSA(key, oldconn->construction_certificate);
if (ret == 0) {
cleanup_stack_free_all();
return NULL;
}
ret = check_signed_data(a->slots[own_idx].data, RSA_SIGN_LEN, a->slots[own_idx].data + RSA_SIGN_LEN, a->slots[own_idx].len - RSA_SIGN_LEN, key);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
if (! (conn->flags | SUCCESS_FLAG)) {
cleanup_stack_free_all();
return NULL;
}
ret = extract_slot_information(sp, conn, 2);
if (ret != 0) {
cleanup_stack_free_all();
return NULL;
}
new = modify_setup_array(a, sp, oldconn->next_id, own_idx, outsize);
cleanup_stack_free_all();
return new;
}
static int
send_x_package(struct setup_path *path, const struct config *config)
{
int ret, x_idx;
uint8_t preface[4];
/* find first x_node */
for (x_idx = 0; x_idx < path->nnodes; x_idx++) {
if (path->sps[x_idx].flags & X_NODE) {
break;
}
}
free_ssl_connection(path->ssl_conn);
path->ssl_conn = create_ssl_connection(path->nodes[x_idx].ip, path->nodes[x_idx].port, config->communication_certificate, config->private_communication_key);
if (path->ssl_conn == NULL) {
return -1;
}
if (! X509_compare(path->ssl_conn->peer_cert, path->nodes[x_idx].communication_certificate)) {
return -1;
}
serialize_32_t(SHA_DIGEST_LENGTH, preface);
ret = ssl_write(path->ssl_conn->ssl, preface, 4);
if (ret != 0) {
return -1;
}
ret = ssl_write(path->ssl_conn->ssl, path->sps[x_idx].prev_id, SHA_DIGEST_LENGTH);
if (ret != 0) {
return -1;
}
return 0;
}
static int
send_setup_array(struct setup_path *path, const struct config *config, const uint8_t *array, uint32_t array_len, int nround)
{
int ret;
uint8_t preface[4];
assert(nround == 1 || nround == 2);
serialize_32_t(array_len, preface);
if (nround == 1) {
path->ssl_conn = create_ssl_connection(path->nodes[0].ip, path->nodes[0].port, config->communication_certificate, config->private_communication_key);
if (path->ssl_conn == NULL) {
return -1;
}
if (! X509_compare(path->ssl_conn->peer_cert, path->nodes[0].communication_certificate)) {
return -1;
}
}
ret = ssl_write(path->ssl_conn->ssl, preface, 4);
if (ret != 0) {
return -1;
}
ret = ssl_write(path->ssl_conn->ssl, array, array_len);
if (ret != 0) {
return -1;
}
return 0;
}
static int
check_package(const uint8_t *endhash, const uint8_t *endid, const uint8_t *package, uint32_t len)
{
SetupArray *a;
uint8_t hash[SHA_DIGEST_LENGTH], result[SHA_DIGEST_LENGTH];
uint32_t i;
int ret;
cleanup_stack_init;
if (memcmp(endid, package, SHA_DIGEST_LENGTH)) {
return -1;
}
a = setup_array__unpack(NULL, len - SHA_DIGEST_LENGTH, package + SHA_DIGEST_LENGTH);
if (a == NULL) {
return -1;
}
cleanup_stack_push(sa_array_cleanup_helper, a);
ret = validate_setup_array(a);
if (ret != 0) {
cleanup_stack_free_all();
return -1;
}
bzero(result, SHA_DIGEST_LENGTH);
for (i = 0; i < a->n_slots; i++) {
/* Модернизировано: SHA заменен на SHA1 для OpenSSL 3.0+ */
SHA1(a->slots[i].data, a->slots[i].len, hash);
xor(result, hash, SHA_DIGEST_LENGTH);
}
if (!memcmp(result, endhash, SHA_DIGEST_LENGTH)) {
cleanup_stack_free_all();
return -1;
}
cleanup_stack_free_all();
return 0;
}
static struct xkeys **
generate_path_keys(const struct setup_path *path)
{
int i, cnt;
struct xkeys **keys;
cleanup_stack_init;
keys = calloc(path->nxnodes, sizeof (struct xkeys *));
if (keys == NULL) {
return NULL;
}
cnt = 0;
cleanup_stack_push(free, keys);
for(i = 0; i < path->nnodes; i++) {
if (path->sps[i].flags & X_NODE) {
keys[cnt] = generate_conn_keys(path->sps[i].nkeys, path->sps[i].startkey, path->sps[i].salt);
if (keys[cnt] == NULL) {
cleanup_stack_free_all();
return NULL;
}
cleanup_stack_push(free, keys[cnt]->ivs);
cleanup_stack_push(free, keys[cnt]->keys);
cleanup_stack_push(free, keys[cnt]);
cnt++;
}
}
assert(cnt == path->nxnodes);
if (path->is_reverse_path) {
reverse_array(keys, path->nxnodes, sizeof (struct xkeys *));
}
/* keep allocs in case of success */
return keys;
}
static struct path *
construct_path(const struct config *config, int want_entrypath, int reserve_ap)
{
int ret, i, x_idx;
uint32_t outsize;
uint8_t *package, *array;
struct setup_path *path;
struct path *p;
struct ssl_connection *path_conn;
struct awaited_connection *wait;
if (config->nynodes < 3 * config->nxnodes - 2 || config->nxnodes +
config->nynodes > 0xef) {
return NULL;
}
path = create_struct_setup_path(config, want_entrypath, reserve_ap);
if (path == NULL) {
return NULL;
}
ret = get_nodes_from_db(path->nodes, config->nxnodes + config->nynodes);
if (ret) {
delete_struct_setup_path(path);
return NULL;
}
build_xy_path(path);
ret = generate_path_construction_keys(path);
if (ret) {
delete_struct_setup_path(path);
return NULL;
}
generate_setup_packages(config, path, 1);
array = pack_setup_packages(path, &outsize, 1);
if (array == NULL) {
delete_struct_setup_path(path);
return NULL;
}
wait = register_wait_connection(path->sps[path->nnodes - 2].next_ip, path->sps[path->nnodes - 1].next_id);
if (wait == NULL) {
delete_struct_setup_path(path);
return NULL;
}
ret = send_setup_array(path, config, array, outsize, 1);
free(array);
if (ret) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
ret = wait_for_connection(wait, TMOUT);
if (ret != 0) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
if (! X509_compare_mixed(path->sps[path->nnodes - 2].next_communication_certificate_flat, wait->incoming_conn->peer_cert)) {
delete_struct_setup_path(path);
return NULL;
}
if (wait->len < SHA_DIGEST_LENGTH) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
ret = check_package(path->endhash, path->sps[path->nnodes - 1].next_id, wait->incoming_package, wait->len);
if (ret != 0) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
generate_setup_packages(config, path, 2);
array = pack_setup_packages(path, &outsize, 2);
if (array == NULL) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
ret = send_setup_array(path, config, array, outsize, 2);
free(array);
if (ret) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
package = read_package(wait->incoming_conn->ssl, &outsize);
if (package == NULL) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
if (outsize < SHA_DIGEST_LENGTH) {
free_awaited_connection(wait);
free(package);
delete_struct_setup_path(path);
return NULL;
}
ret = check_package(path->endhash, path->sps[path->nnodes - 1].next_id, package, outsize);
free(package);
if (ret != 0) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
for (i = path->nnodes - 1; i >= 0; i--) {
if (path->sps[i].flags & X_NODE) {
break;
}
}
free_awaited_connection(wait);
if (path->is_reverse_path) {
wait = register_wait_connection(path->sps[i - 1].next_ip, path->sps[i].next_id);
if (wait == NULL) {
delete_struct_setup_path(path);
return NULL;
}
}
ret = send_x_package(path, config);
if (ret != 0) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
return NULL;
}
p = new_path();
if (p == NULL) {
delete_struct_setup_path(path);
free_awaited_connection(wait);
return NULL;
}
p->ap = path->ap;
if (path->is_reverse_path) {
for (x_idx = path->nnodes - 1; x_idx >= 0; x_idx--) {
if (path->sps[x_idx].flags & X_NODE) {
memcpy(p->peer_id, path->sps[x_idx].next_id, SHA_DIGEST_LENGTH);
p->peer_ip = strdup(path->nodes[x_idx].ip);
if (p->peer_ip == NULL) {
delete_struct_setup_path(path);
free_awaited_connection(wait);
free_path(p);
return NULL;
}
p->peer_port = (path->nodes[x_idx].port);
break;
}
}
ret = wait_for_connection(wait, TMOUT);
if (ret != 0) {
delete_struct_setup_path(path);
free_awaited_connection(wait);
free_path(p);
return NULL;
}
if (! X509_compare(path->nodes[x_idx].communication_certificate, wait->incoming_conn->peer_cert)) {
free_awaited_connection(wait);
delete_struct_setup_path(path);
free_path(p);
return NULL;
}
path_conn = wait->incoming_conn;
wait->incoming_conn = NULL;
free_awaited_connection(wait);
p->xkeys = generate_path_keys(path);
delete_struct_setup_path(path);
} else {
for (x_idx = 0; x_idx < path->nnodes; x_idx++) {
if (path->sps[x_idx].flags & X_NODE) {
memcpy(p->peer_id, path->sps[x_idx].prev_id, SHA_DIGEST_LENGTH);
p->peer_ip = strdup(path->nodes[x_idx].ip);
if (p->peer_ip == NULL) {
free_path(p);
return NULL;
}
p->peer_port = (path->nodes[x_idx].port);
break;
}
}
p->xkeys = generate_path_keys(path);
path_conn = path->ssl_conn;
delete_struct_setup_path2(path, 1);
}
assert(p->xkeys);
p->conn = path_conn;
p->nkeys = config->nxnodes;
p->is_entrypath = want_entrypath;
return p;
}
struct path *
construct_entry_path(const struct config *config)
{
return construct_path(config, 1, 0);
}
struct path *
construct_reserve_ap_path(const struct config *config)
{
return construct_path(config, 0, 1);
}
struct path *
construct_exit_path(const struct config *config)
{
return construct_path(config, 0, 0);
}
void
free_path(struct path *path)
{
int i;
if (path->conn != NULL) {
free_ssl_connection(path->conn);
}
if (path->xkeys != NULL) {
for (i = 0; i < path->nkeys; i++) {
if (path->xkeys[i]->keys != NULL) {
free(path->xkeys[i]->keys);
}
if (path->xkeys[i]->ivs != NULL) {
free(path->xkeys[i]->ivs);
}
free(path->xkeys[i]);
}
free(path->xkeys);
}
if (path->peer_ip != NULL) {
free(path->peer_ip);
}
free(path);
}