public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH common 0/2] ticket: sign RSA tickets with SHA-256
@ 2026-10-23 18:36 Mark Atwood
  2026-10-23 18:57 ` [PATCH common 1/2] ticket: accept RSA tickets signed " Mark Atwood
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Mark Atwood @ 2026-10-23 18:36 UTC (permalink / raw)
  To: pve-devel

RSA tickets are currently signed with SHA-1, because that is the
Crypt::OpenSSL::RSA default. NIST SP 800-131A Rev. 2 disallows SHA-1 for
creating signatures. With a FIPS provider that enforces this, such as
wolfProvider or the OpenSSL FIPS provider with its signature digest
check enabled, the signing call fails and nobody can log in.

The change is split in two so that it can be staged for clusters with
mixed versions, where an updated node forwards its tickets to nodes
that are not updated yet:

1/2 makes verification accept SHA-256 as well as SHA-1. It changes no
    issued ticket and can be applied right away.
2/2 switches signing to SHA-256. Nodes without 1/2 reject the new
    tickets, so this should go in once 1/2 has been in a released
    version for a while, for example with the next major release.

The SHA-1 verification fallback can be dropped in a later major release,
once no supported version signs with SHA-1 anymore. Tickets are only
valid for two hours, so it does not need to cover old tickets, only
nodes running old versions.

Tested with "make check" on Debian trixie with the build dependencies
from pve-no-subscription. The new test fails on master for the SHA-256
cases and, with only 1/2 applied, for the signing checks of 2/2. I also
checked a ticket issued with the series applied with
"openssl dgst -sha256 -verify", which accepts it, and with -sha1, which
rejects it.

A separate patch for pve-access-control removes the leftover SHA-1 CSRF
token fallback there.

Mark Atwood (2):
  ticket: accept RSA tickets signed with SHA-256
  ticket: sign RSA tickets with SHA-256

 src/PVE/Ticket.pm   |  19 ++++++++-
 test/Makefile       |   1 +
 test/ticket_test.pl | 102 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 121 insertions(+), 1 deletion(-)
 create mode 100755 test/ticket_test.pl


base-commit: d3e8405874f43e4a128a11d7ce829102fda2eefd
-- 
2.55.0

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH common 1/2] ticket: accept RSA tickets signed with SHA-256
  2026-10-23 18:36 [PATCH common 0/2] ticket: sign RSA tickets with SHA-256 Mark Atwood
@ 2026-10-23 18:57 ` Mark Atwood
  2026-11-02 14:07   ` Shannon Sterz
  2026-10-23 18:57 ` [PATCH common 2/2] ticket: sign RSA tickets " Mark Atwood
  2026-11-02 14:07 ` [PATCH common 0/2] " Shannon Sterz
  2 siblings, 1 reply; 6+ messages in thread
From: Mark Atwood @ 2026-10-23 18:57 UTC (permalink / raw)
  To: pve-devel

Crypt::OpenSSL::RSA signs and verifies with SHA-1 unless told otherwise,
so all RSA tickets (login, TFA, VNC, tunnel) are RSA PKCS#1 v1.5
signatures over SHA-1. NIST SP 800-131A Rev. 2 no longer allows SHA-1 for
creating digital signatures. FIPS providers for OpenSSL 3 can enforce
this, for example wolfProvider, or the OpenSSL FIPS provider with its
signature digest check enabled. With such a provider no ticket can be
issued and nobody can log in.

As a first step, make verification accept tickets signed with SHA-256 in
addition to SHA-1. Once all nodes of a cluster run a version with this
change, the signing side can switch to SHA-256 without locking users out
of nodes that were not yet upgraded.

The hash is set explicitly before each attempt, since callers cache and
reuse the key object. A provider that does not allow SHA-1 raises an
error from verify() instead of returning false, so the call is wrapped
in eval and such a ticket is treated like any other invalid ticket.

Add a test that signs tickets directly with Crypt::OpenSSL::RSA using
either hash, and checks that they are accepted and that modified,
expired, foreign-key or wrong-secret tickets are rejected.

Signed-off-by: Mark Atwood <mark@wolfssl.com>
---
 src/PVE/Ticket.pm   | 15 +++++++++-
 test/Makefile       |  1 +
 test/ticket_test.pl | 68 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 83 insertions(+), 1 deletion(-)
 create mode 100755 test/ticket_test.pl

diff --git a/src/PVE/Ticket.pm b/src/PVE/Ticket.pm
index 764c02d..f700f39 100644
--- a/src/PVE/Ticket.pm
+++ b/src/PVE/Ticket.pm
@@ -79,8 +79,21 @@ sub verify_rsa_ticket {
         my $sig = $2;
 
         my $full = defined($secret_data) ? "$plain:$secret_data" : $plain;
+        my $raw_sig = decode_base64($sig);
+
+        # Older versions sign with SHA-1, so during a rolling upgrade a cluster has tickets
+        # with either hash. A provider that refuses SHA-1 raises an error rather than
+        # returning false, which simply makes such a ticket invalid.
+        my $valid;
+        for my $use_hash ('use_sha256_hash', 'use_sha1_hash') {
+            $valid = eval {
+                $rsa_pub->$use_hash();
+                $rsa_pub->verify($full, $raw_sig);
+            };
+            last if $valid;
+        }
 
-        if ($rsa_pub->verify($full, decode_base64($sig))) {
+        if ($valid) {
             if ($plain =~ m/^\Q$prefix\E:(?:(\S+):)?([A-Z0-9]{8})$/) {
                 my $data = $1; # Note: not all tickets contains data
                 my $timestamp = $2;
diff --git a/test/Makefile b/test/Makefile
index 1bce185..7eac9ea 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -16,6 +16,7 @@ TESTS = lock_file.test			\
 	section_config_property_isolation_test.test \
 	file-test.test \
 	cmd-test.test \
+	ticket_test.test \
 
 all:
 
diff --git a/test/ticket_test.pl b/test/ticket_test.pl
new file mode 100755
index 0000000..37e30b4
--- /dev/null
+++ b/test/ticket_test.pl
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+# Tests for signing and verifying RSA tickets with PVE::Ticket
+
+use v5.36;
+
+use lib '../src';
+
+use Crypt::OpenSSL::RSA;
+use MIME::Base64;
+use Test::More;
+
+use PVE::Ticket;
+
+my $rsa_priv = Crypt::OpenSSL::RSA->generate_key(2048);
+my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($rsa_priv->get_public_key_string());
+
+# sign like a node would, with an explicitly chosen hash, independent of PVE::Ticket
+sub sign_ticket($priv, $hash, $plain, $secret_data = undef) {
+    my $full = defined($secret_data) ? "$plain:$secret_data" : $plain;
+    my $use_hash = "use_${hash}_hash";
+    $priv->$use_hash();
+    return $plain . "::" . encode_base64($priv->sign($full), '');
+}
+
+my $now = sprintf("%08X", time());
+my $old = sprintf("%08X", time() - 7200);
+
+for my $hash (qw(sha256 sha1)) {
+    my $ticket = sign_ticket($rsa_priv, $hash, "PVE:root\@pam:$now");
+    my ($data, $age) = PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $ticket, undef, -1, 60, 1);
+    is($data, 'root@pam', "$hash signed ticket is accepted");
+    ok(defined($age) && $age >= 0 && $age < 60, "$hash signed ticket has plausible age");
+
+    $ticket = sign_ticket($rsa_priv, $hash, "PVEVNC:$now", 'vnc-secret');
+    ok(
+        PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVEVNC', $ticket, 'vnc-secret', -20, 40, 1),
+        "$hash signed ticket with secret data is accepted",
+    );
+    ok(
+        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVEVNC', $ticket, 'other', -20, 40, 1),
+        "$hash signed ticket with wrong secret data is rejected",
+    );
+
+    $ticket = sign_ticket($rsa_priv, $hash, "PVE:root\@pam:$now");
+    (my $tampered = $ticket) =~ s/^PVE:root\@pam:/PVE:admin\@pam:/;
+    ok(
+        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $tampered, undef, -1, 60, 1),
+        "$hash signed ticket with modified data is rejected",
+    );
+    eval { PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $tampered, undef, -1, 60) };
+    like($@, qr/invalid PVE ticket/, "$hash signed modified ticket raises error without noerr");
+
+    $ticket = sign_ticket($rsa_priv, $hash, "PVE:root\@pam:$old");
+    ok(
+        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $ticket, undef, -1, 60, 1),
+        "$hash signed expired ticket is rejected",
+    );
+
+    my $other_priv = Crypt::OpenSSL::RSA->generate_key(2048);
+    $ticket = sign_ticket($other_priv, $hash, "PVE:root\@pam:$now");
+    ok(
+        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $ticket, undef, -1, 60, 1),
+        "$hash signed ticket from another key is rejected",
+    );
+}
+
+done_testing();
-- 
2.55.0




^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH common 2/2] ticket: sign RSA tickets with SHA-256
  2026-10-23 18:36 [PATCH common 0/2] ticket: sign RSA tickets with SHA-256 Mark Atwood
  2026-10-23 18:57 ` [PATCH common 1/2] ticket: accept RSA tickets signed " Mark Atwood
