From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 518041FF0AF for ; Fri, 23 Oct 2026 20:58:04 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id C08D12161A; Fri, 23 Oct 2026 20:58:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=wolfssl-com.20251104.gappssmtp.com; s=20251104; t=1792781872; x=1793386672; darn=lists.proxmox.com; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=MEkwDa5JduCZkEwSCpSKHng4RXZUBFu8mhVX7eKT+vc=; b=c913glKPj6y8aCvfNJ8ZloEp9ExwPf0inxgYAOq/4okEaJPFLvd0SPX5+xrHLhDJUV lbp4jbsQBgjTcoNk4EUk05a7VmCgHafbZuDkGwFrLwVgNGixdLGADE5MCn4WqJST2kf/ QUG2OWebOcU3C4Az2N7tohAxO3NG3G8jF3ucrwebgEZ3SdOKcM4OjutmtaqpFHZNnFP3 4cUDZKhN2XMo8bhU7rm4Y2oNhXOSBBrAZ6OMDc4a6p1vn4hPFUC6b9rcIW9/aIYTcatg lY2BMWSwFuB6ZTjf3ETP0aq/BGr3tEH+Qz5E0BSSCye9yfivYGJkDVld+vGh085aE61u +RbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792781872; x=1793386672; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=MEkwDa5JduCZkEwSCpSKHng4RXZUBFu8mhVX7eKT+vc=; b=ukB6DqHTZXdDm36Y6fq6jkS8bBrwdPvHlwEP4NEAQRTuAkkrLs1kRhNVdn3qGdPr5C dIc6+NDktSZtl8LXX7DvLg8bi0O+HyHWSA7x7sfRpUdR6hnwvSJNE1DbCfxZI9jSfmsS jYQxvv7WKkcxJxel9BcKtKvnFz5/3fLGWUZnWd5/v4grwHWFyKEBNfkca03BhwvyHaLn kfAYHtbsp4BeCB+bcUbJzBysIW+0NZhYRujzRnk06gANA5ptdexOuWS8WU1ljDqfFVMM alSOEZae+JJYzky0MYOz0gkzkNVxM9kL3mifsG7GqX0C1lT0OwGRuaFh/2ddzK+GREI1 JGbw== X-Gm-Message-State: AFq9FYKPcnMB8TCtWf94qcUkquRxaXscYfR0JuvauvS4tIS+pVt2kZcJ 34kKUfwVDIpin3CnTflpZmseUimEAZAIhO21KnymMjxZKxAH4bTPe1abUNcfFj/rwPedQrIFFz/ ywykMNsQ= X-Gm-Gg: AYBFou1yHEkD44KxspV2IJTJd4aXaINeYbN7GxxZpvfMCI5XODpX10kc8jHMmVhAdtF yT86P2s+R/MI6GPElSE7k6FB1QYO6HD4i53KC9EbRrdGM+VJ+YkYiBXqtMRCUeuT5HL2PKzp017 J1CJZ8E/TWnH4vL0Dy3VNta3FYecauJFw+LIYM9nIFACaAX/lQTmQydj5V48ALOaIE1j2HQNjyW fnvIPJdjdVOlhpiDdewGEFw1u3VruvUz7x2qCBmur11U5HGftYB7ORHEMnnFxIHuqaNfIyDoLc7 V5ybw8OxRC60qds/QAKwwsRS7SaCpHSkrYUumYcqk1RvmnQ2AgOikpPqHOmkQ0bwAAUhG4zNTdN D99VYZ+jRP+yJYxkqPodoV1iiz9FzWbyd9D7YkTy8RA7jQFSKUjBjswq6OO7QKMaFtS6ks5wnAe 3eZuzKWJPpjoOw3CJHwzb4UsWHFX7Xy1ZWz7cIny8Fmf7zylDD3FjfAAXNKgC6kKa63CjuEIX7A +zT6iosX0uw7BQ4R+zIe7zKYKnHhC9D0Kg630bo/DFYIEM+TOGMm/EhLDyqjAoIaowbUE0s32l2 CUHDcsi2fXQ898+0if3NhaKtl+wGDsHQRAXz7urdqY1wj78jpDrarM2Qji6D9KT6E6M+0Lb/diD Vma0Nj9VEUvuj5VJl5+R/IdzW5Fe0o8/yWluptn4aiZBLAXZRRu2ValJ29fQTb0tHEHLGUG4u X-Received: by 2002:a17:90b:1c8f:b0:3ac:af8b:6f4 with SMTP id 98e67ed59e1d1-3adeae62b81mr172483a91.37.1792781872071; Fri, 23 Oct 2026 11:57:52 -0700 (PDT) From: Mark Atwood To: pve-devel@lists.proxmox.com Subject: [PATCH common 2/2] ticket: sign RSA tickets with SHA-256 Date: Fri, 23 Oct 2026 11:57:49 -0700 Message-ID: <20261023185749.509347-2-mark@wolfssl.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DMARC_PASS -0.1 DMARC pass policy RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: U5GJ2URD5FS6356WCDKZVRANFD6STWYG X-Message-ID-Hash: U5GJ2URD5FS6356WCDKZVRANFD6STWYG X-MailFrom: mark@wolfssl.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: 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 --- 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