tls13echv.c (4807B)
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 { 16 PRUint8 v = 0xff; 17 if (c >= '0' && c <= '9') { 18 v = c - '0'; 19 } else if (radix > 10) { 20 if (c >= 'a' && c <= 'f') { 21 v = c - 'a'; 22 } else if (c >= 'A' && c <= 'F') { 23 v = c - 'A'; 24 } 25 } 26 if (v >= radix) { 27 return SECFailure; 28 } 29 *d = v; 30 return SECSuccess; 31 } 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 { 44 if (*i == len || str[*i] == '.') { 45 return SECFailure; 46 } 47 if (str[*i] == '0') { 48 (*i)++; 49 if (*i < len && (str[*i] == 'x' || str[*i] == 'X')) { 50 (*i)++; 51 if (*i == len || str[*i] == '.') { 52 return SECFailure; 53 } 54 *radix = 16; 55 } else { 56 *radix = 8; 57 } 58 } else { 59 *radix = 10; 60 } 61 return SECSuccess; 62 } 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 { 73 PRUint8 radix; 74 SECStatus rv = tls13_IpRadix(str, len, i, &radix); 75 if (rv != SECSuccess) { 76 return SECFailure; 77 } 78 PRUint64 part = 0; 79 while (*i < len) { 80 PRUint8 d; 81 rv = tls13_IpDigit(str[*i], radix, &d); 82 if (rv != SECSuccess) { 83 if (str[*i] != '.') { 84 return SECFailure; 85 } 86 break; 87 } 88 part = part * radix + d; 89 if (part > PR_UINT32_MAX) { 90 return SECFailure; 91 } 92 (*i)++; 93 } 94 *v = part; 95 return SECSuccess; 96 } 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 { 104 if (!end) { 105 return PR_FALSE; 106 } 107 return v <= limit; 108 } 109 110 /* Returns true if `str` contains an IPv4 address. */ 111 PRBool 112 tls13_IsIp(const PRUint8 *str, unsigned int len) 113 { 114 PRUint32 part; 115 PRUint32 v; 116 unsigned int i = 0; 117 for (part = 0; part < 4; part++) { 118 SECStatus rv = tls13_IpValue(str, len, &i, &v); 119 if (rv != SECSuccess) { 120 return PR_FALSE; 121 } 122 if (v > 0xff || i == len) { 123 return tls13_IpLastPart(i == len, v, PR_UINT32_MAX >> (part * 8)); 124 } 125 PORT_Assert(str[i] == '.'); 126 i++; 127 } 128 129 return tls13_IpLastPart(i == len, v, 0xff); 130 } 131 132 static PRBool 133 tls13_IsLD(PRUint8 c) 134 { 135 return (c >= 'a' && c <= 'z') || 136 (c >= 'A' && c <= 'Z') || 137 (c >= '0' && c <= '9') || 138 c == '_'; /* not in spec, but in the world; bug 1136616 */ 139 } 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 { 147 unsigned int i = 0; 148 while (i < len && tls13_IsLD(str[i])) { 149 unsigned int labelEnd = PR_MIN(len, i + 63); 150 i++; 151 while (i < labelEnd && (tls13_IsLD(str[i]) || str[i] == '-')) { 152 i++; 153 } 154 if (str[i - 1] == '-') { 155 /* labels cannot end in a hyphen */ 156 return PR_FALSE; 157 } 158 if (i == len) { 159 return PR_TRUE; 160 } 161 if (str[i] != '.') { 162 return PR_FALSE; 163 } 164 i++; 165 } 166 return PR_FALSE; 167 }