@ 2026-10-23 18:57 ` Mark Atwood
  2026-11-02 14:07   ` Shannon Sterz
  2026-11-02 14:07 ` [PATCH common 0/2] " Shannon Sterz
  2 siblings, 1 reply; 6+ messages in thread
From: Mark Atwood @ 2026-10-23 18:57 UTC (permalink / raw)
  To: pve-devel

Crypt::OpenSSL::RSA signs with SHA-1 unless told otherwise. NIST SP
800-131A Rev. 2 no longer allows SHA-1 for creating digital signatures,
and a FIPS provider that enforces this makes assemble_rsa_ticket die, so
no ticket can be issued.

Select SHA-256 explicitly before signing. The signature length only
depends on the RSA key size, so tickets keep their size.

Nodes with the previous commit accept both hashes. Nodes running an
older version only verify SHA-1, so in a cluster with mixed versions a
ticket issued by an updated node is rejected by a not yet updated one,
for example on API requests that the updated node proxies to it. This
change should thus only roll out once the verification side has been
available for a while.

Extend the test to check, against the RSA primitive with an explicitly
chosen hash, that issued tickets carry a SHA-256 signature, also when
the key object was last used with SHA-1.

Signed-off-by: Mark Atwood <mark@wolfssl.com>
---
 src/PVE/Ticket.pm   |  4 ++++
 test/ticket_test.pl | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/src/PVE/Ticket.pm b/src/PVE/Ticket.pm
index f700f39..86dfd5a 100644
--- a/src/PVE/Ticket.pm
+++ b/src/PVE/Ticket.pm
@@ -66,6 +66,10 @@ sub assemble_rsa_ticket {
 
     my $full = defined($secret_data) ? "$plain:$secret_data" : $plain;
 
+    # Crypt::OpenSSL::RSA signs with SHA-1 unless told otherwise. NIST SP 800-131A no longer
+    # allows SHA-1 for creating signatures, and FIPS providers may refuse to.
+    $rsa_priv->use_sha256_hash();
+
     my $ticket = $plain . "::" . encode_base64($rsa_priv->sign($full), '');
 
     return $ticket;
diff --git a/test/ticket_test.pl b/test/ticket_test.pl
index 37e30b4..503c421 100755
--- a/test/ticket_test.pl
+++ b/test/ticket_test.pl
@@ -65,4 +65,38 @@ for my $hash (qw(sha256 sha1)) {
     );
 }
 
+# check what PVE::Ticket signs against the RSA primitive with an explicitly chosen hash
+sub signature_hash($pub, $ticket, $secret_data = undef) {
+    my ($plain, $sig) = $ticket =~ m/^(\S+)::([^:\s]+)$/ or return undef;
+    my $full = defined($secret_data) ? "$plain:$secret_data" : $plain;
+    my $raw_sig = decode_base64($sig);
+    my @matching;
+    for my $hash (qw(sha256 sha1)) {
+        my $use_hash = "use_${hash}_hash";
+        push @matching, $hash if eval { $pub->$use_hash(); $pub->verify($full, $raw_sig) };
+    }
+    return join(',', @matching);
+}
+
+my $ticket = PVE::Ticket::assemble_rsa_ticket($rsa_priv, 'PVE', 'root@pam');
+is(signature_hash($rsa_pub, $ticket), 'sha256', "ticket is signed with SHA-256 only");
+is(
+    scalar(PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $ticket, undef, -1, 60, 1)),
+    'root@pam',
+    "signed ticket is accepted",
+);
+
+# a key object that was last used with SHA-1 must still sign with SHA-256
+$rsa_priv->use_sha1_hash();
+$ticket = PVE::Ticket::assemble_rsa_ticket($rsa_priv, 'PVEVNC', undef, 'vnc-secret');
+is(
+    signature_hash($rsa_pub, $ticket, 'vnc-secret'),
+    'sha256',
+    "ticket with secret data is signed with SHA-256",
+);
+ok(
+    PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVEVNC', $ticket, 'vnc-secret', -20, 40, 1),
+    "signed ticket with secret data is accepted",
+);
+
 done_testing();
-- 
2.55.0




^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH common 1/2] ticket: accept RSA tickets signed with SHA-256
  2026-10-23 18:57 ` [PATCH common 1/2] ticket: accept RSA tickets signed " Mark Atwood
