Coverage Report

Created: 2026-09-12 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl40/crypto/asn1/a_mbstr.c
Line
Count
Source
1
/*
2
 * Copyright 1999-2026 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <stdio.h>
11
#include "crypto/ctype.h"
12
#include "internal/cryptlib.h"
13
#include "internal/unicode.h"
14
#include <openssl/asn1.h>
15
16
#include <crypto/asn1.h>
17
18
static int traverse_string(const unsigned char *p, int len, int inform,
19
    int (*rfunc)(unsigned long value, void *in),
20
    void *arg);
21
static int in_utf8(unsigned long value, void *arg);
22
static int out_utf8(unsigned long value, void *arg);
23
static int type_str(unsigned long value, void *arg);
24
static int cpy_asc(unsigned long value, void *arg);
25
static int cpy_bmp(unsigned long value, void *arg);
26
static int cpy_univ(unsigned long value, void *arg);
27
static int cpy_utf8(unsigned long value, void *arg);
28
29
/*
30
 * These functions take a string in UTF8, ASCII or multibyte form and a mask
31
 * of permissible ASN1 string types. It then works out the minimal type
32
 * (using the order Numeric < Printable < IA5 < T61 < BMP < Universal < UTF8)
33
 * and creates a string of the correct type with the supplied data. Yes this is
34
 * horrible: it has to be :-( The 'ncopy' form checks minimum and maximum
35
 * size limits too.
36
 */
37
38
int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len,
39
    int inform, unsigned long mask)
40
7.79M
{
41
7.79M
    return ASN1_mbstring_ncopy(out, in, len, inform, mask, 0, 0);
42
7.79M
}
43
44
int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
45
    int inform, unsigned long mask,
46
    long minsize, long maxsize)
47
5.13M
{
48
5.13M
    int str_type;
49
5.13M
    int ret;
50
5.13M
    char free_out;
51
5.13M
    int outform, outlen = 0;
52
5.13M
    ASN1_STRING *dest;
53
5.13M
    unsigned char *p;
54
5.13M
    int nchar;
55
5.13M
    int (*cpyfunc)(unsigned long, void *) = NULL;
56
5.13M
    if (len == -1) {
57
0
        size_t len_s = strlen((const char *)in);
58
59
0
        if (len_s >= INT_MAX) {
60
0
            ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
61
0
            return -1;
62
0
        }
63
0
        len = (int)len_s;
64
0
    }
65
5.13M
    if (!mask)
66
0
        mask = DIRSTRING_TYPE;
67
5.13M
    if (len < 0) {
68
0
        ERR_raise(ERR_LIB_ASN1, ERR_R_PASSED_INVALID_ARGUMENT);
69
0
        return -1;
70
5.13M
    } else if (len >= INT_MAX) {
71
0
        ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
72
0
        return -1;
73
0
    }
74
75
    /* First do a string check and work out the number of characters */
76
5.13M
    switch (inform) {
77
78
529k
    case MBSTRING_BMP:
79
529k
        if (len & 1) {
80
0
            ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH);
81
0
            return -1;
82
0
        }
83
529k
        nchar = len >> 1;
84
529k
        break;
85
86
785k
    case MBSTRING_UNIV:
87
785k
        if (len & 3) {
88
0
            ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH);
89
0
            return -1;
90
0
        }
91
785k
        nchar = len >> 2;
92
785k
        break;
93
94
915k
    case MBSTRING_UTF8:
95
915k
        nchar = 0;
96
        /* This counts the characters and does utf8 syntax checking */
97
915k
        ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar);
98
915k
        if (ret < 0) {
99
10.7k
            ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
100
10.7k
            return -1;
101
10.7k
        }
102
905k
        break;
103
104
2.90M
    case MBSTRING_ASC:
105
2.90M
        nchar = len;
106
2.90M
        break;
107
108
0
    default:
109
0
        ERR_raise(ERR_LIB_ASN1, ASN1_R_UNKNOWN_FORMAT);
110
0
        return -1;
111
5.13M
    }
