Skip to content

Commit e0d372e

Browse files
committed
capabilities/v2/actions/confidentialrelay: Use semver comparison for legacy compute request hash scheme
Use semver parsing to check if a ComputeRequest version is legacy (<= 0.0.6) instead of checking for a single, exact string match. This ensures all historical and legacy versions correctly use the legacy hashing scheme, while newer versions leverage the updated scheme.
1 parent 3bdfe6b commit e0d372e

2 files changed

Lines changed: 59 additions & 10 deletions

File tree

pkg/capabilities/v2/actions/confidentialrelay/computerequest.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package confidentialrelay
33
import (
44
"crypto/sha256"
55

6+
"github.com/Masterminds/semver/v3"
67
"github.com/smartcontractkit/libocr/ragep2p/peeridhelper"
78
)
89

@@ -24,10 +25,10 @@ const signedComputeRequestSignaturePrefix = "CONFIDENTIAL_COMPUTE_PAYLOAD_"
2425

2526
// computeRequestLegacyVersion is vendored from confidential-compute
2627
// types.ServiceConfidentialComputeVersionLegacy. Hash includes the Version field only
27-
// when it equals this value, matching the source (confidential-compute is migrating
28-
// Version out of the hash for newer versions). It MUST stay in sync with the source, or
29-
// ComputeRequest.Hash will diverge from the digest the Workflow DON nodes signed once the
30-
// enclave moves past the legacy version.
28+
// when the request uses the legacy scheme, matching the source (confidential-compute
29+
// is migrating Version out of the hash for newer versions). It MUST stay in sync with
30+
// the source, or ComputeRequest.Hash will diverge from the digest the Workflow DON nodes
31+
// signed once the enclave moves past the legacy version.
3132
const computeRequestLegacyVersion = "0.0.6"
3233

3334
// SignedComputeRequestSignaturePayload reconstructs the exact payload a Workflow DON node
@@ -63,8 +64,8 @@ type ComputeRequest struct {
6364
// reuses this package's length-prefix helpers (writeBytes/writeString/
6465
// writeLengthPrefix), which are identical to the source's writeWithLength/
6566
// writeLengthPrefix. EncryptedDecryptionKeyShares is intentionally excluded, and
66-
// Version is included only for the legacy version, and ApplicationRequestID is
67-
// included only for non-legacy versions, both matching the source.
67+
// Version is included only for the legacy hashing scheme, and ApplicationRequestID
68+
// is included only for non-legacy versions, both matching the source.
6869
func (cr ComputeRequest) Hash() [32]byte {
6970
h := sha256.New()
7071

@@ -89,10 +90,10 @@ func (cr ComputeRequest) Hash() [32]byte {
8990
writeBytes(h, cr.MasterPublicKey)
9091

9192
writeString(h, cr.AppID)
92-
// Version is included in the hash only for the legacy version, matching
93+
// Version is included in the hash only for the legacy scheme, matching
9394
// confidential-compute (which is migrating Version out of the hash). Newer
9495
// versions bind the application-specific request ID instead.
95-
if cr.Version == computeRequestLegacyVersion {
96+
if usesLegacyComputeRequestHash(cr.Version) {
9697
writeString(h, cr.Version)
9798
} else {
9899
writeString(h, cr.ApplicationRequestID)
@@ -103,6 +104,26 @@ func (cr ComputeRequest) Hash() [32]byte {
103104
return result
104105
}
105106

107+
func usesLegacyComputeRequestHash(version string) bool {
108+
c, err := compareComputeRequestVersions(version, computeRequestLegacyVersion)
109+
if err != nil {
110+
return version == computeRequestLegacyVersion
111+
}
112+
return c <= 0
113+
}
114+
115+
func compareComputeRequestVersions(a, b string) (int, error) {
116+
av, err := semver.NewVersion(a)
117+
if err != nil {
118+
return 0, err
119+
}
120+
bv, err := semver.NewVersion(b)
121+
if err != nil {
122+
return 0, err
123+
}
124+
return av.Compare(bv), nil
125+
}
126+
106127
// SignedComputeRequest is vendored from confidential-compute
107128
// types.SignedComputeRequest: a ComputeRequest plus one Workflow DON node's
108129
// signature over ComputeRequest.Hash. The enclave forwards the F+1 signed requests

pkg/capabilities/v2/actions/confidentialrelay/computerequest_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ func TestComputeRequestHash_Deterministic(t *testing.T) {
2828
require.Equal(t, sampleComputeRequest().Hash(), sampleComputeRequest().Hash())
2929
}
3030

31+
func TestUsesLegacyComputeRequestHash(t *testing.T) {
32+
for _, v := range []string{computeRequestLegacyVersion, "0.0.1", "0.0.5"} {
33+
require.True(t, usesLegacyComputeRequestHash(v), "version %q should use the legacy hashing scheme", v)
34+
}
35+
36+
for _, v := range []string{"0.0.7", "0.1.0", "1.2.3"} {
37+
require.False(t, usesLegacyComputeRequestHash(v), "version %q should use the current hashing scheme", v)
38+
}
39+
40+
for _, v := range []string{"", "not-a-version", "1.x"} {
41+
require.False(t, usesLegacyComputeRequestHash(v), "unparseable version %q degrades to the current scheme", v)
42+
}
43+
}
44+
3145
// Every field the source binds must change the hash. (Conformance with
3246
// confidential-compute's source Hash is enforced by a test in that repo, which can
3347
// import this package; chainlink-common cannot import confidential-compute.)
@@ -61,9 +75,9 @@ func TestComputeRequestHash_IgnoresEncryptedShares(t *testing.T) {
6175
require.Equal(t, sampleComputeRequest().Hash(), withShares.Hash())
6276
}
6377

64-
// Version is hashed only for the legacy version, matching confidential-compute (which is
78+
// Version is hashed for the legacy scheme, matching confidential-compute (which is
6579
// migrating Version out of the hash). Non-legacy versions are excluded, so different
66-
// non-legacy versions hash identically, while the legacy version is bound.
80+
// non-legacy versions hash identically, while legacy-scheme versions are bound.
6781
func TestComputeRequestHash_VersionOnlyHashedForLegacy(t *testing.T) {
6882
nonLegacyA := sampleComputeRequest()
6983
nonLegacyA.Version = "0.0.7"
@@ -74,6 +88,12 @@ func TestComputeRequestHash_VersionOnlyHashedForLegacy(t *testing.T) {
7488
legacy := sampleComputeRequest()
7589
legacy.Version = computeRequestLegacyVersion
7690
require.NotEqual(t, legacy.Hash(), nonLegacyA.Hash(), "legacy Version must be bound into the hash")
91+
92+
olderLegacyA := sampleComputeRequest()
93+
olderLegacyA.Version = "0.0.5"
94+
olderLegacyB := sampleComputeRequest()
95+
olderLegacyB.Version = "0.0.4"
96+
require.NotEqual(t, olderLegacyA.Hash(), olderLegacyB.Hash(), "versions at or below legacy must be bound into the hash")
7797
}
7898

7999
// ApplicationRequestID is the post-legacy replacement for binding application-level
@@ -85,6 +105,14 @@ func TestComputeRequestHash_ApplicationRequestIDOnlyHashedForNonLegacy(t *testin
85105
legacyB.ApplicationRequestID = "exec-b"
86106
require.Equal(t, legacyA.Hash(), legacyB.Hash(), "legacy ApplicationRequestID must not affect the hash")
87107

108+
olderLegacyA := sampleComputeRequest()
109+
olderLegacyA.Version = "0.0.5"
110+
olderLegacyA.ApplicationRequestID = "exec-a"
111+
olderLegacyB := sampleComputeRequest()
112+
olderLegacyB.Version = "0.0.5"
113+
olderLegacyB.ApplicationRequestID = "exec-b"
114+
require.Equal(t, olderLegacyA.Hash(), olderLegacyB.Hash(), "legacy-scheme ApplicationRequestID must not affect the hash")
115+
88116
nonLegacyA := sampleComputeRequest()
89117
nonLegacyA.Version = "0.0.7"
90118
nonLegacyA.ApplicationRequestID = "exec-a"

0 commit comments

Comments
 (0)