diff --git a/CMakeLists.txt b/CMakeLists.txt index 3747e2b6..3c98271e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -544,6 +544,7 @@ if (ENABLE_TLS) list(APPEND src src/socket.c src/tls.c + src/tls_alpn.c src/tls_ext.c src/tls_psk.c src/tls_sni.c diff --git a/include/gmssl/tls.h b/include/gmssl/tls.h index 330b5c8e..fd3d4914 100644 --- a/include/gmssl/tls.h +++ b/include/gmssl/tls.h @@ -836,6 +836,8 @@ typedef struct { // 16. application_layer_protocol_negotiation int application_layer_protocol_negotiation; + char *alpn_protocols[4]; + size_t alpn_protocols_cnt; // 18. signed_certificate_timestamp int signed_certificate_timestamp; @@ -1166,6 +1168,7 @@ typedef struct { // 16. application_layer_protocol_negotiation int application_layer_protocol_negotiation; + char *alpn_selected; // 18. signed_certificate_timestamp int signed_certificate_timestamp; @@ -1764,6 +1767,25 @@ int tls_server_name_from_bytes(const uint8_t **host_name, size_t *host_name_len, const uint8_t *ext_data, size_t ext_datalen); int tls_server_name_print(FILE *fp, int fmt, int ind, const uint8_t *ext_data, size_t ext_datalen); +// 16. application_layer_protocol_negotiation +int tls_ctx_set_application_layer_protocol_negotiation(TLS_CTX *ctx, + char *protocols[], size_t protocols_cnt); +int tls_application_layer_protocol_negotiation_ext_to_bytes( + char *protocols[], size_t protocols_cnt, uint8_t **out, size_t *outlen); +int tls_application_layer_protocol_negotiation_selected_ext_to_bytes( + char *protocol, uint8_t **out, size_t *outlen); +int tls_application_layer_protocol_negotiation_from_bytes( + const uint8_t **protocol_name_list, size_t *protocol_name_list_len, + const uint8_t *ext_data, size_t ext_datalen); +int tls_application_layer_protocol_negotiation_select( + const uint8_t *ext_data, size_t ext_datalen, + char *local_protocols[], size_t local_protocols_cnt, char **selected); +int tls_application_layer_protocol_negotiation_selected_from_bytes( + char **selected, const uint8_t *ext_data, size_t ext_datalen, + char *local_protocols[], size_t local_protocols_cnt); +int tls_application_layer_protocol_negotiation_print(FILE *fp, int fmt, int ind, + const uint8_t *ext_data, size_t ext_datalen); + // 5. status_request (OCSP stapling) enum { diff --git a/src/tls.c b/src/tls.c index 28d070a8..88c9612f 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2670,6 +2670,39 @@ int tls_ctx_set_signature_algorithms(TLS_CTX *ctx, const int *sig_algs, size_t s return 1; } +int tls_ctx_set_application_layer_protocol_negotiation(TLS_CTX *ctx, + char *protocols[], size_t protocols_cnt) +{ + size_t i; + + if (!ctx || !protocols || !protocols_cnt) { + error_print(); + return -1; + } + if (protocols_cnt > sizeof(ctx->alpn_protocols)/sizeof(ctx->alpn_protocols[0])) { + error_print(); + return -1; + } + for (i = 0; i < protocols_cnt; i++) { + size_t protocol_len; + + if (!protocols[i]) { + error_print(); + return -1; + } + protocol_len = strlen(protocols[i]); + if (protocol_len < 1 || protocol_len > 255) { + error_print(); + return -1; + } + ctx->alpn_protocols[i] = protocols[i]; + } + ctx->alpn_protocols_cnt = protocols_cnt; + ctx->application_layer_protocol_negotiation = 1; + + return 1; +} + int tls_ctx_set_key_update_seq_num_limit(TLS_CTX *ctx, size_t max_seq_num) { if (!ctx) { @@ -2919,5 +2952,3 @@ int tls_compute_verify_data(const uint8_t master_secret[48], } return 1; } - - diff --git a/src/tls13.c b/src/tls13.c index 53faa064..88d90baa 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -3879,6 +3879,16 @@ int tls13_send_client_hello(TLS_CONNECT *conn) } } + // application_layer_protocol_negotiation + if (conn->ctx->alpn_protocols_cnt) { + if (tls_application_layer_protocol_negotiation_ext_to_bytes( + conn->ctx->alpn_protocols, conn->ctx->alpn_protocols_cnt, + &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + // psk_key_exchange_modes if (conn->key_exchange_modes & (TLS_KE_PSK_DHE|TLS_KE_PSK)) { if (tls13_psk_key_exchange_modes_ext_to_bytes( @@ -4473,6 +4483,16 @@ int tls13_send_client_hello_again(TLS_CONNECT *conn) } } + // application_layer_protocol_negotiation + if (conn->ctx->alpn_protocols_cnt) { + if (tls_application_layer_protocol_negotiation_ext_to_bytes( + conn->ctx->alpn_protocols, conn->ctx->alpn_protocols_cnt, + &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + // key_share (re-generated) if ((curve_oid = tls_named_curve_oid(conn->key_exchange_group)) == OID_undef) { error_print(); @@ -5102,6 +5122,7 @@ int tls13_recv_encrypted_extensions(TLS_CONNECT *conn) int server_name = 0; int early_data = 0; + int alpn = 0; printf("recv {EncryptedExtensions}\n"); @@ -5183,6 +5204,13 @@ int tls13_recv_encrypted_extensions(TLS_CONNECT *conn) return -1; } break; + case TLS_extension_application_layer_protocol_negotiation: + if (!ext_data) { + error_print(); + tls13_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + break; } switch (ext_type) { @@ -5228,6 +5256,30 @@ int tls13_recv_encrypted_extensions(TLS_CONNECT *conn) break; case TLS_extension_application_layer_protocol_negotiation: + if (!conn->ctx->alpn_protocols_cnt) { + error_print(); + tls13_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + if (alpn) { + error_print(); + tls13_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + if ((ret = tls_application_layer_protocol_negotiation_selected_from_bytes( + &conn->alpn_selected, ext_data, ext_datalen, + conn->ctx->alpn_protocols, conn->ctx->alpn_protocols_cnt)) < 0) { + error_print(); + tls13_send_alert(conn, TLS_alert_decode_error); + return -1; + } else if (ret == 0) { + error_print(); + tls13_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + alpn = 1; + break; + case TLS_extension_use_srtp: case TLS_extension_client_certificate_type: case TLS_extension_server_certificate_type: @@ -6429,6 +6481,8 @@ int tls13_recv_client_hello(TLS_CONNECT *conn) size_t key_share_len; const uint8_t *server_name = NULL; size_t server_name_len; + const uint8_t *alpn = NULL; + size_t alpn_len; const uint8_t *psk_key_exchange_modes = NULL; size_t psk_key_exchange_modes_len; const uint8_t *pre_shared_key = NULL; @@ -6565,6 +6619,7 @@ int tls13_recv_client_hello(TLS_CONNECT *conn) case TLS_extension_certificate_authorities: case TLS_extension_key_share: case TLS_extension_server_name: + case TLS_extension_application_layer_protocol_negotiation: case TLS_extension_psk_key_exchange_modes: case TLS_extension_pre_shared_key: case TLS_extension_status_request: @@ -6653,6 +6708,16 @@ int tls13_recv_client_hello(TLS_CONNECT *conn) server_name_len = ext_datalen; break; + case TLS_extension_application_layer_protocol_negotiation: + if (alpn) { + error_print(); + tls13_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + alpn = ext_data; + alpn_len = ext_datalen; + break; + case TLS_extension_psk_key_exchange_modes: if (psk_key_exchange_modes) { error_print(); @@ -6845,6 +6910,27 @@ int tls13_recv_client_hello(TLS_CONNECT *conn) conn->server_name = 1; } + // application_layer_protocol_negotiation + if (alpn) { + if (!conn->ctx->alpn_protocols_cnt) { + error_print(); + tls13_send_alert(conn, TLS_alert_no_application_protocol); + return -1; + } + if ((ret = tls_application_layer_protocol_negotiation_select( + alpn, alpn_len, + conn->ctx->alpn_protocols, conn->ctx->alpn_protocols_cnt, + &conn->alpn_selected)) < 0) { + error_print(); + tls13_send_alert(conn, TLS_alert_decode_error); + return -1; + } else if (ret == 0) { + error_print(); + tls13_send_alert(conn, TLS_alert_no_application_protocol); + return -1; + } + } + // select server cert_chain // * signature_algorithms // * [signature_algorithms_cert] @@ -7778,6 +7864,15 @@ int tls13_send_encrypted_extensions(TLS_CONNECT *conn) } } + // application_layer_protocol_negotiation + if (conn->alpn_selected) { + if (tls_application_layer_protocol_negotiation_selected_ext_to_bytes( + conn->alpn_selected, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + if (tls13_record_set_handshake_encrypted_extensions( conn->plain_record, &conn->plain_recordlen, exts, extslen) != 1) { diff --git a/src/tls_alpn.c b/src/tls_alpn.c new file mode 100644 index 00000000..ee1e9bd4 --- /dev/null +++ b/src/tls_alpn.c @@ -0,0 +1,295 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + + +#include +#include +#include +#include +#include + + +#define tls_application_layer_protocol_negotiation_max_count() \ + (sizeof(((TLS_CTX *)0)->alpn_protocols) \ + / sizeof(((TLS_CTX *)0)->alpn_protocols[0])) + + +/* +16. application_layer_protocol_negotiation + +opaque ProtocolName<1..2^8-1>; + +struct { + ProtocolName protocol_name_list<2..2^16-1> +} ProtocolNameList; + +ClientHello.application_layer_protocol_negotiation + ext_data = ProtocolNameList + protocol_name_list contains one or more ProtocolName values + +EncryptedExtensions.application_layer_protocol_negotiation + ext_data = ProtocolNameList + protocol_name_list contains exactly one selected ProtocolName + +GmSSL keeps ALPN protocols as caller-owned char * strings. The selected +protocol returned by these helpers points to a local protocol string, not to +the peer's extension buffer. +*/ + +static int tls_application_layer_protocol_negotiation_check( + const char *protocol) +{ + size_t len; + + if (!protocol) { + error_print(); + return -1; + } + len = strlen(protocol); + if (len < 1 || len > 255) { + error_print(); + return -1; + } + return 1; +} + +static int tls_application_layer_protocol_negotiation_match( + const uint8_t *peer_protocol, size_t peer_protocol_len, + const char *local_protocol) +{ + size_t local_protocol_len; + + if (!peer_protocol || !peer_protocol_len || !local_protocol) { + return 0; + } + local_protocol_len = strlen(local_protocol); + if (peer_protocol_len != local_protocol_len) { + return 0; + } + if (memcmp(peer_protocol, local_protocol, peer_protocol_len) != 0) { + return 0; + } + return 1; +} + +int tls_application_layer_protocol_negotiation_ext_to_bytes( + char *protocols[], size_t protocols_cnt, + uint8_t **out, size_t *outlen) +{ + size_t protocol_name_list_len = 0; + size_t ext_datalen; + size_t i; + + if (!protocols || !protocols_cnt + || protocols_cnt > tls_application_layer_protocol_negotiation_max_count() + || !outlen) { + error_print(); + return -1; + } + for (i = 0; i < protocols_cnt; i++) { + size_t len; + + if (tls_application_layer_protocol_negotiation_check(protocols[i]) != 1) { + error_print(); + return -1; + } + len = strlen(protocols[i]); + protocol_name_list_len += tls_uint8_size() + len; + } + if (protocol_name_list_len < 2 || protocol_name_list_len > 65535) { + error_print(); + return -1; + } + ext_datalen = tls_uint16_size() + protocol_name_list_len; + + tls_uint16_to_bytes(TLS_extension_application_layer_protocol_negotiation, out, outlen); + tls_uint16_to_bytes((uint16_t)ext_datalen, out, outlen); + tls_uint16_to_bytes((uint16_t)protocol_name_list_len, out, outlen); + for (i = 0; i < protocols_cnt; i++) { + size_t len = strlen(protocols[i]); + tls_uint8_to_bytes((uint8_t)len, out, outlen); + if (out && *out) { + memcpy(*out, protocols[i], len); + *out += len; + } + *outlen += len; + } + + return 1; +} + +int tls_application_layer_protocol_negotiation_selected_ext_to_bytes( + char *protocol, uint8_t **out, size_t *outlen) +{ + return tls_application_layer_protocol_negotiation_ext_to_bytes(&protocol, 1, out, outlen); +} + +int tls_application_layer_protocol_negotiation_from_bytes( + const uint8_t **protocol_name_list, size_t *protocol_name_list_len, + const uint8_t *ext_data, size_t ext_datalen) +{ + if (!protocol_name_list || !protocol_name_list_len) { + error_print(); + return -1; + } + if (tls_uint16array_from_bytes(protocol_name_list, protocol_name_list_len, + &ext_data, &ext_datalen) != 1 + || tls_length_is_zero(ext_datalen) != 1) { + error_print(); + return -1; + } + if (!*protocol_name_list || *protocol_name_list_len < 2) { + error_print(); + return -1; + } + return 1; +} + +int tls_application_layer_protocol_negotiation_select( + const uint8_t *ext_data, size_t ext_datalen, + char *local_protocols[], size_t local_protocols_cnt, char **selected) +{ + const uint8_t *protocol_name_list; + size_t protocol_name_list_len; + size_t i; + + if (!local_protocols || !local_protocols_cnt + || local_protocols_cnt > tls_application_layer_protocol_negotiation_max_count() + || !selected) { + error_print(); + return -1; + } + *selected = NULL; + + for (i = 0; i < local_protocols_cnt; i++) { + if (tls_application_layer_protocol_negotiation_check(local_protocols[i]) != 1) { + error_print(); + return -1; + } + } + if (tls_application_layer_protocol_negotiation_from_bytes( + &protocol_name_list, &protocol_name_list_len, + ext_data, ext_datalen) != 1) { + error_print(); + return -1; + } + + while (protocol_name_list_len) { + const uint8_t *peer_protocol; + size_t peer_protocol_len; + + if (tls_uint8array_from_bytes(&peer_protocol, &peer_protocol_len, + &protocol_name_list, &protocol_name_list_len) != 1) { + error_print(); + return -1; + } + if (!peer_protocol || !peer_protocol_len) { + error_print(); + return -1; + } + + for (i = 0; i < local_protocols_cnt; i++) { + if (tls_application_layer_protocol_negotiation_match( + peer_protocol, peer_protocol_len, + local_protocols[i]) == 1) { + *selected = local_protocols[i]; + return 1; + } + } + } + + return 0; +} + +int tls_application_layer_protocol_negotiation_selected_from_bytes( + char **selected, const uint8_t *ext_data, size_t ext_datalen, + char *local_protocols[], size_t local_protocols_cnt) +{ + const uint8_t *protocol_name_list; + size_t protocol_name_list_len; + const uint8_t *peer_protocol; + size_t peer_protocol_len; + size_t i; + + if (!selected || !local_protocols || !local_protocols_cnt + || local_protocols_cnt > tls_application_layer_protocol_negotiation_max_count()) { + error_print(); + return -1; + } + *selected = NULL; + + for (i = 0; i < local_protocols_cnt; i++) { + if (tls_application_layer_protocol_negotiation_check(local_protocols[i]) != 1) { + error_print(); + return -1; + } + } + if (tls_application_layer_protocol_negotiation_from_bytes( + &protocol_name_list, &protocol_name_list_len, + ext_data, ext_datalen) != 1) { + error_print(); + return -1; + } + if (tls_uint8array_from_bytes(&peer_protocol, &peer_protocol_len, + &protocol_name_list, &protocol_name_list_len) != 1 + || tls_length_is_zero(protocol_name_list_len) != 1) { + error_print(); + return -1; + } + if (!peer_protocol || !peer_protocol_len) { + error_print(); + return -1; + } + + for (i = 0; i < local_protocols_cnt; i++) { + if (tls_application_layer_protocol_negotiation_match( + peer_protocol, peer_protocol_len, + local_protocols[i]) == 1) { + *selected = local_protocols[i]; + return 1; + } + } + + return 0; +} + +int tls_application_layer_protocol_negotiation_print( + FILE *fp, int fmt, int ind, + const uint8_t *ext_data, size_t ext_datalen) +{ + const uint8_t *protocol_name_list; + size_t protocol_name_list_len; + + format_print(fp, fmt, ind, "protocol_name_list\n"); + ind += 4; + + if (tls_application_layer_protocol_negotiation_from_bytes( + &protocol_name_list, &protocol_name_list_len, + ext_data, ext_datalen) != 1) { + error_print(); + return -1; + } + while (protocol_name_list_len) { + const uint8_t *protocol; + size_t protocol_len; + + if (tls_uint8array_from_bytes(&protocol, &protocol_len, + &protocol_name_list, &protocol_name_list_len) != 1) { + error_print(); + return -1; + } + if (!protocol || !protocol_len) { + error_print(); + return -1; + } + format_string(fp, fmt, ind, "ProtocolName", protocol, protocol_len); + } + + return 1; +}