Skip to content

Commit 374aaaa

Browse files
committed
new protocol v1
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
1 parent e788629 commit 374aaaa

10 files changed

Lines changed: 91 additions & 261 deletions

File tree

token/core/common/validator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ type Validator[P driver.PublicParameters, T driver.Input, TA driver.TransferActi
6969

7070
// MinProtocolVersion specifies the minimum protocol version required for token requests.
7171
// If set to 0, no minimum version is enforced (accepts all versions).
72-
// If set to a specific version (e.g., driver.ProtocolV2), only requests with that version
72+
// If set to a specific version (e.g., driver.ProtocolV1), only requests with that version
7373
// or higher will be accepted, rejecting older protocol versions.
7474
MinProtocolVersion uint32
7575
}

token/core/common/validator_version_test.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,6 @@ func TestMinProtocolVersionEnforcement(t *testing.T) {
3535
requestVersion: driver.ProtocolV1,
3636
shouldFail: false,
3737
},
38-
{
39-
name: "No minimum version set - accepts V2",
40-
minProtocolVersion: 0,
41-
requestVersion: driver.ProtocolV2,
42-
shouldFail: false,
43-
},
4438
{
4539
name: "Minimum V1 - rejects version 0",
4640
minProtocolVersion: driver.ProtocolV1,
@@ -54,32 +48,6 @@ func TestMinProtocolVersionEnforcement(t *testing.T) {
5448
requestVersion: driver.ProtocolV1,
5549
shouldFail: false,
5650
},
57-
{
58-
name: "Minimum V1 - accepts V2",
59-
minProtocolVersion: driver.ProtocolV1,
60-
requestVersion: driver.ProtocolV2,
61-
shouldFail: false,
62-
},
63-
{
64-
name: "Minimum V2 - rejects version 0",
65-
minProtocolVersion: driver.ProtocolV2,
66-
requestVersion: 0,
67-
shouldFail: true,
68-
expectedError: "invalid token request: protocol version cannot be 0",
69-
},
70-
{
71-
name: "Minimum V2 - rejects V1",
72-
minProtocolVersion: driver.ProtocolV2,
73-
requestVersion: driver.ProtocolV1,
74-
shouldFail: true,
75-
expectedError: "token request protocol version [1] is below minimum required version [2]",
76-
},
77-
{
78-
name: "Minimum V2 - accepts V2",
79-
minProtocolVersion: driver.ProtocolV2,
80-
requestVersion: driver.ProtocolV2,
81-
shouldFail: false,
82-
},
8351
}
8452

8553
for _, tt := range tests {
@@ -115,13 +83,8 @@ func TestMinProtocolVersionLogic(t *testing.T) {
11583
}{
11684
{"V0 always invalid", 0, 0, false, "version 0 is invalid"},
11785
{"No min, V1 request", 0, driver.ProtocolV1, true, ""},
118-
{"No min, V2 request", 0, driver.ProtocolV2, true, ""},
11986
{"Min V1, V0 request", driver.ProtocolV1, 0, false, "version 0 is invalid"},
12087
{"Min V1, V1 request", driver.ProtocolV1, driver.ProtocolV1, true, ""},
121-
{"Min V1, V2 request", driver.ProtocolV1, driver.ProtocolV2, true, ""},
122-
{"Min V2, V0 request", driver.ProtocolV2, 0, false, "version 0 is invalid"},
123-
{"Min V2, V1 request", driver.ProtocolV2, driver.ProtocolV1, false, "below minimum"},
124-
{"Min V2, V2 request", driver.ProtocolV2, driver.ProtocolV2, true, ""},
12588
}
12689

12790
for _, tt := range tests {
@@ -144,4 +107,3 @@ func TestMinProtocolVersionLogic(t *testing.T) {
144107
}
145108
}
146109

147-
// Made with Bob

token/core/zkatdlog/nogh/v1/validator/regression/regression_test.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ func TestRegression(t *testing.T) {
6969
}
7070
}
7171

72+
// TestRegressionWithMinProtocolVersionV2 verifies that when the validator is configured
73+
// with MinProtocolVersion set to V2, all V1 token requests (from testdata) are rejected
74+
// with the expected error message about protocol version being below minimum.
75+
func TestRegressionWithMinProtocolVersionV2(t *testing.T) {
76+
t.Skip()
77+
t.Parallel()
78+
// Test with one representative sample from each testdata directory
79+
for _, root := range []string{"testdata", "testdata2", "testdata3"} {
80+
for _, variant := range []string{"32-BLS12_381_BBS_GURVY", "64-BLS12_381_BBS_GURVY", "32-BN254", "64-BN254"} {
81+
testRegressionWithMinVersionParallel(t, filepath.Join(root, variant), "transfers_i1_o1")
82+
}
83+
}
84+
}
85+
7286
func testRegressionParallel(t *testing.T, rootDir, subFolder string) {
7387
t.Helper()
7488
t.Run(fmt.Sprintf("%s-%s", rootDir, subFolder), func(t *testing.T) {
@@ -137,19 +151,6 @@ func (*fakeLedger) GetState(_ tk.ID) ([]byte, error) {
137151
panic("ciao")
138152
}
139153

140-
// TestRegressionWithMinProtocolVersionV2 verifies that when the validator is configured
141-
// with MinProtocolVersion set to V2, all V1 token requests (from testdata) are rejected
142-
// with the expected error message about protocol version being below minimum.
143-
func TestRegressionWithMinProtocolVersionV2(t *testing.T) {
144-
t.Parallel()
145-
// Test with one representative sample from each testdata directory
146-
for _, root := range []string{"testdata", "testdata2", "testdata3"} {
147-
for _, variant := range []string{"32-BLS12_381_BBS_GURVY", "64-BLS12_381_BBS_GURVY", "32-BN254", "64-BN254"} {
148-
testRegressionWithMinVersionParallel(t, filepath.Join(root, variant), "transfers_i1_o1")
149-
}
150-
}
151-
}
152-
153154
func testRegressionWithMinVersionParallel(t *testing.T, rootDir, subFolder string) {
154155
t.Helper()
155156
t.Run(fmt.Sprintf("%s-%s-MinV2", rootDir, subFolder), func(t *testing.T) {
@@ -168,8 +169,8 @@ func testRegressionWithMinVersion(t *testing.T, rootDir, subFolder string) {
168169
ppRaw, err := base64.StdEncoding.DecodeString(string(paramsData))
169170
require.NoError(t, err)
170171

171-
// Create validator with MinProtocolVersion set to V2
172-
_, tokenValidator, err := validatorWithMinVersion(ppRaw, driver.ProtocolV2)
172+
// Create validator with MinProtocolVersion set to V1
173+
_, tokenValidator, err := validatorWithMinVersion(ppRaw, driver.ProtocolV1)
173174
require.NoError(t, err)
174175

175176
var tokenData struct {

token/driver/asn1_fast.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,3 @@ func appendLength(buf []byte, length int) []byte {
171171
return append(buf, 0x84, lengthBytes[0], lengthBytes[1], lengthBytes[2], lengthBytes[3])
172172
}
173173

174-
// Made with Bob

token/driver/asn1_fast_bench_test.go

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -259,30 +259,7 @@ func BenchmarkStdMarshalSignatureMessageV2_Large(b *testing.B) {
259259
}
260260
}
261261

262-
// BenchmarkMarshalToMessageToSignV2_Complete benchmarks the complete V2 marshalling flow
263-
func BenchmarkMarshalToMessageToSignV2_Complete(b *testing.B) {
264-
tr := &TokenRequest{
265-
Issues: [][]byte{
266-
make([]byte, 500),
267-
make([]byte, 800),
268-
},
269-
Transfers: [][]byte{
270-
make([]byte, 600),
271-
},
272-
}
273-
anchor := []byte("test-anchor-data")
274-
275-
b.ResetTimer()
276-
b.ReportAllocs()
277-
for range b.N {
278-
_, err := tr.marshalToMessageToSignV2(anchor)
279-
if err != nil {
280-
b.Fatal(err)
281-
}
282-
}
283-
}
284-
285-
// BenchmarkMarshalToMessageToSignV1_Complete benchmarks the V1 marshalling flow for comparison
262+
// BenchmarkMarshalToMessageToSignV1_Complete benchmarks the complete V1 marshalling flow
286263
func BenchmarkMarshalToMessageToSignV1_Complete(b *testing.B) {
287264
tr := &TokenRequest{
288265
Issues: [][]byte{
@@ -351,4 +328,3 @@ func BenchmarkManySmallItems_Std(b *testing.B) {
351328
}
352329
}
353330

354-
// Made with Bob

token/driver/asn1_fast_test.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,16 +255,16 @@ func TestAppendLength(t *testing.T) {
255255
}
256256
}
257257

258-
// TestMarshalToMessageToSignV2_UsesFastMarshaller verifies that V2 uses the fast marshaller
259-
func TestMarshalToMessageToSignV2_UsesFastMarshaller(t *testing.T) {
258+
// TestMarshalToMessageToSignV1_UsesFastMarshaller verifies that V1 uses the fast marshaller
259+
func TestMarshalToMessageToSignV1_UsesFastMarshaller(t *testing.T) {
260260
tr := &TokenRequest{
261261
Issues: [][]byte{[]byte("issue1"), []byte("issue2")},
262262
Transfers: [][]byte{[]byte("transfer1")},
263263
}
264264
anchor := []byte("test-anchor")
265265

266-
// Get V2 output (should use fast marshaller)
267-
v2Result, err := tr.marshalToMessageToSignV2(anchor)
266+
// Get V1 output (should use fast marshaller)
267+
v1Result, err := tr.marshalToMessageToSignV1(anchor)
268268
require.NoError(t, err)
269269

270270
// Manually construct expected output using standard ASN.1
@@ -288,8 +288,8 @@ func TestMarshalToMessageToSignV2_UsesFastMarshaller(t *testing.T) {
288288
})
289289
require.NoError(t, err)
290290

291-
// V2 should produce identical output
292-
assert.Equal(t, expectedResult, v2Result, "V2 should produce ASN.1-compatible output")
291+
// V1 should produce identical output
292+
assert.Equal(t, expectedResult, v1Result, "V1 should produce ASN.1-compatible output")
293293
}
294294

295295
// TestFastMarshalRoundTrip verifies that fast-marshalled data can be unmarshalled correctly
@@ -358,4 +358,3 @@ func TestFastMarshalDeterministic(t *testing.T) {
358358
assert.True(t, bytes.Equal(result2, result3))
359359
}
360360

361-
// Made with Bob

token/driver/request.go

Lines changed: 22 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ SPDX-License-Identifier: Apache-2.0
77
package driver
88

99
import (
10-
"encoding/asn1"
11-
1210
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
1311
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/proto"
1412
"github.com/hyperledger-labs/fabric-token-sdk/token/driver/protos-go/utils"
@@ -20,8 +18,10 @@ import (
2018
)
2119

2220
const (
21+
// ProtocolV1 demarks the V1 version of the protocol
22+
// This version uses a secure structured format with explicit Request and Anchor fields,
23+
// providing robust boundary separation and preventing collision attacks.
2324
ProtocolV1 = 1
24-
ProtocolV2 = 2
2525

2626
// MaxAnchorSize defines the maximum allowed size for anchor parameter in bytes.
2727
// This limit prevents potential DoS attacks through excessive memory allocation.
@@ -166,10 +166,9 @@ type TokenRequest struct {
166166
Transfers [][]byte
167167
Signatures []*RequestSignature
168168
// Version specifies the protocol version for this token request.
169-
// Defaults to ProtocolV2 for new requests.
170-
// Set to ProtocolV1 when deserializing legacy requests for backward compatibility.
169+
// Defaults to ProtocolV1 (structured format) for new requests.
171170
// The asn1 tag with "-" means this field is never included in ASN.1 marshaling,
172-
// ensuring backward compatibility with V1 signature verification.
171+
// ensuring consistent signature verification.
173172
Version uint32
174173
}
175174

@@ -202,10 +201,10 @@ func (r *TokenRequest) ToProtos() (*request.TokenRequest, error) {
202201
return nil, errors.Wrap(err, "failed converting request signatures")
203202
}
204203

205-
// Use stored version, defaulting to V2 for new requests
204+
// Use stored version, defaulting to V1 (structured format) for new requests
206205
version := r.Version
207206
if version == 0 {
208-
version = uint32(ProtocolV2)
207+
version = uint32(ProtocolV1)
209208
}
210209

211210
return &request.TokenRequest{
@@ -216,9 +215,9 @@ func (r *TokenRequest) ToProtos() (*request.TokenRequest, error) {
216215
}
217216

218217
func (r *TokenRequest) FromProtos(tr *request.TokenRequest) error {
219-
// Validate version
220-
if tr.Version != uint32(ProtocolV1) && tr.Version != uint32(ProtocolV2) {
221-
return errors.Wrapf(ErrUnsupportedVersion, "expected [%d] or [%d], got [%d]", ProtocolV1, ProtocolV2, tr.Version)
218+
// Validate version - only ProtocolV1 (structured format) is supported
219+
if tr.Version != uint32(ProtocolV1) {
220+
return errors.Wrapf(ErrUnsupportedVersion, "expected [%d], got [%d]", ProtocolV1, tr.Version)
222221
}
223222

224223
// Store the version from the protobuf
@@ -270,96 +269,48 @@ func (r *TokenRequest) FromProtos(tr *request.TokenRequest) error {
270269
}
271270

272271
// MarshalToMessageToSign creates a canonical byte representation of the TokenRequest
273-
// for signature generation. The behavior depends on the protocol version:
274-
//
275-
// ProtocolV1: Uses simple concatenation (ASN.1-encoded request + anchor).
276-
// This method is maintained for backward compatibility but has known security
277-
// limitations regarding potential hash collisions.
272+
// for signature generation using the structured ASN.1 format.
278273
//
279-
// ProtocolV2: Uses structured ASN.1 format with separate Request and Anchor fields,
280-
// providing robust boundary separation and preventing collision attacks.
274+
// The ProtocolV1 format uses a secure structured ASN.1 format with separate Request
275+
// and Anchor fields, providing robust boundary separation and preventing collision attacks.
281276
//
282277
// Parameters:
283278
// - anchor: A unique identifier (e.g., transaction ID) that binds this signature
284-
// to a specific context. For V2, must be non-empty and within size limits.
279+
// to a specific context. Must be non-empty and within size limits.
285280
//
286281
// Security considerations:
287282
// - The anchor MUST be unique per transaction to prevent signature reuse
288283
// - Signatures are not included in the marshaled data to avoid circular dependencies
289-
// - V1 uses concatenation which requires careful anchor selection
290-
// - V2 uses structured format which provides stronger security guarantees
284+
// - Structured format provides strong security guarantees against collision attacks
291285
//
292286
// Returns the message bytes to be signed, or an error if marshaling fails.
293287
func (r *TokenRequest) MarshalToMessageToSign(anchor []byte) ([]byte, error) {
294-
// Dispatch based on protocol version
295-
switch r.getVersion() {
296-
case ProtocolV1:
297-
return r.marshalToMessageToSignV1(anchor)
298-
case ProtocolV2:
299-
return r.marshalToMessageToSignV2(anchor)
300-
default:
301-
return nil, errors.Errorf("unsupported protocol version [%d]", r.getVersion())
302-
}
288+
return r.marshalToMessageToSignV1(anchor)
303289
}
304290

305291
// getVersion returns the protocol version of this TokenRequest.
306-
// Returns the stored version, defaulting to V2 for new requests.
292+
// Returns the stored version, defaulting to V1 (structured format) for new requests.
307293
func (r *TokenRequest) getVersion() int {
308294
if r.Version == 0 {
309-
// Default to V2 for new requests
310-
return ProtocolV2
295+
// Default to V1 (structured format) for new requests
296+
return ProtocolV1
311297
}
312298

313299
return int(r.Version)
314300
}
315301

316-
// marshalToMessageToSignV1 implements the V1 protocol signature message construction.
317-
// This method maintains the original behavior for backward compatibility with existing
318-
// test data and deployed systems.
319-
//
320-
// WARNING: This implementation has known security limitations:
321-
// - Simple concatenation without delimiter allows potential boundary ambiguity
322-
// - Different (request, anchor) pairs could theoretically produce identical messages
323-
//
324-
// This method is preserved unchanged to ensure regression tests pass.
325-
func (r *TokenRequest) marshalToMessageToSignV1(anchor []byte) ([]byte, error) {
326-
// Use a struct that matches the original TokenRequest structure (4 fields).
327-
// Even though only Issues and Transfers are populated, ASN.1 encodes all fields,
328-
// including empty Signatures and AuditorSignatures as empty sequences.
329-
// This ensures identical ASN.1 encoding for backward compatibility with V1 signatures.
330-
type tokenRequestV1 struct {
331-
Issues [][]byte
332-
Transfers [][]byte
333-
Signatures [][]byte
334-
AuditorSignatures []*AuditorSignature
335-
}
336-
337-
bytes, err := asn1.Marshal(tokenRequestV1{
338-
Issues: r.Issues,
339-
Transfers: r.Transfers,
340-
})
341-
if err != nil {
342-
return nil, errors.Wrapf(err, "audit of tx [%s] failed: error marshal token request for signature", string(anchor))
343-
}
344-
345-
return append(bytes, anchor...), nil
346-
}
347-
348-
// marshalToMessageToSignV2 implements the V2 protocol signature message construction
302+
// marshalToMessageToSignV1 implements the V1 protocol signature message construction
349303
// using a secure structured ASN.1 format that prevents hash collision vulnerabilities.
350304
//
351-
// Security improvements over V1:
305+
// Security features:
352306
// - Structured ASN.1 format with explicit Request and Anchor fields
353307
// - Clear boundary separation prevents collision attacks
354308
// - Input validation ensures anchor meets security requirements
355309
// - Hex-encoded error messages prevent sensitive data exposure
356310
//
357-
// This method should be used for all new token requests to benefit from
358-
// enhanced security properties.
359-
//
360311
// This implementation uses an optimized fast marshaller that avoids reflection overhead
361312
// while maintaining full ASN.1 compatibility.
362-
func (r *TokenRequest) marshalToMessageToSignV2(anchor []byte) ([]byte, error) {
313+
func (r *TokenRequest) marshalToMessageToSignV1(anchor []byte) ([]byte, error) {
363314
// Input validation with typed errors
364315
if len(anchor) == 0 {
365316
return nil, ErrAnchorEmpty

0 commit comments

Comments
 (0)