/* * ngtcp2 * * Copyright (c) 2017 ngtcp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_crypto.h" #include #include #include "ngtcp2_str.h" #include "ngtcp2_conv.h" #include "ngtcp2_conn.h" #include "ngtcp2_net.h" #include "ngtcp2_conversion.h" int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, size_t secretlen, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, const ngtcp2_mem *mem) { int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, ivlen, mem); if (rv != 0) { return rv; } if (secretlen) { memcpy((*pckm)->secret.base, secret, secretlen); } if (aead_ctx) { (*pckm)->aead_ctx = *aead_ctx; } memcpy((*pckm)->iv.base, iv, ivlen); return 0; } int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, size_t ivlen, const ngtcp2_mem *mem) { size_t len; uint8_t *p; len = sizeof(ngtcp2_crypto_km) + secretlen + ivlen; *pckm = ngtcp2_mem_malloc(mem, len); if (*pckm == NULL) { return NGTCP2_ERR_NOMEM; } p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km); (*pckm)->secret.base = p; (*pckm)->secret.len = secretlen; p += secretlen; (*pckm)->iv.base = p; (*pckm)->iv.len = ivlen; (*pckm)->aead_ctx.native_handle = NULL; (*pckm)->pkt_num = -1; (*pckm)->use_count = 0; (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE; return 0; } void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem) { if (ckm == NULL) { return; } ngtcp2_mem_free(mem, ckm); } void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, int64_t pkt_num) { size_t i; uint64_t n; assert(ivlen >= 8); memcpy(dest, iv, ivlen); n = ngtcp2_htonl64((uint64_t)pkt_num); for (i = 0; i < 8; ++i) { dest[ivlen - 8 + i] ^= ((uint8_t *)&n)[i]; } } /* * varint_paramlen returns the length of a single transport parameter * which has variable integer in its parameter. */ static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t param) { size_t valuelen = ngtcp2_put_uvarintlen(param); return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(valuelen) + valuelen; } /* * write_varint_param writes parameter |id| of the given |value| in * varint encoding. It returns p + the number of bytes written. */ static uint8_t *write_varint_param(uint8_t *p, ngtcp2_transport_param_id id, uint64_t value) { p = ngtcp2_put_uvarint(p, id); p = ngtcp2_put_uvarint(p, ngtcp2_put_uvarintlen(value)); return ngtcp2_put_uvarint(p, value); } /* * zero_paramlen returns the length of a single transport parameter * which has zero length value in its parameter. */ static size_t zero_paramlen(ngtcp2_transport_param_id id) { return ngtcp2_put_uvarintlen(id) + 1; } /* * write_zero_param writes parameter |id| that has zero length value. * It returns p + the number of bytes written. */ static uint8_t *write_zero_param(uint8_t *p, ngtcp2_transport_param_id id) { p = ngtcp2_put_uvarint(p, id); *p++ = 0; return p; } /* * cid_paramlen returns the length of a single transport parameter * which has |cid| as value. */ static size_t cid_paramlen(ngtcp2_transport_param_id id, const ngtcp2_cid *cid) { return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(cid->datalen) + cid->datalen; } /* * write_cid_param writes parameter |id| of the given |cid|. It * returns p + the number of bytes written. */ static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id, const ngtcp2_cid *cid) { assert(cid->datalen == 0 || cid->datalen >= NGTCP2_MIN_CIDLEN); assert(cid->datalen <= NGTCP2_MAX_CIDLEN); p = ngtcp2_put_uvarint(p, id); p = ngtcp2_put_uvarint(p, cid->datalen); if (cid->datalen) { p = ngtcp2_cpymem(p, cid->data, cid->datalen); } return p; } static const uint8_t empty_address[16]; ngtcp2_ssize ngtcp2_transport_params_encode_versioned( uint8_t *dest, size_t destlen, int transport_params_version, const ngtcp2_transport_params *params) { uint8_t *p; size_t len = 0; /* For some reason, gcc 7.3.0 requires this initialization. */ size_t preferred_addrlen = 0; size_t version_infolen = 0; const ngtcp2_sockaddr_in *sa_in; const ngtcp2_sockaddr_in6 *sa_in6; ngtcp2_transport_params paramsbuf; params = ngtcp2_transport_params_convert_to_latest( ¶msbuf, transport_params_version, params); if (params->original_dcid_present) { len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, ¶ms->original_dcid); } if (params->stateless_reset_token_present) { len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) + ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) + NGTCP2_STATELESS_RESET_TOKENLEN; } if (params->preferred_addr_present) { assert(params->preferred_addr.cid.datalen >= NGTCP2_MIN_CIDLEN); assert(params->preferred_addr.cid.datalen <= NGTCP2_MAX_CIDLEN); preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ + 2 /* ipv6Port */ + 1 + params->preferred_addr.cid.datalen /* CID */ + NGTCP2_STATELESS_RESET_TOKENLEN; len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) + ngtcp2_put_uvarintlen(preferred_addrlen) + preferred_addrlen; } if (params->retry_scid_present) { len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, ¶ms->retry_scid); } if (params->initial_scid_present) { len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, ¶ms->initial_scid); } if (params->initial_max_stream_data_bidi_local) { len += varint_paramlen( NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, params->initial_max_stream_data_bidi_local); } if (params->initial_max_stream_data_bidi_remote) { len += varint_paramlen( NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, params->initial_max_stream_data_bidi_remote); } if (params->initial_max_stream_data_uni) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, params->initial_max_stream_data_uni); } if (params->initial_max_data) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, params->initial_max_data); } if (params->initial_max_streams_bidi) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, params->initial_max_streams_bidi); } if (params->initial_max_streams_uni) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, params->initial_max_streams_uni); } if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); } if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, params->ack_delay_exponent); } if (params->disable_active_migration) { len += zero_paramlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION); } if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, params->max_ack_delay / NGTCP2_MILLISECONDS); } if (params->max_idle_timeout) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, params->max_idle_timeout / NGTCP2_MILLISECONDS); } if (params->active_connection_id_limit && params->active_connection_id_limit != NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, params->active_connection_id_limit); } if (params->max_datagram_frame_size) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, params->max_datagram_frame_size); } if (params->grease_quic_bit) { len += zero_paramlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT); } if (params->version_info_present) { version_infolen = sizeof(uint32_t) + params->version_info.available_versionslen; len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION) + ngtcp2_put_uvarintlen(version_infolen) + version_infolen; } if (dest == NULL && destlen == 0) { return (ngtcp2_ssize)len; } if (destlen < len) { return NGTCP2_ERR_NOBUF; } p = dest; if (params->original_dcid_present) { p = write_cid_param( p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, ¶ms->original_dcid); } if (params->stateless_reset_token_present) { p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN); p = ngtcp2_put_uvarint(p, sizeof(params->stateless_reset_token)); p = ngtcp2_cpymem(p, params->stateless_reset_token, sizeof(params->stateless_reset_token)); } if (params->preferred_addr_present) { p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS); p = ngtcp2_put_uvarint(p, preferred_addrlen); if (params->preferred_addr.ipv4_present) { sa_in = ¶ms->preferred_addr.ipv4; p = ngtcp2_cpymem(p, &sa_in->sin_addr, sizeof(sa_in->sin_addr)); p = ngtcp2_put_uint16(p, sa_in->sin_port); } else { p = ngtcp2_cpymem(p, empty_address, sizeof(sa_in->sin_addr)); p = ngtcp2_put_uint16(p, 0); } if (params->preferred_addr.ipv6_present) { sa_in6 = ¶ms->preferred_addr.ipv6; p = ngtcp2_cpymem(p, &sa_in6->sin6_addr, sizeof(sa_in6->sin6_addr)); p = ngtcp2_put_uint16(p, sa_in6->sin6_port); } else { p = ngtcp2_cpymem(p, empty_address, sizeof(sa_in6->sin6_addr)); p = ngtcp2_put_uint16(p, 0); } *p++ = (uint8_t)params->preferred_addr.cid.datalen; if (params->preferred_addr.cid.datalen) { p = ngtcp2_cpymem(p, params->preferred_addr.cid.data, params->preferred_addr.cid.datalen); } p = ngtcp2_cpymem(p, params->preferred_addr.stateless_reset_token, sizeof(params->preferred_addr.stateless_reset_token)); } if (params->retry_scid_present) { p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, ¶ms->retry_scid); } if (params->initial_scid_present) { p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, ¶ms->initial_scid); } if (params->initial_max_stream_data_bidi_local) { p = write_varint_param( p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, params->initial_max_stream_data_bidi_local); } if (params->initial_max_stream_data_bidi_remote) { p = write_varint_param( p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, params->initial_max_stream_data_bidi_remote); } if (params->initial_max_stream_data_uni) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, params->initial_max_stream_data_uni); } if (params->initial_max_data) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, params->initial_max_data); } if (params->initial_max_streams_bidi) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, params->initial_max_streams_bidi); } if (params->initial_max_streams_uni) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, params->initial_max_streams_uni); } if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); } if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, params->ack_delay_exponent); } if (params->disable_active_migration) { p = write_zero_param(p, NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION); } if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, params->max_ack_delay / NGTCP2_MILLISECONDS); } if (params->max_idle_timeout) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, params->max_idle_timeout / NGTCP2_MILLISECONDS); } if (params->active_connection_id_limit && params->active_connection_id_limit != NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, params->active_connection_id_limit); } if (params->max_datagram_frame_size) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, params->max_datagram_frame_size); } if (params->grease_quic_bit) { p = write_zero_param(p, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT); } if (params->version_info_present) { p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION); p = ngtcp2_put_uvarint(p, version_infolen); p = ngtcp2_put_uint32be(p, params->version_info.chosen_version); if (params->version_info.available_versionslen) { p = ngtcp2_cpymem(p, params->version_info.available_versions, params->version_info.available_versionslen); } } assert((size_t)(p - dest) == len); return (ngtcp2_ssize)len; } /* * decode_varint decodes a single varint from the buffer pointed by * |*pp| of length |end - *pp|. If it decodes an integer * successfully, it stores the integer in |*pdest|, increment |*pp| by * the number of bytes read from |*pp|, and returns 0. Otherwise it * returns -1. */ static int decode_varint(uint64_t *pdest, const uint8_t **pp, const uint8_t *end) { const uint8_t *p = *pp; size_t len; if (p == end) { return -1; } len = ngtcp2_get_uvarintlen(p); if ((uint64_t)(end - p) < len) { return -1; } *pp = ngtcp2_get_uvarint(pdest, p); return 0; } /* * decode_varint_param decodes length prefixed value from the buffer * pointed by |*pp| of length |end - *pp|. The length and value are * encoded in varint form. If it decodes a value successfully, it * stores the value in |*pdest|, increment |*pp| by the number of * bytes read from |*pp|, and returns 0. Otherwise it returns -1. */ static int decode_varint_param(uint64_t *pdest, const uint8_t **pp, const uint8_t *end) { const uint8_t *p = *pp; uint64_t valuelen; if (decode_varint(&valuelen, &p, end) != 0) { return -1; } if (p == end) { return -1; } if ((uint64_t)(end - p) < valuelen) { return -1; } if (ngtcp2_get_uvarintlen(p) != valuelen) { return -1; } *pp = ngtcp2_get_uvarint(pdest, p); return 0; } /* * decode_zero_param decodes zero length value from the buffer pointed * by |*pp| of length |end - *pp|. The length is encoded in varint * form. If it decodes zero length value successfully, it increments * |*pp| by 1, and returns 0. Otherwise it returns -1. */ static int decode_zero_param(const uint8_t **pp, const uint8_t *end) { if (*pp == end || **pp != 0) { return -1; } ++*pp; return 0; } /* * decode_cid_param decodes length prefixed ngtcp2_cid from the buffer * pointed by |*pp| of length |end - *pp|. The length is encoded in * varint form. If it decodes a value successfully, it stores the * value in |*pdest|, increment |*pp| by the number of read from * |*pp|, and returns the number of bytes read. Otherwise it returns * the one of the negative error code: * * NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM * Could not decode Connection ID. */ static int decode_cid_param(ngtcp2_cid *pdest, const uint8_t **pp, const uint8_t *end) { const uint8_t *p = *pp; uint64_t valuelen; if (decode_varint(&valuelen, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if ((valuelen != 0 && valuelen < NGTCP2_MIN_CIDLEN) || valuelen > NGTCP2_MAX_CIDLEN || (size_t)(end - p) < valuelen) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } ngtcp2_cid_init(pdest, p, (size_t)valuelen); p += valuelen; *pp = p; return 0; } int ngtcp2_transport_params_decode_versioned(int transport_params_version, ngtcp2_transport_params *dest, const uint8_t *data, size_t datalen) { const uint8_t *p, *end, *lend; size_t len; uint64_t param_type; uint64_t valuelen; int rv; ngtcp2_sockaddr_in *sa_in; ngtcp2_sockaddr_in6 *sa_in6; uint32_t version; ngtcp2_transport_params *params, paramsbuf; if (transport_params_version == NGTCP2_TRANSPORT_PARAMS_VERSION) { params = dest; } else { params = ¶msbuf; } /* Set default values */ memset(params, 0, sizeof(*params)); params->original_dcid_present = 0; params->initial_scid_present = 0; params->initial_max_streams_bidi = 0; params->initial_max_streams_uni = 0; params->initial_max_stream_data_bidi_local = 0; params->initial_max_stream_data_bidi_remote = 0; params->initial_max_stream_data_uni = 0; params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; params->stateless_reset_token_present = 0; params->preferred_addr_present = 0; params->disable_active_migration = 0; params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; params->max_idle_timeout = 0; params->active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; params->retry_scid_present = 0; params->max_datagram_frame_size = 0; memset(¶ms->retry_scid, 0, sizeof(params->retry_scid)); memset(¶ms->initial_scid, 0, sizeof(params->initial_scid)); memset(¶ms->original_dcid, 0, sizeof(params->original_dcid)); params->version_info_present = 0; p = data; end = data + datalen; for (; (size_t)(end - p) >= 2;) { if (decode_varint(¶m_type, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } switch (param_type) { case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: if (decode_varint_param(¶ms->initial_max_stream_data_bidi_local, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: if (decode_varint_param(¶ms->initial_max_stream_data_bidi_remote, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI: if (decode_varint_param(¶ms->initial_max_stream_data_uni, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA: if (decode_varint_param(¶ms->initial_max_data, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI: if (decode_varint_param(¶ms->initial_max_streams_bidi, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (params->initial_max_streams_bidi > NGTCP2_MAX_STREAMS) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI: if (decode_varint_param(¶ms->initial_max_streams_uni, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (params->initial_max_streams_uni > NGTCP2_MAX_STREAMS) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT: if (decode_varint_param(¶ms->max_idle_timeout, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } params->max_idle_timeout *= NGTCP2_MILLISECONDS; break; case NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE: if (decode_varint_param(¶ms->max_udp_payload_size, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN: if (decode_varint(&valuelen, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if ((size_t)valuelen != sizeof(params->stateless_reset_token)) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if ((size_t)(end - p) < sizeof(params->stateless_reset_token)) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } p = ngtcp2_get_bytes(params->stateless_reset_token, p, sizeof(params->stateless_reset_token)); params->stateless_reset_token_present = 1; break; case NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT: if (decode_varint_param(¶ms->ack_delay_exponent, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (params->ack_delay_exponent > 20) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS: if (decode_varint(&valuelen, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if ((size_t)(end - p) < valuelen) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } len = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ + 2 /* ipv6Port */ + 1 /* cid length */ + NGTCP2_STATELESS_RESET_TOKENLEN; if (valuelen < len) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } sa_in = ¶ms->preferred_addr.ipv4; p = ngtcp2_get_bytes(&sa_in->sin_addr, p, sizeof(sa_in->sin_addr)); p = ngtcp2_get_uint16be(&sa_in->sin_port, p); if (sa_in->sin_port || memcmp(empty_address, &sa_in->sin_addr, sizeof(sa_in->sin_addr)) != 0) { sa_in->sin_family = NGTCP2_AF_INET; params->preferred_addr.ipv4_present = 1; } sa_in6 = ¶ms->preferred_addr.ipv6; p = ngtcp2_get_bytes(&sa_in6->sin6_addr, p, sizeof(sa_in6->sin6_addr)); p = ngtcp2_get_uint16be(&sa_in6->sin6_port, p); if (sa_in6->sin6_port || memcmp(empty_address, &sa_in6->sin6_addr, sizeof(sa_in6->sin6_addr)) != 0) { sa_in6->sin6_family = NGTCP2_AF_INET6; params->preferred_addr.ipv6_present = 1; } /* cid */ params->preferred_addr.cid.datalen = *p++; len += params->preferred_addr.cid.datalen; if (valuelen != len || params->preferred_addr.cid.datalen > NGTCP2_MAX_CIDLEN || params->preferred_addr.cid.datalen < NGTCP2_MIN_CIDLEN) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (params->preferred_addr.cid.datalen) { p = ngtcp2_get_bytes(params->preferred_addr.cid.data, p, params->preferred_addr.cid.datalen); } /* stateless reset token */ p = ngtcp2_get_bytes( params->preferred_addr.stateless_reset_token, p, sizeof(params->preferred_addr.stateless_reset_token)); params->preferred_addr_present = 1; break; case NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION: if (decode_zero_param(&p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } params->disable_active_migration = 1; break; case NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID: rv = decode_cid_param(¶ms->original_dcid, &p, end); if (rv != 0) { return rv; } params->original_dcid_present = 1; break; case NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID: rv = decode_cid_param(¶ms->retry_scid, &p, end); if (rv != 0) { return rv; } params->retry_scid_present = 1; break; case NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID: rv = decode_cid_param(¶ms->initial_scid, &p, end); if (rv != 0) { return rv; } params->initial_scid_present = 1; break; case NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY: if (decode_varint_param(¶ms->max_ack_delay, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (params->max_ack_delay >= 16384) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } params->max_ack_delay *= NGTCP2_MILLISECONDS; break; case NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT: if (decode_varint_param(¶ms->active_connection_id_limit, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE: if (decode_varint_param(¶ms->max_datagram_frame_size, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } break; case NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT: if (decode_zero_param(&p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } params->grease_quic_bit = 1; break; case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION: if (decode_varint(&valuelen, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if ((size_t)(end - p) < valuelen) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } p = ngtcp2_get_uint32(¶ms->version_info.chosen_version, p); if (params->version_info.chosen_version == 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (valuelen > sizeof(uint32_t)) { params->version_info.available_versions = (uint8_t *)p; params->version_info.available_versionslen = (size_t)valuelen - sizeof(uint32_t); for (lend = p + (valuelen - sizeof(uint32_t)); p != lend;) { p = ngtcp2_get_uint32(&version, p); if (version == 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } } } params->version_info_present = 1; break; default: /* Ignore unknown parameter */ if (decode_varint(&valuelen, &p, end) != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if ((size_t)(end - p) < valuelen) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } p += valuelen; break; } } if (end - p != 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } if (transport_params_version != NGTCP2_TRANSPORT_PARAMS_VERSION) { ngtcp2_transport_params_convert_to_old(transport_params_version, dest, params); } return 0; } static int transport_params_copy_new(ngtcp2_transport_params **pdest, const ngtcp2_transport_params *src, const ngtcp2_mem *mem) { size_t len = sizeof(**pdest); ngtcp2_transport_params *dest; uint8_t *p; if (src->version_info_present) { len += src->version_info.available_versionslen; } dest = ngtcp2_mem_malloc(mem, len); if (dest == NULL) { return NGTCP2_ERR_NOMEM; } *dest = *src; if (src->version_info_present && src->version_info.available_versionslen) { p = (uint8_t *)dest + sizeof(*dest); memcpy(p, src->version_info.available_versions, src->version_info.available_versionslen); dest->version_info.available_versions = p; } *pdest = dest; return 0; } int ngtcp2_transport_params_decode_new(ngtcp2_transport_params **pparams, const uint8_t *data, size_t datalen, const ngtcp2_mem *mem) { int rv; ngtcp2_transport_params params; rv = ngtcp2_transport_params_decode(¶ms, data, datalen); if (rv < 0) { return rv; } if (mem == NULL) { mem = ngtcp2_mem_default(); } return transport_params_copy_new(pparams, ¶ms, mem); } void ngtcp2_transport_params_del(ngtcp2_transport_params *params, const ngtcp2_mem *mem) { if (params == NULL) { return; } if (mem == NULL) { mem = ngtcp2_mem_default(); } ngtcp2_mem_free(mem, params); } int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest, const ngtcp2_transport_params *src, const ngtcp2_mem *mem) { if (src == NULL) { *pdest = NULL; return 0; } return transport_params_copy_new(pdest, src, mem); }