Home | History | Annotate | Line # | Download | only in test
punycode_test.c revision 1.1
      1  1.1  christos /*
      2  1.1  christos  * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
      3  1.1  christos  *
      4  1.1  christos  * Licensed under the Apache License 2.0 (the "License").  You may not use
      5  1.1  christos  * this file except in compliance with the License.  You can obtain a copy
      6  1.1  christos  * in the file LICENSE in the source distribution or at
      7  1.1  christos  * https://www.openssl.org/source/license.html
      8  1.1  christos  */
      9  1.1  christos 
     10  1.1  christos #include <openssl/crypto.h>
     11  1.1  christos #include <string.h>
     12  1.1  christos 
     13  1.1  christos #include "crypto/punycode.h"
     14  1.1  christos #include "internal/nelem.h"
     15  1.1  christos #include "internal/packet.h"
     16  1.1  christos #include "testutil.h"
     17  1.1  christos 
     18  1.1  christos 
     19  1.1  christos static const struct puny_test {
     20  1.1  christos     unsigned int raw[50];
     21  1.1  christos     const char *encoded;
     22  1.1  christos } puny_cases[] = {
     23  1.1  christos     { /* Test of 4 byte codepoint using smileyface emoji */
     24  1.1  christos         { 0x1F600
     25  1.1  christos         },
     26  1.1  christos         "e28h"
     27  1.1  christos     },
     28  1.1  christos     /* Test cases from RFC 3492 */
     29  1.1  christos     {   /* Arabic (Egyptian) */
     30  1.1  christos         { 0x0644, 0x064A, 0x0647, 0x0645, 0x0627, 0x0628, 0x062A, 0x0643, 0x0644,
     31  1.1  christos           0x0645, 0x0648, 0x0634, 0x0639, 0x0631, 0x0628, 0x064A, 0x061F
     32  1.1  christos         },
     33  1.1  christos         "egbpdaj6bu4bxfgehfvwxn"
     34  1.1  christos     },
     35  1.1  christos     {   /* Chinese (simplified) */
     36  1.1  christos         { 0x4ED6, 0x4EEC, 0x4E3A, 0x4EC0, 0x4E48, 0x4E0D, 0x8BF4, 0x4E2D, 0x6587
     37  1.1  christos         },
     38  1.1  christos         "ihqwcrb4cv8a8dqg056pqjye"
     39  1.1  christos     },
     40  1.1  christos     {   /* Chinese (traditional) */
     41  1.1  christos         { 0x4ED6, 0x5011, 0x7232, 0x4EC0, 0x9EBD, 0x4E0D, 0x8AAA, 0x4E2D, 0x6587
     42  1.1  christos         },
     43  1.1  christos         "ihqwctvzc91f659drss3x8bo0yb"
     44  1.1  christos     },
     45  1.1  christos     {    /* Czech: Pro<ccaron>prost<ecaron>nemluv<iacute><ccaron>esky */
     46  1.1  christos         { 0x0050, 0x0072, 0x006F, 0x010D, 0x0070, 0x0072, 0x006F, 0x0073, 0x0074,
     47  1.1  christos           0x011B, 0x006E, 0x0065, 0x006D, 0x006C, 0x0075, 0x0076, 0x00ED, 0x010D,
     48  1.1  christos           0x0065, 0x0073, 0x006B, 0x0079
     49  1.1  christos        },
     50  1.1  christos         "Proprostnemluvesky-uyb24dma41a"
     51  1.1  christos     },
     52  1.1  christos     {   /* Hebrew */
     53  1.1  christos         { 0x05DC, 0x05DE, 0x05D4, 0x05D4, 0x05DD, 0x05E4, 0x05E9, 0x05D5, 0x05D8,
     54  1.1  christos           0x05DC, 0x05D0, 0x05DE, 0x05D3, 0x05D1, 0x05E8, 0x05D9, 0x05DD, 0x05E2,
     55  1.1  christos           0x05D1, 0x05E8, 0x05D9, 0x05EA
     56  1.1  christos         },
     57  1.1  christos         "4dbcagdahymbxekheh6e0a7fei0b"
     58  1.1  christos     },
     59  1.1  christos     {   /* Hindi (Devanagari) */
     60  1.1  christos         { 0x092F, 0x0939, 0x0932, 0x094B, 0x0917, 0x0939, 0x093F, 0x0928, 0x094D,
     61  1.1  christos           0x0926, 0x0940, 0x0915, 0x094D, 0x092F, 0x094B, 0x0902, 0x0928, 0x0939,
     62  1.1  christos           0x0940, 0x0902, 0x092C, 0x094B, 0x0932, 0x0938, 0x0915, 0x0924, 0x0947,
     63  1.1  christos           0x0939, 0x0948, 0x0902
     64  1.1  christos         },
     65  1.1  christos         "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd"
     66  1.1  christos     },
     67  1.1  christos     {   /* Japanese (kanji and hiragana) */
     68  1.1  christos         { 0x306A, 0x305C, 0x307F, 0x3093, 0x306A, 0x65E5, 0x672C, 0x8A9E, 0x3092,
     69  1.1  christos           0x8A71, 0x3057, 0x3066, 0x304F, 0x308C, 0x306A, 0x3044, 0x306E, 0x304B
     70  1.1  christos         },
     71  1.1  christos         "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa"
     72  1.1  christos     },
     73  1.1  christos     {   /* Korean (Hangul syllables) */
     74  1.1  christos         { 0xC138, 0xACC4, 0xC758, 0xBAA8, 0xB4E0, 0xC0AC, 0xB78C, 0xB4E4, 0xC774,
     75  1.1  christos           0xD55C, 0xAD6D, 0xC5B4, 0xB97C, 0xC774, 0xD574, 0xD55C, 0xB2E4, 0xBA74,
     76  1.1  christos           0xC5BC, 0xB9C8, 0xB098, 0xC88B, 0xC744, 0xAE4C
     77  1.1  christos         },
     78  1.1  christos         "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c"
     79  1.1  christos     },
     80  1.1  christos     {   /* Russian (Cyrillic) */
     81  1.1  christos         { 0x043F, 0x043E, 0x0447, 0x0435, 0x043C, 0x0443, 0x0436, 0x0435, 0x043E,
     82  1.1  christos           0x043D, 0x0438, 0x043D, 0x0435, 0x0433, 0x043E, 0x0432, 0x043E, 0x0440,
     83  1.1  christos           0x044F, 0x0442, 0x043F, 0x043E, 0x0440, 0x0443, 0x0441, 0x0441, 0x043A,
     84  1.1  christos           0x0438
     85  1.1  christos         },
     86  1.1  christos         "b1abfaaepdrnnbgefbaDotcwatmq2g4l"
     87  1.1  christos     },
     88  1.1  christos     {   /* Spanish */
     89  1.1  christos         { 0x0050, 0x006F, 0x0072, 0x0071, 0x0075, 0x00E9, 0x006E, 0x006F, 0x0070,
     90  1.1  christos           0x0075, 0x0065, 0x0064, 0x0065, 0x006E, 0x0073, 0x0069, 0x006D, 0x0070,
     91  1.1  christos           0x006C, 0x0065, 0x006D, 0x0065, 0x006E, 0x0074, 0x0065, 0x0068, 0x0061,
     92  1.1  christos           0x0062, 0x006C, 0x0061, 0x0072, 0x0065, 0x006E, 0x0045, 0x0073, 0x0070,
     93  1.1  christos           0x0061, 0x00F1, 0x006F, 0x006C
     94  1.1  christos         },
     95  1.1  christos         "PorqunopuedensimplementehablarenEspaol-fmd56a"
     96  1.1  christos     },
     97  1.1  christos     {   /* Vietnamese */
     98  1.1  christos         { 0x0054, 0x1EA1, 0x0069, 0x0073, 0x0061, 0x006F, 0x0068, 0x1ECD, 0x006B,
     99  1.1  christos           0x0068, 0x00F4, 0x006E, 0x0067, 0x0074, 0x0068, 0x1EC3, 0x0063, 0x0068,
    100  1.1  christos           0x1EC9, 0x006E, 0x00F3, 0x0069, 0x0074, 0x0069, 0x1EBF, 0x006E, 0x0067,
    101  1.1  christos           0x0056, 0x0069, 0x1EC7, 0x0074
    102  1.1  christos         },
    103  1.1  christos         "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g"
    104  1.1  christos     },
    105  1.1  christos     {   /* Japanese: 3<nen>B<gumi><kinpachi><sensei> */
    106  1.1  christos         { 0x0033, 0x5E74, 0x0042, 0x7D44, 0x91D1, 0x516B, 0x5148, 0x751F
    107  1.1  christos         },
    108  1.1  christos         "3B-ww4c5e180e575a65lsy2b"
    109  1.1  christos     },
    110  1.1  christos     {   /* Japanese: <amuro><namie>-with-SUPER-MONKEYS */
    111  1.1  christos         { 0x5B89, 0x5BA4, 0x5948, 0x7F8E, 0x6075, 0x002D, 0x0077, 0x0069, 0x0074,
    112  1.1  christos           0x0068, 0x002D, 0x0053, 0x0055, 0x0050, 0x0045, 0x0052, 0x002D, 0x004D,
    113  1.1  christos           0x004F, 0x004E, 0x004B, 0x0045, 0x0059, 0x0053
    114  1.1  christos         },
    115  1.1  christos         "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n"
    116  1.1  christos     },
    117  1.1  christos     {   /* Japanese: Hello-Another-Way-<sorezore><no><basho> */
    118  1.1  christos         { 0x0048, 0x0065, 0x006C, 0x006C, 0x006F, 0x002D, 0x0041, 0x006E, 0x006F,
    119  1.1  christos           0x0074, 0x0068, 0x0065, 0x0072, 0x002D, 0x0057, 0x0061, 0x0079, 0x002D,
    120  1.1  christos           0x305D, 0x308C, 0x305E, 0x308C, 0x306E, 0x5834, 0x6240
    121  1.1  christos         },
    122  1.1  christos         "Hello-Another-Way--fc4qua05auwb3674vfr0b"
    123  1.1  christos     },
    124  1.1  christos     {   /* Japanese: <hitotsu><yane><no><shita>2 */
    125  1.1  christos         { 0x3072, 0x3068, 0x3064, 0x5C4B, 0x6839, 0x306E, 0x4E0B, 0x0032
    126  1.1  christos         },
    127  1.1  christos         "2-u9tlzr9756bt3uc0v"
    128  1.1  christos     },
    129  1.1  christos     {   /* Japanese: Maji<de>Koi<suru>5<byou><mae> */
    130  1.1  christos         { 0x004D, 0x0061, 0x006A, 0x0069, 0x3067, 0x004B, 0x006F, 0x0069, 0x3059,
    131  1.1  christos           0x308B, 0x0035, 0x79D2, 0x524D
    132  1.1  christos         },
    133  1.1  christos         "MajiKoi5-783gue6qz075azm5e"
    134  1.1  christos     },
    135  1.1  christos     {   /* Japanese: <pafii>de<runba> */
    136  1.1  christos         { 0x30D1, 0x30D5, 0x30A3, 0x30FC, 0x0064, 0x0065, 0x30EB, 0x30F3, 0x30D0
    137  1.1  christos         },
    138  1.1  christos         "de-jg4avhby1noc0d"
    139  1.1  christos     },
    140  1.1  christos     {   /* Japanese: <sono><supiido><de> */
    141  1.1  christos         { 0x305D, 0x306E, 0x30B9, 0x30D4, 0x30FC, 0x30C9, 0x3067
    142  1.1  christos         },
    143  1.1  christos         "d9juau41awczczp"
    144  1.1  christos     },
    145  1.1  christos     {   /* -> $1.00 <- */
    146  1.1  christos         { 0x002D, 0x003E, 0x0020, 0x0024, 0x0031, 0x002E, 0x0030, 0x0030, 0x0020,
    147  1.1  christos           0x003C, 0x002D
    148  1.1  christos         },
    149  1.1  christos         "-> $1.00 <--"
    150  1.1  christos     }
    151  1.1  christos };
    152  1.1  christos 
    153  1.1  christos static int test_punycode(int n)
    154  1.1  christos {
    155  1.1  christos     const struct puny_test *tc = puny_cases + n;
    156  1.1  christos     unsigned int buffer[50];
    157  1.1  christos     unsigned int bsize = OSSL_NELEM(buffer);
    158  1.1  christos     size_t i;
    159  1.1  christos 
    160  1.1  christos     if (!TEST_true(ossl_punycode_decode(tc->encoded, strlen(tc->encoded),
    161  1.1  christos                                         buffer, &bsize)))
    162  1.1  christos         return 0;
    163  1.1  christos     for (i = 0; i < OSSL_NELEM(tc->raw); i++)
    164  1.1  christos         if (tc->raw[i] == 0)
    165  1.1  christos             break;
    166  1.1  christos     if (!TEST_mem_eq(buffer, bsize * sizeof(*buffer),
    167  1.1  christos                      tc->raw, i * sizeof(*tc->raw)))
    168  1.1  christos         return 0;
    169  1.1  christos     return 1;
    170  1.1  christos }
    171  1.1  christos 
    172  1.1  christos static const struct bad_decode_test {
    173  1.1  christos     size_t outlen;
    174  1.1  christos     const char input[20];
    175  1.1  christos } bad_decode_tests[] = {
    176  1.1  christos     { 20, "xn--e-*" },   /* bad digit '*' */
    177  1.1  christos     { 10, "xn--e-999" }, /* loop > enc_len */
    178  1.1  christos     { 20, "xn--e-999999999" }, /* Too big */
    179  1.1  christos     { 20, {'x', 'n', '-', '-', (char)0x80, '-' } }, /* Not basic */
    180  1.1  christos     { 20, "xn--e-Oy65t" }, /* codepoint > 0x10FFFF */
    181  1.1  christos };
    182  1.1  christos 
    183  1.1  christos static int test_a2ulabel_bad_decode(int tst)
    184  1.1  christos {
    185  1.1  christos     char out[20];
    186  1.1  christos 
    187  1.1  christos     return TEST_int_eq(ossl_a2ulabel(bad_decode_tests[tst].input, out, bad_decode_tests[tst].outlen), -1);
    188  1.1  christos }
    189  1.1  christos 
    190  1.1  christos static int test_a2ulabel(void)
    191  1.1  christos {
    192  1.1  christos     char out[50];
    193  1.1  christos     char in[530] = { 0 };
    194  1.1  christos 
    195  1.1  christos     /*
    196  1.1  christos      * The punycode being passed in and parsed is malformed but we're not
    197  1.1  christos      * verifying that behaviour here.
    198  1.1  christos      */
    199  1.1  christos     if (!TEST_int_eq(ossl_a2ulabel("xn--a.b.c", out, 1), 0)
    200  1.1  christos             || !TEST_int_eq(ossl_a2ulabel("xn--a.b.c", out, 7), 1))
    201  1.1  christos         return 0;
    202  1.1  christos     /* Test for an off by one on the buffer size works */
    203  1.1  christos     if (!TEST_int_eq(ossl_a2ulabel("xn--a.b.c", out, 6), 0)
    204  1.1  christos             || !TEST_int_eq(ossl_a2ulabel("xn--a.b.c", out, 7), 1)
    205  1.1  christos             || !TEST_str_eq(out,"\xc2\x80.b.c"))
    206  1.1  christos         return 0;
    207  1.1  christos 
    208  1.1  christos     /* Test 4 byte smiley face */
    209  1.1  christos     if (!TEST_int_eq(ossl_a2ulabel("xn--e28h.com", out, 10), 1))
    210  1.1  christos         return 0;
    211  1.1  christos 
    212  1.1  christos     /* Test that we dont overflow the fixed internal buffer of 512 bytes when the starting bytes are copied */
    213  1.1  christos     strcpy(in, "xn--");
    214  1.1  christos     memset(in + 4, 'e', 513);
    215  1.1  christos     memcpy(in + 517, "-3ya", 4);
    216  1.1  christos     if (!TEST_int_eq(ossl_a2ulabel(in, out, 50), -1))
    217  1.1  christos         return 0;
    218  1.1  christos 
    219  1.1  christos     return 1;
    220  1.1  christos }
    221  1.1  christos 
    222  1.1  christos static int test_puny_overrun(void)
    223  1.1  christos {
    224  1.1  christos     static const unsigned int out[] = {
    225  1.1  christos         0x0033, 0x5E74, 0x0042, 0x7D44, 0x91D1, 0x516B, 0x5148, 0x751F
    226  1.1  christos     };
    227  1.1  christos     static const char *in = "3B-ww4c5e180e575a65lsy2b";
    228  1.1  christos     unsigned int buf[OSSL_NELEM(out)];
    229  1.1  christos     unsigned int bsize = OSSL_NELEM(buf) - 1;
    230  1.1  christos 
    231  1.1  christos     if (!TEST_false(ossl_punycode_decode(in, strlen(in), buf, &bsize))) {
    232  1.1  christos         if (TEST_mem_eq(buf, bsize * sizeof(*buf), out, sizeof(out)))
    233  1.1  christos             TEST_error("CRITICAL: buffer overrun detected!");
    234  1.1  christos         return 0;
    235  1.1  christos     }
    236  1.1  christos     return 1;
    237  1.1  christos }
    238  1.1  christos 
    239  1.1  christos static int test_dotted_overflow(void)
    240  1.1  christos {
    241  1.1  christos     static const char string[] = "a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a";
    242  1.1  christos     const size_t num_reps = OSSL_NELEM(string) / 2;
    243  1.1  christos     WPACKET p;
    244  1.1  christos     BUF_MEM *in;
    245  1.1  christos     char *out = NULL;
    246  1.1  christos     size_t i;
    247  1.1  christos     int res = 0;
    248  1.1  christos 
    249  1.1  christos     /* Create out input punycode string */
    250  1.1  christos     if (!TEST_ptr(in = BUF_MEM_new()))
    251  1.1  christos         return 0;
    252  1.1  christos     if (!TEST_true(WPACKET_init_len(&p, in, 0))) {
    253  1.1  christos         BUF_MEM_free(in);
    254  1.1  christos         return 0;
    255  1.1  christos     }
    256  1.1  christos     for (i = 0; i < num_reps; i++) {
    257  1.1  christos         if (i > 1 && !TEST_true(WPACKET_put_bytes_u8(&p, '.')))
    258  1.1  christos             goto err;
    259  1.1  christos         if (!TEST_true(WPACKET_memcpy(&p, "xn--a", sizeof("xn--a") - 1)))
    260  1.1  christos             goto err;
    261  1.1  christos     }
    262  1.1  christos     if (!TEST_true(WPACKET_put_bytes_u8(&p, '\0')))
    263  1.1  christos             goto err;
    264  1.1  christos     if (!TEST_ptr(out = OPENSSL_malloc(in->length)))
    265  1.1  christos         goto err;
    266  1.1  christos 
    267  1.1  christos     /* Test the decode into an undersized buffer */
    268  1.1  christos     memset(out, 0x7f, in->length - 1);
    269  1.1  christos     if (!TEST_int_le(ossl_a2ulabel(in->data, out, num_reps), 0)
    270  1.1  christos             || !TEST_int_eq(out[num_reps], 0x7f))
    271  1.1  christos         goto err;
    272  1.1  christos 
    273  1.1  christos     /* Test the decode works into a full size buffer */
    274  1.1  christos     if (!TEST_int_gt(ossl_a2ulabel(in->data, out, in->length), 0)
    275  1.1  christos             || !TEST_size_t_eq(strlen(out), num_reps * 3))
    276  1.1  christos         goto err;
    277  1.1  christos 
    278  1.1  christos     res = 1;
    279  1.1  christos  err:
    280  1.1  christos     WPACKET_cleanup(&p);
    281  1.1  christos     BUF_MEM_free(in);
    282  1.1  christos     OPENSSL_free(out);
    283  1.1  christos     return res;
    284  1.1  christos }
    285  1.1  christos 
    286  1.1  christos int setup_tests(void)
    287  1.1  christos {
    288  1.1  christos     ADD_ALL_TESTS(test_punycode, OSSL_NELEM(puny_cases));
    289  1.1  christos     ADD_TEST(test_dotted_overflow);
    290  1.1  christos     ADD_TEST(test_a2ulabel);
    291  1.1  christos     ADD_TEST(test_puny_overrun);
    292  1.1  christos     ADD_ALL_TESTS(test_a2ulabel_bad_decode, OSSL_NELEM(bad_decode_tests));
    293  1.1  christos     return 1;
    294  1.1  christos }
    295