112
113
5.12M
    if ((minsize > 0) && (nchar < minsize)) {
114
0
        ERR_raise_data(ERR_LIB_ASN1, ASN1_R_STRING_TOO_SHORT,
115
0
            "minsize=%ld", minsize);
116
0
        return -1;
117
0
    }
118
119
5.12M
    if ((maxsize > 0) && (nchar > maxsize)) {
120
0
        ERR_raise_data(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG,
121
0
            "maxsize=%ld", maxsize);
122
0
        return -1;
123
0
    }
124
125
    /* Now work out minimal type (if any) */
126
5.12M
    if (traverse_string(in, len, inform, type_str, &mask) < 0) {
127
2.53k
        ERR_raise(ERR_LIB_ASN1, ASN1_R_ILLEGAL_CHARACTERS);
128
2.53k
        return -1;
129
2.53k
    }
130
131
    /*
132
     * Now work out output format and string type.
133
     * These checks should be in sync with the checks in type_str.
134
     */
135
5.11M
    outform = MBSTRING_ASC;
136
5.11M
    if (mask & B_ASN1_NUMERICSTRING)
137
0
        str_type = V_ASN1_NUMERICSTRING;
138
5.11M
    else if (mask & B_ASN1_PRINTABLESTRING)
139
0
        str_type = V_ASN1_PRINTABLESTRING;
140
5.11M
    else if (mask & B_ASN1_IA5STRING)
141
0
        str_type = V_ASN1_IA5STRING;
142
5.11M
    else if (mask & B_ASN1_T61STRING)
143
0
        str_type = V_ASN1_T61STRING;
144
5.11M
    else if (mask & B_ASN1_BMPSTRING) {
145
0
        str_type = V_ASN1_BMPSTRING;
146
0
        outform = MBSTRING_BMP;
147
5.11M
    } else if (mask & B_ASN1_UNIVERSALSTRING) {
148
0
        str_type = V_ASN1_UNIVERSALSTRING;
149
0
        outform = MBSTRING_UNIV;
150
5.11M
    } else {
151
5.11M
        str_type = V_ASN1_UTF8STRING;
152
5.11M
        outform = MBSTRING_UTF8;
153
5.11M
    }
154
5.11M
    if (!out)
155
0
        return str_type;
156
5.11M
    if (*out) {
157
5.11M
        free_out = 0;
158
5.11M
        dest = *out;
159
5.11M
        ASN1_STRING_set0(dest, NULL, 0);
160
5.11M
        dest->type = str_type;
161
5.11M
    } else {
162
0
        free_out = 1;
163
0
        dest = ASN1_STRING_type_new(str_type);
164
0
        if (dest == NULL) {
165
0
            ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB);
166
0
            return -1;
167
0
        }
168
0
        *out = dest;
169
0
    }
170
    /* If both the same type just copy across */
171
5.11M
    if (inform == outform) {
172
905k
        if (!ASN1_STRING_set(dest, in, len)) {
173
0
            if (free_out) {
174
0
                ASN1_STRING_free(dest);
175
0
                *out = NULL;
176
0
            }
177
0
            ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB);
178
0
            return -1;
179
0
        }
180
905k
        return str_type;
181
905k
    }
182
183
    /* Work out how much space the destination will need */
184
4.21M
    switch (outform) {
185
0
    case MBSTRING_ASC:
186
0
        outlen = nchar;
187
0
        cpyfunc = cpy_asc;
188
0
        break;
189
190
0
    case MBSTRING_BMP:
191
0
        if (nchar > INT_MAX / 2) {
192
0
            ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
193
0
            if (free_out) {
194
0
                ASN1_STRING_free(dest);
195
0
                *out = NULL;
196
0
            }
197
0
            return -1;
198
0
        }
199
0
        outlen = nchar << 1;
200
0
        cpyfunc = cpy_bmp;
201
0
        break;
202
203
0
    case MBSTRING_UNIV:
204
0
        if (nchar > INT_MAX / 4) {
205
0
            ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
206
0
            if (free_out) {
207
0
                ASN1_STRING_free(dest);
208
0
                *out = NULL;
209
0
            }
210
0
            return -1;
211
0
        }
212
0
        outlen = nchar << 2;
213
0
        cpyfunc = cpy_univ;
214
0
        break;
215
216
4.21M
    case MBSTRING_UTF8:
217
4.21M
        outlen = 0;
218
4.21M
        ret = traverse_string(in, len, inform, out_utf8, &outlen);
219
4.21M
        if (ret < 0) { /* error already raised in out_utf8() */
220
0
            if (free_out) {
221
0
                ASN1_STRING_free(dest);
222
0
                *out = NULL;
223
0
            }
224
0
            return -1;
225
0
        }
226
4.21M
        cpyfunc = cpy_utf8;
227
4.21M
        break;
228
4.21M
    }
