Coverage Report

Created: 2026-10-28 06:28

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
9.01M
{
41
9.01M
    return ASN1_mbstring_ncopy(out, in, len, inform, mask, 0, 0);
42
9.01M
}
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.75M
{
48
5.75M
    int str_type;
49
5.75M
    int ret;
50
5.75M
    char free_out;
51
5.75M
    int outform, outlen = 0;
52
5.75M
    ASN1_STRING *dest;
53
5.75M
    unsigned char *p;
54
5.75M
    int nchar;
55
5.75M
    int (*cpyfunc)(unsigned long, void *) = NULL;
56
5.75M
    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.75M
    if (!mask)
66
0
        mask = DIRSTRING_TYPE;
67
5.75M
    if (len < 0) {
68
0
        ERR_raise(ERR_LIB_ASN1, ERR_R_PASSED_INVALID_ARGUMENT);
69
0
        return -1;
70
5.75M
    } 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.75M
    switch (inform) {
77
78
653k
    case MBSTRING_BMP:
79
653k
        if (len & 1) {
80
0
            ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH);
81
0
            return -1;
82
0
        }
83
653k
        nchar = len >> 1;
84
653k
        break;
85
86
946k
    case MBSTRING_UNIV:
87
946k
        if (len & 3) {
88
0
            ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH);
89
0
            return -1;
90
0
        }
91
946k
        nchar = len >> 2;
92
946k
        break;
93
94
1.01M
    case MBSTRING_UTF8:
95
1.01M
        nchar = 0;
96
        /* This counts the characters and does utf8 syntax checking */
97
1.01M
        ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar);
98
1.01M
        if (ret < 0) {
99
13.2k
            ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
100
13.2k
            return -1;
101
13.2k
        }
102
1.00M
        break;
103
104
3.13M
    case MBSTRING_ASC:
105
3.13M
        nchar = len;
106
3.13M
        break;
107
108
0
    default:
109
0
        ERR_raise(ERR_LIB_ASN1, ASN1_R_UNKNOWN_FORMAT);
110
0
        return -1;
111
5.75M
    }
112
113
5.74M
    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.74M
    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.74M
    if (traverse_string(in, len, inform, type_str, &mask) < 0) {
127
2.63k
        ERR_raise(ERR_LIB_ASN1, ASN1_R_ILLEGAL_CHARACTERS);
128
2.63k
        return -1;
129
2.63k
    }
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.74M
    outform = MBSTRING_ASC;
136
5.74M
    if (mask & B_ASN1_NUMERICSTRING)
137
0
        str_type = V_ASN1_NUMERICSTRING;
138
5.74M
    else if (mask & B_ASN1_PRINTABLESTRING)
139
0
        str_type = V_ASN1_PRINTABLESTRING;
140
5.74M
    else if (mask & B_ASN1_IA5STRING)
141
0
        str_type = V_ASN1_IA5STRING;
142
5.74M
    else if (mask & B_ASN1_T61STRING)
143
0
        str_type = V_ASN1_T61STRING;
144
5.74M
    else if (mask & B_ASN1_BMPSTRING) {
145
0
        str_type = V_ASN1_BMPSTRING;
146
0
        outform = MBSTRING_BMP;
147
5.74M
    } else if (mask & B_ASN1_UNIVERSALSTRING) {
148
0
        str_type = V_ASN1_UNIVERSALSTRING;
149
0
        outform = MBSTRING_UNIV;
150
5.74M
    } else {
151
5.74M
        str_type = V_ASN1_UTF8STRING;
152
5.74M
        outform = MBSTRING_UTF8;
153
5.74M
    }
154
5.74M
    if (!out)
155
0
        return str_type;
156
5.74M
    if (*out) {
157
5.74M
        free_out = 0;
158
5.74M
        dest = *out;
159
5.74M
        ASN1_STRING_set0(dest, NULL, 0);
160
5.74M
        dest->type = str_type;
161
5.74M
    } 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.74M
    if (inform == outform) {
172
1.00M
        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
1.00M
        return str_type;
181
1.00M
    }
182
183
    /* Work out how much space the destination will need */
184
4.73M
    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.73M
    case MBSTRING_UTF8:
217
4.73M
        outlen = 0;
218
4.73M
        ret = traverse_string(in, len, inform, out_utf8, &outlen);
219
4.73M
        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.73M
        cpyfunc = cpy_utf8;
227
4.73M
        break;
228
4.73M
    }
229
4.73M
    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.73M
    dest->length = outlen;
237
4.73M
    dest->data = p;
238
4.73M
    p[outlen] = 0;
239
4.73M
    traverse_string(in, len, inform, cpyfunc, &p);
240
4.73M
    return str_type;
