feat: turns out, PayloadEncoding can be multiple values for EIP191
This commit is contained in:
13
common.go
13
common.go
@@ -1,5 +1,7 @@
|
||||
package varsig
|
||||
|
||||
import "fmt"
|
||||
|
||||
// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
|
||||
|
||||
// Ed25519 produces a varsig for EdDSA using Ed25519 curve.
|
||||
@@ -58,7 +60,14 @@ func ES512(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error)
|
||||
|
||||
// EIP191 produces a varsig for ECDSA using the Secp256k1 curve, Keccak256 and encoded
|
||||
// with the "personal_sign" format defined by [EIP191].
|
||||
// payloadEncoding must be either PayloadEncodingEIP191Raw or PayloadEncodingEIP191Cbor.
|
||||
// [EIP191]: https://eips.ethereum.org/EIPS/eip-191
|
||||
func EIP191(opts ...Option) (ECDSAVarsig, error) {
|
||||
return NewECDSAVarsig(CurveSecp256k1, HashKeccak256, PayloadEncodingEIP191, opts...)
|
||||
func EIP191(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error) {
|
||||
switch payloadEncoding {
|
||||
case PayloadEncodingEIP191Raw, PayloadEncodingEIP191Cbor:
|
||||
default:
|
||||
return ECDSAVarsig{}, fmt.Errorf("%w for EIP191: %v", ErrUnsupportedPayloadEncoding, payloadEncoding)
|
||||
}
|
||||
|
||||
return NewECDSAVarsig(CurveSecp256k1, HashKeccak256, payloadEncoding, opts...)
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ func TestRoundTrip(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "EIP191",
|
||||
varsig: must(varsig.EIP191()),
|
||||
dataHex: "3401ec01e7011b91a303",
|
||||
varsig: must(varsig.EIP191(varsig.PayloadEncodingEIP191Raw)),
|
||||
dataHex: "3401ec01e7011b91c3035f",
|
||||
},
|
||||
|
||||
// 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) {
|
||||
varsig: must(varsig.ES256K(varsig.PayloadEncodingVerbatim)),
|
||||
dataBytes: []byte{52, 1, 236, 1, 231, 1, 18, 95},
|
||||
},
|
||||
// the two cases below in iso-ucan are actually EIP191 preset where the encoding is overridden
|
||||
// therefore, we build them manually.
|
||||
{
|
||||
name: "EIP191+RAW",
|
||||
varsig: must(varsig.NewECDSAVarsig(varsig.CurveSecp256k1, varsig.HashKeccak256, varsig.PayloadEncodingVerbatim)),
|
||||
varsig: must(varsig.EIP191(varsig.PayloadEncodingEIP191Raw)),
|
||||
dataBytes: []byte{52, 1, 236, 1, 231, 1, 27, 145, 195, 3, 95},
|
||||
},
|
||||
{
|
||||
name: "EIP191+DAG-CBOR",
|
||||
varsig: must(varsig.NewECDSAVarsig(varsig.CurveSecp256k1, varsig.HashKeccak256, varsig.PayloadEncodingDAGCBOR)),
|
||||
varsig: must(varsig.EIP191(varsig.PayloadEncodingEIP191Cbor)),
|
||||
dataBytes: []byte{52, 1, 236, 1, 231, 1, 27, 145, 195, 3, 113},
|
||||
},
|
||||
} {
|
||||
|
||||
130
constant.go
130
constant.go
@@ -78,73 +78,129 @@ func DecodeHashAlgorithm(r BytesReader) (Hash, error) {
|
||||
// PayloadEncoding specifies the encoding of the data being (hashed and)
|
||||
// signed. A canonical representation of the data is required to produce
|
||||
// consistent hashes and signatures.
|
||||
type PayloadEncoding uint64
|
||||
type PayloadEncoding int
|
||||
|
||||
// Constant values that allow Varsig implementations to specify how the
|
||||
// payload content is encoded before being hashed.
|
||||
// In varsig >= v1, only canonical encoding is allowed.
|
||||
const (
|
||||
PayloadEncodingUnspecified PayloadEncoding = 0x00
|
||||
PayloadEncodingVerbatim PayloadEncoding = 0x5f
|
||||
PayloadEncodingDAGPB = PayloadEncoding(0x70)
|
||||
PayloadEncodingDAGCBOR = PayloadEncoding(0x71)
|
||||
PayloadEncodingDAGJSON = PayloadEncoding(0x0129)
|
||||
PayloadEncodingEIP191 = PayloadEncoding(0xd191)
|
||||
PayloadEncodingJWT PayloadEncoding = 0x6a77
|
||||
PayloadEncodingUnspecified = PayloadEncoding(iota)
|
||||
PayloadEncodingVerbatim
|
||||
PayloadEncodingDAGPB
|
||||
PayloadEncodingDAGCBOR
|
||||
PayloadEncodingDAGJSON
|
||||
PayloadEncodingEIP191Raw
|
||||
PayloadEncodingEIP191Cbor
|
||||
PayloadEncodingJWT
|
||||
)
|
||||
|
||||
const (
|
||||
encodingSegmentVerbatim = uint64(0x5f)
|
||||
encodingSegmentDAGPB = uint64(0x70)
|
||||
encodingSegmentDAGCBOR = uint64(0x71)
|
||||
encodingSegmentDAGJSON = uint64(0x0129)
|
||||
encodingSegmentEIP191 = uint64(0xe191)
|
||||
encodingSegmentJWT = uint64(0x6a77)
|
||||
)
|
||||
|
||||
// DecodePayloadEncoding reads and validates the expected canonical payload
|
||||
// encoding of the data to be signed.
|
||||
func DecodePayloadEncoding(r BytesReader, vers Version) (PayloadEncoding, error) {
|
||||
u, err := binary.ReadUvarint(r)
|
||||
seg1, err := binary.ReadUvarint(r)
|
||||
if err != nil {
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: %w", ErrUnsupportedPayloadEncoding, err)
|
||||
}
|
||||
|
||||
payEnc := PayloadEncoding(u)
|
||||
|
||||
switch vers {
|
||||
case Version0:
|
||||
return decodeEncodingInfoV0(payEnc)
|
||||
switch seg1 {
|
||||
case encodingSegmentVerbatim:
|
||||
return PayloadEncodingVerbatim, nil
|
||||
case encodingSegmentDAGPB:
|
||||
return PayloadEncodingDAGPB, nil
|
||||
case encodingSegmentDAGCBOR:
|
||||
return PayloadEncodingDAGCBOR, nil
|
||||
case encodingSegmentDAGJSON:
|
||||
return PayloadEncodingDAGJSON, nil
|
||||
case encodingSegmentEIP191:
|
||||
seg2, err := binary.ReadUvarint(r)
|
||||
if err != nil {
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: incomplete EIP191 encoding: %w", ErrUnsupportedPayloadEncoding, err)
|
||||
}
|
||||
switch seg2 {
|
||||
case encodingSegmentVerbatim:
|
||||
return PayloadEncodingEIP191Raw, nil
|
||||
case encodingSegmentDAGCBOR:
|
||||
return PayloadEncodingEIP191Cbor, nil
|
||||
default:
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x+%x", ErrUnsupportedPayloadEncoding, vers, seg1, seg2)
|
||||
}
|
||||
case encodingSegmentJWT:
|
||||
return PayloadEncodingJWT, nil
|
||||
default:
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, vers, seg1)
|
||||
}
|
||||
case Version1:
|
||||
return decodeEncodingInfoV1(payEnc)
|
||||
switch seg1 {
|
||||
case encodingSegmentVerbatim:
|
||||
return PayloadEncodingVerbatim, nil
|
||||
case encodingSegmentDAGCBOR:
|
||||
return PayloadEncodingDAGCBOR, nil
|
||||
case encodingSegmentDAGJSON:
|
||||
return PayloadEncodingDAGJSON, nil
|
||||
case encodingSegmentEIP191:
|
||||
seg2, err := binary.ReadUvarint(r)
|
||||
if err != nil {
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: incomplete EIP191 encoding: %w", ErrUnsupportedPayloadEncoding, err)
|
||||
}
|
||||
switch seg2 {
|
||||
case encodingSegmentVerbatim:
|
||||
return PayloadEncodingEIP191Raw, nil
|
||||
case encodingSegmentDAGCBOR:
|
||||
return PayloadEncodingEIP191Cbor, nil
|
||||
default:
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x+%x", ErrUnsupportedPayloadEncoding, vers, seg1, seg2)
|
||||
}
|
||||
default:
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, vers, seg1)
|
||||
}
|
||||
default:
|
||||
return 0, ErrUnsupportedVersion
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ChainAgnostic/varsig#4-payload-encoding
|
||||
func decodeEncodingInfoV0(payEnc PayloadEncoding) (PayloadEncoding, error) {
|
||||
switch payEnc {
|
||||
case PayloadEncodingVerbatim,
|
||||
PayloadEncodingDAGPB,
|
||||
PayloadEncodingDAGCBOR,
|
||||
PayloadEncodingDAGJSON,
|
||||
PayloadEncodingJWT,
|
||||
PayloadEncodingEIP191:
|
||||
return payEnc, nil
|
||||
// EncodePayloadEncoding returns the PayloadEncoding as serialized bytes.
|
||||
// If enc is not a valid PayloadEncoding, this function will panic.
|
||||
func EncodePayloadEncoding(enc PayloadEncoding) []byte {
|
||||
res := make([]byte, 0, 8)
|
||||
switch enc {
|
||||
case PayloadEncodingVerbatim:
|
||||
res = binary.AppendUvarint(res, encodingSegmentVerbatim)
|
||||
case PayloadEncodingDAGPB:
|
||||
res = binary.AppendUvarint(res, encodingSegmentDAGPB)
|
||||
case PayloadEncodingDAGCBOR:
|
||||
res = binary.AppendUvarint(res, encodingSegmentDAGCBOR)
|
||||
case PayloadEncodingDAGJSON:
|
||||
res = binary.AppendUvarint(res, encodingSegmentDAGJSON)
|
||||
case PayloadEncodingEIP191Raw:
|
||||
res = binary.AppendUvarint(res, encodingSegmentEIP191)
|
||||
res = binary.AppendUvarint(res, encodingSegmentVerbatim)
|
||||
case PayloadEncodingEIP191Cbor:
|
||||
res = binary.AppendUvarint(res, encodingSegmentEIP191)
|
||||
res = binary.AppendUvarint(res, encodingSegmentDAGCBOR)
|
||||
case PayloadEncodingJWT:
|
||||
res = binary.AppendUvarint(res, encodingSegmentJWT)
|
||||
default:
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, Version0, payEnc)
|
||||
panic(fmt.Sprintf("invalid encoding: %v", enc))
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/expede/varsig/blob/main/README.md#payload-encoding
|
||||
func decodeEncodingInfoV1(payEnc PayloadEncoding) (PayloadEncoding, error) {
|
||||
switch payEnc {
|
||||
case PayloadEncodingVerbatim,
|
||||
PayloadEncodingDAGCBOR,
|
||||
PayloadEncodingDAGJSON,
|
||||
PayloadEncodingEIP191:
|
||||
return payEnc, nil
|
||||
default:
|
||||
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, Version1, payEnc)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Discriminator is (usually) the value representing the public key type of
|
||||
// the algorithm used to create the signature.
|
||||
//
|
||||
// There is not set list of constants here, nor is there a decode function
|
||||
// There is no set list of constants here, nor is there a decode function
|
||||
// as the author of an implementation should include the constant with the
|
||||
// implementation, and the decoding is handled by the Handler, which uses
|
||||
// the Discriminator to choose the correct implementation. Also note that
|
||||
|
||||
2
ecdsa.go
2
ecdsa.go
@@ -106,7 +106,7 @@ func (v ECDSAVarsig) Encode() []byte {
|
||||
}
|
||||
|
||||
buf = binary.AppendUvarint(buf, uint64(v.hashAlg))
|
||||
buf = binary.AppendUvarint(buf, uint64(v.payEnc))
|
||||
buf = append(buf, EncodePayloadEncoding(v.payEnc)...)
|
||||
buf = append(buf, v.Signature()...)
|
||||
|
||||
return buf
|
||||
|
||||
2
eddsa.go
2
eddsa.go
@@ -103,7 +103,7 @@ func (v EdDSAVarsig) Encode() []byte {
|
||||
}
|
||||
|
||||
buf = binary.AppendUvarint(buf, uint64(v.hashAlg))
|
||||
buf = binary.AppendUvarint(buf, uint64(v.payEnc))
|
||||
buf = append(buf, EncodePayloadEncoding(v.payEnc)...)
|
||||
buf = append(buf, v.Signature()...)
|
||||
|
||||
return buf
|
||||
|
||||
2
rsa.go
2
rsa.go
@@ -51,7 +51,7 @@ func (v RSAVarsig) Encode() []byte {
|
||||
buf := v.encode()
|
||||
buf = binary.AppendUvarint(buf, uint64(v.hashAlg))
|
||||
buf = binary.AppendUvarint(buf, v.sigLen)
|
||||
buf = binary.AppendUvarint(buf, uint64(v.payEnc))
|
||||
buf = append(buf, EncodePayloadEncoding(v.payEnc)...)
|
||||
buf = append(buf, v.Signature()...)
|
||||
|
||||
return buf
|
||||
|
||||
Reference in New Issue
Block a user