From 9bcc64c39b9f4b57361d3535829743d6999d4aad Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 25 Jul 2026 17:03:22 +0200 Subject: [PATCH] curl: make --httpsig-key take a key OR a file name for key Verified by test 5022 Closes #22392 --- docs/cmdline-opts/httpsig-key.md | 19 ++++++----- src/config2setopts.c | 19 ++++++++--- src/tool_listhelp.c | 4 +-- tests/data/Makefile.am | 2 +- tests/data/test5001 | 2 +- tests/data/test5002 | 2 +- tests/data/test5003 | 2 +- tests/data/test5005 | 2 +- tests/data/test5006 | 2 +- tests/data/test5007 | 2 +- tests/data/test5010 | 2 +- tests/data/test5011 | 2 +- tests/data/test5012 | 2 +- tests/data/test5013 | 2 +- tests/data/test5014 | 2 +- tests/data/test5015 | 2 +- tests/data/test5016 | 2 +- tests/data/test5018 | 2 +- tests/data/test5021 | 2 +- tests/data/test5022 | 55 ++++++++++++++++++++++++++++++++ 20 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 tests/data/test5022 diff --git a/docs/cmdline-opts/httpsig-key.md b/docs/cmdline-opts/httpsig-key.md index a2d8f611d8..c0a0405148 100644 --- a/docs/cmdline-opts/httpsig-key.md +++ b/docs/cmdline-opts/httpsig-key.md @@ -3,8 +3,8 @@ c: Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl Long: httpsig-key Protocols: HTTP -Arg: -Help: Key file for HTTP Message Signatures +Arg: +Help: Key for HTTP Message Signatures Category: auth http Added: 8.22.0 Multi: single @@ -13,16 +13,19 @@ See-also: - httpsig-algo - httpsig-keyid Example: - - --httpsig-algo ed25519 --httpsig-key key.hex --httpsig-keyid "my-key" $URL + - --httpsig-algo ed25519 --httpsig-key @key.hex --httpsig-keyid "my-key" $URL + - --httpsig-key 123a56fb72197633bc --httpsig-keyid "my-key" $URL --- # `--httpsig-key` -Path to the key file used for RFC 9421 HTTP Message Signatures. +The key to use for RFC 9421 HTTP Message Signatures. Provide it as-is, or as +`@filename`. If the argument starts with an `@`, the rest is treated as a file +name for the key. -The file must contain a hex-encoded key on its first line. For **ed25519**, -this is the 32-byte private seed (64 hex characters). For **hmac-sha256**, -this is the shared secret. PEM files are not supported. +The key is formatted as a series of hexadecimal digits in a single line. For +**ed25519**, this is the 32-byte private seed (64 hex characters). For +**hmac-sha256**, this is the shared secret. PEM files are not supported. ## Generating Ed25519 keys @@ -32,4 +35,4 @@ With OpenSSL 3: openssl pkey -in k.pem -outform RAW -out k.raw xxd -p -c 64 k.raw | tr -d '\n' > k.hex -Use `k.hex` with `--httpsig-key`. +Use `@k.hex` with `--httpsig-key`. diff --git a/src/config2setopts.c b/src/config2setopts.c index 40543637de..47b67aed0b 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -599,8 +599,9 @@ static CURLcode httpsig_setopts(struct OperationConfig *config, CURL *curl) my_setopt_long(curl, CURLOPT_HTTPSIG_ALGORITHM, httpsig_alg); MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_HEADERS, config->httpsig_headers); MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_KEYID, config->httpsig_keyid); - { - FILE *keyf = curlx_fopen(config->httpsig_key, FOPEN_READTEXT); + + if(config->httpsig_key[0] == '@') { + FILE *keyf = curlx_fopen(&config->httpsig_key[1], FOPEN_READTEXT); if(keyf) { char *hexdata = NULL; ParameterError pe = file2string(&hexdata, keyf); @@ -612,12 +613,13 @@ static CURLcode httpsig_setopts(struct OperationConfig *config, CURL *curl) } if(pe == PARAM_READ_ERROR) { curlx_safefree(hexdata); - errorf("httpsig: cannot read key file '%s'", config->httpsig_key); + errorf("httpsig: cannot read key file '%s'", + &config->httpsig_key[1]); return CURLE_READ_ERROR; } if(!hexdata || !*hexdata) { curlx_safefree(hexdata); - errorf("httpsig: key file '%s' is empty", config->httpsig_key); + errorf("httpsig: key file '%s' is empty", &config->httpsig_key[1]); return CURLE_BAD_FUNCTION_ARGUMENT; } /* can't use the MY_SETOPT_STR() macro here since it returns on error @@ -628,10 +630,17 @@ static CURLcode httpsig_setopts(struct OperationConfig *config, CURL *curl) return result; } else { - errorf("httpsig: cannot open key file '%s'", config->httpsig_key); + errorf("httpsig: cannot open key file '%s'", &config->httpsig_key[1]); return CURLE_READ_ERROR; } } + else { + if(!config->httpsig_key[0]) { + errorf("httpsig: key is empty"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + MY_SETOPT_STR(curl, CURLOPT_HTTPSIG_KEY, config->httpsig_key); + } } return CURLE_OK; } diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 63cc990622..6232fa8b52 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -314,8 +314,8 @@ const struct helptxt helptext[] = { CURLHELP_AUTH | CURLHELP_HTTP }, #endif #ifndef CURL_DISABLE_HTTPSIG - { " --httpsig-key ", - "Key file for HTTP Message Signatures", + { " --httpsig-key ", + "Key for HTTP Message Signatures", CURLHELP_AUTH | CURLHELP_HTTP }, #endif #ifndef CURL_DISABLE_HTTPSIG diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f012cc58c0..209cc43598 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -294,7 +294,7 @@ test4000 test4001 \ \ test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 \ test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 \ -test5016 test5017 test5018 test5019 test5020 test5021 +test5016 test5017 test5018 test5019 test5020 test5021 test5022 EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 \ data1461.txt data1463.txt \ diff --git a/tests/data/test5001 b/tests/data/test5001 index 705289019e..7465aab2b7 100644 --- a/tests/data/test5001 +++ b/tests/data/test5001 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: CLI GET with query -"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT +"http://example.com:8000/%TESTNUMBER/resource?action=read" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5002 b/tests/data/test5002 index 0d002f831f..143d06d122 100644 --- a/tests/data/test5002 +++ b/tests/data/test5002 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: CLI GET with HMAC-SHA256 -"http://example.com:7000/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --connect-to example.com:7000:%HOSTIP:%HTTPPORT +"http://example.com:7000/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --connect-to example.com:7000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5003 b/tests/data/test5003 index 9d8bbfb06f..6468aa60e3 100644 --- a/tests/data/test5003 +++ b/tests/data/test5003 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: custom --httpsig-headers -"http://example.com:6000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority" --connect-to example.com:6000:%HOSTIP:%HTTPPORT +"http://example.com:6000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority" --connect-to example.com:6000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5005 b/tests/data/test5005 index f52c94d8d5..e090c943de 100644 --- a/tests/data/test5005 +++ b/tests/data/test5005 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: sign with a regular HTTP header -"http://example.com:5000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5000:%HOSTIP:%HTTPPORT +"http://example.com:5000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5006 b/tests/data/test5006 index fee77488ea..046f499648 100644 --- a/tests/data/test5006 +++ b/tests/data/test5006 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: component name lowercasing -"http://example.com:5100/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method Content-Type:" -H "Content-Type: text/plain" --connect-to example.com:5100:%HOSTIP:%HTTPPORT +"http://example.com:5100/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method Content-Type:" -H "Content-Type: text/plain" --connect-to example.com:5100:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5007 b/tests/data/test5007 index 42d72bfd1b..1184ebcffb 100644 --- a/tests/data/test5007 +++ b/tests/data/test5007 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: POST method -"http://example.com:5200/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -d "postbody" --connect-to example.com:5200:%HOSTIP:%HTTPPORT +"http://example.com:5200/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -d "postbody" --connect-to example.com:5200:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5010 b/tests/data/test5010 index ff77e59ceb..51fd8dd888 100644 --- a/tests/data/test5010 +++ b/tests/data/test5010 @@ -33,7 +33,7 @@ httpsig HTTP RFC 9421 Message Signatures: error - non-existent key file -"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key /nonexistent/key.hex --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @/nonexistent/key.hex --httpsig-keyid "my-key" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5011 b/tests/data/test5011 index 94206854a2..c883bbe249 100644 --- a/tests/data/test5011 +++ b/tests/data/test5011 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: query string with special characters -"http://example.com:5300/%TESTNUMBER/resource?name=me%AMPnoval%AMPaim=b%25ad" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:5300:%HOSTIP:%HTTPPORT +"http://example.com:5300/%TESTNUMBER/resource?name=me%AMPnoval%AMPaim=b%25ad" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:5300:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5012 b/tests/data/test5012 index e119c29d58..8f183738e5 100644 --- a/tests/data/test5012 +++ b/tests/data/test5012 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: default port omitted from @authority -"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com::%HOSTIP:%HTTPPORT +"http://example.com/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com::%HOSTIP:%HTTPPORT diff --git a/tests/data/test5013 b/tests/data/test5013 index 9cad55190c..beb3a1cf70 100644 --- a/tests/data/test5013 +++ b/tests/data/test5013 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: non-default port in @authority -"http://example.com:9000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:9000:%HOSTIP:%HTTPPORT +"http://example.com:9000/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:9000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5014 b/tests/data/test5014 index 6033e40bec..51fab716bd 100644 --- a/tests/data/test5014 +++ b/tests/data/test5014 @@ -36,7 +36,7 @@ httpsig HTTP RFC 9421 Message Signatures: @query explicitly requested, no query in URL -"http://example.com:5400/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority query" --connect-to example.com:5400:%HOSTIP:%HTTPPORT +"http://example.com:5400/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --httpsig-headers "method authority query" --connect-to example.com:5400:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5015 b/tests/data/test5015 index f9459be37a..5f6af68bc0 100644 --- a/tests/data/test5015 +++ b/tests/data/test5015 @@ -37,7 +37,7 @@ httpsig HTTP RFC 9421 Message Signatures: POST with HMAC-SHA256 -"http://example.com:5500/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" -d "postbody" --connect-to example.com:5500:%HOSTIP:%HTTPPORT +"http://example.com:5500/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" -d "postbody" --connect-to example.com:5500:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5016 b/tests/data/test5016 index f6fbe2b013..01a12a1ab0 100644 --- a/tests/data/test5016 +++ b/tests/data/test5016 @@ -37,7 +37,7 @@ httpsig HTTP RFC 9421 Message Signatures: HMAC-SHA256 with custom headers -"http://example.com:5600/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key %SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5600:%HOSTIP:%HTTPPORT +"http://example.com:5600/%TESTNUMBER/resource" --httpsig-algo "hmac-sha256" --httpsig-key @%SRCDIR/data/data-httpsig-hmac-sha256.key --httpsig-keyid "shared-key-1" --httpsig-headers "method authority content-type:" -H "Content-Type: application/json" --connect-to example.com:5600:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5018 b/tests/data/test5018 index 417c1f0b5b..ab03747070 100644 --- a/tests/data/test5018 +++ b/tests/data/test5018 @@ -39,7 +39,7 @@ putdata HTTP RFC 9421 Message Signatures: PUT upload -"http://example.com:5800/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -T log/upload5018 --connect-to example.com:5800:%HOSTIP:%HTTPPORT +"http://example.com:5800/%TESTNUMBER/resource" --httpsig-algo "ed25519" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" -T log/upload5018 --connect-to example.com:5800:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5021 b/tests/data/test5021 index da5f5af672..9370943853 100644 --- a/tests/data/test5021 +++ b/tests/data/test5021 @@ -35,7 +35,7 @@ httpsig HTTP RFC 9421 Message Signatures: default algorithm (ed25519) when --httpsig-algo omitted -"http://example.com:6100/%TESTNUMBER/resource" --httpsig-key %SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:6100:%HOSTIP:%HTTPPORT +"http://example.com:6100/%TESTNUMBER/resource" --httpsig-key @%SRCDIR/data/data-httpsig-ed25519.key --httpsig-keyid "test-key-ed25519" --connect-to example.com:6100:%HOSTIP:%HTTPPORT diff --git a/tests/data/test5022 b/tests/data/test5022 new file mode 100644 index 0000000000..a27ab7701f --- /dev/null +++ b/tests/data/test5022 @@ -0,0 +1,55 @@ + + + + +HTTP +httpsig +RFC9421 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +Content-Length: 6 +Connection: close +Content-Type: text/html + +-foo- + + + +# Client-side + + +http + + +Debug +httpsig + + +HTTP RFC 9421 Message Signatures: pass key directly + + +"http://example.com:8000/5001/resource?action=read" --httpsig-algo "ed25519" --variable key@%SRCDIR/data/data-httpsig-ed25519.key --expand-httpsig-key '{{key:trim}}' --httpsig-keyid "my-key-1" --connect-to example.com:8000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET /5001/resource?action=read HTTP/1.1 +Host: example.com:8000 +Signature-Input: sig1=("@method" "@authority" "@path" "@query");created=0;keyid="my-key-1";alg="ed25519" +Signature: sig1=:RQniOeqmwdRzGvoDIMJ8XJha75evJWgqo5/66EeuJeEGczZtnP2U/F52Lzd/y7Vd1DCb8oUcCKHrKi2VJI7lBA==: +User-Agent: curl/%VERSION +Accept: */* + + + +