@ 2026-11-02 14:07   ` Shannon Sterz
  0 siblings, 0 replies; 6+ messages in thread
From: Shannon Sterz @ 2026-11-02 14:07 UTC (permalink / raw)
  To: Mark Atwood, pve-devel

On Fri Oct 23, 2026 at 8:57 PM CEST, Mark Atwood wrote:
> Crypt::OpenSSL::RSA signs and verifies with SHA-1 unless told otherwise,
> so all RSA tickets (login, TFA, VNC, tunnel) are RSA PKCS#1 v1.5
> signatures over SHA-1. NIST SP 800-131A Rev. 2 no longer allows SHA-1 for
> creating digital signatures. FIPS providers for OpenSSL 3 can enforce
> this, for example wolfProvider, or the OpenSSL FIPS provider with its
> signature digest check enabled. With such a provider no ticket can be
> issued and nobody can log in.
>
> As a first step, make verification accept tickets signed with SHA-256 in
> addition to SHA-1. Once all nodes of a cluster run a version with this
> change, the signing side can switch to SHA-256 without locking users out
> of nodes that were not yet upgraded.
>
> The hash is set explicitly before each attempt, since callers cache and
> reuse the key object. A provider that does not allow SHA-1 raises an
> error from verify() instead of returning false, so the call is wrapped
> in eval and such a ticket is treated like any other invalid ticket.
>
> Add a test that signs tickets directly with Crypt::OpenSSL::RSA using
> either hash, and checks that they are accepted and that modified,
> expired, foreign-key or wrong-secret tickets are rejected.
>
> Signed-off-by: Mark Atwood <mark@wolfssl.com>
> ---
>  src/PVE/Ticket.pm   | 15 +++++++++-
>  test/Makefile       |  1 +
>  test/ticket_test.pl | 68 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 83 insertions(+), 1 deletion(-)
>  create mode 100755 test/ticket_test.pl
>
> diff --git a/src/PVE/Ticket.pm b/src/PVE/Ticket.pm
> index 764c02d..f700f39 100644
> --- a/src/PVE/Ticket.pm
> +++ b/src/PVE/Ticket.pm
> @@ -79,8 +79,21 @@ sub verify_rsa_ticket {
>          my $sig = $2;
>
>          my $full = defined($secret_data) ? "$plain:$secret_data" : $plain;
> +        my $raw_sig = decode_base64($sig);
> +
> +        # Older versions sign with SHA-1, so during a rolling upgrade a cluster has tickets
> +        # with either hash. A provider that refuses SHA-1 raises an error rather than
> +        # returning false, which simply makes such a ticket invalid.

nit: This could probably benefit from comment like:

    # TODO: Remove SHA-1 fallback once SHA-256 signed tickets are the
    # default across PMG and PVE for at least one major release.

> +        my $valid;
> +        for my $use_hash ('use_sha256_hash', 'use_sha1_hash') {
> +            $valid = eval {
> +                $rsa_pub->$use_hash();
> +                $rsa_pub->verify($full, $raw_sig);

It might be worth to add documentation that the state of the RSA pub key
object may be modified depending on what ticket is being verified. If a
SHA-256 ticket is verified, other users of `$rsa_pub` will now use
SHA-256 for verifying signatures in other contexts; if the ticket is
SHA-1, then SHA-1 will be used.

This can make it hard to tell from a caller's perspective in what state
the object will be after a call to this function. IMO, this should at
least be documented or maybe we should reset the object after the last
`verify()` call unconditionally. What do you think?

Note: From a quick check, none of the users should have a reason to call
`verify()` outside of the functions in `PVE::Ticket`. Here you always
correctly set the hash function before using the object, so it shouldn't
be an issue in practice.

> +            };

The eval call also swallows *all* errors that could happen during
verification, might be worth checking what `$@` contains and warning
about it.

> +            last if $valid;
> +        }
>
> -        if ($rsa_pub->verify($full, decode_base64($sig))) {
> +        if ($valid) {
>              if ($plain =~ m/^\Q$prefix\E:(?:(\S+):)?([A-Z0-9]{8})$/) {
>                  my $data = $1; # Note: not all tickets contains data
>                  my $timestamp = $2;
> diff --git a/test/Makefile b/test/Makefile
> index 1bce185..7eac9ea 100644
> --- a/test/Makefile
> +++ b/test/Makefile
> @@ -16,6 +16,7 @@ TESTS = lock_file.test			\
>  	section_config_property_isolation_test.test \
>  	file-test.test \
>  	cmd-test.test \
> +	ticket_test.test \
>
>  all:
>
> diff --git a/test/ticket_test.pl b/test/ticket_test.pl
> new file mode 100755
> index 0000000..37e30b4
> --- /dev/null
> +++ b/test/ticket_test.pl
> @@ -0,0 +1,68 @@
> +#!/usr/bin/perl
> +
> +# Tests for signing and verifying RSA tickets with PVE::Ticket
> +
> +use v5.36;
> +
> +use lib '../src';
> +
> +use Crypt::OpenSSL::RSA;
> +use MIME::Base64;
> +use Test::More;
> +
> +use PVE::Ticket;
> +
> +my $rsa_priv = Crypt::OpenSSL::RSA->generate_key(2048);
> +my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($rsa_priv->get_public_key_string());
> +
> +# sign like a node would, with an explicitly chosen hash, independent of PVE::Ticket
> +sub sign_ticket($priv, $hash, $plain, $secret_data = undef) {
> +    my $full = defined($secret_data) ? "$plain:$secret_data" : $plain;
> +    my $use_hash = "use_${hash}_hash";
> +    $priv->$use_hash();
> +    return $plain . "::" . encode_base64($priv->sign($full), '');
> +}
> +
> +my $now = sprintf("%08X", time());
> +my $old = sprintf("%08X", time() - 7200);
> +
> +for my $hash (qw(sha256 sha1)) {
> +    my $ticket = sign_ticket($rsa_priv, $hash, "PVE:root\@pam:$now");
> +    my ($data, $age) = PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $ticket, undef, -1, 60, 1);
> +    is($data, 'root@pam', "$hash signed ticket is accepted");
> +    ok(defined($age) && $age >= 0 && $age < 60, "$hash signed ticket has plausible age");
> +
> +    $ticket = sign_ticket($rsa_priv, $hash, "PVEVNC:$now", 'vnc-secret');
> +    ok(
> +        PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVEVNC', $ticket, 'vnc-secret', -20, 40, 1),
> +        "$hash signed ticket with secret data is accepted",
> +    );
> +    ok(
> +        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVEVNC', $ticket, 'other', -20, 40, 1),
> +        "$hash signed ticket with wrong secret data is rejected",
> +    );
> +
> +    $ticket = sign_ticket($rsa_priv, $hash, "PVE:root\@pam:$now");
> +    (my $tampered = $ticket) =~ s/^PVE:root\@pam:/PVE:admin\@pam:/;
> +    ok(
> +        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $tampered, undef, -1, 60, 1),
> +        "$hash signed ticket with modified data is rejected",
> +    );
> +    eval { PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $tampered, undef, -1, 60) };
> +    like($@, qr/invalid PVE ticket/, "$hash signed modified ticket raises error without noerr");

nit: Would benefit from a test case that checks whether verification
succeeds without `$noerr` set too.

> +
> +    $ticket = sign_ticket($rsa_priv, $hash, "PVE:root\@pam:$old");
> +    ok(
> +        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $ticket, undef, -1, 60, 1),
> +        "$hash signed expired ticket is rejected",
> +    );
> +
> +    my $other_priv = Crypt::OpenSSL::RSA->generate_key(2048);
> +    $ticket = sign_ticket($other_priv, $hash, "PVE:root\@pam:$now");
> +    ok(
> +        !PVE::Ticket::verify_rsa_ticket($rsa_pub, 'PVE', $ticket, undef, -1, 60, 1),
> +        "$hash signed ticket from another key is rejected",
> +    );
> +}
> +
> +done_testing();





^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH common 2/2] ticket: sign RSA tickets with SHA-256
  2026-10-23 18:57 ` [PATCH common 2/2] ticket: sign RSA tickets " Mark Atwood
@ 2026-11-02 14:07   ` Shannon Sterz
  0 siblings, 0 replies; 6+ messages in thread
From: Shannon Sterz @ 2026-11-02 14:07 UTC (permalink / raw)
  To: Mark Atwood, pve-devel

On Fri Oct 23, 2026 at 8:57 PM CEST, Mark Atwood wrote:
> Crypt::OpenSSL::RSA signs with SHA-1 unless told otherwise. NIST SP
> 800-131A Rev. 2 no longer allows SHA-1 for creating digital signatures,
> and a FIPS provider that enforces this makes assemble_rsa_ticket die, so
> no ticket can be issued.
>
> Select SHA-256 explicitly before signing. The signature length only
> depends on the RSA key size, so tickets keep their size.
>
> Nodes with the previous commit accept both hashes. Nodes running an
> older version only verify SHA-1, so in a cluster with mixed versions a
> ticket issued by an updated node is rejected by a not yet updated one,
> for example on API requests that the updated node proxies to it. This
> change should thus only roll out once the verification side has been
> available for a while.

One thing I wonder is whether we should maybe make this an opt-in as it
can already break certain users' setups (as you mentioned). An opt-in
would allow users that require a FIPS provider to use this already
instead of having to wait for use decide when it's been "long enough".
We can then eventually remove the opt-in check move everyone over.

We could also write a file to `/etc/pve` that indicates what nodes of
the cluster are ready to use the new hash function and switch
automatically if all nodes are updated. This would handle this
automatically, but would be more complex to implement.

-->8 snip 8<--




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH common 0/2] ticket: sign RSA tickets with SHA-256
  2026-10-23 18:36 [PATCH common 0/2] ticket: sign RSA tickets with SHA-256 Mark Atwood
  2026-10-23 18:57 ` [PATCH common 1/2] ticket: accept RSA tickets signed " Mark Atwood
  2026-10-23 18:57 ` [PATCH common 2/2] ticket: sign RSA tickets " Mark Atwood
@ 2026-11-02 14:07 ` Shannon Sterz
  2 siblings, 0 replies; 6+ messages in thread
