Browse Source

added self-monitoring time stamp accuracy (#595)

pull/599/head
Logan oos Even 4 years ago
committed by GitHub
parent
commit
5cd1c9030c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      doc/Crypto.md
  2. 79
      src/n2n.c

6
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 ! ! ! 64-bit checksum of the whole packet ! 0x00 ! !
+ - - - - - - - - - - - - - - - - - - - - - - - XOR - - - - - - - - - - - - - - - - - - - - - - -! 32 pseudo-random bits ! + - - - - - - - - - - - - - - - - - - - - - - - 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. 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. - 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. - 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.

79
src/n2n.c

@ -616,20 +616,78 @@ int gettimeofday (struct timeval *tp, void *tzp) {
#endif #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) { uint64_t time_stamp (void) {
struct timeval tod; struct timeval tod;
uint64_t micro_seconds; uint64_t micro_seconds;
uint64_t co, mask_lo, mask_hi, hi_unchanged, counter, new_co;
gettimeofday(&tod, NULL); 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); micro_seconds = ((uint64_t)(tod.tv_sec) << 32) + ((uint64_t)tod.tv_usec << 12);
// more exact but more costly due to the multiplication: // more exact but more costly due to the multiplication:
// micro_seconds = ((uint64_t)(tod.tv_sec) * 1000000ULL + tod.tv_usec) << 12; // 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; return micro_seconds;
} }
@ -645,16 +703,10 @@ uint64_t initial_time_stamp (void) {
// and, in case of validity, updates the "last valid time stamp" // 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) { 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) co = (stamp << 63) >> 63;
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
}
// is it around current time (+/- allowed deviation TIME_STAMP_FRAME)? // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)?
diff = stamp - time_stamp(); 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) { if(NULL != previous_stamp) {
diff = stamp - *previous_stamp; diff = stamp - *previous_stamp;
if(allow_jitter) { 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) { if(diff <= 0) {

Loading…
Cancel
Save