Coverage Report

Created: 2026-07-07 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/ssl/tls13echv.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
/* Validation functions for ECH public names. */
7
8
#include "seccomon.h"
9
10
/* Convert a single character `c` into a number `*d` with the given radix.
11
 * Fails if the character isn't valid for the radix.
12
 */
13
static SECStatus
14
tls13_IpDigit(PRUint8 c, PRUint8 radix, PRUint8 *d)
15
34.1k
{
16
34.1k
    PRUint8 v = 0xff;
17
34.1k
    if (c >= '0' && c <= '9') {
18
13.8k
        v = c - '0';
19
20.3k
    } else if (radix > 10) {
20
6.33k
        if (c >= 'a' && c <= 'f') {
21
1.10k
            v = c - 'a';
22
5.22k
        } else if (c >= 'A' && c <= 'F') {
23
2.59k
            v = c - 'A';
24
2.59k
        }
25
6.33k
    }
26
34.1k
    if (v >= radix) {
27
16.6k
        return SECFailure;
28
16.6k
    }
29
17.5k
    *d = v;
30
17.5k
    return SECSuccess;
31
34.1k
}
32
33
/* This function takes the first couple of characters from `str`, starting at offset
34
 * `*i` and calculates a radix.  If it starts with "0x" or "0X", then `*i` is moved up
35
 * by two and `*radix` is set to 16 (hexadecimal).  If it starts with "0", then `*i` is
36
 * moved up by one and `*radix` is set to 8 (octal).  Otherwise, `*i` is left alone and
37
 * `*radix` is set to 10 (decimal).
38
 * Fails if there are no characters remaining or the next character is '.', either at
39
 * the start or after "0x".
40
 */
41
static SECStatus
42
tls13_IpRadix(const PRUint8 *str, unsigned int len, unsigned int *i, PRUint8 *radix)
43
18.0k
{
44
18.0k
    if (*i == len || str[*i] == '.') {
45
0
        return SECFailure;
46
0
    }
47
18.0k
    if (str[*i] == '0') {
48
5.83k
        (*i)++;
49
5.83k
        if (*i < len && (str[*i] == 'x' || str[*i] == 'X')) {
50
3.90k
            (*i)++;
51
3.90k
            if (*i == len || str[*i] == '.') {
52
651
                return SECFailure;
53
651
            }
54
3.25k
            *radix = 16;
55
3.25k
        } else {
56
1.93k
            *radix = 8;
57
1.93k
        }
58
12.2k
    } else {
59
12.2k
        *radix = 10;
60
12.2k
    }
61
17.4k
    return SECSuccess;
62
18.0k
}
63
64
/* Take a number from `str` from offset `*i` and put the value in `*v`.
65
 * This calculates the radix and returns a value between 0 and 2^32-1, using all
66
 * of the digits up to the end of the string (determined by `len`) or a period ('.').
67
 * Fails if there is no value, if there a non-digit characters, or if the value is
68
 * too large.
69
 */
70
static SECStatus
71
tls13_IpValue(const PRUint8 *str, unsigned int len, unsigned int *i, PRUint32 *v)
72
18.0k
{
73
18.0k
    PRUint8 radix;
74
18.0k
    SECStatus rv = tls13_IpRadix(str, len, i, &radix);
75
18.0k
    if (rv != SECSuccess) {
76
651
        return SECFailure;
77
651
    }
78
17.4k
    PRUint64 part = 0;
79
34.7k
    while (*i < len) {
80
34.1k
        PRUint8 d;
81
34.1k
        rv = tls13_IpDigit(str[*i], radix, &d);
82
34.1k
        if (rv != SECSuccess) {
83
16.6k
            if (str[*i] != '.') {
84
11.3k
                return SECFailure;
85
11.3k
            }
86
5.23k
            break;
87
16.6k
        }
88
17.5k
        part = part * radix + d;
89
17.5k
        if (part > PR_UINT32_MAX) {
90
219
            return SECFailure;
91
219
        }
92
17.3k
        (*i)++;
93
17.3k
    }
94
5.80k
    *v = part;
95
5.80k
    return SECSuccess;
96
17.4k
}
97
98
/* Returns true if `end` is true and `v` is within the `limit`. Used to validate the
99
 * last part of an IPv4 address, which can hold larger numbers if there are fewer then
100
 * four parts. */
101
static PRBool
102
tls13_IpLastPart(PRBool end, PRUint32 v, PRUint32 limit)
103
1.42k
{
104
1.42k
    if (!end) {
105
852
        return PR_FALSE;
106
852
    }
107
571
    return v <= limit;
108
1.42k
}
109
110
/* Returns true if `str` contains an IPv4 address. */
111
PRBool
112
tls13_IsIp(const PRUint8 *str, unsigned int len)
113
13.6k
{
114
13.6k
    PRUint32 part;
115
13.6k
    PRUint32 v;
116
13.6k
    unsigned int i = 0;
117
18.4k
    for (part = 0; part < 4; part++) {
118
18.0k
        SECStatus rv = tls13_IpValue(str, len, &i, &v);
119
18.0k
        if (rv != SECSuccess) {
120
12.2k
            return PR_FALSE;
121
12.2k
        }
122
5.80k
        if (v > 0xff || i == len) {
123
1.00k
            return tls13_IpLastPart(i == len, v, PR_UINT32_MAX >> (part * 8));
124
1.00k
        }
125
4.79k
        PORT_Assert(str[i] == '.');
126
4.79k
        i++;
127
4.79k
    }
128
129
415
    return tls13_IpLastPart(i == len, v, 0xff);
130
13.6k
}
131
132
static PRBool
133
tls13_IsLD(PRUint8 c)
134
207k
{
135
207k
    return (c >= 'a' && c <= 'z') ||
136
63.1k
           (c >= 'A' && c <= 'Z') ||
137
53.4k
           (c >= '0' && c <= '9') ||
138
26.1k
           c == '_'; /* not in spec, but in the world; bug 1136616 */
139
207k
}
140
141
/* Is this a valid dotted LDH string (that is, an A-Label domain name)?
142
 * This does not tolerate a trailing '.', where the DNS generally does.
143
 */
144
PRBool
145
tls13_IsLDH(const PRUint8 *str, unsigned int len)
146
13.7k
{
147
13.7k
    unsigned int i = 0;
148
30.4k
    while (i < len && tls13_IsLD(str[i])) {
149
30.4k
        unsigned int labelEnd = PR_MIN(len, i + 63);
150
30.4k
        i++;
151
191k
        while (i < labelEnd && (tls13_IsLD(str[i]) || str[i] == '-')) {
152
160k
            i++;
153
160k
        }
154
30.4k
        if (str[i - 1] == '-') {
155
            /* labels cannot end in a hyphen */
156
7
            return PR_FALSE;
157
7
        }
158
30.3k
        if (i == len) {
159
13.6k
            return PR_TRUE;
160
13.6k
        }
161
16.7k
        if (str[i] != '.') {
162
49
            return PR_FALSE;
163
49
        }
164
16.6k
        i++;
165
16.6k
    }
166
51
    return PR_FALSE;
167
13.7k
}