Skip to content

Commit 35ef54f

Browse files
committed
feat: turns out, PayloadEncoding can be multiple values for EIP191
1 parent 2f22cb9 commit 35ef54f

File tree

6 files changed

+111
-48
lines changed

6 files changed

+111
-48
lines changed

common.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package varsig
22

3+
import "fmt"
4+
35
// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
46

57
// Ed25519 produces a varsig for EdDSA using Ed25519 curve.
@@ -58,7 +60,14 @@ func ES512(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error)
5860

5961
// EIP191 produces a varsig for ECDSA using the Secp256k1 curve, Keccak256 and encoded
6062
// with the "personal_sign" format defined by [EIP191].
63+
// payloadEncoding must be either PayloadEncodingEIP191Raw or PayloadEncodingEIP191Cbor.
6164
// [EIP191]: https://eips.ethereum.org/EIPS/eip-191
62-
func EIP191(opts ...Option) (ECDSAVarsig, error) {
63-
return NewECDSAVarsig(CurveSecp256k1, HashKeccak256, PayloadEncodingEIP191, opts...)
65+
func EIP191(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error) {
66+
switch payloadEncoding {
67+
case PayloadEncodingEIP191Raw, PayloadEncodingEIP191Cbor:
68+
default:
69+
return ECDSAVarsig{}, fmt.Errorf("%w for EIP191: %v", ErrUnsupportedPayloadEncoding, payloadEncoding)
70+
}
71+
72+
return NewECDSAVarsig(CurveSecp256k1, HashKeccak256, payloadEncoding, opts...)
6473
}

common_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ func TestRoundTrip(t *testing.T) {
6464
},
6565
{
6666
name: "EIP191",
67-
varsig: must(varsig.EIP191()),
68-
dataHex: "3401ec01e7011b91a303",
67+
varsig: must(varsig.EIP191(varsig.PayloadEncodingEIP191Raw)),
68+
dataHex: "3401ec01e7011b91c3035f",
6969
},
7070

7171
// from https://github.com/hugomrdias/iso-repo/blob/main/packages/iso-ucan/test/varsig.test.js
@@ -89,16 +89,14 @@ func TestRoundTrip(t *testing.T) {
8989
varsig: must(varsig.ES256K(varsig.PayloadEncodingVerbatim)),
9090
dataBytes: []byte{52, 1, 236, 1, 231, 1, 18, 95},
9191
},
92-
// the two cases below in iso-ucan are actually EIP191 preset where the encoding is overridden
93-
// therefore, we build them manually.
9492
{
9593
name: "EIP191+RAW",
96-
varsig: must(varsig.NewECDSAVarsig(varsig.CurveSecp256k1, varsig.HashKeccak256, varsig.PayloadEncodingVerbatim)),
94+
varsig: must(varsig.EIP191(varsig.PayloadEncodingEIP191Raw)),
9795
dataBytes: []byte{52, 1, 236, 1, 231, 1, 27, 145, 195, 3, 95},
9896
},
9997
{
10098
name: "EIP191+DAG-CBOR",
101-
varsig: must(varsig.NewECDSAVarsig(varsig.CurveSecp256k1, varsig.HashKeccak256, varsig.PayloadEncodingDAGCBOR)),
99+
varsig: must(varsig.EIP191(varsig.PayloadEncodingEIP191Cbor)),
102100
dataBytes: []byte{52, 1, 236, 1, 231, 1, 27, 145, 195, 3, 113},
103101
},
104102
} {

constant.go

Lines changed: 93 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -78,73 +78,129 @@ func DecodeHashAlgorithm(r BytesReader) (Hash, error) {
7878
// PayloadEncoding specifies the encoding of the data being (hashed and)
7979
// signed. A canonical representation of the data is required to produce
8080
// consistent hashes and signatures.
81-
type PayloadEncoding uint64
81+
type PayloadEncoding int
8282

8383
// Constant values that allow Varsig implementations to specify how the
8484
// payload content is encoded before being hashed.
8585
// In varsig >= v1, only canonical encoding is allowed.
8686
const (
87-
PayloadEncodingUnspecified PayloadEncoding = 0x00
88-
PayloadEncodingVerbatim PayloadEncoding = 0x5f
89-
PayloadEncodingDAGPB = PayloadEncoding(0x70)
90-
PayloadEncodingDAGCBOR = PayloadEncoding(0x71)
91-
PayloadEncodingDAGJSON = PayloadEncoding(0x0129)
92-
PayloadEncodingEIP191 = PayloadEncoding(0xd191)
93-
PayloadEncodingJWT PayloadEncoding = 0x6a77
87+
PayloadEncodingUnspecified = PayloadEncoding(iota)
88+
PayloadEncodingVerbatim
89+
PayloadEncodingDAGPB
90+
PayloadEncodingDAGCBOR
91+
PayloadEncodingDAGJSON
92+
PayloadEncodingEIP191Raw
93+
PayloadEncodingEIP191Cbor
94+
PayloadEncodingJWT
95+
)
96+
97+
const (
98+
encodingSegmentVerbatim = uint64(0x5f)
99+
encodingSegmentDAGPB = uint64(0x70)
100+
encodingSegmentDAGCBOR = uint64(0x71)
101+
encodingSegmentDAGJSON = uint64(0x0129)
102+
encodingSegmentEIP191 = uint64(0xe191)
103+
encodingSegmentJWT = uint64(0x6a77)
94104
)
95105

96106
// DecodePayloadEncoding reads and validates the expected canonical payload
97107
// encoding of the data to be signed.
98108
func DecodePayloadEncoding(r BytesReader, vers Version) (PayloadEncoding, error) {
99-
u, err := binary.ReadUvarint(r)
109+
seg1, err := binary.ReadUvarint(r)
100110
if err != nil {
101111
return PayloadEncodingUnspecified, fmt.Errorf("%w: %w", ErrUnsupportedPayloadEncoding, err)
102112
}
103113

104-
payEnc := PayloadEncoding(u)
105-
106114
switch vers {
107115
case Version0:
108-
return decodeEncodingInfoV0(payEnc)
116+
switch seg1 {
117+
case encodingSegmentVerbatim:
118+
return PayloadEncodingVerbatim, nil
119+
case encodingSegmentDAGPB:
120+
return PayloadEncodingDAGPB, nil
121+
case encodingSegmentDAGCBOR:
122+
return PayloadEncodingDAGCBOR, nil
123+
case encodingSegmentDAGJSON:
124+
return PayloadEncodingDAGJSON, nil
125+
case encodingSegmentEIP191:
126+
seg2, err := binary.ReadUvarint(r)
127+
if err != nil {
128+
return PayloadEncodingUnspecified, fmt.Errorf("%w: incomplete EIP191 encoding: %w", ErrUnsupportedPayloadEncoding, err)
129+
}
130+
switch seg2 {
131+
case encodingSegmentVerbatim:
132+
return PayloadEncodingEIP191Raw, nil
133+
case encodingSegmentDAGCBOR:
134+
return PayloadEncodingEIP191Cbor, nil
135+
default:
136+
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x+%x", ErrUnsupportedPayloadEncoding, vers, seg1, seg2)
137+
}
138+
case encodingSegmentJWT:
139+
return PayloadEncodingJWT, nil
140+
default:
141+
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, vers, seg1)
142+
}
109143
case Version1:
110-
return decodeEncodingInfoV1(payEnc)
144+
switch seg1 {
145+
case encodingSegmentVerbatim:
146+
return PayloadEncodingVerbatim, nil
147+
case encodingSegmentDAGCBOR:
148+
return PayloadEncodingDAGCBOR, nil
149+
case encodingSegmentDAGJSON:
150+
return PayloadEncodingDAGJSON, nil
151+
case encodingSegmentEIP191:
152+
seg2, err := binary.ReadUvarint(r)
153+
if err != nil {
154+
return PayloadEncodingUnspecified, fmt.Errorf("%w: incomplete EIP191 encoding: %w", ErrUnsupportedPayloadEncoding, err)
155+
}
156+
switch seg2 {
157+
case encodingSegmentVerbatim:
158+
return PayloadEncodingEIP191Raw, nil
159+
case encodingSegmentDAGCBOR:
160+
return PayloadEncodingEIP191Cbor, nil
161+
default:
162+
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x+%x", ErrUnsupportedPayloadEncoding, vers, seg1, seg2)
163+
}
164+
default:
165+
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, vers, seg1)
166+
}
111167
default:
112168
return 0, ErrUnsupportedVersion
113169
}
114170
}
115171

116-
// https://github.com/ChainAgnostic/varsig#4-payload-encoding
117-
func decodeEncodingInfoV0(payEnc PayloadEncoding) (PayloadEncoding, error) {
118-
switch payEnc {
119-
case PayloadEncodingVerbatim,
120-
PayloadEncodingDAGPB,
121-
PayloadEncodingDAGCBOR,
122-
PayloadEncodingDAGJSON,
123-
PayloadEncodingJWT,
124-
PayloadEncodingEIP191:
125-
return payEnc, nil
172+
// EncodePayloadEncoding returns the PayloadEncoding as serialized bytes.
173+
// If enc is not a valid PayloadEncoding, this function will panic.
174+
func EncodePayloadEncoding(enc PayloadEncoding) []byte {
175+
res := make([]byte, 0, 8)
176+
switch enc {
177+
case PayloadEncodingVerbatim:
178+
res = binary.AppendUvarint(res, encodingSegmentVerbatim)
179+
case PayloadEncodingDAGPB:
180+
res = binary.AppendUvarint(res, encodingSegmentDAGPB)
181+
case PayloadEncodingDAGCBOR:
182+
res = binary.AppendUvarint(res, encodingSegmentDAGCBOR)
183+
case PayloadEncodingDAGJSON:
184+
res = binary.AppendUvarint(res, encodingSegmentDAGJSON)
185+
case PayloadEncodingEIP191Raw:
186+
res = binary.AppendUvarint(res, encodingSegmentEIP191)
187+
res = binary.AppendUvarint(res, encodingSegmentVerbatim)
188+
case PayloadEncodingEIP191Cbor:
189+
res = binary.AppendUvarint(res, encodingSegmentEIP191)
190+
res = binary.AppendUvarint(res, encodingSegmentDAGCBOR)
191+
case PayloadEncodingJWT:
192+
res = binary.AppendUvarint(res, encodingSegmentJWT)
126193
default:
127-
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, Version0, payEnc)
194+
panic(fmt.Sprintf("invalid encoding: %v", enc))
128195
}
129-
}
130196

131-
// https://github.com/expede/varsig/blob/main/README.md#payload-encoding
132-
func decodeEncodingInfoV1(payEnc PayloadEncoding) (PayloadEncoding, error) {
133-
switch payEnc {
134-
case PayloadEncodingVerbatim,
135-
PayloadEncodingDAGCBOR,
136-
PayloadEncodingDAGJSON,
137-
PayloadEncodingEIP191:
138-
return payEnc, nil
139-
default:
140-
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, Version1, payEnc)
141-
}
197+
return res
142198
}
143199

