summaryrefslogtreecommitdiff
path: root/include/internal/quic_wire_pkt.h
blob: 60528811ad0aaa4b6554b5e2fb04b87914774566 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
/*
 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#ifndef OSSL_QUIC_WIRE_PKT_H
# define OSSL_QUIC_WIRE_PKT_H

# include <openssl/ssl.h>
# include "internal/packet.h"
# include "internal/quic_types.h"

# define QUIC_VERSION_NONE   ((uint32_t)0)   /* Used for version negotiation */
# define QUIC_VERSION_1      ((uint32_t)1)   /* QUIC v1 */

/* QUIC logical packet type. These do not match wire values. */
# define QUIC_PKT_TYPE_INITIAL        1
# define QUIC_PKT_TYPE_0RTT           2
# define QUIC_PKT_TYPE_HANDSHAKE      3
# define QUIC_PKT_TYPE_RETRY          4
# define QUIC_PKT_TYPE_1RTT           5
# define QUIC_PKT_TYPE_VERSION_NEG    6

/*
 * Determine encryption level from packet type. Returns QUIC_ENC_LEVEL_NUM if
 * the packet is not of a type which is encrypted.
 */
static ossl_inline ossl_unused uint32_t
ossl_quic_pkt_type_to_enc_level(uint32_t pkt_type)
{
    switch (pkt_type) {
        case QUIC_PKT_TYPE_INITIAL:
            return QUIC_ENC_LEVEL_INITIAL;
        case QUIC_PKT_TYPE_HANDSHAKE:
            return QUIC_ENC_LEVEL_HANDSHAKE;
        case QUIC_PKT_TYPE_0RTT:
            return QUIC_ENC_LEVEL_0RTT;
        case QUIC_PKT_TYPE_1RTT:
            return QUIC_ENC_LEVEL_1RTT;
        default:
            return QUIC_ENC_LEVEL_NUM;
    }
}

/* Determine if a packet type contains an encrypted payload. */
static ossl_inline ossl_unused int
ossl_quic_pkt_type_is_encrypted(uint32_t pkt_type)
{
    switch (pkt_type) {
        case QUIC_PKT_TYPE_RETRY:
        case QUIC_PKT_TYPE_VERSION_NEG:
            return 0;
        default:
            return 1;
    }
}

/* Determine if a packet type contains a PN field. */
static ossl_inline ossl_unused int
ossl_quic_pkt_type_has_pn(uint32_t pkt_type)
{
    /*
     * Currently a packet has a PN iff it is encrypted. This could change
     * someday.
     */
    return ossl_quic_pkt_type_is_encrypted(pkt_type);
}

/*
 * Determine if a packet type can appear with other packets in a datagram. Some
 * packet types must be the sole packet in a datagram.
 */
static ossl_inline ossl_unused int
ossl_quic_pkt_type_can_share_dgram(uint32_t pkt_type)
{
    /*
     * Currently only the encrypted packet types can share a datagram. This
     * could change someday.
     */
    return ossl_quic_pkt_type_is_encrypted(pkt_type);
}

/*
 * Determine if the packet type must come at the end of the datagram (due to the
 * lack of a length field).
 */
static ossl_inline ossl_unused int
ossl_quic_pkt_type_must_be_last(uint32_t pkt_type)
{
    /*
     * Any packet type which cannot share a datagram obviously must come last.
     * 1-RTT also must come last as it lacks a length field.
     */
    return !ossl_quic_pkt_type_can_share_dgram(pkt_type)
        || pkt_type == QUIC_PKT_TYPE_1RTT;
}

/*
 * Smallest possible QUIC packet size as per RFC (aside from version negotiation
 * packets).
 */
#define QUIC_MIN_VALID_PKT_LEN_CRYPTO      21
#define QUIC_MIN_VALID_PKT_LEN_VERSION_NEG  7
#define QUIC_MIN_VALID_PKT_LEN              QUIC_MIN_VALID_PKT_LEN_VERSION_NEG

