exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test_crypto_confirmation.c (31057B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2026 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  * @file util/test_crypto_confirmation.c
     18  * @brief tests for POS confirmation computation
     19  * @author Bohdan Potuzhnyi
     20  * @author Volodymyr Potuzhnyi
     21  */
     22 #include "platform.h"
     23 #include "taler/taler_util.h"
     24 
     25 
     26 /**
     27  * Number of checks that failed.
     28  */
     29 static unsigned int fails;
     30 
     31 
     32 /**
     33  * Record the outcome of a single check.
     34  *
     35  * @param ok true if the check passed
     36  * @param label human-readable description of the check
     37  */
     38 static void
     39 check (bool ok,
     40        const char *label)
     41 {
     42   if (! ok)
     43   {
     44     fprintf (stderr,
     45              "FAIL %s\n",
     46              label);
     47     fails++;
     48     return;
     49   }
     50   fprintf (stderr,
     51            "ok   %s\n",
     52            label);
     53 }
     54 
     55 
     56 /**
     57  * Check that @a enc decodes to exactly @a len bytes.
     58  *
     59  * @param enc Crockford base32-encoded value
     60  * @param len expected number of bytes
     61  * @return true if the length matches
     62  */
     63 static bool
     64 decodes_to (const char *enc,
     65             size_t len)
     66 {
     67   unsigned char buf[128];
     68 
     69   GNUNET_assert (len <= sizeof (buf));
     70   return (GNUNET_OK ==
     71           GNUNET_STRINGS_string_to_data (enc,
     72                                          strlen (enc),
     73                                          buf,
     74                                          len));
     75 }
     76 
     77 
     78 /**
     79  * Run the full challenge-signature test suite for one algorithm.
     80  *
     81  * @param alg algorithm to exercise
     82  * @param name human-readable name of @a alg
     83  * @param pub_len expected public key length in bytes
     84  */
     85 static void
     86 test_challenge_alg (enum TALER_MerchantConfirmationAlgorithm alg,
     87                     const char *name,
     88                     size_t pub_len)
     89 {
     90   char *priv = NULL;
     91   char *pub = NULL;
     92   char *priv2 = NULL;
     93   char *pub2 = NULL;
     94   char *conf;
     95   char *conf2;
     96   struct TALER_PosChallengeP ch;
     97   struct TALER_PosChallengeP ch2;
     98   char label[256];
     99 
    100 #define LABEL(what) \
    101   GNUNET_snprintf (label, sizeof (label), "%s: %s", name, what)
    102 
    103   fprintf (stderr,
    104            "-- %s --\n",
    105            name);
    106   LABEL ("keygen");
    107   check (GNUNET_OK ==
    108          TALER_otp_device_key_create (alg,
    109                                       &priv,
    110                                       &pub),
    111          label);
    112   if ( (NULL == priv) ||
    113        (NULL == pub) )
    114     return;
    115   LABEL ("public key has the documented length");
    116   check (decodes_to (pub,
    117                      pub_len),
    118          label);
    119 
    120   GNUNET_CRYPTO_random_block (&ch,
    121                               sizeof (ch));
    122   conf = TALER_build_pos_confirmation_sig (priv,
    123                                            alg,
    124                                            &ch);
    125   LABEL ("sign");
    126   check (NULL != conf,
    127          label);
    128   if (NULL == conf)
    129     return;
    130   LABEL ("signature is 64 bytes");
    131   check (decodes_to (conf,
    132                      64),
    133          label);
    134   LABEL ("verify accepts a fresh confirmation");
    135   check (GNUNET_OK ==
    136          TALER_check_pos_confirmation_sig (pub,
    137                                            alg,
    138                                            &ch,
    139                                            conf),
    140          label);
    141 
    142   /* DD 97: modifying the challenge must invalidate the confirmation */
    143   ch2 = ch;
    144   ch2.challenge[0] ^= 0x01;
    145   LABEL ("mutated challenge is rejected");
    146   check (GNUNET_OK !=
    147          TALER_check_pos_confirmation_sig (pub,
    148                                            alg,
    149                                            &ch2,
    150                                            conf),
    151          label);
    152 
    153   /* DD 97: another device's key must not accept the confirmation */
    154   check (GNUNET_OK ==
    155          TALER_otp_device_key_create (alg,
    156                                       &priv2,
    157                                       &pub2),
    158          "second keygen");
    159   LABEL ("confirmation is rejected under a different key");
    160   check (GNUNET_OK !=
    161          TALER_check_pos_confirmation_sig (pub2,
    162                                            alg,
    163                                            &ch,
    164                                            conf),
    165          label);
    166 
    167   conf2 = TALER_build_pos_confirmation_sig (priv2,
    168                                             alg,
    169                                             &ch);
    170   LABEL ("a different key yields a different confirmation");
    171   check ( (NULL != conf2) &&
    172           (0 != strcmp (conf,
    173                         conf2)),
    174           label);
    175 
    176   {
    177     char *bad = GNUNET_strdup (conf);
    178 
    179     bad[0] = ('A' == bad[0]) ? 'B' : 'A';
    180     LABEL ("mauled confirmation is rejected");
    181     check (GNUNET_OK !=
    182            TALER_check_pos_confirmation_sig (pub,
    183                                              alg,
    184                                              &ch,
    185                                              bad),
    186            label);
    187     GNUNET_free (bad);
    188   }
    189 
    190   /* DD 97: must not return a confirmation we cannot compute */
    191   LABEL ("missing challenge fails closed");
    192   check (NULL ==
    193          TALER_build_pos_confirmation_sig (priv,
    194                                            alg,
    195                                            NULL),
    196          label);
    197 #undef LABEL
    198   GNUNET_free (conf);
    199   GNUNET_free (conf2);
    200   GNUNET_free (priv);
    201   GNUNET_free (pub);
    202   GNUNET_free (priv2);
    203   GNUNET_free (pub2);
    204 }
    205 
    206 
    207 /**
    208  * Known-answer vectors. These pin the on-the-wire format an offline
    209  * verifier depends on: a round-trip test would happily follow us if
    210  * the salt or the field order ever changed.
    211  */
    212 #define KAT_EDDSA_PRIV "041061050R3GG28A1C60T3GF208H44RM2MB1E60S38DHR78Y3WG0"
    213 #define KAT_EDDSA_PUB  "F6TNCBMFWSAFJG3RP49EHACBMY81Z19TWTAVXNZ0WE8GQB84JSJ0"
    214 #define KAT_EDDSA_SIG \
    215   "VKTQA94GZCQ6R5AWV51X51X4P6Q4EE8F0MXWWZXMGRKJKR2KV2BRBXJG8YV4E0VY56GG" \
    216   "CBCG5XAMEV1C1AD9FNXHDBXK4JXNZ008E08"
    217 #define KAT_ECDSA_PUB  "0CZV95QR7R1GKGFFDAW2N4ZMAMXTWYF4AHX7XR87F3XW5KZV9C922"
    218 #define KAT_ECDSA_SIG \
    219   "03Q7RNNBBRPKG4YNH5JH92H557JHHGV92M7MMFXD9MH0MQFJG46QADH7GXNFNGTPD6MD" \
    220   "80Y4CC3ESKTRM7AF9PRBXVN4FMYEY7JWMM8"
    221 
    222 
    223 /**
    224  * Check the confirmations we produce against fixed vectors, so that a
    225  * change to the signed message is caught instead of silently breaking
    226  * every already-deployed offline verifier.
    227  */
    228 static void
    229 test_known_answers (void)
    230 {
    231   struct TALER_PosChallengeP ch;
    232   char *conf;
    233 
    234   fprintf (stderr,
    235            "-- known-answer vectors --\n");
    236   for (unsigned int i = 0; i < sizeof (ch.challenge); i++)
    237     ch.challenge[i] = (unsigned char) (0xF0 - i);
    238 
    239   /* EdDSA signing is deterministic, so the exact bytes are pinned */
    240   conf = TALER_build_pos_confirmation_sig (KAT_EDDSA_PRIV,
    241                                            TALER_MCA_EDDSA_CHALLENGE,
    242                                            &ch);
    243   check ( (NULL != conf) &&
    244           (0 == strcmp (conf,
    245                         KAT_EDDSA_SIG)),
    246           "EdDSA confirmation matches the known-answer vector");
    247   GNUNET_free (conf);
    248   check (GNUNET_OK ==
    249          TALER_check_pos_confirmation_sig (KAT_EDDSA_PUB,
    250                                            TALER_MCA_EDDSA_CHALLENGE,
    251                                            &ch,
    252                                            KAT_EDDSA_SIG),
    253          "EdDSA known-answer vector verifies");
    254 
    255   /* ECDSA signing is randomized, so only verification can be pinned */
    256   check (GNUNET_OK ==
    257          TALER_check_pos_confirmation_sig (KAT_ECDSA_PUB,
    258                                            TALER_MCA_ECDSA_CHALLENGE,
    259                                            &ch,
    260                                            KAT_ECDSA_SIG),
    261          "ECDSA known-answer vector verifies");
    262 }
    263 
    264 
    265 /**
    266  * Re-encode @a raw as a confirmation string.
    267  *
    268  * @param raw 64 raw signature bytes
    269  * @return encoded signature, to be freed by the caller
    270  */
    271 static char *
    272 encode_sig (const unsigned char *raw)
    273 {
    274   return GNUNET_STRINGS_data_to_string_alloc (raw,
    275                                               64);
    276 }
    277 
    278 
    279 /**
    280  * Decode the confirmation @a enc into its 64 raw bytes.
    281  *
    282  * @param enc encoded signature
    283  * @param[out] raw where to write the raw signature
    284  */
    285 static void
    286 decode_sig (const char *enc,
    287             unsigned char *raw)
    288 {
    289   GNUNET_assert (GNUNET_OK ==
    290                  GNUNET_STRINGS_string_to_data (enc,
    291                                                 strlen (enc),
    292                                                 raw,
    293                                                 64));
    294 }
    295 
    296 
    297 /**
    298  * Add the Ed25519 group order L to the little-endian scalar S in
    299  * place.  The result is a different encoding of the same scalar
    300  * modulo L, which a verifier that omits the canonicality check would
    301  * wrongly accept.
    302  *
    303  * @param[in,out] s the 32 little-endian bytes of S
    304  */
    305 static void
    306 add_ed25519_order (unsigned char *s)
    307 {
    308   static const unsigned char L[32] = {
    309     0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
    310     0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
    311     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    312     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
    313   };
    314   unsigned int carry = 0;
    315 
    316   for (unsigned int i = 0; i < 32; i++)
    317   {
    318     unsigned int v = s[i] + L[i] + carry;
    319 
    320     s[i] = (unsigned char) (v & 0xff);
    321     carry = v >> 8;
    322   }
    323 }
    324 
    325 
    326 /**
    327  * Replace the big-endian P-256 scalar @a s by n-s in place.  Both
    328  * (r,s) and (r,n-s) are mathematically valid ECDSA signatures; we
    329  * accept only the smaller one.
    330  *
    331  * @param[in,out] s the 32 big-endian bytes of s
    332  */
    333 static void
    334 negate_p256_scalar (unsigned char *s)
    335 {
    336   static const unsigned char n[32] = {
    337     0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
    338     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    339     0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
    340     0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
    341   };
    342   int borrow = 0;
    343 
    344   for (int i = 31; i >= 0; i--)
    345   {
    346     int v = (int) n[i] - (int) s[i] - borrow;
    347 
    348     if (v < 0)
    349     {
    350       v += 256;
    351       borrow = 1;
    352     }
    353     else
    354     {
    355       borrow = 0;
    356     }
    357     s[i] = (unsigned char) v;
    358   }
    359 }
    360 
    361 
    362 /**
    363  * A confirmation must not be transformable into a second confirmation
    364  * that a verifier would also accept: an offline device that tracked
    365  * spent confirmations instead of spent challenges would otherwise be
    366  * replayable.
    367  */
    368 static void
    369 test_malleability (void)
    370 {
    371   char *priv;
    372   char *pub;
    373   char *conf;
    374   char *mauled;
    375   struct TALER_PosChallengeP ch;
    376   unsigned char raw[64];
    377 
    378   fprintf (stderr,
    379            "-- malleability --\n");
    380   GNUNET_CRYPTO_random_block (&ch,
    381                               sizeof (ch));
    382 
    383   /* Ed25519: S+L encodes the same scalar; RFC 8032 requires S < L */
    384   GNUNET_assert (GNUNET_OK ==
    385                  TALER_otp_device_key_create (TALER_MCA_EDDSA_CHALLENGE,
    386                                               &priv,
    387                                               &pub));
    388   conf = TALER_build_pos_confirmation_sig (priv,
    389                                            TALER_MCA_EDDSA_CHALLENGE,
    390                                            &ch);
    391   GNUNET_assert (NULL != conf);
    392   decode_sig (conf,
    393               raw);
    394   add_ed25519_order (&raw[32]);
    395   mauled = encode_sig (raw);
    396   check (GNUNET_OK !=
    397          TALER_check_pos_confirmation_sig (pub,
    398                                            TALER_MCA_EDDSA_CHALLENGE,
    399                                            &ch,
    400                                            mauled),
    401          "EdDSA non-canonical S (S+L) is rejected");
    402   GNUNET_free (mauled);
    403   GNUNET_free (conf);
    404   GNUNET_free (priv);
    405   GNUNET_free (pub);
    406 
    407   /* P-256: (r, n-s) is a valid signature that we reject by policy */
    408   GNUNET_assert (GNUNET_OK ==
    409                  TALER_otp_device_key_create (TALER_MCA_ECDSA_CHALLENGE,
    410                                               &priv,
    411                                               &pub));
    412   conf = TALER_build_pos_confirmation_sig (priv,
    413                                            TALER_MCA_ECDSA_CHALLENGE,
    414                                            &ch);
    415   GNUNET_assert (NULL != conf);
    416   decode_sig (conf,
    417               raw);
    418   negate_p256_scalar (&raw[32]);
    419   mauled = encode_sig (raw);
    420   check (GNUNET_OK !=
    421          TALER_check_pos_confirmation_sig (pub,
    422                                            TALER_MCA_ECDSA_CHALLENGE,
    423                                            &ch,
    424                                            mauled),
    425          "ECDSA high-s variant is rejected");
    426   GNUNET_free (mauled);
    427   GNUNET_free (conf);
    428   GNUNET_free (priv);
    429   GNUNET_free (pub);
    430 }
    431 
    432 
    433 /**
    434  * Every ECDSA signature we emit must be the canonical low-s one, and
    435  * the public key must be a compressed point.  Runs enough iterations
    436  * that a scalar with a leading zero byte -- which the fixed-width
    437  * encoding has to pad -- is hit with near certainty.
    438  */
    439 static void
    440 test_ecdsa_encoding (void)
    441 {
    442   unsigned int low_s_failures = 0;
    443   unsigned int prefix_failures = 0;
    444   unsigned int verify_failures = 0;
    445   unsigned int short_scalars = 0;
    446 
    447   fprintf (stderr,
    448            "-- ECDSA encoding (300 iterations) --\n");
    449   for (unsigned int i = 0; i < 300; i++)
    450   {
    451     char *priv;
    452     char *pub;
    453     char *conf;
    454     struct TALER_PosChallengeP ch;
    455     unsigned char raw[64];
    456     unsigned char pubraw[33];
    457     unsigned char neg[32];
    458 
    459     GNUNET_assert (GNUNET_OK ==
    460                    TALER_otp_device_key_create (TALER_MCA_ECDSA_CHALLENGE,
    461                                                 &priv,
    462                                                 &pub));
    463     GNUNET_CRYPTO_random_block (&ch,
    464                                 sizeof (ch));
    465     conf = TALER_build_pos_confirmation_sig (priv,
    466                                              TALER_MCA_ECDSA_CHALLENGE,
    467                                              &ch);
    468     GNUNET_assert (NULL != conf);
    469     GNUNET_assert (GNUNET_OK ==
    470                    GNUNET_STRINGS_string_to_data (pub,
    471                                                   strlen (pub),
    472                                                   pubraw,
    473                                                   sizeof (pubraw)));
    474     if ( (0x02 != pubraw[0]) &&
    475          (0x03 != pubraw[0]) )
    476       prefix_failures++;
    477     decode_sig (conf,
    478                 raw);
    479     /* s is low exactly when it is smaller than n-s */
    480     memcpy (neg,
    481             &raw[32],
    482             sizeof (neg));
    483     negate_p256_scalar (neg);
    484     if (memcmp (&raw[32],
    485                 neg,
    486                 sizeof (neg)) >= 0)
    487       low_s_failures++;
    488     /* count fixed-width scalars that needed zero padding */
    489     if ( (0x00 == raw[0]) ||
    490          (0x00 == raw[32]) )
    491       short_scalars++;
    492     if (GNUNET_OK !=
    493         TALER_check_pos_confirmation_sig (pub,
    494                                           TALER_MCA_ECDSA_CHALLENGE,
    495                                           &ch,
    496                                           conf))
    497       verify_failures++;
    498     GNUNET_free (conf);
    499     GNUNET_free (priv);
    500     GNUNET_free (pub);
    501   }
    502   check (0 == prefix_failures,
    503          "every public key is a compressed point (0x02/0x03)");
    504   check (0 == low_s_failures,
    505          "every signature uses the canonical low-s form");
    506   check (0 == verify_failures,
    507          "every signature verifies");
    508   fprintf (stderr,
    509            "     (%u/300 signatures had a scalar needing zero padding)\n",
    510            short_scalars);
    511 }
    512 
    513 
    514 /**
    515  * Ed25519 signing is deterministic while ECDSA is randomized; a
    516  * repeated ECDSA nonce would be catastrophic, so the signatures must
    517  * differ.
    518  */
    519 static void
    520 test_signing_determinism (void)
    521 {
    522   char *priv;
    523   char *pub;
    524   char *a;
    525   char *b;
    526   struct TALER_PosChallengeP ch;
    527 
    528   fprintf (stderr,
    529            "-- signing determinism --\n");
    530   GNUNET_CRYPTO_random_block (&ch,
    531                               sizeof (ch));
    532   GNUNET_assert (GNUNET_OK ==
    533                  TALER_otp_device_key_create (TALER_MCA_EDDSA_CHALLENGE,
    534                                               &priv,
    535                                               &pub));
    536   a = TALER_build_pos_confirmation_sig (priv,
    537                                         TALER_MCA_EDDSA_CHALLENGE,
    538                                         &ch);
    539   b = TALER_build_pos_confirmation_sig (priv,
    540                                         TALER_MCA_EDDSA_CHALLENGE,
    541                                         &ch);
    542   check ( (NULL != a) &&
    543           (NULL != b) &&
    544           (0 == strcmp (a,
    545                         b)),
    546           "EdDSA signing is deterministic");
    547   GNUNET_free (a);
    548   GNUNET_free (b);
    549   GNUNET_free (priv);
    550   GNUNET_free (pub);
    551 
    552   GNUNET_assert (GNUNET_OK ==
    553                  TALER_otp_device_key_create (TALER_MCA_ECDSA_CHALLENGE,
    554                                               &priv,
    555                                               &pub));
    556   a = TALER_build_pos_confirmation_sig (priv,
    557                                         TALER_MCA_ECDSA_CHALLENGE,
    558                                         &ch);
    559   b = TALER_build_pos_confirmation_sig (priv,
    560                                         TALER_MCA_ECDSA_CHALLENGE,
    561                                         &ch);
    562   check ( (NULL != a) &&
    563           (NULL != b) &&
    564           (0 != strcmp (a,
    565                         b)),
    566           "ECDSA signing uses a fresh nonce each time");
    567   check ( (GNUNET_OK ==
    568            TALER_check_pos_confirmation_sig (pub,
    569                                              TALER_MCA_ECDSA_CHALLENGE,
    570                                              &ch,
    571                                              a)) &&
    572           (GNUNET_OK ==
    573            TALER_check_pos_confirmation_sig (pub,
    574                                              TALER_MCA_ECDSA_CHALLENGE,
    575                                              &ch,
    576                                              b)),
    577           "both ECDSA signatures verify");
    578   GNUNET_free (a);
    579   GNUNET_free (b);
    580   GNUNET_free (priv);
    581   GNUNET_free (pub);
    582 }
    583 
    584 
    585 /**
    586  * Is the compressed point @a raw refused as the public key for the
    587  * confirmation @a sig?
    588  *
    589  * @param raw 33 bytes to offer as a public key
    590  * @param ch challenge the confirmation is bound to
    591  * @param sig a confirmation that is valid under the real key
    592  * @return true if verification refused @a raw
    593  */
    594 static bool
    595 pub_rejected (const unsigned char *raw,
    596               const struct TALER_PosChallengeP *ch,
    597               const char *sig)
    598 {
    599   char *enc;
    600   enum GNUNET_GenericReturnValue r;
    601 
    602   enc = GNUNET_STRINGS_data_to_string_alloc (raw,
    603                                              33);
    604   r = TALER_check_pos_confirmation_sig (enc,
    605                                         TALER_MCA_ECDSA_CHALLENGE,
    606                                         ch,
    607                                         sig);
    608   GNUNET_free (enc);
    609   return (GNUNET_OK != r);
    610 }
    611 
    612 
    613 /**
    614  * A public key that is not a valid curve point must be refused.  The
    615  * compressed encoding bounds what can even be expressed, so the cases
    616  * are an X with no square root, an X at or beyond the field prime,
    617  * and an undefined prefix byte.  libgcrypt refuses all of them today
    618  * and this pins that, so that changing how the point is handed to the
    619  * backend cannot silently start accepting garbage.
    620  */
    621 static void
    622 test_ecdsa_invalid_points (void)
    623 {
    624   char *priv;
    625   char *pub;
    626   char *conf;
    627   struct TALER_PosChallengeP ch;
    628   unsigned char real[33];
    629   unsigned char raw[33];
    630   unsigned int accepted = 0;
    631 
    632   fprintf (stderr,
    633            "-- ECDSA invalid public points --\n");
    634   GNUNET_CRYPTO_random_block (&ch,
    635                               sizeof (ch));
    636   GNUNET_assert (GNUNET_OK ==
    637                  TALER_otp_device_key_create (TALER_MCA_ECDSA_CHALLENGE,
    638                                               &priv,
    639                                               &pub));
    640   conf = TALER_build_pos_confirmation_sig (priv,
    641                                            TALER_MCA_ECDSA_CHALLENGE,
    642                                            &ch);
    643   GNUNET_assert (NULL != conf);
    644   GNUNET_assert (GNUNET_OK ==
    645                  GNUNET_STRINGS_string_to_data (pub,
    646                                                 strlen (pub),
    647                                                 real,
    648                                                 sizeof (real)));
    649 
    650   /* X = 1 has no square root modulo p, so these 33 bytes do not
    651      encode a point at all (cross-checked against OpenSSL) */
    652   memset (raw,
    653           0,
    654           sizeof (raw));
    655   raw[0] = 0x02;
    656   raw[32] = 1;
    657   check (pub_rejected (raw,
    658                        &ch,
    659                        conf),
    660          "an X with no valid Y is rejected");
    661 
    662   /* X = 5 is on the curve, but it is not our key: this must fail as a
    663      signature check rather than as a decoding error */
    664   memset (raw,
    665           0,
    666           sizeof (raw));
    667   raw[0] = 0x02;
    668   raw[32] = 5;
    669   check (pub_rejected (raw,
    670                        &ch,
    671                        conf),
    672          "a valid but unrelated point is rejected");
    673 
    674   /* X beyond the field prime */
    675   raw[0] = 0x02;
    676   memset (&raw[1],
    677           0xFF,
    678           32);
    679   check (pub_rejected (raw,
    680                        &ch,
    681                        conf),
    682          "an X beyond the field prime is rejected");
    683 
    684   /* undefined or wrong prefix bytes on an otherwise real key */
    685   memcpy (raw,
    686           real,
    687           sizeof (raw));
    688   raw[0] = 0x00;
    689   check (pub_rejected (raw,
    690                        &ch,
    691                        conf),
    692          "prefix 0x00 is rejected");
    693   memcpy (raw,
    694           real,
    695           sizeof (raw));
    696   raw[0] = 0x04;
    697   check (pub_rejected (raw,
    698                        &ch,
    699                        conf),
    700          "the uncompressed-point prefix 0x04 is rejected");
    701   memcpy (raw,
    702           real,
    703           sizeof (raw));
    704   raw[0] = 0x05;
    705   check (pub_rejected (raw,
    706                        &ch,
    707                        conf),
    708          "an undefined prefix is rejected");
    709 
    710   /* flipping the parity bit selects the other point with the same X,
    711      which is a valid point but the wrong public key */
    712   memcpy (raw,
    713           real,
    714           sizeof (raw));
    715   raw[0] = (0x02 == real[0]) ? 0x03 : 0x02;
    716   check (pub_rejected (raw,
    717                        &ch,
    718                        conf),
    719          "flipping the point parity is rejected");
    720 
    721   /* Small X values, both prefixes.  Some are points that are simply
    722      not our key; the rest have no square root modulo p and do not
    723      decode at all. None of them may verify. The prefix picks between
    724      +Y and -Y, so it does not affect whether a Y exists.
    725 
    726      on the curve:  5, 6, 8, 9, 12, 13, 17
    727      off the curve: 1, 2, 3, 4, 7, 10, 11, 14, 15, 16, 18, 19, 20 */
    728   for (unsigned int x = 1; x <= 20; x++)
    729   {
    730     for (unsigned int parity = 0; parity < 2; parity++)
    731     {
    732       memset (raw,
    733               0,
    734               sizeof (raw));
    735       raw[0] = (0 == parity) ? 0x02 : 0x03;
    736       raw[32] = (unsigned char) x;
    737       if (! pub_rejected (raw,
    738                           &ch,
    739                           conf))
    740         accepted++;
    741     }
    742   }
    743   check (0 == accepted,
    744          "40 small compressed points are all rejected");
    745 
    746   GNUNET_free (conf);
    747   GNUNET_free (priv);
    748   GNUNET_free (pub);
    749 }
    750 
    751 
    752 /**
    753  * Malformed and mismatched inputs must be refused rather than
    754  * misinterpreted.
    755  */
    756 static void
    757 test_bad_inputs (void)
    758 {
    759   char *ed_priv;
    760   char *ed_pub;
    761   char *ec_priv;
    762   char *ec_pub;
    763   char *ed_sig;
    764   char *ec_sig;
    765   struct TALER_PosChallengeP ch;
    766 
    767   fprintf (stderr,
    768            "-- malformed and mismatched inputs --\n");
    769   GNUNET_CRYPTO_random_block (&ch,
    770                               sizeof (ch));
    771 
    772   /* key generation is only defined for the challenge algorithms */
    773   {
    774     char *k = NULL;
    775     char *p = NULL;
    776 
    777     check (GNUNET_OK !=
    778            TALER_otp_device_key_create (TALER_MCA_NONE,
    779                                         &k,
    780                                         &p),
    781            "keygen refuses NONE");
    782     check (GNUNET_OK !=
    783            TALER_otp_device_key_create (TALER_MCA_WITHOUT_PRICE,
    784                                         &k,
    785                                         &p),
    786            "keygen refuses TOTP_WITHOUT_PRICE");
    787     check (GNUNET_OK !=
    788            TALER_otp_device_key_create (TALER_MCA_WITH_PRICE,
    789                                         &k,
    790                                         &p),
    791            "keygen refuses TOTP_WITH_PRICE");
    792   }
    793 
    794   GNUNET_assert (GNUNET_OK ==
    795                  TALER_otp_device_key_create (TALER_MCA_EDDSA_CHALLENGE,
    796                                               &ed_priv,
    797                                               &ed_pub));
    798   GNUNET_assert (GNUNET_OK ==
    799                  TALER_otp_device_key_create (TALER_MCA_ECDSA_CHALLENGE,
    800                                               &ec_priv,
    801                                               &ec_pub));
    802   ed_sig = TALER_build_pos_confirmation_sig (ed_priv,
    803                                              TALER_MCA_EDDSA_CHALLENGE,
    804                                              &ch);
    805   ec_sig = TALER_build_pos_confirmation_sig (ec_priv,
    806                                              TALER_MCA_ECDSA_CHALLENGE,
    807                                              &ch);
    808   GNUNET_assert ( (NULL != ed_sig) &&
    809                   (NULL != ec_sig) );
    810 
    811   check (GNUNET_OK !=
    812          TALER_check_pos_confirmation_sig (ed_pub,
    813                                            TALER_MCA_WITH_PRICE,
    814                                            &ch,
    815                                            ed_sig),
    816          "verify refuses a TOTP algorithm");
    817   /* an Ed25519 key is 32 bytes and a P-256 key 33, so the encodings
    818      are not interchangeable even before the curve differs */
    819   check (GNUNET_OK !=
    820          TALER_check_pos_confirmation_sig (ed_pub,
    821                                            TALER_MCA_ECDSA_CHALLENGE,
    822                                            &ch,
    823                                            ed_sig),
    824          "an EdDSA confirmation does not verify as ECDSA");
    825   check (GNUNET_OK !=
    826          TALER_check_pos_confirmation_sig (ec_pub,
    827                                            TALER_MCA_EDDSA_CHALLENGE,
    828                                            &ch,
    829                                            ec_sig),
    830          "an ECDSA confirmation does not verify as EdDSA");
    831   check (GNUNET_OK !=
    832          TALER_check_pos_confirmation_sig (ec_pub,
    833                                            TALER_MCA_ECDSA_CHALLENGE,
    834                                            &ch,
    835                                            ed_sig),
    836          "a confirmation made with the wrong key is rejected");
    837   check (GNUNET_OK !=
    838          TALER_check_pos_confirmation_sig ("!!!not base32!!!",
    839                                            TALER_MCA_EDDSA_CHALLENGE,
    840                                            &ch,
    841                                            ed_sig),
    842          "a malformed public key is rejected");
    843   check (GNUNET_OK !=
    844          TALER_check_pos_confirmation_sig (ed_pub,
    845                                            TALER_MCA_EDDSA_CHALLENGE,
    846                                            &ch,
    847                                            "!!!not base32!!!"),
    848          "a malformed confirmation is rejected");
    849   {
    850     char *truncated = GNUNET_strdup (ed_sig);
    851 
    852     truncated[strlen (truncated) - 4] = '\0';
    853     check (GNUNET_OK !=
    854            TALER_check_pos_confirmation_sig (ed_pub,
    855                                              TALER_MCA_EDDSA_CHALLENGE,
    856                                              &ch,
    857                                              truncated),
    858            "a truncated confirmation is rejected");
    859     GNUNET_free (truncated);
    860   }
    861   {
    862     char *truncated = GNUNET_strdup (ed_pub);
    863 
    864     truncated[strlen (truncated) - 4] = '\0';
    865     check (GNUNET_OK !=
    866            TALER_check_pos_confirmation_sig (truncated,
    867                                              TALER_MCA_EDDSA_CHALLENGE,
    868                                              &ch,
    869                                              ed_sig),
    870            "a truncated public key is rejected");
    871     GNUNET_free (truncated);
    872   }
    873   /* signing must refuse a key that is not the right size */
    874   check (NULL ==
    875          TALER_build_pos_confirmation_sig ("TOOSHORT",
    876                                            TALER_MCA_EDDSA_CHALLENGE,
    877                                            &ch),
    878          "signing refuses a short EdDSA key");
    879   check (NULL ==
    880          TALER_build_pos_confirmation_sig ("TOOSHORT",
    881                                            TALER_MCA_ECDSA_CHALLENGE,
    882                                            &ch),
    883          "signing refuses a short ECDSA key");
    884 
    885   GNUNET_free (ed_sig);
    886   GNUNET_free (ec_sig);
    887   GNUNET_free (ed_priv);
    888   GNUNET_free (ed_pub);
    889   GNUNET_free (ec_priv);
    890   GNUNET_free (ec_pub);
    891 }
    892 
    893 
    894 /**
    895  * The challenge algorithms must not have disturbed the TOTP
    896  * algorithms, which stay bit-for-bit as they were.
    897  */
    898 static void
    899 test_totp_unchanged (void)
    900 {
    901   /* RFC 3548 base32, as a TOTP application would provide it */
    902   const char *pos_key = "JBSWY3DPEHPK3PXP";
    903   struct TALER_Amount total;
    904   char *code;
    905 
    906   fprintf (stderr,
    907            "-- TOTP regression --\n");
    908   code = TALER_build_pos_confirmation (pos_key,
    909                                        TALER_MCA_WITHOUT_PRICE,
    910                                        NULL,
    911                                        GNUNET_TIME_UNIT_ZERO_TS);
    912   check (NULL != code,
    913          "TOTP_WITHOUT_PRICE still produces a code");
    914   GNUNET_free (code);
    915 
    916   GNUNET_assert (GNUNET_OK ==
    917                  TALER_string_to_amount ("EUR:1.5",
    918                                          &total));
    919   code = TALER_build_pos_confirmation (pos_key,
    920                                        TALER_MCA_WITH_PRICE,
    921                                        &total,
    922                                        GNUNET_TIME_UNIT_ZERO_TS);
    923   check (NULL != code,
    924          "TOTP_WITH_PRICE still produces a code");
    925   GNUNET_free (code);
    926 
    927   /* an invalid amount must fail closed rather than sign nothing */
    928   code = TALER_build_pos_confirmation (pos_key,
    929                                        TALER_MCA_WITH_PRICE,
    930                                        NULL,
    931                                        GNUNET_TIME_UNIT_ZERO_TS);
    932   check (NULL == code,
    933          "TOTP_WITH_PRICE without an amount fails closed");
    934   GNUNET_free (code);
    935 }
    936 
    937 
    938 int
    939 main (int argc,
    940       char *const *argv)
    941 {
    942   (void) argc;
    943   (void) argv;
    944   GNUNET_log_setup ("test-crypto-confirmation",
    945                     "WARNING",
    946                     NULL);
    947   test_challenge_alg (TALER_MCA_EDDSA_CHALLENGE,
    948                       "EDDSA_CHALLENGE",
    949                       32);
    950   test_challenge_alg (TALER_MCA_ECDSA_CHALLENGE,
    951                       "ECDSA_CHALLENGE",
    952                       33);
    953   test_known_answers ();
    954   test_malleability ();
    955   test_ecdsa_encoding ();
    956   test_signing_determinism ();
    957   test_ecdsa_invalid_points ();
    958   test_bad_inputs ();
    959   test_totp_unchanged ();
    960   return (0 == fails) ? 0 : 1;
    961 }
    962 
    963 
    964 /* end of test_crypto_confirmation.c */