Browse Source

OTPChecker: parallelize OTP generation using BThreadWork

ambrop7 15 năm trước cách đây
mục cha
commit
f272be1cd2

+ 2 - 1
client/DatagramPeerIO.c

@@ -147,6 +147,7 @@ int DatagramPeerIO_Init (
     PacketPassInterface *recv_userif,
     int otp_warning_count,
     DatagramPeerIO_handler_otp_warning handler_otp_warning,
+    DatagramPeerIO_handler_otp_ready handler_otp_ready,
     void *user,
     BThreadWorkDispatcher *twd
 )
@@ -200,7 +201,7 @@ int DatagramPeerIO_Init (
     PacketPassNotifier_Init(&o->recv_notifier, FragmentProtoAssembler_GetInput(&o->recv_assembler), BReactor_PendingGroup(o->reactor));
     
     // init decoder
-    if (!SPProtoDecoder_Init(&o->recv_decoder, PacketPassNotifier_GetInput(&o->recv_notifier), o->sp_params, 2, BReactor_PendingGroup(o->reactor))) {
+    if (!SPProtoDecoder_Init(&o->recv_decoder, PacketPassNotifier_GetInput(&o->recv_notifier), o->sp_params, 2, BReactor_PendingGroup(o->reactor), twd, handler_otp_ready, user)) {
         BLog(BLOG_ERROR, "SPProtoDecoder_Init failed");
         goto fail1;
     }

+ 9 - 0
client/DatagramPeerIO.h

@@ -58,6 +58,13 @@
  */
 typedef void (*DatagramPeerIO_handler_otp_warning) (void *user);
 
+/**
+ * Handler called when OTP generation for a new receive seed is finished.
+ * 
+ * @param user as in {@link DatagramPeerIO_Init}
+ */
+typedef void (*DatagramPeerIO_handler_otp_ready) (void *user);
+
 /**
  * Object for comminicating with a peer using a datagram socket.
  *
@@ -134,6 +141,7 @@ typedef struct {
  * @param otp_warning_count If using OTPs, after how many encoded packets to call the handler.
  *                          In this case, must be >0 and <=sp_params.otp_num.
  * @param handler_otp_warning OTP warning handler
+ * @param handler_otp_ready handler called when OTP generation for a new receive seed is finished
  * @param user value to pass to handler
  * @param twd thread work dispatcher
  * @return 1 on success, 0 on failure
@@ -149,6 +157,7 @@ int DatagramPeerIO_Init (
     PacketPassInterface *recv_userif,
     int otp_warning_count,
     DatagramPeerIO_handler_otp_warning handler_otp_warning,
+    DatagramPeerIO_handler_otp_ready handler_otp_ready,
     void *user,
     BThreadWorkDispatcher *twd
 ) WARN_UNUSED;

+ 17 - 3
client/client.c

@@ -279,6 +279,9 @@ static void peer_msg_youretry (struct peer_data *peer, uint8_t *data, int data_l
 // handler from DatagramPeerIO when we should generate a new OTP send seed
 static void peer_udp_pio_handler_seed_warning (struct peer_data *peer);
 
+// handler from DatagramPeerIO when a new OTP seed can be recognized once it was provided to it
+static void peer_udp_pio_handler_seed_ready (struct peer_data *peer);
+
 // handler from StreamPeerIO when an error occurs on the connection
 static void peer_tcp_pio_handler_error (struct peer_data *peer);
 
@@ -1536,6 +1539,7 @@ int peer_init_link (struct peer_data *peer)
             options.fragmentation_latency, PEER_UDP_ASSEMBLER_NUM_FRAMES, &peer->recv_ppi,
             options.otp_num_warn,
             (DatagramPeerIO_handler_otp_warning)peer_udp_pio_handler_seed_warning,
+            (DatagramPeerIO_handler_otp_ready)peer_udp_pio_handler_seed_ready,
             peer, &twd
         )) {
             peer_log(peer, BLOG_ERROR, "DatagramPeerIO_Init failed");
@@ -2073,9 +2077,8 @@ void peer_msg_seed (struct peer_data *peer, uint8_t *data, int data_len)
     // add receive seed
     DatagramPeerIO_AddOTPRecvSeed(&peer->pio.udp.pio, seed_id, key, iv);
     
-    // send confirmation
-    peer_send_confirmseed(peer, seed_id);
-    return;
+    // remember seed ID so we can confirm it from peer_udp_pio_handler_seed_ready
+    peer->pio.udp.pending_recvseed_id = seed_id;
 }
 
 void peer_msg_confirmseed (struct peer_data *peer, uint8_t *data, int data_len)
@@ -2155,6 +2158,17 @@ void peer_udp_pio_handler_seed_warning (struct peer_data *peer)
     }
 }
 
+void peer_udp_pio_handler_seed_ready (struct peer_data *peer)
+{
+    ASSERT(options.transport_mode == TRANSPORT_MODE_UDP)
+    ASSERT(SPPROTO_HAVE_OTP(sp_params))
+    ASSERT(peer->have_link)
+    
+    // send confirmation
+    peer_send_confirmseed(peer, peer->pio.udp.pending_recvseed_id);
+    return;
+}
+
 void peer_tcp_pio_handler_error (struct peer_data *peer)
 {
     ASSERT(options.transport_mode == TRANSPORT_MODE_TCP)

+ 1 - 0
client/client.h

@@ -137,6 +137,7 @@ struct peer_data {
             uint16_t sendseed_sent_id;
             uint8_t sendseed_sent_key[BENCRYPTION_MAX_KEY_SIZE];
             uint8_t sendseed_sent_iv[BENCRYPTION_MAX_BLOCK_SIZE];
+            uint16_t pending_recvseed_id;
         } udp;
         struct {
             StreamPeerIO pio;

+ 2 - 2
flow/SPProtoDecoder.c

@@ -167,7 +167,7 @@ static void output_handler_done (SPProtoDecoder *o)
     PacketPassInterface_Done(&o->input);
 }
 
-int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg)
+int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, SPProtoDecoder_otp_handler otp_handler, void *user)
 {
     spproto_assert_security_params(sp_params);
     ASSERT(spproto_carrier_mtu_for_payload_mtu(sp_params, PacketPassInterface_GetMTU(output)) >= 0)
@@ -210,7 +210,7 @@ int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct
     
     // init OTP checker
     if (SPPROTO_HAVE_OTP(o->sp_params)) {
-        if (!OTPChecker_Init(&o->otpchecker, o->sp_params.otp_num, o->sp_params.otp_mode, num_otp_seeds)) {
+        if (!OTPChecker_Init(&o->otpchecker, o->sp_params.otp_num, o->sp_params.otp_mode, num_otp_seeds, twd, otp_handler, user)) {
             goto fail1;
         }
     }

+ 15 - 2
flow/SPProtoDecoder.h

@@ -36,6 +36,13 @@
 #include <security/OTPChecker.h>
 #include <flow/PacketPassInterface.h>
 
+/**
+ * Handler called when OTP generation for a new seed is finished.
+ * 
+ * @param user as in {@link SPProtoDecoder_Init}
+ */
+typedef void (*SPProtoDecoder_otp_handler) (void *user);
+
 /**
  * Object which decodes packets according to SPProto.
  * Input is with {@link PacketPassInterface}.
@@ -68,9 +75,12 @@ typedef struct {
  * @param num_otp_seeds if using OTPs, how many OTP seeds to keep for checking
  *                      receiving packets. Must be >=2 if using OTPs.
  * @param pg pending group
+ * @param twd thread work dispatcher
+ * @param otp_handler handler called when OTP generation is finished
+ * @param user argument to handler
  * @return 1 on success, 0 on failure
  */
-int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg) WARN_UNUSED;
+int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, SPProtoDecoder_otp_handler otp_handler, void *user) WARN_UNUSED;
 
 /**
  * Frees the object.
@@ -107,7 +117,10 @@ void SPProtoDecoder_SetEncryptionKey (SPProtoDecoder *o, uint8_t *encryption_key
 void SPProtoDecoder_RemoveEncryptionKey (SPProtoDecoder *o);
 
 /**
- * Adds a new OTP seed to check received packets against.
+ * Starts generating OTPs for a seed to check received packets against.
+ * OTPs for this seed will not be recognized until the {@link SPProtoDecoder_otp_handler} handler
+ * is called.
+ * If OTPs are still being generated for the previous seed, it will be forgotten.
  * OTPs must be enabled.
  *
  * @param o the object

+ 73 - 12
security/OTPChecker.c

@@ -20,6 +20,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include <string.h>
+
 #include <security/OTPChecker.h>
 
 static void OTPChecker_Table_Empty (OTPChecker *mc, oc_table *t);
@@ -107,7 +109,34 @@ int OTPChecker_Table_CheckOTP (OTPChecker *mc, oc_table *t, otp_t otp)
     return 0;
 }
 
-int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables)
+static void work_func (OTPChecker *mc)
+{
+    oc_table *table = oc_tables_tables_at(&mc->tables_params, mc->tables, mc->next_table);
+    OTPChecker_Table_Generate(mc, table, &mc->calc, mc->tw_key, mc->tw_iv);
+}
+
+static void work_done_handler (OTPChecker *mc)
+{
+    DebugObject_Access(&mc->d_obj);
+    
+    // free work
+    BThreadWork_Free(&mc->tw);
+    mc->tw_have = 0;
+    
+    // update next table number
+    mc->next_table = BMODADD(mc->next_table, 1, mc->num_tables);
+    
+    // update number of used tables if not all are used yet
+    if (mc->tables_used < mc->num_tables) {
+        mc->tables_used++;
+    }
+    
+    // call handler
+    mc->handler(mc->user);
+    return;
+}
+
+int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables, BThreadWorkDispatcher *twd, OTPChecker_handler handler, void *user)
 {
     ASSERT(num_otps > 0)
     ASSERT(BEncryption_cipher_valid(cipher))
@@ -115,7 +144,11 @@ int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables)
     
     // init arguments
     mc->num_otps = num_otps;
+    mc->cipher = cipher;
     mc->num_tables = num_tables;
+    mc->twd = twd;
+    mc->handler = handler;
+    mc->user = user;
     
     // set number of entries
     mc->num_entries = 2 * mc->num_otps;
@@ -140,9 +173,10 @@ int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables)
         OTPChecker_Table_Empty(mc, oc_tables_tables_at(&mc->tables_params, mc->tables, i));
     }
     
-    // init debug object
-    DebugObject_Init(&mc->d_obj);
+    // have no work
+    mc->tw_have = 0;
     
+    DebugObject_Init(&mc->d_obj);
     return 1;
     
 fail1:
@@ -153,9 +187,13 @@ fail0:
 
 void OTPChecker_Free (OTPChecker *mc)
 {
-    // free debug object
     DebugObject_Free(&mc->d_obj);
     
+    // free work
+    if (mc->tw_have) {
+        BThreadWork_Free(&mc->tw);
+    }
+    
     // free tables
     free(mc->tables);
     
@@ -167,31 +205,54 @@ void OTPChecker_AddSeed (OTPChecker *mc, uint16_t seed_id, uint8_t *key, uint8_t
 {
     ASSERT(mc->next_table >= 0)
     ASSERT(mc->next_table < mc->num_tables)
+    DebugObject_Access(&mc->d_obj);
+    
+    // free existing work
+    if (mc->tw_have) {
+        BThreadWork_Free(&mc->tw);
+    }
     
-    // initialize next table
+    // set table's seed ID
     oc_table *table = oc_tables_tables_at(&mc->tables_params, mc->tables, mc->next_table);
     *oc_table_id(&mc->tables_params.tables_params, table) = seed_id;
-    OTPChecker_Table_Generate(mc, table, &mc->calc, key, iv);
     
-    // update next table number
-    mc->next_table = BMODADD(mc->next_table, 1, mc->num_tables);
-    // update number of used tables if not all are used yet
-    if (mc->tables_used < mc->num_tables) {
-        mc->tables_used++;
-    }
+    // copy key and IV
+    memcpy(mc->tw_key, key, BEncryption_cipher_key_size(mc->cipher));
+    memcpy(mc->tw_iv, iv, BEncryption_cipher_block_size(mc->cipher));
+    
+    // start work
+    BThreadWork_Init(&mc->tw, mc->twd, (BThreadWork_handler_done)work_done_handler, mc, (BThreadWork_work_func)work_func, mc);
+    
+    // set have work
+    mc->tw_have = 1;
 }
 
 void OTPChecker_RemoveSeeds (OTPChecker *mc)
 {
+    DebugObject_Access(&mc->d_obj);
+    
+    // free existing work
+    if (mc->tw_have) {
+        BThreadWork_Free(&mc->tw);
+        mc->tw_have = 0;
+    }
+    
     mc->tables_used = 0;
     mc->next_table = 0;
 }
 
 int OTPChecker_CheckOTP (OTPChecker *mc, uint16_t seed_id, otp_t otp)
 {
+    DebugObject_Access(&mc->d_obj);
+    
     // try tables in reverse order
     for (int i = 1; i <= mc->tables_used; i++) {
         int table_index = BMODADD(mc->next_table, mc->num_tables - i, mc->num_tables);
+        if (table_index == mc->next_table && mc->tw_have) {
+            // ignore table that is being generated
+            continue;
+        }
+        
         oc_table *table = oc_tables_tables_at(&mc->tables_params, mc->tables, table_index);
         if (*oc_table_id(&mc->tables_params.tables_params, table) == seed_id) {
             return OTPChecker_Table_CheckOTP(mc, table, otp);

+ 26 - 3
security/OTPChecker.h

@@ -34,6 +34,7 @@
 #include <misc/modadd.h>
 #include <security/OTPCalculator.h>
 #include <system/DebugObject.h>
+#include <threadwork/BThreadWork.h>
 
 struct OTPChecker_entry {
     otp_t otp;
@@ -42,12 +43,23 @@ struct OTPChecker_entry {
 
 #include <generated/bstruct_OTPChecker.h>
 
+/**
+ * Handler called when OTP generation for a seed is finished and new OTPs
+ * can be recognized.
+ * 
+ * @param user as in {@link OTPChecker_Init}
+ */
+typedef void (*OTPChecker_handler) (void *user);
+
 /**
  * Object that checks OTPs agains known seeds.
  */
 typedef struct {
-    DebugObject d_obj;
+    BThreadWorkDispatcher *twd;
+    OTPChecker_handler handler;
+    void *user;
     int num_otps;
+    int cipher;
     int num_entries;
     int num_tables;
     int tables_used;
@@ -55,6 +67,11 @@ typedef struct {
     OTPCalculator calc;
     oc_tablesParams tables_params;
     oc_tables *tables;
+    int tw_have;
+    BThreadWork tw;
+    uint8_t tw_key[BENCRYPTION_MAX_KEY_SIZE];
+    uint8_t tw_iv[BENCRYPTION_MAX_BLOCK_SIZE];
+    DebugObject d_obj;
 } OTPChecker;
 
 /**
@@ -65,9 +82,13 @@ typedef struct {
  * @param cipher encryption cipher for calculating the OTPs. Must be valid
  *               according to {@link BEncryption_cipher_valid}.
  * @param num_tables number of tables to keep, each for one seed. Must be >0.
+ * @param twd thread work dispatcher
+ * @param handler handler to call when generation of new OTPs is complete,
+ *                after {@link OTPChecker_AddSeed} was called.
+ * @param user argument to handler
  * @return 1 on success, 0 on failure
  */
-int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables) WARN_UNUSED;
+int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables, BThreadWorkDispatcher *twd, OTPChecker_handler handler, void *user) WARN_UNUSED;
 
 /**
  * Frees the checker.
@@ -77,7 +98,9 @@ int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables) W
 void OTPChecker_Free (OTPChecker *mc);
 
 /**
- * Adds a seed whose OTPs should be recognized.
+ * Starts generating OTPs to recognize for a seed.
+ * OTPs for this seed will not be recognized until the {@link OTPChecker_handler} handler is called.
+ * If OTPs are still being generated for a previous seed, it will be forgotten.
  *
  * @param mc the object
  * @param seed_id seed identifier