typedef struct quic_pkt_hdr_ptrs_st QUIC_PKT_HDR_PTRS;

/*
 * QUIC Packet Header Protection
 * =============================
 *
 * Functions to apply and remove QUIC packet header protection. A header
 * protector is initialised using ossl_quic_hdr_protector_init and must be
 * destroyed using ossl_quic_hdr_protector_cleanup when no longer needed.
 */
typedef struct quic_hdr_protector_st {
    OSSL_LIB_CTX       *libctx;
    const char         *propq;
    EVP_CIPHER_CTX     *cipher_ctx;
    EVP_CIPHER         *cipher;
    uint32_t            cipher_id;
} QUIC_HDR_PROTECTOR;

# define QUIC_HDR_PROT_CIPHER_AES_128    1
# define QUIC_HDR_PROT_CIPHER_AES_256    2
# define QUIC_HDR_PROT_CIPHER_CHACHA     3

/*
 * Initialises a header protector.
 *
 *   cipher_id:
 *      The header protection cipher method to use. One of
 *      QUIC_HDR_PROT_CIPHER_*. Must be chosen based on negotiated TLS cipher
 *      suite.
 *
 *   quic_hp_key:
 *      This must be the "quic hp" key derived from a traffic secret.
 *
 *      The length of the quic_hp_key must correspond to that expected for the
 *      given cipher ID.
 *
 * The header protector performs amortisable initialisation in this function,
 * therefore a header protector should be used for as long as possible.
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_hdr_protector_init(QUIC_HDR_PROTECTOR *hpr,
                                 OSSL_LIB_CTX *libctx,
                                 const char *propq,
                                 uint32_t cipher_id,
                                 const unsigned char *quic_hp_key,
                                 size_t quic_hp_key_len);

/*
 * Destroys a header protector. This is also safe to call on a zero-initialized
 * OSSL_QUIC_HDR_PROTECTOR structure which has not been initialized, or which
 * has already been destroyed.
 */
void ossl_quic_hdr_protector_cleanup(QUIC_HDR_PROTECTOR *hpr);

/*
 * Removes header protection from a packet. The packet payload must currently be
 * encrypted (i.e., you must remove header protection before decrypting packets
 * received). The function examines the header buffer to determine which bytes
 * of the header need to be decrypted.
 *
 * If this function fails, no data is modified.
 *
 * This is implemented as a call to ossl_quic_hdr_protector_decrypt_fields().
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_hdr_protector_decrypt(QUIC_HDR_PROTECTOR *hpr,
                                    QUIC_PKT_HDR_PTRS *ptrs);

/*
 * Applies header protection to a packet. The packet payload must already have
 * been encrypted (i.e., you must apply header protection after encrypting
 * a packet). The function examines the header buffer to determine which bytes
 * of the header need to be encrypted.
 *
 * This is implemented as a call to ossl_quic_hdr_protector_encrypt_fields().
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_hdr_protector_encrypt(QUIC_HDR_PROTECTOR *hpr,
                                    QUIC_PKT_HDR_PTRS *ptrs);

/*
 * Removes header protection from a packet. The packet payload must currently
 * be encrypted. This is a low-level function which assumes you have already
 * determined which parts of the packet header need to be decrypted.
 *
 * sample:
 *   The range of bytes in the packet to be used to generate the header
 *   protection mask. It is permissible to set sample_len to the size of the
 *   remainder of the packet; this function will only use as many bytes as
 *   needed. If not enough sample bytes are provided, this function fails.
 *
 * first_byte:
 *   The first byte of the QUIC packet header to be decrypted.
 *
 * pn:
 *   Pointer to the start of the PN field. The caller is responsible
 *   for ensuring at least four bytes follow this pointer.
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_hdr_protector_decrypt_fields(QUIC_HDR_PROTECTOR *hpr,
                                           const unsigned char *sample,
                                           size_t sample_len,
                                           unsigned char *first_byte,
                                           unsigned char *pn_bytes);

/*
 * Works analogously to ossl_hdr_protector_decrypt_fields, but applies header
 * protection instead of removing it.
 */
