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 D8B2D1FF0E1 for ; Mon, 27 Jul 2026 09:30:35 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 5E5632134B; Mon, 27 Jul 2026 09:30:35 +0200 (CEST) From: Thomas Ellmenreich To: pve-devel@lists.proxmox.com Subject: [PATCH proxmox-acme v2] fix #7749: always serialize in same order Date: Mon, 27 Jul 2026 09:29:58 +0200 Message-ID: <20260727072958.37999-1-t.ellmenreich@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1785137366170 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.110 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_LOW -0.7 Sender listed at https://www.dnswl.org/, low 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: VR4LVEYJKWZU2ZKKNE7TQ5DKKFA5LVYB X-Message-ID-Hash: VR4LVEYJKWZU2ZKKNE7TQ5DKKFA5LVYB X-MailFrom: t.ellmenreich@proxmox.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 CC: Thomas Ellmenreich , Elias Huhsovitz X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: All objects are now serialized with "canonical => 1" so that the resulting bytes are directly comparable. Perls randomized hash key orders lead to different serialization outputs even with the same input. The 'tojs' function also now explicitly overrides the utf8 and canonical options to make sure that they are always set. Signed-off-by: Thomas Ellmenreich Reviewed-by: Elias Huhsovitz --- Serializing acme responses to json, because of perls randomized hash key order, would lead to unequal serialized responses if they were compared as bytes. By setting "canonical => 1" the serializations now always have the same order and can thus be compared bytewise. I have tested the patch against a local instance of HashiCorp Vault PKI and the EAB registration works. It seems that "canonical" can have performance implications when serialising larger objects, but since the objects produced in our acme client are relatively small, I consider this acceptable. src/PVE/ACME.pm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm index e6fb9c2..fb93eca 100644 --- a/src/PVE/ACME.pm +++ b/src/PVE/ACME.pm @@ -64,9 +64,13 @@ sub encode($) { # acme requires 'base64url' encoding return encode_base64url($_[0]); } -sub tojs($;%) { # shortcut for to_json with utf8=>1 - my ($data, %data) = @_; - return to_json($data, { utf8 => 1, %data }); +sub tojs($;%) { # shortcut for to_json with utf8=>1 and canonical=>1 + my ($data, %opts) = @_; + + $opts{utf8} = 1; + $opts{canonical} = 1; + + return to_json($data, %opts); } sub fromjs($) { @@ -155,7 +159,7 @@ sub save { } # pretty => 1 for readability # canonical => 1 to reduce churn - file_set_contents($self->{path}, tojs($o, pretty => 1, canonical => 1)); + file_set_contents($self->{path}, tojs($o, pretty => 1)); } # Load serialized account JSON file into $self @@ -196,7 +200,7 @@ sub jwk { sub jwk_thumbprint { my ($self) = @_; my $jwk = $self->jwk(1); # $pure = 1 - return encode(sha256(tojs($jwk, canonical => 1))); # canonical sorts + return encode(sha256(tojs($jwk))); } # A key authorization string in acme is a challenge token dot-connected with -- 2.47.3