From: William Lallemand Date: Tue, 6 Oct 2026 12:59:43 +0000 (+0200) Subject: MINOR: ssl: add curve2name converter X-Git-Tag: v3.5-dev9~185 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=4ea77731a971c5e17861fc2cfcf78632695aeba4;p=haproxy.git MINOR: ssl: add curve2name converter Add a string converter to normalize curve names to IANA, ANSI X9.62 or NIST notation. Match both the requested format and input names case-insensitively, and resolve old ecdh_* aliases to canonical names. Keep the original string unchanged when no curve matches or the requested naming convention has no corresponding name. The converter uses the curve table independently of the SSL library's available NIDs. Document the supported formats and add regression coverage for curve aliases, mixed-case formats and unchanged-string fallbacks. The build and all three SSL curve regression tests pass. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index d7d259296..a2d27804b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -21599,6 +21599,7 @@ concat([start[,var[,end]]]) string string cpl integer integer crc32([avalanche]) binary integer crc32c([avalanche]) binary integer +curve2name(format) string string cut_crlf string string da-csv-conv(prop[,prop*]) string string date string integer @@ -22039,6 +22040,28 @@ crc32c([]) not be used for security purposes as a 32-bit hash is trivial to break. See also "djb2", "sdbm", "wt6", "crc32" and the "hash-type" directive. +curve2name() + Converts a curve name to the naming convention selected by . The + following formats are supported: + + - iana: registered TLS group name, such as "secp256r1" or "x25519". + Names are listed in the IANA TLS Supported Groups registry: + https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 + - ansi: ANSI X9.62 curve name, such as "prime256v1". + - nist: NIST curve name, such as "P-256". + + The format is mandatory and case-insensitive. Input names are matched + case-insensitively against the known names and compatibility aliases. This + converter requires SSL support. + + The original input is returned unchanged if it is unknown or the curve has + no name in the requested convention. For example, + "prime256v1,curve2name(iana)" produces "secp256r1", and + "secp256r1,curve2name(nist)" produces "P-256". + + Example: + http-response set-header X-Curve %[ssl_fc_curve,curve2name(iana)] + cut_crlf Cuts the string representation of the input sample on the first carriage return ('\r') or newline ('\n') character found. Only the string length is diff --git a/reg-tests/ssl/curve2name.vtc b/reg-tests/ssl/curve2name.vtc new file mode 100644 index 000000000..d6a92fdb7 --- /dev/null +++ b/reg-tests/ssl/curve2name.vtc @@ -0,0 +1,58 @@ +#REGTEST_TYPE=devel + +varnishtest "Convert curve names between naming conventions" +feature ignore_unknown_macro +feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'" + +haproxy h1 -conf { + defaults + mode http + timeout connect 5s + timeout client 5s + timeout server 5s + + frontend fe + bind "fd@${fe}" + http-request return status 200 hdr x-iana %[req.hdr(curve),curve2name(IANA)] hdr x-ansi %[req.hdr(curve),curve2name(ANSI)] hdr x-nist %[req.hdr(curve),curve2name(NiSt)] +} -start + +client c1 -connect ${h1_fe_sock} { + txreq -hdr "curve: prime256v1" + rxresp + expect resp.status == 200 + expect resp.http.x-iana == "secp256r1" + expect resp.http.x-ansi == "prime256v1" + expect resp.http.x-nist == "P-256" + + txreq -hdr "curve: p-192" + rxresp + expect resp.http.x-iana == "secp192r1" + expect resp.http.x-ansi == "prime192v1" + expect resp.http.x-nist == "P-192" + + txreq -hdr "curve: secp384r1" + rxresp + expect resp.http.x-iana == "secp384r1" + expect resp.http.x-ansi == "secp384r1" + expect resp.http.x-nist == "P-384" + + txreq -hdr "curve: X25519" + rxresp + expect resp.http.x-iana == "x25519" + expect resp.http.x-ansi == "X25519" + expect resp.http.x-nist == "X25519" + + txreq -hdr "curve: ecdh_x448" + rxresp + expect resp.http.x-iana == "x448" + + txreq -hdr "curve: MLKEM768" + rxresp + expect resp.http.x-iana == "MLKEM768" + + txreq -hdr "curve: secp256r1-extra" + rxresp + expect resp.http.x-iana == "secp256r1-extra" + expect resp.http.x-ansi == "secp256r1-extra" + expect resp.http.x-nist == "secp256r1-extra" +} -run diff --git a/src/ssl_utils.c b/src/ssl_utils.c index ee8e5c398..15f190431 100644 --- a/src/ssl_utils.c +++ b/src/ssl_utils.c @@ -13,9 +13,12 @@ #include +#include #include #include #include +#include +#include #include #include @@ -1154,3 +1157,68 @@ int curves2nid(const char *curve) return -1; } + +/* Validate the requested curve naming convention. */ +static int check_curve2name(struct arg *args, struct sample_conv *conv, + const char *file, int line, char **err) +{ + const char *format = args[0].data.str.area; + + if (!strcasecmp(format, "iana") || + !strcasecmp(format, "ansi") || !strcasecmp(format, "nist")) + return 1; + + memprintf(err, "expected 'iana', 'ansi' or 'nist'"); + return 0; +} + +/* Compare a sample string without requiring a terminating NUL. */ +static int curve_name_matches(const struct buffer *input, const char *name) +{ + return name && input->data == strlen(name) && + !strncasecmp(input->area, name, input->data); +} + +static int sample_conv_curve2name(const struct arg *args, struct sample *smp, + void *private) +{ + struct curve *item, *canonical; + const char *format = args[0].data.str.area; + const char *name = NULL; + + for (item = curves_list; item->curve_id; item++) { + if (curve_name_matches(&smp->data.u.str, item->ansi) || + curve_name_matches(&smp->data.u.str, item->iana) || + curve_name_matches(&smp->data.u.str, item->nist)) + break; + } + if (!item->curve_id) + return 1; + + /* Resolve compatibility aliases to the first entry for the group. */ + for (canonical = curves_list; canonical->curve_id != item->curve_id; canonical++) + ; + + if (!strcasecmp(format, "iana")) + name = canonical->iana; + else if (!strcasecmp(format, "ansi")) + name = canonical->ansi; + else if (!strcasecmp(format, "nist")) + name = canonical->nist; + + if (!name) + return 1; + + smp->data.u.str.area = (char *)name; + smp->data.u.str.data = strlen(name); + smp->data.u.str.size = 0; + smp->flags |= SMP_F_CONST; + return 1; +} + +static struct sample_conv_kw_list curve_conv_kws = { ILH, { + { "curve2name", sample_conv_curve2name, ARG1(1,STR), check_curve2name, SMP_T_STR, SMP_T_STR }, + { NULL, NULL, 0, 0, 0 }, +}}; + +INITCALL1(STG_REGISTER, sample_register_convs, &curve_conv_kws);