241
4.73M
}
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
25.2M
{
252
25.2M
    unsigned long value;
253
25.2M
    int ret;
254
2.01G
    while (len) {
255
1.98G
        if (inform == MBSTRING_ASC) {
256
1.77G
            value = *p++;
257
1.77G
            len--;
258
1.77G
        } else if (inform == MBSTRING_BMP) {
259
105M
            value = *p++ << 8;
260
105M
            value |= *p++;
261
105M
            len -= 2;
262
110M
        } else if (inform == MBSTRING_UNIV) {
263
210k
            value = ((unsigned long)*p++) << 24;
264
210k
            value |= ((unsigned long)*p++) << 16;
265
210k
            value |= *p++ << 8;
266
210k
            value |= *p++;
267
210k
            len -= 4;
268
110M
        } else {
269
110M
            ret = UTF8_getc(p, len, &value);
270
110M
            if (ret < 0)
271
22.3k
                return -1;
272
110M
            len -= ret;
273
110M
            p += ret;
274
110M
        }
275
1.98G
        if (rfunc) {
276
1.98G
            ret = rfunc(value, arg);
277
1.98G
            if (ret <= 0)
278
4.55k
                return ret;
279
1.98G
        }
280
1.98G
    }
281
25.2M
    return 1;
282
25.2M
}
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
56.3M
{
290
56.3M
    int *nchar;
291
292
56.3M
    if (!is_unicode_valid(value))
293
0
        return -2;
294
56.3M
    nchar = arg;
295
56.3M
    (*nchar)++;
296
56.3M
    return 1;
297
56.3M
}
298
299
/* Determine size of output as a UTF8 String */
300
301
static int out_utf8(unsigned long value, void *arg)
302
622M
{
303
622M
    int *outlen, len;
304
305
622M
    len = UTF8_putc(NULL, -1, value);
306
622M
    if (len <= 0) {
307
0
        ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
308
0
        return len;
309
0
    }
310
622M
    outlen = arg;
311
622M
    if (*outlen >= INT_MAX - len) {
312
0
        ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
313
0
        return -1;
314
0
    }
315
622M
    *outlen += len;
316
622M
    return 1;
317
622M
}
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
681M
{
326
681M
    unsigned long usable_types = *((unsigned long *)arg);
327
681M
    unsigned long types = usable_types;
328
681M
    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
681M
    types &= B_ASN1_NUMERICSTRING | B_ASN1_PRINTABLESTRING
336
681M
        | B_ASN1_IA5STRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING
337
681M
        | 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
681M
    if (types != usable_types)
344
30
        types |= B_ASN1_UTF8STRING;
345
346
    /*
347
     * These checks should be in sync with ASN1_mbstring_ncopy.
348
     */
349
681M
    if ((types & B_ASN1_NUMERICSTRING) && !(ossl_isdigit(native) || native == ' '))
350
21
        types &= ~B_ASN1_NUMERICSTRING;
351
681M
    if ((types & B_ASN1_PRINTABLESTRING) && !ossl_isasn1print(native))
352
12
        types &= ~B_ASN1_PRINTABLESTRING;
353
681M
    if ((types & B_ASN1_IA5STRING) && !ossl_isascii(native))
354
26
        types &= ~B_ASN1_IA5STRING;
355
681M
    if ((types & B_ASN1_T61STRING) && (value > 0xff))
356
105
        types &= ~B_ASN1_T61STRING;
357
681M
    if ((types & B_ASN1_BMPSTRING) && (value > 0xffff))
358
18
        types &= ~B_ASN1_BMPSTRING;
359
681M
    if ((types & B_ASN1_UTF8STRING) && !is_unicode_valid(value))
360
4.36k
        types &= ~B_ASN1_UTF8STRING;
361
681M
    if (!types)
362
4.55k
        return -1;
363
681M
    *((unsigned long *)arg) = types;
364
681M
    return 1;
365
681M
}
366
367
/* Copy one byte per character ASCII like strings */
368
369
static int cpy_asc(unsigned long value, void *arg)
370
555
{
371
555
    unsigned char **p, *q;
372
555
    p = arg;
373
555
    q = *p;
374
555
    *q = (unsigned char)value;
375
555
    (*p)++;
376
555
    return 1;
377
555
}
378
379
/* Copy two byte per character BMPStrings */
380
381
static int cpy_bmp(unsigned long value, void *arg)
382
2.54M
{
383
2.54M
    unsigned char **p, *q;
384
2.54M
    p = arg;
385
2.54M
    q = *p;
386
2.54M
    *q++ = (unsigned char)((value >> 8) & 0xff);
387
2.54M
    *q = (unsigned char)(value & 0xff);
388
2.54M
    *p += 2;
389
2.54M
    return 1;
390
2.54M
}
391
392
/* Copy four byte per character UniversalStrings */
393
394
static int cpy_univ(unsigned long value, void *arg)
395
507k
{
396
507k
    unsigned char **p, *q;
397
507k
    p = arg;
398
507k
    q = *p;
399
507k
    *q++ = (unsigned char)((value >> 24) & 0xff);
400
507k
    *q++ = (unsigned char)((value >> 16) & 0xff);
401
507k
    *q++ = (unsigned char)((value >> 8) & 0xff);
402
507k
    *q = (unsigned char)(value & 0xff);
403
507k
    *p += 4;
404
507k
    return 1;
405
507k
}
406
407
/* Copy to a UTF8String */
408
409
static int cpy_utf8(unsigned long value, void *arg)
410
622M
{
411
622M
    unsigned char **p;
412
622M
    int ret;
413
622M
    p = arg;
414
    /* We already know there is enough room so pass 0xff as the length */
415
622M
    ret = UTF8_putc(*p, 0xff, value);
416
622M
    if (ret < 0)
417
0
        return ret;
418
622M
    *p += ret;
419
622M
    return 1;
420
622M
}