tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }