anastasis

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

anastasis-crypto-tvg.c (18674B)


      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 (GNUNET_CRYPTO_QUALITY_WEAK, \
    142                                                    d, \
    143                                                    sizeof (*d));
    144 
    145 
    146 static int
    147 expect_data_fixed (json_t *vec,
    148                    const char *name,
    149                    void *data,
    150                    size_t expect_len)
    151 {
    152   const char *s = json_string_value (json_object_get (vec, name));
    153 
    154   if (NULL == s)
    155     return GNUNET_NO;
    156 
    157   if (GNUNET_OK != GNUNET_STRINGS_string_to_data (s,
    158                                                   strlen (s),
    159                                                   data,
    160                                                   expect_len))
    161     return GNUNET_NO;
    162   return GNUNET_OK;
    163 }
    164 
    165 
    166 static int
    167 expect_data_dynamic (json_t *vec,
    168                      const char *name,
    169                      void **data,
    170                      size_t *ret_len)
    171 {
    172   const char *s = json_string_value (json_object_get (vec, name));
    173   char *tmp;
    174   size_t len;
    175 
    176   if (NULL == s)
    177     return GNUNET_NO;
    178 
    179   len = (strlen (s) * 5) / 8;
    180   if (NULL != ret_len)
    181     *ret_len = len;
    182   tmp = GNUNET_malloc (len);
    183 
    184   if (GNUNET_OK != GNUNET_STRINGS_string_to_data (s, strlen (s), tmp, len))
    185   {
    186     GNUNET_free (tmp);
    187     return GNUNET_NO;
    188   }
    189   *data = tmp;
    190   return GNUNET_OK;
    191 }
    192 
    193 
    194 /**
    195  * Check a single vector.
    196  *
    197  * @param operation operator of the vector
    198  * @param vec the vector, a JSON object.
    199  *
    200  * @returns GNUNET_OK if the vector is okay
    201  */
    202 static int
    203 checkvec (const char *operation,
    204           json_t *vec)
    205 {
    206   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    207               "checking %s\n", operation);
    208 
    209   if (0 == strcmp (operation, "hash"))
    210   {
    211     void *data;
    212     size_t data_len;
    213     struct GNUNET_HashCode hash_out;
    214     struct GNUNET_HashCode hc;
    215 
    216     if (GNUNET_OK != expect_data_dynamic (vec,
    217                                           "input",
    218                                           &data,
    219                                           &data_len))
    220     {
    221       GNUNET_break (0);
    222       return GNUNET_SYSERR;
    223     }
    224     if (GNUNET_OK != expect_data_fixed (vec,
    225                                         "output",
    226                                         &hash_out,
    227                                         sizeof (hash_out)))
    228     {
    229       GNUNET_free (data);
    230       GNUNET_break (0);
    231       return GNUNET_NO;
    232     }
    233 
    234     GNUNET_CRYPTO_hash (data, data_len, &hc);
    235 
    236     if (0 != GNUNET_memcmp (&hc, &hash_out))
    237     {
    238       GNUNET_free (data);
    239       GNUNET_break (0);
    240       return GNUNET_NO;
    241     }
    242     GNUNET_free (data);
    243   }
    244 
    245   return GNUNET_OK;
    246 }
    247 
    248 
    249 /**
    250  * Check test vectors from stdin.
    251  *
    252  * @returns global exit code
    253  */
    254 static int
    255 check_vectors ()
    256 {
    257   json_error_t err;
    258   json_t *vecfile = json_loadf (stdin, 0, &err);
    259   const char *encoding;
    260   json_t *vectors;
    261 
    262   if (NULL == vecfile)
    263   {
    264     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unable to parse JSON\n");
    265     return 1;
    266   }
    267   encoding = json_string_value (json_object_get (vecfile,
    268                                                  "encoding"));
    269   if ( (NULL == encoding) || (0 != strcmp (encoding, "base32crockford")) )
    270   {
    271     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unsupported or missing encoding\n");
    272     json_decref (vecfile);
    273     return 1;
    274   }
    275   vectors = json_object_get (vecfile, "vectors");
    276   if (! json_is_array (vectors))
    277   {
    278     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "bad vectors\n");
    279     json_decref (vecfile);
    280     return 1;
    281   }
    282   {
    283     /* array is a JSON array */
    284     size_t index;
    285     json_t *value;
    286     int ret;
    287 
    288     json_array_foreach (vectors, index, value) {
    289       const char *op = json_string_value (json_object_get (value,
    290                                                            "operation"));
    291 
    292       if (NULL == op)
    293       {
    294         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    295                     "missing operation\n");
    296         ret = GNUNET_SYSERR;
    297         break;
    298       }
    299       ret = checkvec (op, value);
    300       if (GNUNET_OK != ret)
    301       {
    302         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    303                     "bad vector %u\n",
    304                     (unsigned int) index);
    305         break;
    306       }
    307     }
    308     return (ret == GNUNET_OK) ? 0 : 1;
    309   }
    310 }
    311 
    312 
    313 /**
    314  * Output test vectors.
    315  *
    316  * @returns global exit code
    317  */
    318 static int
    319 output_vectors ()
    320 {
    321   json_t *vecfile = json_object ();
    322   json_t *vecs = json_array ();
    323 
    324   GNUNET_assert (NULL != vecfile);
    325   GNUNET_assert (NULL != vecs);
    326   GNUNET_assert (0 ==
    327                  json_object_set_new (vecfile,
    328                                       "encoding",
    329                                       json_string ("base32crockford")));
    330   GNUNET_assert (0 ==
    331                  json_object_set_new (vecfile,
    332                                       "producer",
    333                                       json_string (
    334                                         "GNU Anastasis (C implementation) "
    335                                         PACKAGE_VERSION " "
    336                                         VCS_VERSION)));
    337   GNUNET_assert (0 ==
    338                  json_object_set_new (vecfile,
    339                                       "vectors",
    340                                       vecs));
    341 
    342   {
    343     json_t *vec = vec_for (vecs, "hash");
    344     struct GNUNET_HashCode hc;
    345     const char *str = "Hello, GNUnet";
    346 
    347     GNUNET_CRYPTO_hash (str, strlen (str), &hc);
    348 
    349     d2j (vec, "input", str, strlen (str));
    350     d2j (vec, "output", &hc, sizeof (struct GNUNET_HashCode));
    351   }
    352 
    353   {
    354     json_t *vec = vec_for (vecs, "user_identifier_derive");
    355     struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
    356     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    357     json_t *id_data = json_pack ("{s:s, s:s}",
    358                                  "name",
    359                                  "Fleabag",
    360                                  "ssn",
    361                                  "AB123");
    362     GNUNET_assert (NULL != id_data);
    363     random_auto (&provider_salt);
    364 
    365     ANASTASIS_CRYPTO_user_identifier_derive (id_data,
    366                                              &provider_salt,
    367                                              &id);
    368     GNUNET_assert (0 ==
    369                    json_object_set_new (vec,
    370                                         "input_id_data",
    371                                         id_data));
    372     d2j_auto (vec,
    373               "input_provider_salt",
    374               &provider_salt);
    375     d2j_auto (vec,
    376               "output_id",
    377               &id);
    378   }
    379 
    380   {
    381     json_t *vec = vec_for (vecs, "account_keypair_derive");
    382     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    383     struct ANASTASIS_CRYPTO_AccountPrivateKeyP priv_key;
    384     struct ANASTASIS_CRYPTO_AccountPublicKeyP pub_key;
    385 
    386     random_auto (&id);
    387     ANASTASIS_CRYPTO_account_public_key_derive (&id, &pub_key);
    388     ANASTASIS_CRYPTO_account_private_key_derive (&id, &priv_key);
    389 
    390     d2j_auto (vec, "input_id", &id);
    391     d2j_auto (vec, "output_priv_key", &priv_key);
    392     d2j_auto (vec, "output_pub_key", &pub_key);
    393 
    394   }
    395 
    396   {
    397     json_t *vec = vec_for (vecs, "secure_answer_hash");
    398     const char *answer = "Blah";
    399     struct ANASTASIS_CRYPTO_TruthUUIDP uuid;
    400     struct ANASTASIS_CRYPTO_QuestionSaltP salt;
    401     struct GNUNET_HashCode result;
    402 
    403     random_auto (&uuid);
    404     random_auto (&salt);
    405     ANASTASIS_CRYPTO_secure_answer_hash (answer,
    406                                          &uuid,
    407                                          &salt,
    408                                          &result);
    409     GNUNET_assert (0 ==
    410                    json_object_set_new (vec,
    411                                         "input_answer",
    412                                         json_string (answer)));
    413     d2j_auto (vec, "input_uuid", &uuid);
    414     d2j_auto (vec, "input_salt", &salt);
    415     d2j_auto (vec, "output_hash", &result);
    416   }
    417 
    418   {
    419     json_t *vec = vec_for (vecs, "recovery_document_encryption");
    420     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    421     const void *rec_doc = "my recovery doc";
    422     size_t rd_size = strlen (rec_doc) + 1;
    423     void *enc_rec_doc;
    424     size_t erd_size;
    425 
    426     random_auto (&id);
    427 
    428     ANASTASIS_CRYPTO_recovery_document_encrypt (&id,
    429                                                 rec_doc,
    430                                                 rd_size,
    431                                                 &enc_rec_doc,
    432                                                 &erd_size);
    433     d2j_auto (vec, "input_user_id", &id);
    434     d2j (vec, "input_recovery_document", rec_doc, rd_size);
    435     d2j (vec, "output_encrypted_recovery_document", &enc_rec_doc, erd_size);
    436   }
    437 
    438   {
    439     /* With extra salt */
    440     json_t *vec = vec_for (vecs, "keyshare_encryption");
    441     struct ANASTASIS_CRYPTO_KeyShareP key_share;
    442     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    443     const char *xsalt = "myanswer";
    444     struct ANASTASIS_CRYPTO_EncryptedKeyShareP enc_key_share;
    445 
    446     random_auto (&key_share);
    447     random_auto (&id);
    448 
    449     ANASTASIS_CRYPTO_keyshare_encrypt (&key_share,
    450                                        &id,
    451                                        xsalt,
    452                                        &enc_key_share);
    453     d2j_auto (vec, "input_key_share", &key_share);
    454     d2j_auto (vec, "input_user_id", &id);
    455     GNUNET_assert (0 ==
    456                    json_object_set_new (vec,
    457                                         "input_xsalt",
    458                                         json_string (xsalt)));
    459     d2j_auto (vec,
    460               "output_enc_key_share",
    461               &enc_key_share);
    462   }
    463 
    464   {
    465     /* Without extra salt */
    466     json_t *vec = vec_for (vecs, "keyshare_encryption");
    467     struct ANASTASIS_CRYPTO_KeyShareP key_share;
    468     struct ANASTASIS_CRYPTO_UserIdentifierP id;
    469     char *xsalt = NULL;
    470     struct ANASTASIS_CRYPTO_EncryptedKeyShareP enc_key_share;
    471 
    472     random_auto (&key_share);
    473     random_auto (&id);
    474 
    475     ANASTASIS_CRYPTO_keyshare_encrypt (&key_share,
    476                                        &id,
    477                                        xsalt,
    478                                        &enc_key_share);
    479     d2j_auto (vec, "input_key_share", &key_share);
    480     d2j_auto (vec, "input_user_id", &id);
    481     GNUNET_assert (0 ==
    482                    json_object_set_new (vec,
    483                                         "input_xsalt",
    484                                         json_null ()));
    485     d2j_auto (vec, "output_enc_key_share", &enc_key_share);
    486   }
    487 
    488   {
    489     json_t *vec = vec_for (vecs, "truth_encryption");
    490 
    491     struct ANASTASIS_CRYPTO_NonceP nonce;
    492     struct ANASTASIS_CRYPTO_TruthKeyP truth_enc_key;
    493     char truth[256];
    494     size_t truth_size = 256;
    495     void *enc_truth;
    496     size_t ect_size;
    497 
    498     random_auto (&nonce);
    499     random_auto (&truth);
    500     random_auto (&truth_enc_key);
    501 
    502     ANASTASIS_CRYPTO_truth_encrypt (&nonce,
    503                                     &truth_enc_key,
    504                                     truth,
    505                                     truth_size,
    506                                     &enc_truth,
    507                                     &ect_size);
    508 
    509     d2j_auto (vec, "input_nonce", &nonce);
    510     d2j_auto (vec, "input_truth_enc_key", &truth_enc_key);
    511     d2j (vec, "input_truth", &truth, truth_size);
    512     d2j (vec, "output_encrypted_truth", enc_truth, ect_size);
    513   }
    514 
    515   {
    516     json_t *vec = vec_for (vecs, "policy_key_derive");
    517 
    518     struct ANASTASIS_CRYPTO_KeyShareP key_shares[2];
    519     unsigned int keyshare_length = 2;
    520     struct ANASTASIS_CRYPTO_MasterSaltP salt;
    521     struct ANASTASIS_CRYPTO_PolicyKeyP policy_key;
    522     json_t *key_shares_json = json_array ();
    523 
    524     GNUNET_assert (NULL != key_shares_json);
    525     random_auto (&key_shares[0]);
    526     random_auto (&key_shares[1]);
    527     random_auto (&salt);
    528 
    529     ANASTASIS_CRYPTO_policy_key_derive (key_shares,
    530                                         keyshare_length,
    531                                         &salt,
    532                                         &policy_key);
    533 
    534     d2j_append_auto (key_shares_json, &key_shares[0]);
    535     d2j_append_auto (key_shares_json, &key_shares[1]);
    536     GNUNET_assert (0 ==
    537                    json_object_set_new (vec,
    538                                         "input_key_shares",
    539                                         key_shares_json));
    540     d2j_auto (vec, "input_salt", &salt);
    541     d2j_auto (vec, "output_policy_key", &policy_key);
    542   }
    543 
    544   {
    545     // json_t *vec = vec_for (vecs, "core_secret_encryption");
    546     // struct ANASTASIS_CRYPTO_PolicyKeyP policy_keys[2];
    547     // unsigned int policy_keys_length = 2;
    548     // char core_secret[256];
    549     // size_t core_secret_size = 256;
    550     // void *enc_core_secret;
    551     // struct ANASTASIS_CRYPTO_EncryptedMasterKeyP encrypted_master_keys[2];
    552     // json_t *policy_keys_json = json_array ();
    553     // json_t *encrypted_master_keys_json = json_array ();
    554 
    555     // random_auto (&policy_keys[0]);
    556     // random_auto (&policy_keys[1]);
    557     // random_auto (&core_secret);
    558 
    559     // ANASTASIS_CRYPTO_core_secret_encrypt (policy_keys, policy_keys_length,
    560     //                                       core_secret, core_secret_size,
    561     //                                       &enc_core_secret,
    562     //                                       encrypted_master_keys);
    563 
    564     // d2j_append_auto (policy_keys_json, &policy_keys_json[0]);
    565     // d2j_append_auto (policy_keys_json, &policy_keys_json[1]);
    566     // d2j_append_auto (encrypted_master_keys_json, &encrypted_master_keys[0]);
    567     // d2j_append_auto (encrypted_master_keys_json, &encrypted_master_keys[1]);
    568 
    569     // d2j_auto (vec, "input_core_secret", &core_secret);
    570     // json_object_set_new (vec, "input_policy_keys", policy_keys_json);
    571     // json_object_set_new (vec, "output_encrypted_core_secret", encrypted_master_keys_json);
    572     // json_object_set_new (vec, "output_encrypted_master_keys", encrypted_master_keys_json);
    573   }
    574 
    575 
    576   json_dumpf (vecfile, stdout, JSON_INDENT (2));
    577   json_decref (vecfile);
    578   printf ("\n");
    579 
    580   return 0;
    581 }
    582 
    583 
    584 /**
    585  * Main function that will be run.
    586  *
    587  * @param cls closure
    588  * @param args remaining command-line arguments
    589  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    590  * @param cfg configuration
    591  */
    592 static void
    593 run (void *cls,
    594      char *const *args,
    595      const char *cfgfile,
    596      const struct GNUNET_CONFIGURATION_Handle *cfg)
    597 {
    598   if (GNUNET_YES == verify_flag)
    599     global_ret = check_vectors ();
    600   else
    601     global_ret = output_vectors ();
    602 }
    603 
    604 
    605 /**
    606  * The main function of the test vector generation tool.
    607  *
    608  * @param argc number of arguments from the command line
    609  * @param argv command line arguments
    610  * @return 0 ok, 1 on error
    611  */
    612 int
    613 main (int argc,
    614       char *const *argv)
    615 {
    616   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    617     GNUNET_GETOPT_option_flag ('V',
    618                                "verify",
    619                                gettext_noop (
    620                                  "verify a test vector from stdin"),
    621                                &verify_flag),
    622     GNUNET_GETOPT_OPTION_END
    623   };
    624 
    625   GNUNET_assert (GNUNET_OK ==
    626                  GNUNET_log_setup ("anastasis-crypto-tvg",
    627                                    "INFO",
    628                                    NULL));
    629   if (GNUNET_OK !=
    630       GNUNET_PROGRAM_run (ANASTASIS_project_data (),
    631                           argc, argv,
    632                           "anastasis-crypto-tvg",
    633                           "Generate test vectors for cryptographic operations",
    634                           options,
    635                           &run, NULL))
    636     return 1;
    637   return global_ret;
    638 }
    639 
    640 
    641 /* end of anastasis-crypto-tvg.c */