229
4.21M
    if ((p = OPENSSL_malloc(outlen + 1)) == NULL) {
230
0
        if (free_out) {
231
0
            ASN1_STRING_free(dest);
232
0
            *out = NULL;
233
0
        }
234
0
        return -1;
235
0
    }
236
4.21M
    dest->length = outlen;
237
4.21M
    dest->data = p;
238
4.21M
    p[outlen] = 0;
239
4.21M
    traverse_string(in, len, inform, cpyfunc, &p);
240
4.21M
    return str_type;
241
4.21M
}
242
243
/*
244
 * This function traverses a string and passes the value of each character to
245
 * an optional function along with a void * argument.
246
 */
247
248
static int traverse_string(const unsigned char *p, int len, int inform,
249
    int (*rfunc)(unsigned long value, void *in),
250
    void *arg)
251
22.0M
{
252
22.0M
    unsigned long value;
253
22.0M
    int ret;
254
1.51G
    while (len) {
255
1.49G
        if (inform == MBSTRING_ASC) {
256
1.38G
            value = *p++;
257
1.38G
            len--;
258
1.38G
        } else if (inform == MBSTRING_BMP) {
259
84.7M
            value = *p++ << 8;
260
84.7M
            value |= *p++;
261
84.7M
            len -= 2;
262
84.7M
        } else if (inform == MBSTRING_UNIV) {
263
163k
            value = ((unsigned long)*p++) << 24;
264
163k
            value |= ((unsigned long)*p++) << 16;
265
163k
            value |= *p++ << 8;
266
163k
            value |= *p++;
267
163k
            len -= 4;
268
22.4M
        } else {
269
22.4M
            ret = UTF8_getc(p, len, &value);
270
22.4M
            if (ret < 0)
271
17.3k
                return -1;
272
22.3M
            len -= ret;
273
22.3M
            p += ret;
274
22.3M
        }
275
1.49G
        if (rfunc) {
276
1.49G
            ret = rfunc(value, arg);
277
1.49G
            if (ret <= 0)
278
3.56k
                return ret;
279
1.49G
        }
280
1.49G
    }
281
22.0M
    return 1;
282
22.0M
}
283
284
/* Various utility functions for traverse_string */
285
286
/* Just count number of characters */
287
288
static int in_utf8(unsigned long value, void *arg)
289
11.7M
{
290
11.7M
    int *nchar;
291
292
11.7M
    if (!is_unicode_valid(value))
293
0
        return -2;
294
11.7M
    nchar = arg;
295
11.7M
    (*nchar)++;
296
11.7M
    return 1;
297
11.7M
}
298
299
/* Determine size of output as a UTF8 String */
300
301
static int out_utf8(unsigned long value, void *arg)
302
488M
{
303
488M
    int *outlen, len;
304
305
488M
    len = UTF8_putc(NULL, -1, value);
306
488M
    if (len <= 0) {
307
0
        ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
308
0
        return len;
309
0
    }
310
488M
    outlen = arg;
311
488M
    if (*outlen >= INT_MAX - len) {
312
0
        ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
313
0
        return -1;
314
0
    }
315
488M
    *outlen += len;
316
488M
    return 1;
317
488M
}
318
319
/*
320
 * Determine the "type" of a string: check each character against a supplied
321
 * "mask".
322
 */
