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