header_encryption.c (n2n-2.8) | : | header_encryption.c (n2n-3.0) | ||
---|---|---|---|---|
/** | /** | |||
* (C) 2007-20 - ntop.org and contributors | * (C) 2007-21 - ntop.org and contributors | |||
* | * | |||
* This program is free software; you can redistribute it and/or modify | * This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 3 of the License, or | * the Free Software Foundation; either version 3 of the License, or | |||
* (at your option) any later version. | * (at your option) any later version. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | * GNU General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | |||
* along with this program; if not see see <http://www.gnu.org/licenses/> | * along with this program; if not see see <http://www.gnu.org/licenses/> | |||
* | * | |||
*/ | */ | |||
#include "n2n.h" | #include "n2n.h" | |||
#define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out) | #define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out) | |||
/* ********************************************************************** */ | int packet_header_decrypt (uint8_t packet[], uint16_t packet_len, | |||
char *community_name, | ||||
uint32_t packet_header_decrypt (uint8_t packet[], uint16_t packet_len, | he_context_t *ctx, he_context_t *ctx_iv, | |||
char * community_name, he_context_t * ctx, | uint64_t *stamp) { | |||
he_context_t * ctx_iv, uint64_t * stamp, uint16_t | ||||
* checksum) { | // try community name as possible key and check for magic bytes "n2__" | |||
uint32_t magic = 0x6E320000; | ||||
// assemble IV | uint32_t test_magic; | |||
// the last four are ASCII "n2n!" and do not get overwritten | uint32_t checksum_high = 0; | |||
uint8_t iv[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
0x00, 0x00, 0x00, 0x00, 0x6E, 0x32, 0x6E, 0x21 }; | // check for magic | |||
// the first 96 bits of the packet get padded with ASCII "n2n!" | // so, as a first step, decrypt last 4 bytes from where originally the commu | |||
// to full 128 bit IV | nity name would be | |||
memcpy (iv, packet, 12); | speck_ctr((uint8_t*)&test_magic, &packet[16], 4, packet, (speck_context_t*)c | |||
tx); | ||||
// extract time stamp (first 64 bit) and checksum (last 16 bit) blended in IV | test_magic = be32toh(test_magic); | |||
speck_he_iv_decrypt (iv, (speck_context_t*)ctx_iv); | ||||
*checksum = be16toh (((uint16_t*)iv)[5]); | //extract header length (lower 2 bytes) | |||
*stamp = be64toh (((uint64_t*)iv)[0]); | uint32_t header_len = test_magic - magic; | |||
memcpy (iv, packet, 12); | if(header_len <= packet_len) { | |||
// decrypt the complete header | ||||
// try community name as possible key and check for magic bytes | speck_ctr(&packet[16], &packet[16], header_len - 16, packet, (speck_cont | |||
uint32_t magic = 0x6E326E00; // ="n2n_" | ext_t*)ctx); | |||
uint32_t test_magic; | ||||
// check for magic bytes and reasonable value in header len field | // extract time stamp and un-xor actual checksum (calculated here) from | |||
// so, as a first step, decrypt 4 bytes only starting at byte 12 | it | |||
speck_he ((uint8_t*)&test_magic, &packet[12], 4, iv, (speck_context_t*)ctx); | // if payload was altered (different checksum than original), time stamp | |||
test_magic = be32toh (test_magic); | verification will fail | |||
if( (((test_magic >> 8) << 8) == magic) // check the thre uppermost bytes | // use speck block cipher step (1 block == 128 bit == 16 bytes) | |||
&& (((uint8_t)test_magic) <= packet_len) // lowest 8 bit of test_magic ar | speck_128_decrypt(packet, (speck_context_t*)ctx_iv); | |||
e header_len | ||||
) { | // extract the required data | |||
// decrypt the complete header | *stamp = be64toh(*(uint64_t*)&packet[4]); | |||
speck_he (&packet[12], &packet[12], (uint8_t)(test_magic) - 12, iv, (speck_c | checksum_high = be32toh(*(uint32_t*)packet); | |||
ontext_t*)ctx); | ||||
// restore original packet order | // restore original packet order before calculating checksum | |||
memcpy (&packet[0], &packet[16], 4); | memcpy(&packet[0], &packet[20], 4); | |||
memcpy (&packet[4], community_name, N2N_COMMUNITY_SIZE); | memcpy(&packet[4], community_name, N2N_COMMUNITY_SIZE); | |||
return (1); // successful | uint64_t checksum = pearson_hash_64(packet, packet_len); | |||
} else | ||||
return (0); // unsuccessful | if((checksum >> 32) != checksum_high) { | |||
traceEvent(TRACE_DEBUG, "packet_header_decrypt dropped a packet with | ||||
invalid checksum."); | ||||
// unsuccessful | ||||
return 0; | ||||
} | ||||
*stamp = *stamp ^ (checksum << 32); | ||||
// successful | ||||
return 1; | ||||
} else { | ||||
// unsuccessful | ||||
return 0; | ||||
} | ||||
} | } | |||
/* ********************************************************************** */ | int packet_header_encrypt (uint8_t packet[], uint16_t header_len, uint16_t packe | |||
t_len, | ||||
he_context_t *ctx, he_context_t *ctx_iv, | ||||
uint64_t stamp) { | ||||
uint32_t *p32 = (uint32_t*)packet; | ||||
uint64_t *p64 = (uint64_t*)packet; | ||||
uint64_t checksum = 0; | ||||
uint32_t magic = 0x6E320000; /* == ASCII "n2__" */ | ||||
magic += header_len; | ||||
if(packet_len < 24) { | ||||
traceEvent(TRACE_DEBUG, "packet_header_encrypt dropped a packet too shor | ||||
t to be valid."); | ||||
return -1; | ||||
} | ||||
// we trust in the caller assuring header_len <= packet_len | ||||
checksum = pearson_hash_64(packet, packet_len); | ||||
// re-order packet | ||||
p32[5] = p32[0]; | ||||
int32_t packet_header_encrypt (uint8_t packet[], uint8_t header_len, he_context_ | // add time stamp, checksum, and random to form the pre-IV | |||
t * ctx, | p64[0] = htobe64(checksum); | |||
he_context_t * ctx_iv, uint64_t stamp, uint16_t c | ||||
hecksum) { | ||||
uint8_t iv[16]; | p32[1] = p32[1] ^ htobe32((uint32_t)(stamp >> 32)); | |||
uint16_t *iv16 = (uint16_t*)&iv; | p32[2] = htobe32((uint32_t)stamp); | |||
uint32_t *iv32 = (uint32_t*)&iv; | ||||
uint64_t *iv64 = (uint64_t*)&iv; | ||||
const uint32_t magic = 0x6E326E21; // = ASCII "n2n!" | ||||
if(header_len < 20) { | ||||
traceEvent(TRACE_DEBUG, "packet_header_encrypt dropped a packet too short to | ||||
be valid."); | ||||
return (-1); | ||||
} | ||||
memcpy (&packet[16], &packet[00], 4); | ||||
iv64[0] = htobe64 (stamp); | ||||
iv16[4] = n2n_rand (); | ||||
iv16[5] = htobe16 (checksum); | ||||
iv32[3] = htobe32 (magic); | ||||
// blend checksum into 96-bit IV | ||||
speck_he_iv_encrypt (iv, (speck_context_t*)ctx_iv); | ||||
memcpy (packet, iv, 16); | p32[3] = n2n_rand(); | |||
packet[15] = header_len; | ||||
speck_he (&packet[12], &packet[12], header_len - 12, iv, (speck_context_t*)ctx | // encrypt this pre-IV to IV | |||
); | speck_128_encrypt(packet, (speck_context_t*)ctx_iv); | |||
return (0); | ||||
// place IV plus magic in packet | ||||
p32[4] = htobe32(magic); | ||||
// encrypt, starting from magic | ||||
speck_ctr(&packet[16], &packet[16], header_len - 16, packet, (speck_context_ | ||||
t*)ctx); | ||||
return 0; | ||||
} | } | |||
/* ********************************************************************** */ | void packet_header_setup_key (const char *community_name, | |||
he_context_t **ctx_static, he_context_t **ctx_dyna | ||||
mic, | ||||
he_context_t **ctx_iv_static, he_context_t **ctx_i | ||||
v_dynamic) { | ||||
uint8_t key[16]; | ||||
// for REGISTER_SUPER, REGISTER_SUPER_ACK, REGISTER_SUPER_NAK only; | ||||
// for all other packets, same as static by default (changed by user/pw auth | ||||
scheme | ||||
// calling packet_header_change_dynamic_key later) | ||||
pearson_hash_128(key, (uint8_t*)community_name, N2N_COMMUNITY_SIZE); | ||||
if(!*ctx_static) | ||||
*ctx_static = (he_context_t*)calloc(1, sizeof(speck_context_t)); | ||||
speck_init((speck_context_t**)ctx_static, key, 128); | ||||
if(!*ctx_dynamic) | ||||
*ctx_dynamic = (he_context_t*)calloc(1, sizeof(speck_context_t)); | ||||
speck_init((speck_context_t**)ctx_dynamic, key, 128); | ||||
// hash again and use as key for IV encryption | ||||
pearson_hash_128(key, key, sizeof(key)); | ||||
if(!*ctx_iv_static) | ||||
*ctx_iv_static = (he_context_t*)calloc(1, sizeof(speck_context_t)); | ||||
speck_init((speck_context_t**)ctx_iv_static, key, 128); | ||||
if(!*ctx_iv_dynamic) | ||||
*ctx_iv_dynamic = (he_context_t*)calloc(1, sizeof(speck_context_t)); | ||||
speck_init((speck_context_t**)ctx_iv_dynamic, key, 128); | ||||
} | ||||
void packet_header_setup_key (const char * community_name, he_context_t ** ctx, | void packet_header_change_dynamic_key (uint8_t *key_dynamic, | |||
he_context_t ** ctx_i | he_context_t **ctx_dynamic, he_context_t | |||
v) { | **ctx_iv_dynamic) { | |||
uint8_t key[16]; | uint8_t key[16]; | |||
pearson_hash_128 (key, (uint8_t*)community_name, N2N_COMMUNITY_SIZE); | pearson_hash_128(key, key_dynamic, N2N_AUTH_CHALLENGE_SIZE); | |||
*ctx = (he_context_t*)calloc(1, sizeof (speck_context_t)); | // for REGISTER_SUPER, REGISTER_SUPER_ACK, REGISTER_SUPER_NAK only | |||
speck_expand_key_he (key, (speck_context_t*)*ctx); | // for all other packets, same as static by default (changed by user/pw auth | |||
scheme) | ||||
// hash again and use last 96 bit (skipping 4 bytes) as key for IV encryption | speck_init((speck_context_t**)ctx_dynamic, key, 128); | |||
// REMOVE as soon as checksum and replay protection get their own fields | ||||
pearson_hash_128 (key, key, sizeof (key)); | // hash again and use as key for IV encryption | |||
*ctx_iv = (he_context_t*)calloc(1, sizeof (speck_context_t)); | // REMOVE as soon as checksum and replay protection get their own fields | |||
speck_expand_key_he_iv (&key[4], (speck_context_t*)*ctx_iv); | pearson_hash_128(key, key, sizeof(key)); | |||
speck_init((speck_context_t**)ctx_iv_dynamic, key, 128); | ||||
} | } | |||
End of changes. 11 change blocks. | ||||
87 lines changed or deleted | 145 lines changed or added |