144200
// Discriminator is (usually) the value representing the public key type of
145201
// the algorithm used to create the signature.
146202
//
147-
// There is not set list of constants here, nor is there a decode function
203+
// There is no set list of constants here, nor is there a decode function
148204
// as the author of an implementation should include the constant with the
149205
// implementation, and the decoding is handled by the Handler, which uses
150206
// the Discriminator to choose the correct implementation. Also note that

ecdsa.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (v ECDSAVarsig) Encode() []byte {
106106
}
107107

108108
buf = binary.AppendUvarint(buf, uint64(v.hashAlg))
109-
buf = binary.AppendUvarint(buf, uint64(v.payEnc))
109+
buf = append(buf, EncodePayloadEncoding(v.payEnc)...)
110110
buf = append(buf, v.Signature()...)
111111

112112
return buf

eddsa.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func (v EdDSAVarsig) Encode() []byte {
103103
}
104104

105105
buf = binary.AppendUvarint(buf, uint64(v.hashAlg))
106-
buf = binary.AppendUvarint(buf, uint64(v.payEnc))
106+
buf = append(buf, EncodePayloadEncoding(v.payEnc)...)
107107
buf = append(buf, v.Signature()...)
108108

109109
return buf

rsa.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (v RSAVarsig) Encode() []byte {
5151
buf := v.encode()
5252
buf = binary.AppendUvarint(buf, uint64(v.hashAlg))
5353
buf = binary.AppendUvarint(buf, v.sigLen)
54-
buf = binary.AppendUvarint(buf, uint64(v.payEnc))
54+
buf = append(buf, EncodePayloadEncoding(v.payEnc)...)
5555
buf = append(buf, v.Signature()...)
5656

5757
return buf

0 commit comments

Comments
 (0)