323
324
static int type_str(unsigned long value, void *arg)
325
505M
{
326
505M
    unsigned long usable_types = *((unsigned long *)arg);
327
505M
    unsigned long types = usable_types;
328
505M
    const int native = value > INT_MAX ? INT_MAX : ossl_fromascii(value);
329
330
    /*
331
     * Clear out all the types which are not checked later. If any of those
332
     * is present in the mask, then the UTF8 type will be added and checked
333
     * below.
334
     */
335
505M
    types &= B_ASN1_NUMERICSTRING | B_ASN1_PRINTABLESTRING
336
505M
        | B_ASN1_IA5STRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING
337
505M
        | B_ASN1_UNIVERSALSTRING | B_ASN1_UTF8STRING;
338
339
    /*
340
     * If any other types were in the input mask, they're effectively treated
341
     * as UTF8
342
     */
343
505M
    if (types != usable_types)
344
10
        types |= B_ASN1_UTF8STRING;
345
346
    /*
347
     * These checks should be in sync with ASN1_mbstring_ncopy.
348
     */
349
505M
    if ((types & B_ASN1_NUMERICSTRING) && !(ossl_isdigit(native) || native == ' '))
350
0
        types &= ~B_ASN1_NUMERICSTRING;
351
505M
    if ((types & B_ASN1_PRINTABLESTRING) && !ossl_isasn1print(native))
352
11
        types &= ~B_ASN1_PRINTABLESTRING;
353
505M
    if ((types & B_ASN1_IA5STRING) && !ossl_isascii(native))
354
15
        types &= ~B_ASN1_IA5STRING;
355
505M
    if ((types & B_ASN1_T61STRING) && (value > 0xff))
356
44
        types &= ~B_ASN1_T61STRING;
357
505M
    if ((types & B_ASN1_BMPSTRING) && (value > 0xffff))
358
0
        types &= ~B_ASN1_BMPSTRING;
359
505M
    if ((types & B_ASN1_UTF8STRING) && !is_unicode_valid(value))
360
3.49k
        types &= ~B_ASN1_UTF8STRING;
361
505M
    if (!types)
362
3.56k
        return -1;
363
505M
    *((unsigned long *)arg) = types;
364
505M
    return 1;
365
505M
}
366
367
/* Copy one byte per character ASCII like strings */
368
369
static int cpy_asc(unsigned long value, void *arg)
370
631
{
371
631
    unsigned char **p, *q;
372
631
    p = arg;
373
631
    q = *p;
374
631
    *q = (unsigned char)value;
375
631
    (*p)++;
376
631
    return 1;
377
631
}
378
379
/* Copy two byte per character BMPStrings */
380
381
static int cpy_bmp(unsigned long value, void *arg)
382
1.85M
{
383
1.85M
    unsigned char **p, *q;
384
1.85M
    p = arg;
385
1.85M
    q = *p;
386
1.85M
    *q++ = (unsigned char)((value >> 8) & 0xff);
387
1.85M
    *q = (unsigned char)(value & 0xff);
388
1.85M
    *p += 2;
389
1.85M
    return 1;
390
1.85M
}
391
392
/* Copy four byte per character UniversalStrings */
393
394
static int cpy_univ(unsigned long value, void *arg)
395
402k
{
396
402k
    unsigned char **p, *q;
397
402k
    p = arg;
398
402k
    q = *p;
399
402k
    *q++ = (unsigned char)((value >> 24) & 0xff);
400
402k
    *q++ = (unsigned char)((value >> 16) & 0xff);
401
402k
    *q++ = (unsigned char)((value >> 8) & 0xff);
402
402k
    *q = (unsigned char)(value & 0xff);
403
402k
    *p += 4;
404
402k
    return 1;
405
402k
}
406
407
/* Copy to a UTF8String */
408
409
static int cpy_utf8(unsigned long value, void *arg)
410
488M
{
411
488M
    unsigned char **p;
412
488M
    int ret;
413
488M
    p = arg;
414
    /* We already know there is enough room so pass 0xff as the length */
415
488M
    ret = UTF8_putc(*p, 0xff, value);
416
488M
    if (ret < 0)
417
0
        return ret;
418
488M
    *p += ret;
419
488M
    return 1;
420
488M
}