Skip to content
Snippets Groups Projects
Commit 00881d1e authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa
Browse files

Update programmers' guide

parent d713e3f1
No related merge requests found
...@@ -21,72 +21,77 @@ Please note that I only use my hacked OpenSSL. Don't ask me how to ...@@ -21,72 +21,77 @@ Please note that I only use my hacked OpenSSL. Don't ask me how to
use other TLS libraries: use other TLS libraries:
* `my OpenSSL fork * `my OpenSSL fork
<https://github.com/tatsuhiro-t/openssl/tree/quic-draft-15>`_ <https://github.com/tatsuhiro-t/openssl/tree/OpenSSL_1_1_1g-quic-draft-29>`_
* picotls * picotls
* nss * nss
* BoringSSL * BoringSSL
You should use ngtcp2 draft-18 branch. At the time of this writing,
interop is done with draft-18.
Creating ngtcp2_conn object Creating ngtcp2_conn object
--------------------------- ---------------------------
In order to start handshake, you need to first create ``ngtcp2_conn`` ``ngtcp2_conn`` is the primary object to present a single QUIC
object. Use `ngtcp2_conn_client_new()` for client application, and connection. Use `ngtcp2_conn_client_new()` for client application,
`ngtcp2_conn_server_new()` for server. and `ngtcp2_conn_server_new()` for server.
They require ``ngtcp2_conn_callback`` and ``ngtcp2_settings`` objects. They require ``ngtcp2_conn_callbacks`` and ``ngtcp2_settings``
objects.
The ``ngtcp2_conn_callback`` contains the callback functions which The ``ngtcp2_conn_callbacks`` contains the callback functions which
``ngtcp2_conn`` calls when a specific event happens, say, receiving ``ngtcp2_conn`` calls when a specific event happens, say, receiving
stream data or stream is closed, etc. stream data or stream is closed, etc. Some of the callback functions
are optional. For client application, the following callback
In order to make handshake work for client application, at least the functions must be set:
following fields of ``ngtcp2_conn_callbacks`` must be set:
* ``client_initial``: `ngtcp2_crypto_client_initial_cb()` can be
* client_initial passed directly.
* recv_crypto_data * ``recv_crypto_data``
* in_encrypt * ``encrypt``: `ngtcp2_crypto_encrypt_cb()` can be passed directly.
* in_decrypt * ``decrypt``: `ngtcp2_crypto_decrypt_cb()` can be passed directly.
* encrypt * ``hp_mask``: `ngtcp2_crypto_hp_mask_cb()` can be passed directly.
* decrypt * ``recv_retry``: `ngtcp2_crypto_recv_retry_cb()` can be passed
* in_hp_mask directly.
* hp_mask * ``rand``
* acked_crypto_offset * ``get_new_connection_id``
* recv_retry * ``update_key``: `ngtcp2_crypto_update_key_cb()` can be passed
* rand directly.
* get_new_connection_id * ``delete_crypto_aead_ctx``:
* update_key `ngtcp2_crypto_delete_crypto_aead_ctx_cb()` can be passed directly.
* select_preferred_addr * ``delete_crypto_cipher_ctx``:
`ngtcp2_crypto_delete_crypto_cipher_ctx_cb()` can be passed
For server application: directly.
* recv_client_initial For server application, the following callback functions must be set:
* recv_crypto_data
* in_encrypt * ``recv_client_initial``: `ngtcp2_crypto_recv_client_initial_cb()`
* in_decrypt can be passed directly.
* encrypt * ``recv_crypto_data``
* decrypt * ``encrypt``: `ngtcp2_crypto_encrypt_cb()` can be passed directly.
* in_encrypt_pn * ``decrypt``: `ngtcp2_crypto_decrypt_cb()` can be passed directly.
* encrypt_pn * ``hp_mask``: `ngtcp2_crypto_hp_mask_cb()` can be passed directly.
* acked_crypto_offset * ``rand``
* rand * ``get_new_connection_id``
* get_new_connection_id * ``update_key``: `ngtcp2_crypto_update_key_cb()` can be passed
* update_key directly.
* ``delete_crypto_aead_ctx``:
`ngtcp2_crypto_delete_crypto_aead_ctx_cb()` can be passed directly.
* ``delete_crypto_cipher_ctx``:
`ngtcp2_crypto_delete_crypto_cipher_ctx_cb()` can be passed
directly.
``ngtcp2_crypto_*`` functions are a part of ngtcp2 crypto support
library which provides easy integration with the supported TLS
backend. It vastly simplifies TLS integration and is strongly
recommended.
``ngtcp2_settings`` contains the settings for QUIC connection. All ``ngtcp2_settings`` contains the settings for QUIC connection. All
fields must be set. It would be very useful to enable debug logging fields must be set. Application should call
by setting logging function to ``log_printf`` field. ngtcp2 library `ngtcp2_settings_default()` to set the default values. It would be
relies on the timestamp fed from application. The initial timestamp very useful to enable debug logging by setting logging function to
must be passed to ``initial_ts`` field in nanosecond resolution. ``log_printf`` field. ngtcp2 library relies on the timestamp fed from
ngtcp2 cares about the difference from that initial value. It could application. The initial timestamp must be passed to ``initial_ts``
be any timestamp which increases monotonically, and actual value does field in nanosecond resolution. ngtcp2 cares about the difference
not matter. ``max_packet_size``, ``ack_delay_component``, and from that initial value. It could be any timestamp which increases
``max_ack_delay`` should be set to the draft default, monotonically, and actual value does not matter.
``NGTCP2_MAX_PKT_SIZE``, ``NGTCP2_DEFAULT_ACK_DELAY_EXPONENT``, and
``NGTCP2_DEFAULT_MAX_ACK_DELAY`` respectively. Of course, you can
tweak these values if you know what you are doing.
Client application has to supply Connection IDs to Client application has to supply Connection IDs to
`ngtcp2_conn_client_new()`. The *dcid* parameter is the destination `ngtcp2_conn_client_new()`. The *dcid* parameter is the destination
...@@ -95,134 +100,60 @@ least 8 bytes long. The *scid* is the source connection ID (SCID) ...@@ -95,134 +100,60 @@ least 8 bytes long. The *scid* is the source connection ID (SCID)
which identifies the client itself. The *version* parameter is the which identifies the client itself. The *version* parameter is the
QUIC version to use. It should be ``NGTCP2_PROTO_VER_MAX``. QUIC version to use. It should be ``NGTCP2_PROTO_VER_MAX``.
Similarly, server application has to supply these parameters. But the Similarly, server application has to supply these parameters to
*dcid* must be the same value which is received from client (which is `ngtcp2_conn_server_new()`. But the *dcid* must be the same value
client SCID). The *scid* is chosen by server. Don't use DCID in which is received from client (which is client SCID). The *scid* is
client packet as server SCID. The *version* parameter is the QUIC chosen by server. Don't use DCID in client packet as server SCID.
version to use. It should be ``NGTCP2_PROTO_VER_MAX``. The *version* parameter is the QUIC version to use. It should be
``NGTCP2_PROTO_VER_MAX``.
Client application must create initial secret and derives packet
protection key and IV, and packet number encryption key. See
https://tools.ietf.org/html/draft-ietf-quic-tls-16#section-5.2
A path is very important to QUIC connection. It is the pair of A path is very important to QUIC connection. It is the pair of
endpoints, local and remote. The path passed to endpoints, local and remote. The path passed to
`ngtcp2_conn_client_new()` and `ngtcp2_conn_server_new()` is a network `ngtcp2_conn_client_new()` and `ngtcp2_conn_server_new()` is a network
path that handshake is performed. The path must not change during path that handshake is performed. The path must not change during
handshake. After handshake, client can migrate to new path. In that handshake. After handshake is confirmed, client can migrate to new
case, both endpoints will perform path validation to migrate new path. path. An application must provide actual path to the API function to
An application must provide actual path to the API function to tell tell the library where a packet comes from. The "write" API function
the library where a packet comes from. The "write" API function takes takes path parameter and fills it to which the packet should be sent.
path parameter and fills it with which the written packet should be
sent.
TLS integration TLS integration
--------------- ---------------
QUIC uses modified version of TLSv1.3. The differences are: ngtcp2 crypto support library is strongly recommended because it
vastly simplifies the TLS integration.
* QUIC does not use TLS record layer protocol. Each TLS message is
directly encoded and encrypted by QUIC transport. The most of the TLS work is done by the callback functions passed to
* QUIC does not use End of Early Data TLS message. ``ngtcp2_conn_callbacks`` object. There are some operations left to
* QUIC does not send early (0-RTT) data through TLSv1.3 application application has to perform to make TLS integration work.
message. It is sent outside TLS.
When TLS stack generates new secrets, they have to be installed to
QUIC has 4 types of packets: Initial, Handshake, 0-RTT Protected, and ``ngtcp2_conn`` by calling `ngtcp2_crypto_derive_and_install_rx_key()`
Short. They are encrypted with their own keys. and `ngtcp2_crypto_derive_and_install_tx_key()`.
Initial packet is encrypted by the Initial key which is derived from When TLS stack generates new crypto data to send, they must be passed
client DCID and static salt. to ``ngtcp2_conn`` by calling `ngtcp2_conn_submit_crypto_data()`.
Handshake packet is encrypted by the Handshake key. TLS stack When QUIC handshake is completed, ``handshake_completed`` callback
provides secret, and application derives key and IV using function is called. The local and remote endpoint independently
HKDF-Expand-Label with ``quic key`` and ``quic iv`` labels declare handshake completion. The endpoint has to confirm that the
respectively. The secret is client_handshake_traffic_secret for other endpoint also finished handshake. When the handshake is
client and server_handshake_traffic_secret for server. confirmed, client side ``ngtcp2_conn`` will call
``handshake_confirmed`` callback function. Server confirms handshake
0-RTT Protected packet is encrypted by the 0RTT key. TLS stack when it declares handshake completion, therefore, separate handshake
provides secret, and application derives key and IV using the same confirmation callback is not called.
labels for Handshake key. The secret is client_early_traffic_secret.
Read and write packets
Short packet is encrypted by the 1RTT key. TLS stack provides secret, ----------------------
and application derives key and IV using the same labels for Handshake
key. The secret is client_application_traffic_secret for client and `ngtcp2_conn_read_pkt()` processes the incoming QUIC packets. In
server_application_traffic_secret for server. order to write QUIC packets, call `ngtcp2_conn_writev_stream()` or
`ngtcp2_conn_write_pkt()`.
TLS stack has to implement the interface which notify these keying
materials. They must be installed to `ngtcp2_conn` using the In order to send stream data, the application has to first open a
following functions: stream. Use `ngtcp2_conn_open_bidi_stream()` to open bidirectional
stream. For unidirectional stream, call
* `ngtcp2_conn_install_initial_tx_keys()`: Set encryption key for `ngtcp2_conn_open_uni_stream()`. Call `ngtcp2_conn_writev_stream()`
Initial packet. to send stream data.
* `ngtcp2_conn_install_initial_rx_keys()`: Set decryption key for
Initial packet.
* `ngtcp2_conn_install_handshake_tx_keys()`: Set encryption key for
Handshake packet.
* `ngtcp2_conn_install_handshake_rx_keys()`: Set decryption key for
Handshake packet.
* `ngtcp2_conn_install_early_keys()`: Set key for 0RTT Protected
packet for encryption and decryption.
* `ngtcp2_conn_install_tx_keys()`: Set encryption key for Short
packet.
* `ngtcp2_conn_install_rx_keys()`: Set decryption key for Short
packet.
Clarification of encryption and decryption keys: For client
application, encryption keys are derived from client_*_traffic_secret,
and decryption keys are derived from server_*_traffic_secret. For
server application, encryption keys are derived from
server_*_traffic_secret, and decryption keys are derived from
client_*_traffic_secret.
After Handshake key is available, set AEAD overhead (tag length) using
`ngtcp2_conn_set_aead_overhead()` function.
`ngtcp2_conn_write_pkt()` initiates QUIC handshake. The Initial keys
must be installed before calling this function.
For client application, it first calls
``ngtcp2_conn_callbacks.client_initial`` callback. The callback must
ask TLS stack to produce first TLS message, which is typically
ClientHello. The message must be passed to ``ngtcp2_conn`` object
using `ngtcp2_conn_submit_crypto_data()` function. The function does
not own the passed data. The application should keep the data alive
until ``ngtcp2_conn_callbacks.acked_crypto_offset`` callback tells
that the data is acknowledged by the peer and no longer used. Next,
``ngtcp2_conn_callbacks.in_encrypt`` callback is called to tell
application to encrypt the data using AEAD_AES_128_GCM. And then,
``ngtcp2_conn_callbacks.in_hp_mask`` callback is called to tell
application to produce a mask to encrypt packet header using AES-ECB.
After negotiated Handshake keys are available,
``ngtcp2_conn_callbacks.encrypt`` and
``ngtcp2_conn_callbacks.hp_mask`` are called instead. Use the
negotiated cipher suites. If ChaCha20 based cipher suite is
negotiated, ChaCha20 is used to protect packet header.
`ngtcp2_conn_read_pkt()` reads QUIC handshake packets.
For server application, it first calls
``ngtcp2_conn_callbacks.recv_client_initial`` callback. The callback
must create the Initial key using client DCID and install it to
``ngtcp2_conn``. The library calls
``ngtcp2_conn_callbacks.in_hp_mask`` callback to produce a mask in
order to decrypt packet header. Then
``ngtcp2_conn_callbacks.in_decrypt`` callback is called to decrypt
packet payload. ``ngtcp2_conn_callbacks.recv_crypto_data`` callback
is called with the received TLS messages. Feed them to TLS stack. If
TLS stack produces any TLS message other than Alert, passes them to
``ngtcp2_conn`` through `ngtcp2_conn_submit_crypto_data()` function.
After negotiated Handshake keys are available,
``ngtcp2_conn_callbacks.hp_mask`` and
``ngtcp2_conn_callbacks.decrypt`` are called instead. When peer
acknowledges TLS messages,
``ngtcp2_conn_callbacks.acked_crypto_offset`` callback is called. The
application can throw away data acknowledged.
`ngtcp2_conn_read_pkt()` and `ngtcp2_conn_write_pkt()` performs QUIC
handshake until `ngtcp2_conn_get_handshake_completed()` returns
nonzero which means QUIC handshake has completed. They can be used
for post-handshake data transfer as well. To send stream data, use
`ngtcp2_conn_writev_stream()`.
0RTT data transmission 0RTT data transmission
---------------------- ----------------------
...@@ -239,28 +170,24 @@ application has to open stream to send data using ...@@ -239,28 +170,24 @@ application has to open stream to send data using
`ngtcp2_conn_open_bidi_stream()` (or `ngtcp2_conn_open_uni_stream()` `ngtcp2_conn_open_bidi_stream()` (or `ngtcp2_conn_open_uni_stream()`
for unidirectional stream). for unidirectional stream).
Stateless Retry Stream and crypto data ownershp
--------------- -------------------------------
QUIC allows server to validate client address in a stateless manner. Stream and crypto data passed to ``ngtcp2_conn`` must be held by
When a client receives client address validation request from server, application until ``acked_stream_data_offset`` and
``ngtcp2_conn_callbacks.recv_retry`` callback is called. Most of the ``acked_crypto_offset`` callbacks, respectively, tell that the those
retry logic is done by the library, but the client application has to data are acknowledged by the remote endpoint and no longer used by the
recreate TLS session from scratch to produce fresh keying materials. application.
0RTT data that has already passed to ``ngtcp2_conn`` is still alive. Timers
Client application must not free them until ------
``ngtcp2_conn_callbacks.acked_stream_data_offset`` callback is called.
Timer
-----
The library does not ask any timestamp to an operating system. The library does not ask any timestamp to an operating system.
Instead, an application has to supply timestamp to the library. The Instead, an application has to supply timestamp to the library. The
type of timestamp in ngtcp2 library is ``ngtcp2_tstamp``. At the type of timestamp in ngtcp2 library is ``ngtcp2_tstamp`` which is
moment, it is nanosecond resolution. The library only cares the nanosecond resolution. The library only cares the difference of
difference of timestamp, so it does not have to be a system clock. A timestamp, so it does not have to be a system clock. A monotonic
monotonic clock should work better. It should be same clock passed to clock should work better. It should be same clock passed to
``ngtcp2_setting``. ``ngtcp2_setting``.
`ngtcp2_conn_get_expiry()` tells an application when timer fires. `ngtcp2_conn_get_expiry()` tells an application when timer fires.
...@@ -270,18 +197,9 @@ When timer fires, call `ngtcp2_conn_handle_expiry()` and ...@@ -270,18 +197,9 @@ When timer fires, call `ngtcp2_conn_handle_expiry()` and
After calling these functions, new expiry will be set. The After calling these functions, new expiry will be set. The
application should call `ngtcp2_conn_get_expiry()` to restart timer. application should call `ngtcp2_conn_get_expiry()` to restart timer.
After QUIC handshake Application also handles connection idle timeout.
-------------------- `ngtcp2_conn_get_idle_expiry()` returns the current idle expiry. If
idle timer is expired, the connection should be closed.
After QUIC handshake completed, `ngtcp2_conn_read_pkt()` can read
incoming QUIC packets. To write QUIC packets, call
`ngtcp2_conn_write_pkt()`.
In order to send stream data, the application has to first open a
stream. Use `ngtcp2_conn_open_bidi_stream()` to open bidirectional
stream. For unidirectional stream, call
`ngtcp2_conn_open_uni_stream()`. Call `ngtcp2_conn_writev_stream()`
to send stream data.
Closing connection Closing connection
------------------ ------------------
...@@ -297,15 +215,14 @@ In general, when error is returned from the ngtcp2 library function, ...@@ -297,15 +215,14 @@ In general, when error is returned from the ngtcp2 library function,
just close QUIC connection. just close QUIC connection.
If `ngtcp2_err_is_fatal()` returns true with the returned error code, If `ngtcp2_err_is_fatal()` returns true with the returned error code,
``ngtcp2_conn`` object must be deleted with `ngtcp2_conn_del` without ``ngtcp2_conn`` object must be deleted with `ngtcp2_conn_del()`
any ngtcp2 library functions. Otherwise, call without any ngtcp2 library functions. Otherwise, call
`ngtcp2_conn_write_connection_close()` to get terminal packet. `ngtcp2_conn_write_connection_close()` to get terminal packet.
Sending it finishes QUIC connection. Sending it finishes QUIC connection.
The following error codes must be considered as transitional, and The following error codes must be considered as transitional, and
application should keep connection alive: application should keep connection alive:
* ``NGTCP2_ERR_EARLY_DATA_REJECTED``
* ``NGTCP2_ERR_STREAM_DATA_BLOCKED`` * ``NGTCP2_ERR_STREAM_DATA_BLOCKED``
* ``NGTCP2_ERR_STREAM_SHUT_WR`` * ``NGTCP2_ERR_STREAM_SHUT_WR``
* ``NGTCP2_ERR_STREAM_NOT_FOUND`` * ``NGTCP2_ERR_STREAM_NOT_FOUND``
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment