frosix

Multiparty signature service (experimental)
Log | Files | Refs | README | LICENSE

frosix-httpd_sig-share.c (13663B)


      1 /*
      2   This file is part of Frosix
      3   Copyright (C) 2022, 2023 Joel Urech
      4 
      5   Frosix is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Frosix is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Frosix; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file backend/frosix-httpd_sign-share.c
     18  * @brief functions to handle incoming requests on /sign-share
     19  * @author Joel Urech
     20  */
     21 #include "frosix-httpd_sig.h"
     22 #include "frosix-httpd.h"
     23 #include "frosix_database_plugin.h"
     24 #include "frost_high.h"
     25 #include "frost_verify.h"
     26 #include <taler/taler_util.h>
     27 #include <gnunet/gnunet_util_lib.h>
     28 #include <gnunet/gnunet_db_lib.h>
     29 #include "frosix_authorization_plugin.h"
     30 
     31 /**
     32  * What is the maximum frequency at which we allow
     33  * clients to attempt to answer security questions?
     34  */
     35 #define MAX_QUESTION_FREQ GNUNET_TIME_relative_multiply ( \
     36     GNUNET_TIME_UNIT_SECONDS, 30)
     37 
     38 /**
     39  * How many retries do we allow per code?
     40  */
     41 #define INITIAL_RETRY_COUNTER 3
     42 
     43 struct SignShareContext
     44 {
     45   /**
     46    *
     47   */
     48   uint32_t identifier;
     49 
     50   /**
     51    *
     52   */
     53   uint8_t threshold;
     54 
     55   /**
     56    *
     57   */
     58   struct FROST_Commitment *commitments;
     59 
     60   /**
     61    *
     62   */
     63   struct FROSIX_EncryptionKey encryption_key;
     64 
     65 
     66   /**
     67    *
     68   */
     69   struct FROST_HashCode enc_key_hash;
     70 
     71   /**
     72    *
     73   */
     74   struct FROST_MessageHash message_hash;
     75 
     76   /**
     77    *
     78   */
     79   struct FROSIX_SigRequestIdP id;
     80 
     81   /**
     82    * Our handler context
     83   */
     84   struct TM_HandlerContext *hc;
     85 
     86   /**
     87    * Uploaded JSON data, NULL if upload is not yet complete.
     88   */
     89   json_t *json;
     90 
     91   /**
     92    * Post parser context.
     93    */
     94   void *post_ctx;
     95 
     96   /**
     97    * Connection handle for closing or resuming
     98    */
     99   struct MHD_Connection *connection;
    100 };
    101 
    102 
    103 /**
    104  * FIXME
    105 */
    106 static struct FROST_Commitment *
    107 get_my_commitment (uint32_t identifier,
    108                    struct FROST_Commitment commitments[],
    109                    uint8_t len)
    110 {
    111   for (unsigned int i = 0; i < len; i++)
    112   {
    113     if (identifier == commitments[i].identifier)
    114       return &commitments[i];
    115   }
    116   return NULL;
    117 }
    118 
    119 
    120 static MHD_RESULT
    121 return_sig_share (struct SignShareContext *gc)
    122 {
    123   /* get key data from DB */
    124   struct FROSIX_EncryptionNonceP nonce;
    125   struct FROSIX_KeyDataEncrypted enc_key_data;
    126 
    127   enum GNUNET_DB_QueryStatus qs_key;
    128   qs_key = db->get_key_data (db->cls,
    129                              &gc->enc_key_hash,
    130                              &gc->identifier,
    131                              &nonce,
    132                              &enc_key_data);
    133 
    134   switch (qs_key)
    135   {
    136   case GNUNET_DB_STATUS_HARD_ERROR:
    137   case GNUNET_DB_STATUS_SOFT_ERROR:
    138   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    139     GNUNET_free (gc->commitments);
    140     GNUNET_break (0);
    141     /* FROSIX_EC_KEY_NOT_FOUND */
    142     return TALER_MHD_reply_with_error (gc->connection,
    143                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    144                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    145                                        "key_data_select");
    146   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    147     break;
    148   }
    149 
    150   /* validate identifier from db */
    151   if (gc->identifier <= 0 || gc->identifier >= 255 )
    152   {
    153     GNUNET_free (gc->commitments);
    154     GNUNET_break_op (0);
    155     /* FROSIX_EC_KEY_DATA_CORRUPT */
    156     return TALER_MHD_reply_with_error (gc->connection,
    157                                        MHD_HTTP_BAD_REQUEST,
    158                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    159                                        "Got invalid data from DB");
    160   }
    161 
    162   /* get commitment */
    163   struct FROST_Commitment *my_comm = get_my_commitment (gc->identifier,
    164                                                         gc->commitments,
    165                                                         gc->threshold);
    166 
    167   if (NULL == my_comm)
    168   {
    169     GNUNET_free (gc->commitments);
    170     GNUNET_break_op (0);
    171     /* FROSIX_COMMITMENT NOT_FOUND */
    172     return TALER_MHD_reply_with_error (gc->connection,
    173                                        MHD_HTTP_BAD_REQUEST,
    174                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    175                                        "Commitment not found");
    176   }
    177 
    178   /* compute seed id in db */
    179   struct GNUNET_HashCode db_seed_id;
    180   FROSIX_compute_db_commitment_id (&db_seed_id,
    181                                    &gc->enc_key_hash,
    182                                    my_comm);
    183 
    184   /* get commitment seed from DB */
    185   struct FROST_CommitmentSeed seed;
    186 
    187   enum GNUNET_DB_QueryStatus qs_seed;
    188   qs_seed = db->get_and_delete_commitment_seed (db->cls,
    189                                                 &db_seed_id,
    190                                                 &seed);
    191 
    192   switch (qs_seed)
    193   {
    194   case GNUNET_DB_STATUS_HARD_ERROR:
    195   case GNUNET_DB_STATUS_SOFT_ERROR:
    196   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    197     GNUNET_free (gc->commitments);
    198     GNUNET_break (0);
    199     /* FROSIX_COMMITMENT_NOT_FOUND */
    200     return TALER_MHD_reply_with_error (gc->connection,
    201                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    202                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    203                                        "get_commitment_seed");
    204   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    205     break;
    206   }
    207 
    208   /* compute nonce and commitment */
    209   struct FROST_Nonce com_nonce;
    210   struct FROST_Commitment commitment;
    211   FROST_generate_nonce_and_commitment (&com_nonce,
    212                                        &commitment,
    213                                        &gc->message_hash,
    214                                        &seed);
    215 
    216   /* FIXME check if computed commitment equals send commitment */
    217 
    218   /* decrypt key data from db */
    219   struct FROSIX_KeyDataRaw decrypted;
    220   if (GNUNET_OK != FROSIX_secretbox_keydata_open (&decrypted,
    221                                                   &enc_key_data,
    222                                                   &nonce,
    223                                                   &gc->encryption_key))
    224   {
    225     GNUNET_free (gc->commitments);
    226     GNUNET_break_op (0);
    227     /* FROSIX_EC_DECRYPTION_FAILED */
    228     return TALER_MHD_reply_with_error (gc->connection,
    229                                        MHD_HTTP_BAD_REQUEST,
    230                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    231                                        "Unable to decrypt data from DB");
    232   }
    233 
    234   /* create key pair */
    235   struct FROST_KeyPair key_pair;
    236   key_pair.identifier = gc->identifier;
    237 
    238   FROSIX_raw_key_to_struct (&key_pair,
    239                             &decrypted);
    240 
    241   /* compute signature share */
    242   struct FROST_SignatureShare sig_share;
    243   FROST_sign_message_hash (&sig_share,
    244                            &gc->message_hash,
    245                            gc->commitments,
    246                            gc->threshold,
    247                            &key_pair,
    248                            &com_nonce);
    249 
    250   GNUNET_free (gc->commitments);
    251 
    252   return TALER_MHD_REPLY_JSON_PACK (
    253     gc->connection,
    254     MHD_HTTP_OK,
    255     GNUNET_JSON_pack_data_auto ("signature_share",
    256                                 &sig_share.sig_share),
    257     GNUNET_JSON_pack_data_auto ("public_key_share",
    258                                 &sig_share.pk_i));
    259 }
    260 
    261 
    262 MHD_RESULT
    263 FH_handler_sig_share_post (
    264   struct MHD_Connection *connection,
    265   struct TM_HandlerContext *hc,
    266   const struct FROSIX_SigRequestIdP *id,
    267   const char *sign_share_data,
    268   size_t *sign_share_data_size)
    269 {
    270   enum GNUNET_GenericReturnValue res;
    271   struct SignShareContext *dc = hc->ctx;
    272   json_t *json_commitments = NULL;
    273 
    274   if (NULL == dc)
    275   {
    276     dc = GNUNET_new (struct SignShareContext);
    277     dc->connection = connection;
    278     dc->id = *id;
    279     hc->ctx = dc;
    280   }
    281 
    282   /* parse request body */
    283   if (NULL == dc->json)
    284   {
    285     res = TALER_MHD_parse_post_json (dc->connection,
    286                                      &dc->post_ctx,
    287                                      sign_share_data,
    288                                      sign_share_data_size,
    289                                      &dc->json);
    290     if (GNUNET_SYSERR == res)
    291     {
    292       GNUNET_break (0);
    293       return MHD_NO;
    294     }
    295     if ((GNUNET_NO == res ||
    296          (NULL == dc->json)))
    297     {
    298       return MHD_YES;
    299     }
    300   }
    301 
    302   struct GNUNET_JSON_Specification spec[] = {
    303     GNUNET_JSON_spec_fixed_auto ("message_hash",
    304                                  &dc->message_hash),
    305     GNUNET_JSON_spec_fixed_auto ("encryption_key",
    306                                  &dc->encryption_key),
    307     GNUNET_JSON_spec_json ("commitments",
    308                            &json_commitments),
    309     GNUNET_JSON_spec_end ()
    310   };
    311 
    312   res = TALER_MHD_parse_json_data (connection,
    313                                    dc->json,
    314                                    spec);
    315 
    316   if (GNUNET_SYSERR == res)
    317   {
    318     GNUNET_JSON_parse_free (spec);
    319     GNUNET_break (0);
    320     return MHD_NO;
    321   }
    322   if (GNUNET_NO == res)
    323   {
    324     GNUNET_JSON_parse_free (spec);
    325     /* FROSIX_EC_PARAMETER_MALFORMED */
    326     GNUNET_break_op (0);
    327     return TALER_MHD_reply_with_error (connection,
    328                                        MHD_HTTP_BAD_REQUEST,
    329                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    330                                        "Unable to parse request body");
    331   }
    332 
    333   /* hash encryption key */
    334   FROSIX_hash_encryption_key (&dc->enc_key_hash,
    335                               &dc->encryption_key);
    336 
    337   /* validate id */
    338   {
    339     struct FROSIX_SigRequestIdP req_id;
    340     FROSIX_compute_signature_request_id (&req_id,
    341                                          &dc->enc_key_hash,
    342                                          &dc->message_hash);
    343     if (GNUNET_NO == FROST_hash_cmp (&req_id.id,
    344                                      &id->id))
    345     {
    346       GNUNET_JSON_parse_free (spec);
    347       /* FROSIX_EC_REQUEST_ID_NOT_MATCHING */
    348       GNUNET_break_op (0);
    349       return TALER_MHD_reply_with_error (connection,
    350                                          MHD_HTTP_BAD_REQUEST,
    351                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    352                                          "ID in URL not matching data in body");
    353     }
    354   }
    355 
    356   /* get number of provided commitments */
    357   dc->threshold = json_array_size (json_commitments);
    358   if (0 >= dc->threshold || dc->threshold >= 253)
    359   {
    360     GNUNET_JSON_parse_free (spec);
    361     GNUNET_break_op (0);
    362     /* FROSIX_EC_NO_OR_TOO_MANY_COMMITMENTS */
    363     return TALER_MHD_reply_with_error (connection,
    364                                        MHD_HTTP_BAD_REQUEST,
    365                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    366                                        "No or too many commitments");
    367   }
    368 
    369   /* parse commitments */
    370   dc->commitments = GNUNET_new_array (dc->threshold,
    371                                       struct FROST_Commitment);
    372 
    373   for (int i = 0; i < dc->threshold; i++)
    374   {
    375     struct GNUNET_JSON_Specification comm_spec[] = {
    376       GNUNET_JSON_spec_uint8 ("identifier",
    377                               &dc->commitments[i].identifier),
    378       GNUNET_JSON_spec_fixed_auto ("hiding_commitment",
    379                                    &dc->commitments[i].hiding_commitment),
    380       GNUNET_JSON_spec_fixed_auto ("binding_commitment",
    381                                    &dc->commitments[i].binding_commitment),
    382       GNUNET_JSON_spec_end ()
    383     };
    384 
    385     res = TALER_MHD_parse_json_array (connection,
    386                                       json_commitments,
    387                                       comm_spec,
    388                                       i,
    389                                       -1);
    390 
    391     if (GNUNET_SYSERR == res)
    392     {
    393       GNUNET_free (dc->commitments);
    394       GNUNET_JSON_parse_free (spec);
    395       GNUNET_JSON_parse_free (comm_spec);
    396       GNUNET_break (0);
    397       return MHD_NO;
    398     }
    399     if (GNUNET_NO == res)
    400     {
    401       GNUNET_free (dc->commitments);
    402       GNUNET_JSON_parse_free (spec);
    403       GNUNET_JSON_parse_free (comm_spec);
    404       GNUNET_break_op (0);
    405       /* FROSIX_EC_COMMITMENTS_MALFORMED */
    406       return TALER_MHD_reply_with_error (connection,
    407                                          MHD_HTTP_BAD_REQUEST,
    408                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    409                                          "Unable to parse request body");
    410     }
    411 
    412     GNUNET_JSON_parse_free (comm_spec);
    413   }
    414 
    415   GNUNET_JSON_parse_free (spec);
    416 
    417   /* validate commitments */
    418   for (unsigned int i = 0; i < dc->threshold; i++)
    419   {
    420     if (dc->commitments[i].identifier <= 0 || dc->commitments[i].identifier >=
    421         255 )
    422     {
    423       GNUNET_free (dc->commitments);
    424       GNUNET_break_op (0);
    425       /* FROSIX_EC_COMMITMENT_IDENTIFIER_WRONG */
    426       return TALER_MHD_reply_with_error (connection,
    427                                          MHD_HTTP_BAD_REQUEST,
    428                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    429                                          "Unable to validate commitments");
    430     }
    431 
    432     if (GNUNET_OK != FROST_validate_commitment (&dc->commitments[i]))
    433     {
    434       GNUNET_free (dc->commitments);
    435       GNUNET_break_op (0);
    436       /* FROSIX_EC_COMMITMENT_VALIDATION_FAILED (return id of failing
    437          commitment?) */
    438       return TALER_MHD_reply_with_error (connection,
    439                                          MHD_HTTP_BAD_REQUEST,
    440                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    441                                          "Unable to validate commitments");
    442     }
    443   }
    444 
    445   return return_sig_share (dc);
    446 }