|
| #define | TLS_VERSION_TLS_1_1 0x0302 |
| | TLS version 1.1.
|
| #define | TLS_VERSION_TLS_1_2 0x0303 |
| | TLS version 1.2.
|
| #define | TLS_VERSION_TLS_1_3 0x0304 |
| | TLS version 1.3.
|
| #define | TLS_VERSION_BASE TLS_VERSION_TLS_1_1 |
| | Lowest configurable supported version.
|
| #define | TLS_DESCR_COUNT(desc) |
| | Number of pointer/length values in a descriptor structure.
|
| #define | TLS_DESCR_MAPPING(desc) |
| | Binary encoding of a descriptor structure mapping.
|
| #define | TLS_MAPSZ(desc) |
| | Describe size of a descriptor mapping.
|
| #define | TLS_INDEX(desc, field) |
| | Index of descriptor field.
|
| #define | TLS_EXTNS(desc, field) |
| | Number of extensions of interest within an extension descriptor.
|
| #define | TLS_FIXED(desc, version, field) |
| | Describe a fixed-length field.
|
| #define | TLS_VARIABLE(desc, version, field, bits, extns) |
| | Describe a variable-length field.
|
| #define | TLS_EXTRA(desc, version, field) |
| | Describe a variable-length field that captures all remaining data.
|
| #define | TLS_VAR08(desc, version, field) |
| | Describe a variable-length field with an 8-bit length.
|
| #define | TLS_VAR16(desc, version, field) |
| | Describe a variable-length field with a 16-bit length.
|
| #define | TLS_VAR24(desc, version, field) |
| | Describe a variable-length field with a 24-bit length.
|
| #define | TLS_EXT16(desc, version, field) |
| | Describe a variable-length field containing extensions.
|
| #define | TLS_EXTND(desc, extension, field) |
| | Describe a variable-length field containing an extension of interest.
|
| #define | TLS_MAP_MIN(byte) |
| | Interpret minimum version from an llllllvv byte.
|
| #define | TLS_MAP_FIXED(byte) |
| | Interpret fixed length from an llllllvv byte.
|
| #define | TLS_MAP_LEN_LEN(byte) |
| | Interpret number of length bytes from an xxxxxxnn byte.
|
| #define | TLS_MAP_LEN_LEN_MAX 3 |
| | Maximum number of length bytes.
|
| #define | TLS_MAP_EXTENSIONS(byte) |
| | Interpret number of extensions from an xxxxxxnn byte.
|
| #define | tls_parse(type, version, cursor, desc) |
| | Parse TLS data structure.
|
| #define | tls_parse_opt(type, version, cursor, desc) |
| | Parse optional TLS data structure.
|
| #define | tls_build(type, version, desc, cursor) |
| | Build TLS data structure.
|
| #define | tls_size(type, version, desc, cursor) |
| | Calculate length of TLS data structure.
|
TLS data formats.
TLS uses an ad hoc mixture of fixed-length and variable-length fields. Variable-length fields are preceded by a length field that may be one, two, or three bytes depending on the maximum length defined by the structure.
To avoid open-coding a very large number of bounds checks, we define an abstraction for decomposing the component parts of a TLS data structure into a sequence of field data pointers and lengths (for variable-length fields), along with an efficient binary encoding that can describe the mapping between the decomposition and the raw data structure.
A fixed-length field is described using a simple pointer to the appropriate fixed-length data type. A variable-length field is described using a cursor structure that comprises a void pointer followed by a length. TLS extensions are always variable-length and so are represented in the same way as variable-length fields.
For example, the start of a ServerHello could be described using:
struct tls_server_hello {
uint16_t *version;
struct tls_random *random;
struct tls_cursor session_id;
uint16_t *suite;
uint8_t *compression;
struct {
struct tls_cursor all;
struct tls_cursor renegotiation;
struct tls_cursor extended_master_secret;
} ext;
};
Note that the fixed-length fields are all typed pointers, whereas the variable-length session ID uses a TLS cursor (i.e. a void pointer and a length). Since pointer values and length values are necessarily the same size, we can meaningfully treat this descriptor structure as an array ptrlen[] of pointer/length values.
We create a binary encoding to allow us to define the mapping between this descriptor structure and the raw TLS data structure using a static byte array constructed at build time. The same binary encoding may be used both for parsing and for building a TLS data structure.
Starting at index N=1 within the byte array map[]:
- Interpret map[0] as the length of the map[] array. A zero array length is invalid and is treated as a fatal error.
- Upon reaching the end of the mapping (i.e. N==map[0]), stop processing.
- Interpret map[N] as the eight bits llllllvv, where:
- V = vv is the minimal TLS version that includes this field (encoded as a delta from the lowest version currently supported by the codebase).
- L = llllll is one plus the length of the corresponding fixed-length data structure. An empty fixed-length data structure is pointless, and so we choose a zero fixed length (i.e. L==1) to represent a variable-length data structure. A negative fixed length (i.e. L==0) is invalid and is treated as a fatal error.
- For a fixed-length data structure (with L>1):
- When parsing: store the pointer to the start of the fixed-length data as ptrlen[N-1].
- When building: append a copy of the data from ptrlen[N-1] with length L-1.
- For a variable-length data structure (with L==1), interpret map[N+1] as the eight bits xxxxxxnn, where:
- N = nn is the length of the length header that precedes the variable-length data structure. A zero-byte length header is invalid, and so we choose N==0 to represent a field that covers all remaining data.
- X = xxxxxx is one plus the number of TLS extension types that immediately follow map[N+1] and that represent extensions of interest that may be present within this field. A negative number of extensions (i.e. X==0) is impossible and so we choose this to represent a field that is not used to contain extensions.
- When parsing: store the pointer to the start of the variable-length data as ptrlen[N-1], and store the decoded length of the variable-length data as ptrlen[N].
- When building: append a copy of the data from ptrlen[N-1] (with the correct N-byte length header). Take the length of the variable-length data to be X ? 0 : ptrlen[N] (i.e. always build fields that contain extensions as being empty).
- For each 1<=k<=(X-1) in a variable-length data structure that is used to contain extensions (i.e. that has X>0), interpret T = map[N+2k]:map[N+2k+1] as the inverse of a TLS extension type (in network-endian order):
- If T==0, then terminate processing with a fatal error.
- When parsing: for each matching extension type ~T that is found, store the pointer to the start of the extension data as ptrlen[N+2k-1], and store the decoded length of the variable-length extension data as ptrlen[N+2k].
- When building: if ptrlen[N+2k-1] is set, then append a copy of the data from ptrlen[N+2k-1] with length ptrlen[N+2k] as an extension with type ~T (with the correct extension header, and updating the length of the containing field accordingly).
- Increment N to the next uninterpreted byte, and loop.
Any parsing failure (e.g. insufficient remaining space to contain the fixed-length or variable-length structure) will be treated as a fatal error. Missing extensions of interest will not be treated as errors, and the corresponding ptrlen entries will be zeroed to indicate that the extension was not found. Duplicate extensions of interest will be treated as a fatal error. (Any extensions that are not of interest will be ignored, even if duplicated.)
The encoding allows for fixed-length fields of up to 62 bytes. Longer fixed-length fields must be expressed as a sequence of smaller fixed-length fields.
All fields must be represented using data structures that are packed and that allow for arbitrary byte alignment.
A variable-length field that covers all remaining data (i.e. with N==0) may be used for incremental parsing, as required for the variable number of entries in structures such as a TLS extension list. If no such variable-length field exists, then any leftover data will be treated as a fatal parsing error.
Up to four consecutive versions of TLS may be supported by the encoding. Experience shows that at most three versions of TLS will be supported by the codebase at any one time, and so this is likely to be sufficient in practice.
The encoding is designed to allow for efficient population of the byte array using preprocessor macros (with compile-time checks to ensure that the byte array matches the descriptor structure). A missing populator macro will leave an erroneous zero byte within the byte array, and the encoding has been designed so that stray zero bytes will fail safe: map[0]==0 is treated as a fatal error, L==0 or T==0 are treated as fatal errors, and N==0 would capture all remaining data (and so cause parsing to fail on the subsequent byte).
Definition in file tlsfmt.h.
Build TLS data structure.
- Parameters
-
| map | Data structure descriptor mapping |
| version | Protocol version |
| desc | Data structure descriptor |
| cursor | Cursor to contain TLS data structure |
- Return values
-
Build a TLS data structure at the cursor. The cursor's original length will be ignored and will be set to the overall length of the built data structure. The caller must ensure that sufficient space already exists (e.g. by calling tls_size() first to determine the required length).
The overall length will be calculated based on the variable lengths within the descriptor. The output content will be be copied from any non-NULL pointers within the descriptor (with any missing content initialised to zero).
Pointers within the descriptor will be updated to point to the appropriate location within the built data structure.
The caller must therefore fill in any variable lengths within the descriptor beforehand, but may freely choose to either fill in pointers within the descriptor beforehand or to write through the updated pointers afterwards.
A cursor with a NULL data pointer may be used to calculate the required length without copying in any data or updating any pointers within the descriptor.
Definition at line 468 of file tlsfmt.c.
469 {
478 unsigned int minimum;
479 unsigned int len_len;
481 int exts;
483
484
487
488
491 DBGC (
map,
"TLSFMT %s mapping has no count\n",
494 }
495
496
498
499
502
503
508 DBGC (
map,
"TLSFMT %s #%d mapping missing\n",
511 }
513
514
517 DBGC2 (
map,
"TLSFMT %s #%d not in version "
521 continue;
522 }
523 DBGC2 (
map,
"TLSFMT %s #%d len %d\n",
529 } else {
531 }
535 }
536 continue;
537 }
538
539
541 DBGC (
map,
"TLSFMT %s #%d mapping overrun\n",
544 }
548 if ( exts >= 0 )
549 next += ( exts * 2 );
551 DBGC (
map,
"TLSFMT %s #%d mapping extensions "
554 }
556 DBGC2 (
map,
"TLSFMT %s #%d not in version %d.%d\n",
559 continue;
560 }
561
562
563 max = ( len_len ? ( ( 1 << ( 8 * len_len ) ) - 1 ) :
565 if ( exts >= 0 ) {
568 exts ) ) != 0 ) {
570 }
571 }
573 DBGC (
map,
"TLSFMT %s #%d len %zd too long\n",
576 }
577
578
579 if ( ( exts >= 0 ) && ( ! field->
len ) &&
581 DBGC2 (
map,
"TLSFMT %s #%d optional in version "
584 continue;
585 }
586
587
588 DBGC2 (
map,
"TLSFMT %s #%d len %zd\n",
590 len += ( len_len + field->
len );
592 while ( len_len-- ) {
594 ( field->
len >> ( 8 * len_len ) );
595 }
596 if ( exts < 0 ) {
600 } else {
602 }
603 }
607 }
608 }
609
610
612 DBGC2 (
map,
"TLSFMT %s built len %zd\n",
616
617 return 0;
618}
#define ERANGE
Result too large.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
static int tls_build_extensions(const uint8_t *map, unsigned int index, struct tls_cursor *fields, unsigned int count)
Build TLS extensions within a data structure.
#define TLS_MAP_LEN_LEN_MAX
Maximum number of length bytes.
References assert, container_of, count, data, tls_cursor::data, DBGC, DBGC2, DBGC2_HDA, desc, EINVAL, ERANGE, fixed, index, len, tls_cursor::len, map, max, memcpy(), memset(), next, NULL, rc, tls_build_extensions(), TLS_MAP_EXTENSIONS, TLS_MAP_FIXED, TLS_MAP_LEN_LEN, TLS_MAP_LEN_LEN_MAX, TLS_MAP_MIN, tls_map_name(), TLS_VERSION_TLS_1_3, and version.
Referenced by tls_send_client_key_exchange(), and tls_size_map().
Calculate length of TLS data structure.
- Parameters
-
| type | Descriptor structure name |
| version | Protocol version |
| desc | Data structure descriptor to fill in |
| cursor | Cursor to contain TLS data structure |
- Return values
-
The cursor size will be set to the overall length required to contain the TLS data structure, and the cursor data pointer will be set to NULL.
If this function returns successfully, then a subsequent call to tls_build() with the exact same inputs is guaranteed to succeed and need not be checked for an error return status. The data structure descriptor must not be modified in any way that would affect its length before calling tls_build(): the easiest way to ensure this is to not modify the data structure descriptor at all.
If this function fails, then the cursor length will be set to a value that is too large to be represented by a 24-bit length field. If the cursor itself lies within a containing data structure descriptor (e.g. if tls_size() is being used to calculate the size of an extension within a ClientHello descriptor), then this guarantees that a subsequent call to tls_size() on the containing descriptor will also fail. The caller therefore need only check the return status code from the call to tls_size() for the outermost descriptor.
Note that this optimisation applies only when the result cursor lies within a containing data structure descriptor and so is guaranteed to be consumed by a subsequent call to calculate the length of that containing TLS data structure. If a caller is using the result from tls_size() in any other way (e.g. to add up the lengths of a sequence of CertificateEntry structures), then the caller must check the return status code in the normal way.
Definition at line 658 of file tlsfmt.c.
659 {
661
662
665
668 }
669
670 return 0;
671}
References tls_cursor::data, desc, tls_cursor::len, map, NULL, rc, tls_build_map(), and version.
Referenced by tls_send_client_key_exchange().