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