int ossl_quic_hdr_protector_encrypt_fields(QUIC_HDR_PROTECTOR *hpr,
                                           const unsigned char *sample,
                                           size_t sample_len,
                                           unsigned char *first_byte,
                                           unsigned char *pn_bytes);

/*
 * QUIC Packet Header
 * ==================
 *
 * This structure provides a logical representation of a QUIC packet header.
 *
 * QUIC packet formats fall into the following categories:
 *
 *   Long Packets, which is subdivided into five possible packet types:
 *     Version Negotiation (a special case);
 *     Initial;
 *     0-RTT;
 *     Handshake; and
 *     Retry
 *
 *   Short Packets, which comprises only a single packet type (1-RTT).
 *
 * The packet formats vary and common fields are found in some packets but
 * not others. The below table indicates which fields are present in which
 * kinds of packet. * indicates header protection is applied.
 *
 *   SLLLLL         Legend: 1=1-RTT, i=Initial, 0=0-RTT, h=Handshake
 *   1i0hrv                 r=Retry, v=Version Negotiation
 *   ------
 *   1i0hrv         Header Form (0=Short, 1=Long)
 *   1i0hr          Fixed Bit (always 1)
 *   1              Spin Bit
 *   1       *      Reserved Bits
 *   1       *      Key Phase
 *   1i0h    *      Packet Number Length
 *    i0hr?         Long Packet Type
 *    i0h           Type-Specific Bits
 *    i0hr          Version (note: always 0 for Version Negotiation packets)
 *   1i0hrv         Destination Connection ID
 *    i0hrv         Source Connection ID
 *   1i0h    *      Packet Number
 *    i             Token
 *    i0h           Length
 *       r          Retry Token
 *       r          Retry Integrity Tag
 *
 * For each field below, the conditions under which the field is valid are
 * specified. If a field is not currently valid, it is initialized to a zero or
 * NULL value.
 */
