Line data Source code
1 : #ifndef HEADER_fd_src_disco_shred_fd_rnonce_ss_h
2 : #define HEADER_fd_src_disco_shred_fd_rnonce_ss_h
3 : #include "../../util/fd_util.h"
4 :
5 : /* fd_rnonce_ss_t is a strongly typed version of the 64 byte shared
6 : secret used to generate and verify nonces for repair requests and
7 : responses. */
8 : union fd_rnonce_ss {
9 : uchar bytes[64];
10 : struct {
11 : ulong ss0[3];
12 : ulong slot;
13 : ulong ss1[1];
14 : uint shred_idx;
15 : uint ss2[2];
16 : uint time;
17 : ulong ss3[1];
18 : } private;
19 : };
20 : typedef union fd_rnonce_ss fd_rnonce_ss_t;
21 : FD_STATIC_ASSERT( sizeof(fd_rnonce_ss_t)==64, rnonce_ss );
22 :
23 : FD_PROTOTYPES_BEGIN
24 :
25 : /* fd_rnonce_ss_{compute,verify} compute and verify, respectively, the
26 : nonce for the specifed repair request issued or received at time_ns.
27 : slot and shred_idx specify the slot and shred index of the
28 : requested/received shred. normal_repair must be non-zero if the
29 : request is a "normal" repair request, i.e., one for a specific shred
30 : index. If normal_repair is zero, shred_idx is ignored, and slot is
31 : adjusted (see below). ss is a pointer to the shared secret value.
32 : ss_compute returns the value of the nonce. ss_verify takes the
33 : supposed value of the nonce in the nonce parameter. ss_verify
34 : returns 1 if the nonce is correct and 0 otherwise.
35 :
36 : These satisfy:
37 : 1==ss_verify( ss_v, ss_compute( ss_c, 1, slot_c, shred_idx_c, rq_time ), 1, slot_v, shred_idx_v, rs_time )
38 : when
39 : ss_v == ss_c,
40 : slot_v == slot_c,
41 : shred_idx_v == shred_idx_c, AND
42 : rq_time <= rs_time < rq_time + 1.02 seconds.
43 : When any of these of these conditions is false, it should return 0
44 : with high probability, approx (1 - 2^25).
45 :
46 : And
47 : 1==ss_verify( ss_v, ss_compute( ss_c, 0, slot_c, shred_idx_c, rq_time ), 0, slot_v, shred_idx_v, rs_time )
48 : when
49 : ss_v == ss_c,
50 : -1 <= floor(slot_v/128) - floor(slot_c/128) <= 0, which is looser than slot_c - 128 <= slot_v <= slot_c
51 : rq_time <= rs_time < rq_time + 1.02 seconds.
52 : When any of these of these conditions is false, it should return 0
53 : with high probability, approx (1 - 2^25).
54 : */
55 : static inline uint
56 : fd_rnonce_ss_compute( fd_rnonce_ss_t const * ss,
57 : int normal_repair,
58 : ulong slot,
59 : uint shred_idx,
60 0 : long time_ns ) {
61 0 : fd_rnonce_ss_t temp[1] = { *ss };
62 : /* truncate time down to intervals of 2^32 ns, which is ~4 seconds. */
63 0 : temp->private.time = (uint)(time_ns>>32);
64 0 : temp->private.slot = fd_ulong_if( normal_repair, slot, slot/128UL );
65 0 : temp->private.shred_idx = fd_uint_if ( normal_repair, shred_idx, 0U );
66 : /* seed is fractional part of sqrt(17) */
67 : /* Then we add back in time_ns>>24 (truncated to 16ms intervals).
68 : This is kind of surprising, but it means that we can generate a new
69 : nonce when we re-request a specific shred, but we don't need to
70 : compute a ton of hashes. */
71 0 : return (uint)(
72 0 : fd_ulong_if( normal_repair, 0x80000000UL, 0UL ) |
73 0 : (0x7FFFFFFFUL & (fd_hash( 2270897969802886507UL, temp, sizeof(temp) ) + (((ulong)time_ns)>>24) ) ) );
74 0 : }
75 :
76 : static inline int
77 : fd_rnonce_ss_verify( fd_rnonce_ss_t const * ss,
78 : uint nonce,
79 : ulong slot,
80 : uint shred_idx,
81 0 : long time_ns ) {
82 0 : fd_rnonce_ss_t temp[1] = { *ss };
83 0 : int normal_repair = !!(nonce>>31);
84 :
85 0 : temp->private.time = (uint)(time_ns>>32);
86 0 : temp->private.slot = fd_ulong_if( normal_repair, slot, slot/128UL );
87 0 : temp->private.shred_idx = fd_uint_if ( normal_repair, shred_idx, 0U );
88 0 : #define ALLOWED_TIME_DELTA ((uint)((1000000000UL + (1UL<<24) - 1UL)/(1UL<<24))) /* == 60 */
89 :
90 0 : #define CHECKN( temp ) do{ if( FD_LIKELY( \
91 0 : ( (0x7FFFFFFFUL & (fd_hash( 2270897969802886507UL, temp, sizeof(temp) ) + (((ulong)time_ns)>>24) )) - \
92 0 : (0x7FFFFFFFUL & nonce) ) <= ALLOWED_TIME_DELTA ) ) \
93 0 : return 1; \
94 0 : } while( 0 )
95 :
96 :
97 0 : CHECKN( temp );
98 :
99 0 : int try_prev_time = ((time_ns-1000000000L)>>32) != (time_ns>>32);
100 0 : if( try_prev_time ) {
101 : /* If that doesn't match, check the previous time bucket. */
102 0 : temp->private.time--;
103 0 : CHECKN( temp );
104 0 : temp->private.time++;
105 0 : }
106 :
107 0 : if( FD_UNLIKELY( !normal_repair ) ) {
108 : /* Check the next slot bucket */
109 0 : temp->private.slot++;
110 0 : CHECKN( temp );
111 0 : if( try_prev_time ) {
112 : /* And check it with the prev time bucket */
113 0 : temp->private.time--;
114 0 : CHECKN( temp );
115 0 : }
116 0 : }
117 0 : #undef CHECKN
118 0 : #undef ALLOWED_TIME_DELTA
119 0 : return 0;
120 0 : }
121 :
122 : FD_PROTOTYPES_END
123 :
124 : #endif /* HEADER_fd_src_disco_shred_fd_rnonce_ss_h */
|