(root)/
libxcrypt-4.4.36/
test/
ka-tester.c
       1  /* Test crypt() API with "known answer" hashes.
       2  
       3     Written by Zack Weinberg <zackw at panix.com> in 2019.
       4     To the extent possible under law, Zack Weinberg has waived all
       5     copyright and related or neighboring rights to this work.
       6  
       7     See https://creativecommons.org/publicdomain/zero/1.0/ for further
       8     details.  */
       9  
      10  #include "crypt-port.h"
      11  
      12  #include <stdio.h>
      13  #include <stdlib.h>
      14  #include <errno.h>
      15  
      16  /* The precalculated hashes in ka-table.inc, and some of the
      17     relationships among groups of test cases (see ka-table-gen.py)
      18     are invalidated if the execution character set is not ASCII.  */
      19  static_assert(' ' == 0x20 && 'C' == 0x43 && '~' == 0x7E,
      20                "Execution character set does not appear to be ASCII");
      21  
      22  /* This test verifies three things at once:
      23      - crypt, crypt_r, crypt_rn, and crypt_ra
      24        all produce the same outputs for the same inputs.
      25      - given hash <- crypt(phrase, setting),
      26         then hash == crypt(phrase, hash) also.
      27      - crypt(phrase, setting) == crypt'(phrase, setting)
      28        where crypt' is an independent implementation of the same
      29        hashing method.  (This is the "known answer" part of the test.)
      30  
      31     The independent implementations come from the Python 'passlib'
      32     library: <https://passlib.readthedocs.io/en/stable/>.
      33     See ka-table-gen.py for more detail.
      34  
      35     This file is compiled once for each hash, with macros defined that
      36     make ka-table.inc expose only the subset of the tests that are
      37     relevant to that hash.  This allows the test driver to run the
      38     known-answer tests for each enabled hash in parallel.  */
      39  
      40  struct testcase
      41  {
      42    const char *salt;
      43    const char *expected;
      44    const char *input;
      45  };
      46  
      47  static const struct testcase tests[] =
      48  {
      49  #include "ka-table.inc"
      50  
      51    /* Sentinel.  */
      52    { 0, 0, 0 },
      53  };
      54  
      55  /* Print out a string, using \xXX escapes for any characters that are
      56     not printable ASCII.  Backslash, single quote, and double quote are
      57     also escaped, by preceding them with another backslash.  If machine-
      58     parsing the output, note that we use the Python semantics of \x, not
      59     the C semantics: each \x consumes _exactly two_ subsequent hex digits.
      60     (For instance, \x123 means 0x12 0x33.)  */
      61  static void
      62  print_escaped (const char *s)
      63  {
      64    const unsigned char *p = (const unsigned char *)s;
      65    for (; *p; p++)
      66      {
      67        unsigned char c = *p;
      68        if (c == '\\' || c == '\"' || c == '\'')
      69          {
      70            putchar ('\\');
      71            putchar (c);
      72          }
      73        else if (0x20 <= c && c <= 0x7E)
      74          putchar (c);
      75        else
      76          printf ("\\x%02x", (unsigned int)c);
      77      }
      78  }
      79  
      80  /* Subroutine of report_result.  */
      81  static void
      82  begin_error_report (const struct testcase *tc, const char *tag)
      83  {
      84    printf ("FAIL: %s/", tc->salt);
      85    print_escaped (tc->input);
      86    printf (": %s ", tag);
      87  }
      88  
      89  /* Summarize the result of a single hashing operation.
      90     If everything is as expected, prints nothing and returns 0.
      91     Otherwise, prints a diagnostic message to stdout (not stderr!)
      92     and returns 1.  */
      93  static int
      94  report_result (const char *tag, const char *hash, int errnm,
      95                 const struct testcase *tc, bool expect_failure_tokens)
      96  {
      97    if (hash && hash[0] != '*')
      98      {
      99        /* We don't look at errno in this branch, because errno is
     100           allowed to be set by successful operations.  */
     101        if (!strcmp (hash, tc->expected))
     102          return 0;
     103  
     104        begin_error_report (tc, tag);
     105        printf ("mismatch: expected %s got %s\n", tc->expected, hash);
     106        return 1;
     107      }
     108    else
     109      {
     110        /* Ill-formed setting string arguments to 'crypt' are tested in a
     111           different program, so we never _expect_ a failure.  However, if
     112           we do get a failure, we want to log it in detail.  */
     113        begin_error_report (tc, tag);
     114  
     115        if (hash == 0)
     116          printf ("failure: got (null)");
     117        else
     118          printf ("failure: got %s", hash);
     119  
     120        /* errno should have been set.  */
     121        if (errnm)
     122          printf (", errno = %s", strerror (errnm));
     123        else
     124          printf (", errno not set");
     125  
     126        /* Should the API used have generated a NULL or a failure token?  */
     127        if (hash == 0 && expect_failure_tokens)
     128          printf (", failure token not generated");
     129        if (hash != 0 && !expect_failure_tokens)
     130          printf (", failure token wrongly generated");
     131  
     132        /* A failure token must never compare equal to the setting string
     133           that was used in the computation.  N.B. recrypt uses crypt_rn,
     134           which never produces failure tokens, so in this branch we can
     135           safely assume that the setting string used was tc->salt
     136           (if it generates one anyway that's an automatic failure).  */
     137        if (hash != 0 && !strcmp (tc->salt, hash))
     138          printf (", failure token == salt");
     139  
     140        putchar ('\n');
     141        return 1;
     142      }
     143  }
     144  
     145  static int
     146  calc_hashes_crypt (void)
     147  {
     148    char *hash;
     149    const struct testcase *t;
     150    int status = 0;
     151  
     152    for (t = tests; t->input != 0; t++)
     153      {
     154        errno = 0;
     155        hash = crypt (t->input, t->salt);
     156        status |= report_result ("crypt", hash, errno, t,
     157                                 ENABLE_FAILURE_TOKENS);
     158      }
     159  
     160    return status;
     161  }
     162  
     163  static int
     164  calc_hashes_crypt_r_rn (void)
     165  {
     166    char *hash;
     167    union
     168    {
     169      char pass[CRYPT_MAX_PASSPHRASE_SIZE + 1];
     170      int aligned;
     171    } u;
     172    const struct testcase *t;
     173    struct crypt_data data;
     174    int status = 0;
     175  
     176    memset (&data, 0, sizeof data);
     177    memset (u.pass, 0, CRYPT_MAX_PASSPHRASE_SIZE + 1);
     178    for (t = tests; t->input != 0; t++)
     179      {
     180        strncpy(u.pass + 1, t->input, CRYPT_MAX_PASSPHRASE_SIZE);
     181        printf("[%zu]: %s %s\n", strlen(t->input),
     182               t->input, t->salt);
     183        errno = 0;
     184        hash = crypt_r (u.pass + 1, t->salt, &data);
     185        status |= report_result ("crypt_r", hash, errno, t,
     186                                 ENABLE_FAILURE_TOKENS);
     187  
     188        errno = 0;
     189        hash = crypt_rn (u.pass + 1, t->salt, &data, (int)sizeof data);
     190        status |= report_result ("crypt_rn", hash, errno, t, false);
     191      }
     192  
     193    return status;
     194  }
     195  
     196  static int
     197  calc_hashes_crypt_ra_recrypt (void)
     198  {
     199    char *hash;
     200    const struct testcase *t;
     201    void *datap = 0;
     202    int datasz = 0;
     203    int status = 0;
     204  
     205    for (t = tests; t->input != 0; t++)
     206      {
     207        errno = 0;
     208        hash = crypt_ra (t->input, t->salt, &datap, &datasz);
     209        if (report_result ("crypt_ra", hash, errno, t, false))
     210          status = 1;
     211        else
     212          {
     213            /* if we get here, we know hash == t->expected */
     214            errno = 0;
     215            hash = crypt_ra (t->input, t->expected,
     216                             &datap, &datasz);
     217            status |= report_result ("recrypt", hash, errno, t, false);
     218          }
     219      }
     220  
     221    free (datap);
     222    return status;
     223  }
     224  
     225  int
     226  main (void)
     227  {
     228    int status = 0;
     229  
     230    /* Mark this test SKIPPED if the very first entry in the table is the
     231       sentinel; this happens only when the hash we would test is disabled.  */
     232    if (tests[0].input == 0)
     233      return 77;
     234  
     235    status |= calc_hashes_crypt ();
     236    status |= calc_hashes_crypt_r_rn ();
     237    status |= calc_hashes_crypt_ra_recrypt ();
     238  
     239    return status;
     240  }