| /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* Validation functions for ECH public names. */ |
| |
| #include "seccomon.h" |
| |
| /* Convert a single character `c` into a number `*d` with the given radix. |
| * Fails if the character isn't valid for the radix. |
| */ |
| static SECStatus |
| tls13_IpDigit(PRUint8 c, PRUint8 radix, PRUint8 *d) |
| { |
| PRUint8 v = 0xff; |
| if (c >= '0' && c <= '9') { |
| v = c - '0'; |
| } else if (radix > 10) { |
| if (c >= 'a' && c <= 'f') { |
| v = c - 'a'; |
| } else if (c >= 'A' && c <= 'F') { |
| v = c - 'A'; |
| } |
| } |
| if (v >= radix) { |
| return SECFailure; |
| } |
| *d = v; |
| return SECSuccess; |
| } |
| |
| /* This function takes the first couple of characters from `str`, starting at offset |
| * `*i` and calculates a radix. If it starts with "0x" or "0X", then `*i` is moved up |
| * by two and `*radix` is set to 16 (hexadecimal). If it starts with "0", then `*i` is |
| * moved up by one and `*radix` is set to 8 (octal). Otherwise, `*i` is left alone and |
| * `*radix` is set to 10 (decimal). |
| * Fails if there are no characters remaining or the next character is '.', either at |
| * the start or after "0x". |
| */ |
| static SECStatus |
| tls13_IpRadix(const PRUint8 *str, unsigned int len, unsigned int *i, PRUint8 *radix) |
| { |
| if (*i == len || str[*i] == '.') { |
| return SECFailure; |
| } |
| if (str[*i] == '0') { |
| (*i)++; |
| if (*i < len && (str[*i] == 'x' || str[*i] == 'X')) { |
| (*i)++; |
| if (*i == len || str[*i] == '.') { |
| return SECFailure; |
| } |
| *radix = 16; |
| } else { |
| *radix = 8; |
| } |
| } else { |
| *radix = 10; |
| } |
| return SECSuccess; |
| } |
| |
| /* Take a number from `str` from offset `*i` and put the value in `*v`. |
| * This calculates the radix and returns a value between 0 and 2^32-1, using all |
| * of the digits up to the end of the string (determined by `len`) or a period ('.'). |
| * Fails if there is no value, if there a non-digit characters, or if the value is |
| * too large. |
| */ |
| static SECStatus |
| tls13_IpValue(const PRUint8 *str, unsigned int len, unsigned int *i, PRUint32 *v) |
| { |
| PRUint8 radix; |
| SECStatus rv = tls13_IpRadix(str, len, i, &radix); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| PRUint64 part = 0; |
| while (*i < len) { |
| PRUint8 d; |
| rv = tls13_IpDigit(str[*i], radix, &d); |
| if (rv != SECSuccess) { |
| if (str[*i] != '.') { |
| return SECFailure; |
| } |
| break; |
| } |
| part = part * radix + d; |
| if (part > PR_UINT32_MAX) { |
| return SECFailure; |
| } |
| (*i)++; |
| } |
| *v = part; |
| return SECSuccess; |
| } |
| |
| /* Returns true if `end` is true and `v` is within the `limit`. Used to validate the |
| * last part of an IPv4 address, which can hold larger numbers if there are fewer then |
| * four parts. */ |
| static PRBool |
| tls13_IpLastPart(PRBool end, PRUint32 v, PRUint32 limit) |
| { |
| if (!end) { |
| return PR_FALSE; |
| } |
| return v <= limit; |
| } |
| |
| /* Returns true if `str` contains an IPv4 address. */ |
| PRBool |
| tls13_IsIp(const PRUint8 *str, unsigned int len) |
| { |
| PRUint32 part; |
| PRUint32 v; |
| unsigned int i = 0; |
| for (part = 0; part < 4; part++) { |
| SECStatus rv = tls13_IpValue(str, len, &i, &v); |
| if (rv != SECSuccess) { |
| return PR_FALSE; |
| } |
| if (v > 0xff || i == len) { |
| return tls13_IpLastPart(i == len, v, PR_UINT32_MAX >> (part * 8)); |
| } |
| PORT_Assert(str[i] == '.'); |
| i++; |
| } |
| |
| return tls13_IpLastPart(i == len, v, 0xff); |
| } |
| |
| static PRBool |
| tls13_IsLD(PRUint8 c) |
| { |
| return (c >= 'a' && c <= 'z') || |
| (c >= 'A' && c <= 'Z') || |
| (c >= '0' && c <= '9') || |
| c == '_'; /* not in spec, but in the world; bug 1136616 */ |
| } |
| |
| /* Is this a valid dotted LDH string (that is, an A-Label domain name)? |
| * This does not tolerate a trailing '.', where the DNS generally does. |
| */ |
| PRBool |
| tls13_IsLDH(const PRUint8 *str, unsigned int len) |
| { |
| unsigned int i = 0; |
| while (i < len && tls13_IsLD(str[i])) { |
| unsigned int labelEnd = PR_MIN(len, i + 63); |
| i++; |
| while (i < labelEnd && (tls13_IsLD(str[i]) || str[i] == '-')) { |
| i++; |
| } |
| if (str[i - 1] == '-') { |
| /* labels cannot end in a hyphen */ |
| return PR_FALSE; |
| } |
| if (i == len) { |
| return PR_TRUE; |
| } |
| if (str[i] != '.') { |
| return PR_FALSE; |
| } |
| i++; |
| } |
| return PR_FALSE; |
| } |