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