diff --git a/doc/Crypto.md b/doc/Crypto.md index a8176de..430301b 100644 --- a/doc/Crypto.md +++ b/doc/Crypto.md @@ -194,12 +194,12 @@ The aforementioned 128-bit pre-IV can be depicted as follows: +----------------------------------------------------------------+-------------------------------+-------------------------------+ ! 64-bit checksum of the whole packet ! 0x00 ! ! + - - - - - - - - - - - - - - - - - - - - - - - XOR - - - - - - - - - - - - - - - - - - - - - - -! 32 pseudo-random bits ! - ! 0x00 ! 52-bit time stamp with microsecond-accuracy ! resrvd ! F ! ! + ! 0x00 ! 52-bit time stamp with microsecond-accuracy ! countr ! F ! ! +------------------------------------------------------------------------------------------------+-------------------------------+ ``` -The time stamp consists of the 52-bit microsecond value, a still reserved field for additional accuracy information such as a counter (still under development), a 4-bit flag field F (accuracy indicator, other header encryption features – still under development, too). +The time stamp consists of the 52-bit microsecond value, a 8-bit counter in case of equal following time stamps and, a 4-bit flag field F (accuracy indicator in last bit). edge and supernode monitor their own time stamps for doublets which would indicate an accuracy issue. If the counter overflows on the same time stamp, the sub-second part of the time stamp will also become counter. In this case, the whole stamp carries the accurcy bit flag (lowest bit) set so other edges and supernodes can handle this stamp appropriately. Encrypting this pre-IV using a block cipher step will generate a pseudo-random looking IV which gets written to the packet and used for the header encryption. @@ -209,7 +209,7 @@ Upon receival, the time stamp as well as the checksum can be extracted from the - The (remote) time stamp is checked against the local clock. It may not deviate more than plus/minus 16 seconds. So, edges and supernode need to keep a somewhat current time. This limit can be adjusted by changing the `TIME_STAMP_FRAME` definition. It is time-zone indifferent as UTC is used. -- Valid (remote) time stamps get stored as "last valid time stamp" seen from each node (supernode and edges). So, a newly arriving packet's time stamp can be compared to the last valid one. It should be equal or higher. However, as UDP packets may overtake each other just by taking another path through the internet, they are allowed to be 160 millisecond earlier than the last valid one. This limit can be adjusted by changing the `TIME_STAMP_JITTER` definition. +- Valid (remote) time stamps get stored as "last valid time stamp" seen from each node (supernode and edges). So, a newly arriving packet's time stamp can be compared to the last valid one. It should be equal or higher. However, as UDP packets may overtake each other just by taking another path through the internet, they are allowed to be 160 millisecond earlier than the last valid one. This limit is set with the `TIME_STAMP_JITTER` definition. If the accuracy flag is set, the time stamp will be allowed a jitter eight times as high, corresponding to 1.25 seconds by default. - However, the systemic packets such as REGISTER_SUPER are not allowed any time stamp jitter because n2n relies on the actual sender's socket. A replay from another IP within any allowed jitter time frame would deviate the traffic which shall be prevented (even if it remains undecryptable). Under absolutely rare (!) circumstances, this might cause a re-registration requirement which happens automatically but might cause a small delay – security (including network availability) first! REGISTER packets from the local multicast environment are excempt from the very strict no-jitter requirement because they indeed regularily can show some deviation if compared to time stamps in packets received on the regular socket. As these packets are incoming on different sockets, their processing is more likely to no take place in the order these packets were sent. diff --git a/src/n2n.c b/src/n2n.c index 77cac44..04e013d 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -616,20 +616,78 @@ int gettimeofday (struct timeval *tp, void *tzp) { #endif -// returns a time stamp for use with replay protection +// stores the previously issued time stamp +static uint64_t previously_issued_time_stamp = 0; + + +// returns a time stamp for use with replay protection (branchless code) +// +// depending on the self-detected accuracy, it has the following format +// +// MMMMMMMMCCCCCCCF or +// +// MMMMMMMMSSSSSCCF +// +// with M being the 32-bit second time stamp +// S the 20-bit sub-second (microsecond) time stamp part, if applicable +// C a counter (8 bit or 24 bit) reset to 0 with every MMMMMMMM(SSSSS) turn-over +// F a 4-bit flag field with +// ...c being the accuracy indicator (if set, only counter and no sub-second accuracy) +// uint64_t time_stamp (void) { struct timeval tod; uint64_t micro_seconds; + uint64_t co, mask_lo, mask_hi, hi_unchanged, counter, new_co; gettimeofday(&tod, NULL); - // (roughly) calculate the microseconds since 1970, note that the 8 bits between time stamp and flags remain unset + // (roughly) calculate the microseconds since 1970, leftbound micro_seconds = ((uint64_t)(tod.tv_sec) << 32) + ((uint64_t)tod.tv_usec << 12); // more exact but more costly due to the multiplication: // micro_seconds = ((uint64_t)(tod.tv_sec) * 1000000ULL + tod.tv_usec) << 12; - // note that the lower 4 bits remain unset (flags, for later use) + // extract "counter only" flag (lowest bit) + co = (previously_issued_time_stamp << 63) >> 63; + // set mask accordingly + mask_lo = -co; + mask_lo >>= 32; + // either 0x00000000FFFFFFFF (if co flag set) or 0x0000000000000000 (if co flag not set) + + mask_lo |= (~mask_lo) >> 52; + // either 0x00000000FFFFFFFF (unchanged) or 0x0000000000000FFF (lowest 12 bit set) + + mask_hi = ~mask_lo; + + hi_unchanged = ((previously_issued_time_stamp & mask_hi) == (micro_seconds & mask_hi)); + // 0 if upper bits unchanged (compared to previous stamp), 1 otherwise + + // read counter and shift right for flags + counter = (previously_issued_time_stamp & mask_lo) >> 4; + + counter += hi_unchanged; + counter &= -hi_unchanged; + // either counter++ if upper part of timestamp unchanged, 0 otherwise + + // back to time stamp format + counter <<= 4; + + // set new co flag if counter overflows while upper bits unchanged or if it was set before + new_co |= (((counter & mask_lo) == 0) & hi_unchanged) | co; + + // in case co flag changed, masks need to be recalculated + mask_lo = -new_co; + mask_lo >>= 32; + mask_lo |= (~mask_lo) >> 52; + mask_hi = ~mask_lo; + + // assemble new timestamp + micro_seconds &= mask_hi; + micro_seconds |= counter; + micro_seconds |= new_co; + + previously_issued_time_stamp = micro_seconds; + return micro_seconds; } @@ -645,16 +703,10 @@ uint64_t initial_time_stamp (void) { // and, in case of validity, updates the "last valid time stamp" int time_stamp_verify_and_update (uint64_t stamp, uint64_t *previous_stamp, int allow_jitter) { - int64_t diff; // do not change to unsigned + int64_t diff; /* do not change to unsigned */ + uint64_t co; /* counter only mode (for sub-seconds) */ - // clear any incoming flags (their handling is not suppoted yet) - stamp = (stamp >> 4) << 4; - - // are the 8 bits between time stamp and flags reset? this relies on flags being cleared above - if(stamp << 52) { - traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp with middle bits set."); - return 0; // failure - } + co = (stamp << 63) >> 63; // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)? diff = stamp - time_stamp(); @@ -669,7 +721,8 @@ int time_stamp_verify_and_update (uint64_t stamp, uint64_t *previous_stamp, int if(NULL != previous_stamp) { diff = stamp - *previous_stamp; if(allow_jitter) { - diff += TIME_STAMP_JITTER; + // 8 times higher jitter allowed for counter-only flagged timestamps ( ~ 1.25 sec with 160 ms default jitter) + diff += TIME_STAMP_JITTER << (co << 3); } if(diff <= 0) {