typedef struct quic_pkt_hdr_st {
    /* [ALL] A QUIC_PKT_TYPE_* value. Always valid. */
    unsigned int    type        :8;

    /* [S] Value of the spin bit. Valid if (type == 1RTT). */
    unsigned int    spin_bit    :1;

    /*
     * [S] Value of the Key Phase bit in the short packet.
     * Valid if (type == 1RTT && !partial).
     */
    unsigned int    key_phase   :1;

    /*
     * [1i0h] Length of packet number in bytes. This is the decoded value.
     * Valid if ((type == 1RTT || (version && type != RETRY)) && !partial).
     */
    unsigned int    pn_len      :4;

    /*
     * [ALL] Set to 1 if this is a partial decode because the packet header
     * has not yet been deprotected. pn_len, pn and key_phase are not valid if
     * this is set.
     */
    unsigned int    partial     :1;

    /*
     * [ALL] Whether the fixed bit was set. Note that only Version Negotiation
     * packets are allowed to have this unset, so this will always be 1 for all
     * other packet types (decode will fail if it is not set). Ignored when
     * encoding unless encoding a Version Negotiation packet.
     */
    unsigned int    fixed       :1;

    /* [L] Version field. Valid if (type != 1RTT). */
    uint32_t        version;

    /* [ALL] The destination connection ID. Always valid. */
    QUIC_CONN_ID    dst_conn_id;

    /*
     * [L] The source connection ID.
     * Valid if (type != 1RTT).
     */
    QUIC_CONN_ID    src_conn_id;

    /*
     * [1i0h] Relatively-encoded packet number in raw, encoded form. The correct
     * decoding of this value is context-dependent. The number of bytes valid in
     * this buffer is determined by pn_len above. If the decode was partial,
     * this field is not valid.
     *
     * Valid if ((type == 1RTT || (version && type != RETRY)) && !partial).
     */
    unsigned char           pn[4];

    /*
     * [i] Token field in Initial packet. Points to memory inside the decoded
     * PACKET, and therefore is valid for as long as the PACKET's buffer is
     * valid. token_len is the length of the token in bytes.
     *
     * Valid if (type == INITIAL).
     */
    const unsigned char    *token;
    size_t                  token_len;

    /*
     * [ALL] Payload length in bytes.
     *
     * Though 1-RTT, Retry and Version Negotiation packets do not contain an
     * explicit length field, this field is always valid and is used by the
     * packet header encoding and decoding routines to describe the payload
     * length, regardless of whether the packet type encoded or decoded uses an
     * explicit length indication.
     */
    size_t                  len;

    /*
     * Pointer to start of payload data in the packet. Points to memory inside
     * the decoded PACKET, and therefore is valid for as long as the PACKET'S
     * buffer is valid. The length of the buffer in bytes is in len above.
     *
     * For Version Negotiation packets, points to the array of supported
     * versions.
     *
     * For Retry packets, points to the Retry packet payload, which comprises
     * the Retry Token followed by a 16-byte Retry Integrity Tag.
     *
     * Regardless of whether a packet is a Version Negotiation packet (where the
     * payload contains a list of supported versions), a Retry packet (where the
     * payload contains a Retry Token and Retry Integrity Tag), or any other
     * packet type (where the payload contains frames), the payload is not
     * validated and the user must parse the payload bearing this in mind.
     *
     * If the decode was partial (partial is set), this points to the start of
     * the packet number field, rather than the protected payload, as the length
     * of the packet number field is unknown. The len field reflects this in
     * this case (i.e., the len field is the number of payload bytes plus the
     * number of bytes comprising the PN).
     */
    const unsigned char    *data;
} QUIC_PKT_HDR;

/*
 * Extra information which can be output by the packet header decode functions
 * for the assistance of the header protector. This avoids the header protector
 * needing to partially re-decode the packet header.
 */
struct quic_pkt_hdr_ptrs_st {
    unsigned char    *raw_start;        /* start of packet */
    unsigned char    *raw_sample;       /* start of sampling range */
    size_t            raw_sample_len;   /* maximum length of sampling range */

    /*
     * Start of PN field. Guaranteed to be NULL unless at least four bytes are
     * available via this pointer.
     */
    unsigned char    *raw_pn;
};

