LCOV - code coverage report
Current view: top level - util - test_crypto.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 85.8 % 169 145
Test Date: 2026-04-14 15:39:31 Functions: 100.0 % 9 9

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2015, 2020-2024 Taler Systems SA
       4              : 
       5              :   TALER 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              :   TALER 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              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : 
      17              : /**
      18              :  * @file util/test_crypto.c
      19              :  * @brief Tests for Taler-specific crypto logic
      20              :  * @author Christian Grothoff <christian@grothoff.org>
      21              :  */
      22              : #include "taler/taler_util.h"
      23              : 
      24              : 
      25              : /**
      26              :  * Test high-level link encryption/decryption API.
      27              :  *
      28              :  * @return 0 on success
      29              :  */
      30              : static int
      31            1 : test_high_level (void)
      32              : {
      33              :   struct TALER_CoinSpendPrivateKeyP coin_priv;
      34              :   struct TALER_CoinSpendPublicKeyP coin_pub;
      35              :   struct TALER_TransferPrivateKeyP trans_priv;
      36              :   struct TALER_TransferPublicKeyP trans_pub;
      37              :   struct TALER_TransferSecretP secret;
      38              :   struct TALER_TransferSecretP secret2;
      39              :   union GNUNET_CRYPTO_BlindingSecretP bks1;
      40              :   union GNUNET_CRYPTO_BlindingSecretP bks2;
      41              :   struct TALER_CoinSpendPrivateKeyP coin_priv1;
      42              :   struct TALER_CoinSpendPrivateKeyP coin_priv2;
      43              :   struct TALER_PlanchetMasterSecretP ps1;
      44              :   struct TALER_PlanchetMasterSecretP ps2;
      45            1 :   struct GNUNET_CRYPTO_BlindingInputValues bi = {
      46              :     .cipher = GNUNET_CRYPTO_BSA_RSA
      47              :   };
      48            1 :   struct TALER_ExchangeBlindingValues alg1 = {
      49              :     .blinding_inputs = &bi
      50              :   };
      51            1 :   struct TALER_ExchangeBlindingValues alg2 = {
      52              :     .blinding_inputs = &bi
      53              :   };
      54              : 
      55            1 :   GNUNET_CRYPTO_eddsa_key_create (&coin_priv.eddsa_priv);
      56            1 :   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
      57              :                                       &coin_pub.eddsa_pub);
      58            1 :   GNUNET_CRYPTO_ecdhe_key_create (&trans_priv.ecdhe_priv);
      59            1 :   GNUNET_CRYPTO_ecdhe_key_get_public (&trans_priv.ecdhe_priv,
      60              :                                       &trans_pub.ecdhe_pub);
      61            1 :   TALER_link_derive_transfer_secret (&coin_priv,
      62              :                                      &trans_priv,
      63              :                                      &secret);
      64            1 :   TALER_link_reveal_transfer_secret (&trans_priv,
      65              :                                      &coin_pub,
      66              :                                      &secret2);
      67            1 :   GNUNET_assert (0 ==
      68              :                  GNUNET_memcmp (&secret,
      69              :                                 &secret2));
      70            1 :   TALER_link_recover_transfer_secret (&trans_pub,
      71              :                                       &coin_priv,
      72              :                                       &secret2);
      73            1 :   GNUNET_assert (0 ==
      74              :                  GNUNET_memcmp (&secret,
      75              :                                 &secret2));
      76            1 :   TALER_transfer_secret_to_planchet_secret (&secret,
      77              :                                             0,
      78              :                                             &ps1);
      79            1 :   TALER_planchet_setup_coin_priv (&ps1,
      80              :                                   &alg1,
      81              :                                   &coin_priv1);
      82            1 :   TALER_planchet_blinding_secret_create (&ps1,
      83              :                                          &alg1,
      84              :                                          &bks1);
      85            1 :   TALER_transfer_secret_to_planchet_secret (&secret,
      86              :                                             1,
      87              :                                             &ps2);
      88            1 :   TALER_planchet_setup_coin_priv (&ps2,
      89              :                                   &alg2,
      90              :                                   &coin_priv2);
      91            1 :   TALER_planchet_blinding_secret_create (&ps2,
      92              :                                          &alg2,
      93              :                                          &bks2);
      94            1 :   GNUNET_assert (0 !=
      95              :                  GNUNET_memcmp (&ps1,
      96              :                                 &ps2));
      97            1 :   GNUNET_assert (0 !=
      98              :                  GNUNET_memcmp (&coin_priv1,
      99              :                                 &coin_priv2));
     100            1 :   GNUNET_assert (0 !=
     101              :                  GNUNET_memcmp (&bks1,
     102              :                                 &bks2));
     103            1 :   return 0;
     104              : }
     105              : 
     106              : 
     107              : static struct TALER_AgeMask age_mask = {
     108              :   .bits = 1 | 1 << 8 | 1 << 10 | 1 << 12
     109              :           | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21
     110              : };
     111              : 
     112              : /**
     113              :  * Test the basic planchet functionality of creating a fresh planchet
     114              :  * and extracting the respective signature.
     115              :  *
     116              :  * @return 0 on success
     117              :  */
     118              : static int
     119            2 : test_planchets_rsa (uint8_t age)
     120              : {
     121              :   struct TALER_PlanchetMasterSecretP ps;
     122              :   struct TALER_CoinSpendPrivateKeyP coin_priv;
     123              :   union GNUNET_CRYPTO_BlindingSecretP bks;
     124              :   struct TALER_DenominationPrivateKey dk_priv;
     125              :   struct TALER_DenominationPublicKey dk_pub;
     126              :   const struct TALER_ExchangeBlindingValues *alg_values;
     127              :   struct TALER_PlanchetDetail pd;
     128              :   struct TALER_BlindedDenominationSignature blind_sig;
     129              :   struct TALER_FreshCoin coin;
     130              :   struct TALER_CoinPubHashP c_hash;
     131            2 :   struct TALER_AgeCommitmentHashP *ach = NULL;
     132            2 :   struct TALER_AgeCommitmentHashP ah = {0};
     133              : 
     134            2 :   alg_values = TALER_denom_ewv_rsa_singleton ();
     135            2 :   if (0 < age)
     136              :   {
     137              :     struct TALER_AgeCommitmentProof acp;
     138              :     struct GNUNET_HashCode seed;
     139              : 
     140            1 :     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     141              :                                 &seed,
     142              :                                 sizeof(seed));
     143            1 :     TALER_age_restriction_commit (&age_mask,
     144              :                                   age,
     145              :                                   &seed,
     146              :                                   &acp);
     147            1 :     TALER_age_commitment_hash (&acp.commitment,
     148              :                                &ah);
     149            1 :     ach = &ah;
     150            1 :     TALER_age_commitment_proof_free (&acp);
     151              :   }
     152              : 
     153            2 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
     154              :                               &ps,
     155              :                               sizeof (ps));
     156            2 :   GNUNET_log_skip (1, GNUNET_YES);
     157            2 :   GNUNET_assert (GNUNET_SYSERR ==
     158              :                  TALER_denom_priv_create (&dk_priv,
     159              :                                           &dk_pub,
     160              :                                           GNUNET_CRYPTO_BSA_INVALID));
     161            2 :   GNUNET_log_skip (1, GNUNET_YES);
     162            2 :   GNUNET_assert (GNUNET_SYSERR ==
     163              :                  TALER_denom_priv_create (&dk_priv,
     164              :                                           &dk_pub,
     165              :                                           42));
     166              : 
     167            2 :   GNUNET_assert (GNUNET_OK ==
     168              :                  TALER_denom_priv_create (&dk_priv,
     169              :                                           &dk_pub,
     170              :                                           GNUNET_CRYPTO_BSA_RSA,
     171              :                                           1024));
     172            2 :   TALER_planchet_setup_coin_priv (&ps,
     173              :                                   alg_values,
     174              :                                   &coin_priv);
     175            2 :   TALER_planchet_blinding_secret_create (&ps,
     176              :                                          alg_values,
     177              :                                          &bks);
     178            2 :   GNUNET_assert (GNUNET_OK ==
     179              :                  TALER_planchet_prepare (&dk_pub,
     180              :                                          alg_values,
     181              :                                          &bks,
     182              :                                          NULL,
     183              :                                          &coin_priv,
     184              :                                          ach,
     185              :                                          &c_hash,
     186              :                                          &pd));
     187            2 :   GNUNET_assert (GNUNET_OK ==
     188              :                  TALER_denom_sign_blinded (&blind_sig,
     189              :                                            &dk_priv,
     190              :                                            false,
     191              :                                            &pd.blinded_planchet));
     192            2 :   TALER_planchet_detail_free (&pd);
     193            2 :   GNUNET_assert (GNUNET_OK ==
     194              :                  TALER_planchet_to_coin (&dk_pub,
     195              :                                          &blind_sig,
     196              :                                          &bks,
     197              :                                          &coin_priv,
     198              :                                          ach,
     199              :                                          &c_hash,
     200              :                                          alg_values,
     201              :                                          &coin));
     202            2 :   TALER_blinded_denom_sig_free (&blind_sig);
     203            2 :   TALER_denom_sig_free (&coin.sig);
     204            2 :   TALER_denom_priv_free (&dk_priv);
     205            2 :   TALER_denom_pub_free (&dk_pub);
     206            2 :   return 0;
     207              : }
     208              : 
     209              : 
     210              : /**
     211              :  * Test the basic planchet functionality of creating a fresh planchet with CS denomination
     212              :  * and extracting the respective signature.
     213              :  *
     214              :  * @return 0 on success
     215              :  */
     216              : static int
     217            2 : test_planchets_cs (uint8_t age)
     218              : {
     219              :   struct TALER_PlanchetMasterSecretP ps;
     220              :   struct TALER_CoinSpendPrivateKeyP coin_priv;
     221              :   union GNUNET_CRYPTO_BlindingSecretP bks;
     222              :   struct TALER_DenominationPrivateKey dk_priv;
     223              :   struct TALER_DenominationPublicKey dk_pub;
     224              :   struct TALER_PlanchetDetail pd;
     225              :   struct TALER_CoinPubHashP c_hash;
     226              :   union GNUNET_CRYPTO_BlindSessionNonce nonce;
     227              :   struct TALER_BlindedDenominationSignature blind_sig;
     228              :   struct TALER_FreshCoin coin;
     229              :   struct TALER_ExchangeBlindingValues alg_values;
     230            2 :   struct TALER_AgeCommitmentHashP *ach = NULL;
     231            2 :   struct TALER_AgeCommitmentHashP ah = {0};
     232              : 
     233            2 :   if (0 < age)
     234              :   {
     235              :     struct TALER_AgeCommitmentProof acp;
     236              :     struct GNUNET_HashCode seed;
     237              : 
     238            1 :     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     239              :                                 &seed,
     240              :                                 sizeof(seed));
     241            1 :     TALER_age_restriction_commit (&age_mask,
     242              :                                   age,
     243              :                                   &seed,
     244              :                                   &acp);
     245            1 :     TALER_age_commitment_hash (&acp.commitment,
     246              :                                &ah);
     247            1 :     ach = &ah;
     248            1 :     TALER_age_commitment_proof_free (&acp);
     249              :   }
     250              : 
     251            2 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
     252              :                               &ps,
     253              :                               sizeof (ps));
     254            2 :   GNUNET_assert (GNUNET_OK ==
     255              :                  TALER_denom_priv_create (&dk_priv,
     256              :                                           &dk_pub,
     257              :                                           GNUNET_CRYPTO_BSA_CS));
     258              : #pragma message "phase out TALER_cs_withdraw_nonce_derive"
     259            2 :   TALER_cs_withdraw_nonce_derive (
     260              :     &ps,
     261              :     &nonce.cs_nonce);
     262              :   // FIXME: define Taler abstraction for this:
     263              :   alg_values.blinding_inputs
     264            2 :     = GNUNET_CRYPTO_get_blinding_input_values (dk_priv.bsign_priv_key,
     265              :                                                &nonce,
     266              :                                                "rw");
     267            2 :   TALER_denom_pub_hash (&dk_pub,
     268              :                         &pd.denom_pub_hash);
     269            2 :   TALER_planchet_setup_coin_priv (&ps,
     270              :                                   &alg_values,
     271              :                                   &coin_priv);
     272            2 :   TALER_planchet_blinding_secret_create (&ps,
     273              :                                          &alg_values,
     274              :                                          &bks);
     275            2 :   GNUNET_assert (GNUNET_OK ==
     276              :                  TALER_planchet_prepare (&dk_pub,
     277              :                                          &alg_values,
     278              :                                          &bks,
     279              :                                          &nonce,
     280              :                                          &coin_priv,
     281              :                                          ach,
     282              :                                          &c_hash,
     283              :                                          &pd));
     284            2 :   GNUNET_assert (GNUNET_OK ==
     285              :                  TALER_denom_sign_blinded (&blind_sig,
     286              :                                            &dk_priv,
     287              :                                            false,
     288              :                                            &pd.blinded_planchet));
     289            2 :   GNUNET_assert (GNUNET_OK ==
     290              :                  TALER_planchet_to_coin (&dk_pub,
     291              :                                          &blind_sig,
     292              :                                          &bks,
     293              :                                          &coin_priv,
     294              :                                          ach,
     295              :                                          &c_hash,
     296              :                                          &alg_values,
     297              :                                          &coin));
     298            2 :   TALER_blinded_denom_sig_free (&blind_sig);
     299            2 :   TALER_denom_sig_free (&coin.sig);
     300            2 :   TALER_denom_priv_free (&dk_priv);
     301            2 :   TALER_denom_pub_free (&dk_pub);
     302            2 :   return 0;
     303              : }
     304              : 
     305              : 
     306              : /**
     307              :  * Test the basic planchet functionality of creating a fresh planchet
     308              :  * and extracting the respective signature.
     309              :  * Calls test_planchets_rsa and test_planchets_cs
     310              :  *
     311              :  * @return 0 on success
     312              :  */
     313              : static int
     314            2 : test_planchets (uint8_t age)
     315              : {
     316            2 :   if (0 != test_planchets_rsa (age))
     317            0 :     return -1;
     318            2 :   return test_planchets_cs (age);
     319              : }
     320              : 
     321              : 
     322              : static int
     323            1 : test_exchange_sigs (void)
     324              : {
     325            1 :   const struct TALER_FullPayto pt = {
     326              :     .full_payto
     327              :       = (char *) "payto://x-taler-bank/localhost/Account?receiver-name=ACC"
     328              :   };
     329            1 :   const struct TALER_FullPayto pto = {
     330              :     .full_payto
     331              :       = (char *) "payto://x-taler-bank/localhost/Other?receiver-name=OTH"
     332              :   };
     333              :   struct TALER_MasterPrivateKeyP priv;
     334              :   struct TALER_MasterPublicKeyP pub;
     335              :   struct TALER_MasterSignatureP sig;
     336              :   json_t *rest;
     337              : 
     338            1 :   GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv);
     339            1 :   rest = json_array ();
     340            1 :   GNUNET_assert (NULL != rest);
     341            1 :   TALER_exchange_wire_signature_make (pt,
     342              :                                       NULL,
     343              :                                       "https://example.com/",
     344              :                                       NULL,
     345              :                                       rest,
     346              :                                       rest,
     347              :                                       &priv,
     348              :                                       &sig);
     349            1 :   GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv,
     350              :                                       &pub.eddsa_pub);
     351            1 :   if (GNUNET_OK !=
     352            1 :       TALER_exchange_wire_signature_check (pt,
     353              :                                            NULL,
     354              :                                            "https://example.com/",
     355              :                                            NULL,
     356              :                                            rest,
     357              :                                            rest,
     358              :                                            &pub,
     359              :                                            &sig))
     360              :   {
     361            0 :     GNUNET_break (0);
     362            0 :     return 1;
     363              :   }
     364            1 :   if (GNUNET_OK ==
     365            1 :       TALER_exchange_wire_signature_check (
     366              :         pto,
     367              :         NULL,
     368              :         "https://example.com/",
     369              :         NULL,
     370              :         rest,
     371              :         rest,
     372              :         &pub,
     373              :         &sig))
     374              :   {
     375            0 :     GNUNET_break (0);
     376            0 :     return 1;
     377              :   }
     378            1 :   if (GNUNET_OK ==
     379            1 :       TALER_exchange_wire_signature_check (
     380              :         pt,
     381              :         "http://example.com/",
     382              :         NULL,
     383              :         NULL,
     384              :         rest,
     385              :         rest,
     386              :         &pub,
     387              :         &sig))
     388              :   {
     389            0 :     GNUNET_break (0);
     390            0 :     return 1;
     391              :   }
     392            1 :   json_decref (rest);
     393            1 :   return 0;
     394              : }
     395              : 
     396              : 
     397              : static int
     398            1 : test_merchant_sigs (void)
     399              : {
     400            1 :   const struct TALER_FullPayto pt = {
     401              :     .full_payto
     402              :       = (char *) "payto://x-taler-bank/localhost/Account?receiver-name=ACC"
     403              :   };
     404            1 :   const struct TALER_FullPayto pto = {
     405              :     .full_payto
     406              :       = (char *) "payto://x-taler-bank/localhost/Other?receiver-name=OTH"
     407              :   };
     408              :   struct TALER_WireSaltP salt;
     409              :   struct TALER_MerchantPrivateKeyP priv;
     410              :   struct TALER_MerchantPublicKeyP pub;
     411              :   struct TALER_MerchantSignatureP sig;
     412              : 
     413            1 :   GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv);
     414            1 :   memset (&salt,
     415              :           42,
     416              :           sizeof (salt));
     417            1 :   TALER_merchant_wire_signature_make (pt,
     418              :                                       &salt,
     419              :                                       &priv,
     420              :                                       &sig);
     421            1 :   GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv,
     422              :                                       &pub.eddsa_pub);
     423            1 :   if (GNUNET_OK !=
     424            1 :       TALER_merchant_wire_signature_check (pt,
     425              :                                            &salt,
     426              :                                            &pub,
     427              :                                            &sig))
     428              :   {
     429            0 :     GNUNET_break (0);
     430            0 :     return 1;
     431              :   }
     432            1 :   if (GNUNET_OK ==
     433            1 :       TALER_merchant_wire_signature_check (
     434              :         pto,
     435              :         &salt,
     436              :         &pub,
     437              :         &sig))
     438              :   {
     439            0 :     GNUNET_break (0);
     440            0 :     return 1;
     441              :   }
     442            1 :   memset (&salt,
     443              :           43,
     444              :           sizeof (salt));
     445            1 :   if (GNUNET_OK ==
     446            1 :       TALER_merchant_wire_signature_check (pt,
     447              :                                            &salt,
     448              :                                            &pub,
     449              :                                            &sig))
     450              :   {
     451            0 :     GNUNET_break (0);
     452            0 :     return 1;
     453              :   }
     454            1 :   return 0;
     455              : }
     456              : 
     457              : 
     458              : static int
     459            1 : test_contracts (void)
     460              : {
     461              :   struct TALER_ContractDiffiePrivateP cpriv;
     462              :   struct TALER_PurseContractPublicKeyP purse_pub;
     463              :   struct TALER_PurseContractPrivateKeyP purse_priv;
     464              :   void *econtract;
     465              :   size_t econtract_size;
     466              :   struct TALER_PurseMergePrivateKeyP mpriv_in;
     467              :   struct TALER_PurseMergePrivateKeyP mpriv_out;
     468              :   json_t *c;
     469              : 
     470            1 :   GNUNET_CRYPTO_ecdhe_key_create (&cpriv.ecdhe_priv);
     471            1 :   GNUNET_CRYPTO_eddsa_key_create (&purse_priv.eddsa_priv);
     472            1 :   GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv.eddsa_priv,
     473              :                                       &purse_pub.eddsa_pub);
     474            1 :   memset (&mpriv_in,
     475              :           42,
     476              :           sizeof (mpriv_in));
     477            1 :   c = json_pack ("{s:s}", "test", "value");
     478            1 :   GNUNET_assert (NULL != c);
     479            1 :   TALER_CRYPTO_contract_encrypt_for_merge (&purse_pub,
     480              :                                            &cpriv,
     481              :                                            &mpriv_in,
     482              :                                            c,
     483              :                                            &econtract,
     484              :                                            &econtract_size);
     485            1 :   json_decref (c);
     486            1 :   c = TALER_CRYPTO_contract_decrypt_for_merge (&cpriv,
     487              :                                                &purse_pub,
     488              :                                                econtract,
     489              :                                                econtract_size,
     490              :                                                &mpriv_out);
     491            1 :   GNUNET_free (econtract);
     492            1 :   if (NULL == c)
     493            0 :     return 1;
     494            1 :   json_decref (c);
     495            1 :   if (0 != GNUNET_memcmp (&mpriv_in,
     496              :                           &mpriv_out))
     497            0 :     return 1;
     498            1 :   return 0;
     499              : }
     500              : 
     501              : 
     502              : static int
     503            1 : test_attributes (void)
     504              : {
     505              :   struct TALER_AttributeEncryptionKeyP key;
     506              :   void *eattr;
     507              :   size_t eattr_size;
     508              :   json_t *c;
     509              : 
     510            1 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
     511              :                               &key,
     512              :                               sizeof (key));
     513            1 :   c = json_pack ("{s:s}", "test", "value");
     514            1 :   GNUNET_assert (NULL != c);
     515            1 :   TALER_CRYPTO_kyc_attributes_encrypt (&key,
     516              :                                        c,
     517              :                                        &eattr,
     518              :                                        &eattr_size);
     519            1 :   json_decref (c);
     520            1 :   c = TALER_CRYPTO_kyc_attributes_decrypt (&key,
     521              :                                            eattr,
     522              :                                            eattr_size);
     523            1 :   GNUNET_free (eattr);
     524            1 :   if (NULL == c)
     525              :   {
     526            0 :     GNUNET_break (0);
     527            0 :     return 1;
     528              :   }
     529            1 :   GNUNET_assert (0 ==
     530              :                  strcmp ("value",
     531              :                          json_string_value (json_object_get (c,
     532              :                                                              "test"))));
     533            1 :   json_decref (c);
     534            1 :   return 0;
     535              : }
     536              : 
     537              : 
     538              : int
     539            1 : main (int argc,
     540              :       const char *const argv[])
     541              : {
     542              :   (void) argc;
     543              :   (void) argv;
     544            1 :   GNUNET_log_setup ("test-crypto",
     545              :                     "WARNING",
     546              :                     NULL);
     547            1 :   if (0 != test_high_level ())
     548            0 :     return 1;
     549            1 :   if (0 != test_planchets (0))
     550            0 :     return 2;
     551            1 :   if (0 != test_planchets (13))
     552            0 :     return 3;
     553            1 :   if (0 != test_exchange_sigs ())
     554            0 :     return 4;
     555            1 :   if (0 != test_merchant_sigs ())
     556            0 :     return 5;
     557            1 :   if (0 != test_contracts ())
     558            0 :     return 6;
     559            1 :   if (0 != test_attributes ())
     560            0 :     return 7;
     561            1 :   return 0;
     562              : }
     563              : 
     564              : 
     565              : /* end of test_crypto.c */
        

Generated by: LCOV version 2.0-1