anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis-crypto-tvg.c (18537B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020,2021 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis 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 General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file util/anastasis-crypto-tgv.c
     18  * @brief Generate test vectors for cryptographic operations.
     19  * @author Florian Dold
     20  *
     21  *
     22  * Test vectors have the following format (TypeScript pseudo code):
     23  *
     24  * interface TestVectorFile {
     25  *   encoding: "base32crockford";
     26  *   producer?: string;
     27  *   vectors: TestVector[];
     28  * }
     29  *
     30  * enum Operation {
     31  *  Hash("hash"),
     32  *  ...
     33  * }
     34  *
     35  * interface TestVector {
     36  *   operation: Operation;
     37  *   // Inputs for the operation
     38  *   [ k: string]: string | number;
     39  * };
     40  *
     41  *
     42  */
     43 #include "platform.h"
     44 #include <gnunet/gnunet_util_lib.h>
     45 #include <gnunet/gnunet_signatures.h>
     46 #include <gnunet/gnunet_testing_lib.h>
     47 #include <jansson.h>
     48 #include <gcrypt.h>
     49 #include "anastasis_crypto_lib.h"
     50 #include "anastasis_util_lib.h"
     51 
     52 
     53 /**
     54  * Should we verify or output test vectors?
     55  */
     56 static int verify_flag = GNUNET_NO;
     57 
     58 
     59 /**
     60  * Global exit code.
     61  */
     62 static int global_ret = 0;
     63 
     64 
     65 /**
     66  * Create a fresh test vector for a given operation label.
     67  *
     68  * @param vecs array of vectors to append the new vector to
     69  * @param vecname label for the operation of the vector
     70  * @returns the fresh test vector
     71  */
     72 static json_t *
     73 vec_for (json_t *vecs, const char *vecname)
     74 {
     75   json_t *t = json_object ();
     76 
     77   GNUNET_assert (0 ==
     78                  json_object_set_new (t,
     79                                       "operation",
     80                                       json_string (vecname)));
     81   GNUNET_assert (0 ==
     82                  json_array_append_new (vecs,
     83                                         t));
     84   return t;
     85 }
     86 
     87 
     88 /**
     89  * Add a base32crockford encoded value
     90  * to a test vector.
     91  *
     92  * @param vec test vector to add to
     93  * @param label label for the value
     94  * @param data data to add
     95  * @param size size of data
     96  */
     97 static void
     98 d2j (json_t *vec,
     99      const char *label,
    100      const void *data,
    101      size_t size)
    102 {
    103   char *buf;
    104   json_t *json;
    105 
    106   buf = GNUNET_STRINGS_data_to_string_alloc (data,
    107                                              size);
    108   json = json_string (buf);
    109   GNUNET_free (buf);
    110   GNUNET_break (NULL != json);
    111 
    112   GNUNET_assert (0 ==
    113                  json_object_set_new (vec,
    114                                       label,
    115                                       json));
    116 }
    117 
    118 
    119 static void
    120 d2j_append (json_t *arr,
    121             const void *data,
    122             size_t size)
    123 {
    124   char *buf;
    125   json_t *json;
    126 
    127   buf = GNUNET_STRINGS_data_to_string_alloc (data,
    128                                              size);
    129   json = json_string (buf);
    130   GNUNET_assert (NULL != json);
    131   GNUNET_free (buf);
    132 
    133   GNUNET_assert (0 ==
    134                  json_array_append_new (arr,
    135                                         json));
    136 }
    137 
    138 
    139 #define d2j_auto(vec, label, d) d2j (vec, label, d, sizeof (*d))
    140 #define d2j_append_auto(arr,  d) d2j_append (arr, d, sizeof (*d))
    141 #define random_auto(d) GNUNET_CRYPTO_random_block (d, \
    142                                                    sizeof (*d));
    143 
    144 
    145 static int
    146 expect_data_fixed (json_t *vec,
    147                    const char *name,
    148                    void *data,
    149                    size_t expect_len)
    150 {
    151   const char *s = json_string_value (json_object_get (vec, name));
    152 
    153   if (NULL == s)
    154     return GNUNET_NO;
    155 
    156   if (GNUNET_OK != GNUNET_STRINGS_string_to_data (s,
    157                                                   strlen (s),
    158                                                   data,
    159                                                   expect_len))
    160     return GNUNET_NO;
    161   return GNUNET_OK;
    162 }
    163 
    164 
    165 static int
    166 expect_data_dynamic (json_t *vec,
    167                      const char *name,
    168                      void **data,
    169                      size_t *ret_len)
    170 {
    171   const char *s = json_string_value (json_object_get (vec, name));
    172   char *tmp;
    173   size_t len;
    174 
    175   if (NULL == s)
    176     return GNUNET_NO;
    177 
    178   len = (strlen (s) * 5) / 8;
    179   if (NULL != ret_len)
    180     *ret_len = len;
    181   tmp = GNUNET_malloc (len);
    182 
    183   if (GNUNET_OK != GNUNET_STRINGS_string_to_data (s, strlen (s), tmp, len))
    184   {
    185     GNUNET_free (tmp);
    186     return GNUNET_NO;
    187   }
    188   *data = tmp;
    189   return GNUNET_OK;
    190 }
    191 
    192 
    193 /**
    194  * Check a single vector.
    195  *
    196  * @param operation operator of the vector
    197  * @param vec the vector, a JSON object.
    198  *
    199  * @returns GNUNET_OK if the vector is okay
    200  */
    201 static int
    202 checkvec (const char *operation,
    203           json_t *vec)
    204 {
    205   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    206               "checking %s\n", operation);
    207 
    208   if (0 == strcmp (operation, "hash"))
    209   {
    210     void *data;
    211     size_t data_len;
    212     struct GNUNET_HashCode hash_out;
    213     struct GNUNET_HashCode hc;
    214 
    215     if (GNUNET_OK != expect_data_dynamic (vec,
    216                                           "input",
    217                                           &data,
    218                                           &data_len))
    219     {
    220       GNUNET_break (0);
    221       return GNUNET_SYSERR;
    222     }
    223     if (GNUNET_OK != expect_data_fixed (vec,
    224                                         "output",
    225                                         &hash_out,
    226                                         sizeof (hash_out)))
    227     {
    228       GNUNET_free (data);
    229       GNUNET_break (0);
    230       return GNUNET_NO;
    231     }
    232 
    233     GNUNET_CRYPTO_hash (data, data_len, &hc);
    234 
    235     if (0 != GNUNET_memcmp (&hc, &hash_out))
    236     {
    237       GNUNET_free (data);
    238       GNUNET_break (0);
    239       return GNUNET_NO;
    240     }
    241     GNUNET_free (data);
    242   }
    243 
    244   return GNUNET_OK;
    245 }
    246 
    247 
    248 /**
    249  * Check test vectors from stdin.
    250  *
    251  * @returns global exit code
    252  */
    253 static int
    254 check_vectors ()
    255 {
    256   json_error_t err;
    257   json_t *vecfile = json_loadf (stdin, 0, &err);
    258   const char *encoding;
    259   json_t *vectors;
    260 
    261   if (NULL == vecfile)
    262   {
    263     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unable to parse JSON\n");
    264     return 1;
    265   }
    266   encoding = json_string_value (json_object_get (vecfile,
    267                                                  "encoding"));
    268   if ( (NULL == encoding) || (0 != strcmp (encoding, "base32crockford")) )
    269   {
    270     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unsupported or missing encoding\n");
    271     json_decref (vecfile);
    272     return 1;
    273   }
    274   vectors = json_object_get (vecfile, "vectors");
    275   if (! json_is_array (vectors))
    276   {
    277     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "bad vectors\n");
    278     json_decref (vecfile);
    279     return 1;
    280   }
    281   {
    282     /* array is a JSON array */
    283     size_t index;
    284     json_t *value;
    285     int ret;
    286 
    287     json_array_foreach (vectors, index, value) {
    288       const char *op = json_string_value (json_object_get (value,
    289                                                            "operation"));
    290 
    291       if (NULL == op)
    292       {
    293         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    294                     "missing operation\n");
    295         ret = GNUNET_SYSERR;
    296         break;
    297       }
    298       ret = checkvec (op, value);
    299       if (GNUNET_OK != ret)
    300       {
    301         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    302                     "bad vector %u\n",
    303                     (unsigned int) index);
    304         break;
    305       }
    306     }
    307     return (ret == GNUNET_OK) ? 0 : 1;
    308   }
    309 }
    310 
    311 
    312 /**
    313  * Output test vectors.
    314  *
    315  * @returns global exit code
    316  */
    317 static int
    318 output_vectors ()
    319 {
    320   json_t *vecfile = json_object ();
    321   json_t *vecs = json_array ();
    322 
    323   GNUNET_assert (NULL != vecfile);
    324   GNUNET_assert (NULL != vecs);
    325   GNUNET_assert (0 ==
    326                  json_object_set_new (vecfile,
    327                                       "encoding",
    328                                       json_string ("base32crockford")));
    329   GNUNET_assert (0 ==
    330                  json_object_set_new (vecfile,
    331                                       "producer",
    332                                       json_string (
    333                                         "GNU Anastasis (C implementation) "
    334                                         PACKAGE_VERSION)));
    335   GNUNET_assert (0 ==
    336                  json_object_set_new (vecfile,
    337                                       "vectors",
    338                                       vecs));
    339 
    340   {
    341     json_t *vec = vec_for (vecs, "hash");
    342     struct GNUNET_HashCode hc;
    343     const char *str = "Hello, GNUnet";
    344 
    345     GNUNET_CRYPTO_hash (str, strlen (str), &hc);
    346 
    347     d2j (vec, "input", str, strlen (str));
    348     d2j (vec, "output", &hc, sizeof (struct GNUNET_HashCode));
    349   }
    350 
    351   {
    352     json_t *vec = vec_for (vecs, "user_identifier_derive");
    353     struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
    354     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    355     json_t *id_data = json_pack ("{s:s, s:s}",
    356                                  "name",
    357                                  "Fleabag",
    358                                  "ssn",
    359                                  "AB123");
    360     GNUNET_assert (NULL != id_data);
    361     random_auto (&provider_salt);
    362 
    363     ANASTASIS_CRYPTO_user_identifier_derive (id_data,
    364                                              &provider_salt,
    365                                              &id);
    366     GNUNET_assert (0 ==
    367                    json_object_set_new (vec,
    368                                         "input_id_data",
    369                                         id_data));
    370     d2j_auto (vec,
    371               "input_provider_salt",
    372               &provider_salt);
    373     d2j_auto (vec,
    374               "output_id",
    375               &id);
    376   }
    377 
    378   {
    379     json_t *vec = vec_for (vecs, "account_keypair_derive");
    380     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    381     struct ANASTASIS_CRYPTO_AccountPrivateKeyP priv_key;
    382     struct ANASTASIS_CRYPTO_AccountPublicKeyP pub_key;
    383 
    384     random_auto (&id);
    385     ANASTASIS_CRYPTO_account_public_key_derive (&id, &pub_key);
    386     ANASTASIS_CRYPTO_account_private_key_derive (&id, &priv_key);
    387 
    388     d2j_auto (vec, "input_id", &id);
    389     d2j_auto (vec, "output_priv_key", &priv_key);
    390     d2j_auto (vec, "output_pub_key", &pub_key);
    391 
    392   }
    393 
    394   {
    395     json_t *vec = vec_for (vecs, "secure_answer_hash");
    396     const char *answer = "Blah";
    397     struct ANASTASIS_CRYPTO_TruthUUIDP uuid;
    398     struct ANASTASIS_CRYPTO_QuestionSaltP salt;
    399     struct GNUNET_HashCode result;
    400 
    401     random_auto (&uuid);
    402     random_auto (&salt);
    403     ANASTASIS_CRYPTO_secure_answer_hash (answer,
    404                                          &uuid,
    405                                          &salt,
    406                                          &result);
    407     GNUNET_assert (0 ==
    408                    json_object_set_new (vec,
    409                                         "input_answer",
    410                                         json_string (answer)));
    411     d2j_auto (vec, "input_uuid", &uuid);
    412     d2j_auto (vec, "input_salt", &salt);
    413     d2j_auto (vec, "output_hash", &result);
    414   }
    415 
    416   {
    417     json_t *vec = vec_for (vecs, "recovery_document_encryption");
    418     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    419     const void *rec_doc = "my recovery doc";
    420     size_t rd_size = strlen (rec_doc) + 1;
    421     void *enc_rec_doc;
    422     size_t erd_size;
    423 
    424     random_auto (&id);
    425 
    426     ANASTASIS_CRYPTO_recovery_document_encrypt (&id,
    427                                                 rec_doc,
    428                                                 rd_size,
    429                                                 &enc_rec_doc,
    430                                                 &erd_size);
    431     d2j_auto (vec, "input_user_id", &id);
    432     d2j (vec, "input_recovery_document", rec_doc, rd_size);
    433     d2j (vec, "output_encrypted_recovery_document", &enc_rec_doc, erd_size);
    434   }
    435 
    436   {
    437     /* With extra salt */
    438     json_t *vec = vec_for (vecs, "keyshare_encryption");
    439     struct ANASTASIS_CRYPTO_KeyShareP key_share;
    440     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    441     const char *xsalt = "myanswer";
    442     struct ANASTASIS_CRYPTO_EncryptedKeyShareP enc_key_share;
    443 
    444     random_auto (&key_share);
    445     random_auto (&id);
    446 
    447     ANASTASIS_CRYPTO_keyshare_encrypt (&key_share,
    448                                        &id,
    449                                        xsalt,
    450                                        &enc_key_share);
    451     d2j_auto (vec, "input_key_share", &key_share);
    452     d2j_auto (vec, "input_user_id", &id);
    453     GNUNET_assert (0 ==
    454                    json_object_set_new (vec,
    455                                         "input_xsalt",
    456                                         json_string (xsalt)));
    457     d2j_auto (vec,
    458               "output_enc_key_share",
    459               &enc_key_share);
    460   }
    461 
    462   {
    463     /* Without extra salt */
    464     json_t *vec = vec_for (vecs, "keyshare_encryption");
    465     struct ANASTASIS_CRYPTO_KeyShareP key_share;
    466     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    467     char *xsalt = NULL;
    468     struct ANASTASIS_CRYPTO_EncryptedKeyShareP enc_key_share;
    469 
    470     random_auto (&key_share);
    471     random_auto (&id);
    472 
    473     ANASTASIS_CRYPTO_keyshare_encrypt (&key_share,
    474                                        &id,
    475                                        xsalt,
    476                                        &enc_key_share);
    477     d2j_auto (vec, "input_key_share", &key_share);
    478     d2j_auto (vec, "input_user_id", &id);
    479     GNUNET_assert (0 ==
    480                    json_object_set_new (vec,
    481                                         "input_xsalt",
    482                                         json_null ()));
    483     d2j_auto (vec, "output_enc_key_share", &enc_key_share);
    484   }
    485 
    486   {
    487     json_t *vec = vec_for (vecs, "truth_encryption");
    488 
    489     struct ANASTASIS_CRYPTO_NonceP nonce;
    490     struct ANASTASIS_CRYPTO_TruthKeyP truth_enc_key;
    491     char truth[256];
    492     size_t truth_size = 256;
    493     void *enc_truth;
    494     size_t ect_size;
    495 
    496     random_auto (&nonce);
    497     random_auto (&truth);
    498     random_auto (&truth_enc_key);
    499 
    500     ANASTASIS_CRYPTO_truth_encrypt (&nonce,
    501                                     &truth_enc_key,
    502                                     truth,
    503                                     truth_size,
    504                                     &enc_truth,
    505                                     &ect_size);
    506 
    507     d2j_auto (vec, "input_nonce", &nonce);
    508     d2j_auto (vec, "input_truth_enc_key", &truth_enc_key);
    509     d2j (vec, "input_truth", &truth, truth_size);
    510     d2j (vec, "output_encrypted_truth", enc_truth, ect_size);
    511   }
    512 
    513   {
    514     json_t *vec = vec_for (vecs, "policy_key_derive");
    515 
    516     struct ANASTASIS_CRYPTO_KeyShareP key_shares[2];
    517     unsigned int keyshare_length = 2;
    518     struct ANASTASIS_CRYPTO_MasterSaltP salt;
    519     struct ANASTASIS_CRYPTO_PolicyKeyP policy_key;
    520     json_t *key_shares_json = json_array ();
    521 
    522     GNUNET_assert (NULL != key_shares_json);
    523     random_auto (&key_shares[0]);
    524     random_auto (&key_shares[1]);
    525     random_auto (&salt);
    526 
    527     ANASTASIS_CRYPTO_policy_key_derive (key_shares,
    528                                         keyshare_length,
    529                                         &salt,
    530                                         &policy_key);
    531 
    532     d2j_append_auto (key_shares_json, &key_shares[0]);
    533     d2j_append_auto (key_shares_json, &key_shares[1]);
    534     GNUNET_assert (0 ==
    535                    json_object_set_new (vec,
    536                                         "input_key_shares",
    537                                         key_shares_json));
    538     d2j_auto (vec, "input_salt", &salt);
    539     d2j_auto (vec, "output_policy_key", &policy_key);
    540   }
    541 
    542   {
    543     // json_t *vec = vec_for (vecs, "core_secret_encryption");
    544     // struct ANASTASIS_CRYPTO_PolicyKeyP policy_keys[2];
    545     // unsigned int policy_keys_length = 2;
    546     // char core_secret[256];
    547     // size_t core_secret_size = 256;
    548     // void *enc_core_secret;
    549     // struct ANASTASIS_CRYPTO_EncryptedMasterKeyP encrypted_master_keys[2];
    550     // json_t *policy_keys_json = json_array ();
    551     // json_t *encrypted_master_keys_json = json_array ();
    552 
    553     // random_auto (&policy_keys[0]);
    554     // random_auto (&policy_keys[1]);
    555     // random_auto (&core_secret);
    556 
    557     // ANASTASIS_CRYPTO_core_secret_encrypt (policy_keys, policy_keys_length,
    558     //                                       core_secret, core_secret_size,
    559     //                                       &enc_core_secret,
    560     //                                       encrypted_master_keys);
    561 
    562     // d2j_append_auto (policy_keys_json, &policy_keys_json[0]);
    563     // d2j_append_auto (policy_keys_json, &policy_keys_json[1]);
    564     // d2j_append_auto (encrypted_master_keys_json, &encrypted_master_keys[0]);
    565     // d2j_append_auto (encrypted_master_keys_json, &encrypted_master_keys[1]);
    566 
    567     // d2j_auto (vec, "input_core_secret", &core_secret);
    568     // json_object_set_new (vec, "input_policy_keys", policy_keys_json);
    569     // json_object_set_new (vec, "output_encrypted_core_secret", encrypted_master_keys_json);
    570     // json_object_set_new (vec, "output_encrypted_master_keys", encrypted_master_keys_json);
    571   }
    572 
    573 
    574   json_dumpf (vecfile, stdout, JSON_INDENT (2));
    575   json_decref (vecfile);
    576   printf ("\n");
    577 
    578   return 0;
    579 }
    580 
    581 
    582 /**
    583  * Main function that will be run.
    584  *
    585  * @param cls closure
    586  * @param args remaining command-line arguments
    587  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    588  * @param cfg configuration
    589  */
    590 static void
    591 run (void *cls,
    592      char *const *args,
    593      const char *cfgfile,
    594      const struct GNUNET_CONFIGURATION_Handle *cfg)
    595 {
    596   if (GNUNET_YES == verify_flag)
    597     global_ret = check_vectors ();
    598   else
    599     global_ret = output_vectors ();
    600 }
    601 
    602 
    603 /**
    604  * The main function of the test vector generation tool.
    605  *
    606  * @param argc number of arguments from the command line
    607  * @param argv command line arguments
    608  * @return 0 ok, 1 on error
    609  */
    610 int
    611 main (int argc,
    612       char *const *argv)
    613 {
    614   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    615     GNUNET_GETOPT_option_flag ('V',
    616                                "verify",
    617                                gettext_noop (
    618                                  "verify a test vector from stdin"),
    619                                &verify_flag),
    620     GNUNET_GETOPT_OPTION_END
    621   };
    622 
    623   GNUNET_assert (GNUNET_OK ==
    624                  GNUNET_log_setup ("anastasis-crypto-tvg",
    625                                    "INFO",
    626                                    NULL));
    627   if (GNUNET_OK !=
    628       GNUNET_PROGRAM_run (ANASTASIS_project_data (),
    629                           argc, argv,
    630                           "anastasis-crypto-tvg",
    631                           "Generate test vectors for cryptographic operations",
    632                           options,
    633                           &run, NULL))
    634     return 1;
    635   return global_ret;
    636 }
    637 
    638 
    639 /* end of anastasis-crypto-tvg.c */