/*
 * If partial is 1, reads the unprotected parts of a protected packet header
 * from a PACKET, performing a partial decode.
 *
 * If partial is 0, the input is assumed to have already had header protection
 * removed, and all header fields are decoded.
 *
 * On success, the logical decode of the packet header is written to *hdr.
 * hdr->partial is set or cleared according to whether a partial decode was
 * performed. *ptrs is filled with pointers to various parts of the packet
 * buffer.
 *
 * In order to decode short packets, the connection ID length being used must be
 * known contextually, and should be passed as short_conn_id_len. If
 * short_conn_id_len is set to an invalid value (a value greater than
 * QUIC_MAX_CONN_ID_LEN), this function fails when trying to decode a short
 * packet, but succeeds for long packets.
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_wire_decode_pkt_hdr(PACKET *pkt,
                                  size_t short_conn_id_len,
                                  int partial,
                                  QUIC_PKT_HDR *hdr,
                                  QUIC_PKT_HDR_PTRS *ptrs);

/*
 * Encodes a packet header. The packet is written to pkt.
 *
 * The length of the (encrypted) packet payload should be written to hdr->len
 * and will be placed in the serialized packet header. The payload data itself
 * is not copied; the caller should write hdr->len bytes of encrypted payload to
 * the WPACKET immediately after the call to this function. However,
 * WPACKET_reserve_bytes is called for the payload size.
 *
 * This function does not apply header protection. You must apply header
 * protection yourself after calling this function. *ptrs is filled with
 * pointers which can be passed to a header protector, but this must be
 * performed after the encrypted payload is written.
 *
 * The pointers in *ptrs are direct pointers into the WPACKET buffer. If more
 * data is written to the WPACKET buffer, WPACKET buffer reallocations may
 * occur, causing these pointers to become invalid. Therefore, you must not call
 * any write WPACKET function between this call and the call to
 * ossl_quic_hdr_protector_encrypt. This function calls WPACKET_reserve_bytes
 * for the payload length, so you may assume hdr->len bytes are already free to
 * write at the WPACKET cursor location once this function returns successfully.
 * It is recommended that you call this function, write the encrypted payload,
 * call ossl_quic_hdr_protector_encrypt, and then call
 * WPACKET_allocate_bytes(hdr->len).
 *
 * Version Negotiation and Retry packets do not use header protection; for these
 * header types, the fields in *ptrs are all written as zero. Version
 * Negotiation, Retry and 1-RTT packets do not contain a Length field, but
 * hdr->len bytes of data are still reserved in the WPACKET.
 *
 * If serializing a short packet and short_conn_id_len does not match the DCID
 * specified in hdr, the function fails.
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_wire_encode_pkt_hdr(WPACKET *pkt,
                                  size_t short_conn_id_len,
                                  const QUIC_PKT_HDR *hdr,
                                  QUIC_PKT_HDR_PTRS *ptrs);

/*
 * Retrieves only the DCID from a packet header. This is intended for demuxer
 * use. It avoids the need to parse the rest of the packet header twice.
 *
 * Information on packet length is not decoded, as this only needs to be used on
 * the first packet in a datagram, therefore this takes a buffer and not a
 * PACKET.
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_wire_get_pkt_hdr_dst_conn_id(const unsigned char *buf,
                                           size_t buf_len,
                                           size_t short_conn_id_len,
                                           QUIC_CONN_ID *dst_conn_id);

/*
 * Precisely predicts the encoded length of a packet header structure.
 *
 * May return 0 if the packet header is not valid, but the fact that this
 * function returns non-zero does not guarantee that
 * ossl_quic_wire_encode_pkt_hdr() will succeed.
 */
int ossl_quic_wire_get_encoded_pkt_hdr_len(size_t short_conn_id_len,
                                           const QUIC_PKT_HDR *hdr);

/*
 * Packet Number Encoding
 * ======================
 */

/*
 * Decode an encoded packet header QUIC PN.
 *
 * enc_pn is the raw encoded PN to decode. enc_pn_len is its length in bytes as
 * indicated by packet headers. largest_pn is the largest PN successfully
 * processed in the relevant PN space.
 *
 * The resulting PN is written to *res_pn.
 *
 * Returns 1 on success or 0 on failure.
 */
int ossl_quic_wire_decode_pkt_hdr_pn(const unsigned char *enc_pn,
                                     size_t enc_pn_len,
                                     QUIC_PN largest_pn,
                                     QUIC_PN *res_pn);

/*
 * Determine how many bytes should be used to encode a PN. Returns the number of
 * bytes (which will be in range [1, 4]).
 */
int ossl_quic_wire_determine_pn_len(QUIC_PN pn, QUIC_PN largest_acked);

/*
 * Encode a PN for a packet header using the specified number of bytes, which
 * should have been determined by calling ossl_quic_wire_determine_pn_len. The
 * PN encoding process is done in two parts to allow the caller to override PN
 * encoding length if it wishes.
 *
 * Returns 1 on success and 0 on failure.
 */
int ossl_quic_wire_encode_pkt_hdr_pn(QUIC_PN pn,
                                     unsigned char *enc_pn,
                                     size_t enc_pn_len);
#endif