From: Shannon Sterz @ 2026-11-02 14:07 UTC (permalink / raw)
  To: Mark Atwood, pve-devel

On Fri Oct 23, 2026 at 8:36 PM CEST, Mark Atwood wrote:
> RSA tickets are currently signed with SHA-1, because that is the
> Crypt::OpenSSL::RSA default. NIST SP 800-131A Rev. 2 disallows SHA-1 for
> creating signatures. With a FIPS provider that enforces this, such as
> wolfProvider or the OpenSSL FIPS provider with its signature digest
> check enabled, the signing call fails and nobody can log in.
>
> The change is split in two so that it can be staged for clusters with
> mixed versions, where an updated node forwards its tickets to nodes
> that are not updated yet:
>
> 1/2 makes verification accept SHA-256 as well as SHA-1. It changes no
>     issued ticket and can be applied right away.
> 2/2 switches signing to SHA-256. Nodes without 1/2 reject the new
>     tickets, so this should go in once 1/2 has been in a released
>     version for a while, for example with the next major release.
>
> The SHA-1 verification fallback can be dropped in a later major release,
> once no supported version signs with SHA-1 anymore. Tickets are only
> valid for two hours, so it does not need to cover old tickets, only
> nodes running old versions.
>
> Tested with "make check" on Debian trixie with the build dependencies
> from pve-no-subscription. The new test fails on master for the SHA-256
> cases and, with only 1/2 applied, for the signing checks of 2/2. I also
> checked a ticket issued with the series applied with
> "openssl dgst -sha256 -verify", which accepts it, and with -sha1, which
> rejects it.
>
> A separate patch for pve-access-control removes the leftover SHA-1 CSRF
> token fallback there.
>
> Mark Atwood (2):
>   ticket: accept RSA tickets signed with SHA-256
>   ticket: sign RSA tickets with SHA-256
>
>  src/PVE/Ticket.pm   |  19 ++++++++-
>  test/Makefile       |   1 +
>  test/ticket_test.pl | 102 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 121 insertions(+), 1 deletion(-)
>  create mode 100755 test/ticket_test.pl
>
>
> base-commit: d3e8405874f43e4a128a11d7ce829102fda2eefd


Hey Mark,

thank you for these patches, one small note up-front: these patches will
also affect PMG as it uses the `PVE::Ticket` module for its tickets as
well. This should maybe be mentioned in the commit messages.

Noted a few smaller things on the patches. IMO nothing that needs to
hold up at least the first patch here. The second patch could be
re-written so that we can possibly apply it sooner, too. See my comment
there.

Note that these patches no longer apply as a ticket test module has been
applied since.

Thanks again!




^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-02 14:07 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-23 18:36 [PATCH common 0/2] ticket: sign RSA tickets with SHA-256 Mark Atwood
2026-10-23 18:57 ` [PATCH common 1/2] ticket: accept RSA tickets signed " Mark Atwood
2026-11-02 14:07   ` Shannon Sterz
2026-10-23 18:57 ` [PATCH common 2/2] ticket: sign RSA tickets " Mark Atwood
2026-11-02 14:07   ` Shannon Sterz
2026-11-02 14:07 ` [PATCH common 0/2] " Shannon Sterz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal