@@ -7,8 +7,6 @@ SPDX-License-Identifier: Apache-2.0
77package driver
88
99import (
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
2220const (
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
218217func (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.
293287func (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.
307293func (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