From 2f22cb9b152e6acab5f0fe4b2351b4353b4b6f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 23 Jul 2025 10:58:51 +0200 Subject: [PATCH] feat(test): add more tests for the presets, and compat with iso-ucan --- common.go | 61 +++++++++++----- common_test.go | 191 +++++++++++++++++++++++++++++++++++-------------- varsig_test.go | 26 ------- 3 files changed, 178 insertions(+), 100 deletions(-) diff --git a/common.go b/common.go index 4422896..9a66f8e 100644 --- a/common.go +++ b/common.go @@ -1,41 +1,64 @@ package varsig -// Ed25519 produces a varsig that describes the associated algorithm defined -// by the [IANA JOSE specification]. -// // [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms + +// Ed25519 produces a varsig for EdDSA using Ed25519 curve. +// This algorithm is defined in [IANA JOSE specification]. func Ed25519(payloadEncoding PayloadEncoding, opts ...Option) (EdDSAVarsig, error) { return NewEdDSAVarsig(CurveEd25519, HashSha2_512, payloadEncoding, opts...) } -// Ed448 produces a varsig that describes the associated algorithm defined -// by the [IANA JOSE specification]. -// -// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms +// Ed448 produces a varsig for EdDSA using Ed448 curve. +// This algorithm is defined in [IANA JOSE specification]. func Ed448(payloadEncoding PayloadEncoding, opts ...Option) (EdDSAVarsig, error) { return NewEdDSAVarsig(CurveEd448, HashShake_256, payloadEncoding, opts...) } -// RS256 produces a varsig that describes the associated algorithm defined -// by the [IANA JOSE specification]. -// -// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms +// RS256 produces a varsig for RSASSA-PKCS1-v1_5 using SHA-256. +// This algorithm is defined in [IANA JOSE specification]. func RS256(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) { return NewRSAVarsig(HashSha2_256, keyLength, payloadEncoding, opts...) } -// RS384 produces a varsig that describes the associated algorithm defined -// by the [IANA JOSE specification]. -// -// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms +// RS384 produces a varsig for RSASSA-PKCS1-v1_5 using SHA-384. +// This algorithm is defined in [IANA JOSE specification]. func RS384(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) { return NewRSAVarsig(HashSha2_384, keyLength, payloadEncoding, opts...) } -// RS512 produces a varsig that describes the associated algorithm defined -// by the [IANA JOSE specification]. -// -// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms +// RS512 produces a varsig for RSASSA-PKCS1-v1_5 using SHA-512. +// This algorithm is defined in [IANA JOSE specification]. func RS512(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) { return NewRSAVarsig(HashSha2_512, keyLength, payloadEncoding, opts...) } + +// ES256 produces a varsig for ECDSA using P-256 and SHA-256. +// This algorithm is defined in [IANA JOSE specification]. +func ES256(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error) { + return NewECDSAVarsig(CurveP256, HashSha2_256, payloadEncoding, opts...) +} + +// ES256K produces a varsig for ECDSA using secp256k1 curve and SHA-256. +// This algorithm is defined in [IANA JOSE specification]. +func ES256K(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error) { + return NewECDSAVarsig(CurveSecp256k1, HashSha2_256, payloadEncoding, opts...) +} + +// ES384 produces a varsig for ECDSA using P-384 and SHA-384. +// This algorithm is defined in [IANA JOSE specification]. +func ES384(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error) { + return NewECDSAVarsig(CurveP384, HashSha2_384, payloadEncoding, opts...) +} + +// ES512 produces a varsig for ECDSA using P-521 and SHA-512. +// This algorithm is defined in [IANA JOSE specification]. +func ES512(payloadEncoding PayloadEncoding, opts ...Option) (ECDSAVarsig, error) { + return NewECDSAVarsig(CurveP521, HashSha2_512, payloadEncoding, opts...) +} + +// EIP191 produces a varsig for ECDSA using the Secp256k1 curve, Keccak256 and encoded +// with the "personal_sign" format defined by [EIP191]. +// [EIP191]: https://eips.ethereum.org/EIPS/eip-191 +func EIP191(opts ...Option) (ECDSAVarsig, error) { + return NewECDSAVarsig(CurveSecp256k1, HashKeccak256, PayloadEncodingEIP191, opts...) +} diff --git a/common_test.go b/common_test.go index 31f5955..1ff6f08 100644 --- a/common_test.go +++ b/common_test.go @@ -1,68 +1,149 @@ package varsig_test import ( + "encoding/hex" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/ucan-wg/go-varsig" ) -func TestEd25519(t *testing.T) { - t.Parallel() +func TestRoundTrip(t *testing.T) { + for _, tc := range []struct { + name string + varsig varsig.Varsig + dataHex string + dataBytes []byte + }{ + // Arbitrary use of presets + { + name: "Ed25519", + varsig: must(varsig.Ed25519(varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401ed01ed011371", + }, + { + name: "Ed448", + varsig: must(varsig.Ed448(varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401ed0183241971", + }, + { + name: "RS256", + varsig: must(varsig.RS256(0x100, varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401852412800271", + }, + { + name: "RS384", + varsig: must(varsig.RS384(0x100, varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401852420800271", + }, + { + name: "RS512", + varsig: must(varsig.RS512(0x100, varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401852413800271", + }, + { + name: "ES256", + varsig: must(varsig.ES256(varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401ec0180241271", + }, + { + name: "ES256K", + varsig: must(varsig.ES256K(varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401ec01e7011271", + }, + { + name: "ES384", + varsig: must(varsig.ES384(varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401ec0181242071", + }, + { + name: "ES512", + varsig: must(varsig.ES512(varsig.PayloadEncodingDAGCBOR)), + dataHex: "3401ec0182241371", + }, + { + name: "EIP191", + varsig: must(varsig.EIP191()), + dataHex: "3401ec01e7011b91a303", + }, - in, err := varsig.Ed25519(varsig.PayloadEncodingDAGCBOR) - mustVarsig(t, in, err) - out := roundTrip(t, in, "3401ed01ed011371") - assertEdDSAEqual(t, in, out) + // from https://github.com/hugomrdias/iso-repo/blob/main/packages/iso-ucan/test/varsig.test.js + { + name: "RS256+RAW", + varsig: must(varsig.RS256(256, varsig.PayloadEncodingVerbatim)), + dataBytes: []byte{52, 1, 133, 36, 18, 128, 2, 95}, + }, + { + name: "ES256+RAW", + varsig: must(varsig.ES256(varsig.PayloadEncodingVerbatim)), + dataBytes: []byte{52, 1, 236, 1, 128, 36, 18, 95}, + }, + { + name: "ES512+RAW", + varsig: must(varsig.ES512(varsig.PayloadEncodingVerbatim)), + dataBytes: []byte{52, 1, 236, 1, 130, 36, 19, 95}, + }, + { + name: "ES256K+RAW", + 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)), + 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)), + dataBytes: []byte{52, 1, 236, 1, 231, 1, 27, 145, 195, 3, 113}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + // round-trip encode and back + data := tc.varsig.Encode() + + if tc.dataBytes != nil { + require.Equal(t, tc.dataBytes, data) + } + if tc.dataHex != "" { + require.Equal(t, tc.dataHex, hex.EncodeToString(data)) + } + + rt, err := varsig.Decode(data) + require.NoError(t, err) + + require.Equal(t, tc.varsig.Version(), rt.Version()) + require.Equal(t, tc.varsig.Discriminator(), rt.Discriminator()) + require.Equal(t, tc.varsig.PayloadEncoding(), rt.PayloadEncoding()) + require.Equal(t, tc.varsig.Signature(), rt.Signature()) + + switch vs := tc.varsig.(type) { + case varsig.EdDSAVarsig: + rt := rt.(varsig.EdDSAVarsig) + require.Equal(t, vs.Curve(), rt.Curve()) + require.Equal(t, vs.Hash(), rt.Hash()) + case varsig.ECDSAVarsig: + rt := rt.(varsig.ECDSAVarsig) + require.Equal(t, vs.Curve(), rt.Curve()) + require.Equal(t, vs.Hash(), rt.Hash()) + case varsig.RSAVarsig: + rt := rt.(varsig.RSAVarsig) + require.Equal(t, vs.Hash(), rt.Hash()) + require.Equal(t, vs.KeyLength(), rt.KeyLength()) + default: + t.Fatalf("unexpected varsig type: %T", vs) + } + }) + } } -func TestEd448(t *testing.T) { - t.Parallel() - - in, err := varsig.Ed448(varsig.PayloadEncodingDAGCBOR) - mustVarsig(t, in, err) - out := roundTrip(t, in, "3401ed0183241971") - assertEdDSAEqual(t, in, out) -} - -func TestRS256(t *testing.T) { - t.Parallel() - - in, err := varsig.RS256(0x100, varsig.PayloadEncodingDAGCBOR) - mustVarsig(t, in, err) - out := roundTrip(t, in, "3401852412800271") - assertRSAEqual(t, in, out) -} - -func TestRS384(t *testing.T) { - t.Parallel() - - in, err := varsig.RS384(0x100, varsig.PayloadEncodingDAGCBOR) - mustVarsig(t, in, err) - out := roundTrip(t, in, "3401852420800271") - assertRSAEqual(t, in, out) -} - -func TestRS512(t *testing.T) { - t.Parallel() - - in, err := varsig.RS512(0x100, varsig.PayloadEncodingDAGCBOR) - mustVarsig(t, in, err) - out := roundTrip(t, in, "3401852413800271") - assertRSAEqual(t, in, out) -} - -func assertEdDSAEqual(t *testing.T, in, out varsig.EdDSAVarsig) { - t.Helper() - - assert.Equal(t, in.Curve(), out.Curve()) - assert.Equal(t, in.Hash(), out.Hash()) -} - -func assertRSAEqual(t *testing.T, in, out varsig.RSAVarsig) { - t.Helper() - - assert.Equal(t, in.Hash(), out.Hash()) - assert.Equal(t, in.KeyLength(), out.KeyLength()) +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v } diff --git a/varsig_test.go b/varsig_test.go index 5d158fb..86194de 100644 --- a/varsig_test.go +++ b/varsig_test.go @@ -2,7 +2,6 @@ package varsig_test import ( "encoding/hex" - "errors" "io" "testing" @@ -157,28 +156,3 @@ func TestDecode(t *testing.T) { assert.NotNil(t, vs) // varsig is still returned with just "header" }) } - -func mustVarsig[T varsig.Varsig](t *testing.T, v T, err error) { - t.Helper() - - if err != nil && (v.Version() != varsig.Version0 || !errors.Is(err, varsig.ErrMissingSignature)) { - t.Error(err) - } -} - -func roundTrip[T varsig.Varsig](t *testing.T, in T, expEncHex string) T { - data := in.Encode() - assert.Equal(t, expEncHex, hex.EncodeToString(data)) - - out, err := varsig.Decode(in.Encode()) - if err != nil && (out.Version() != varsig.Version0 || !errors.Is(err, varsig.ErrMissingSignature)) { - t.Fail() - } - - assert.Equal(t, in.Version(), out.Version()) - assert.Equal(t, in.Discriminator(), out.Discriminator()) - assert.Equal(t, in.PayloadEncoding(), out.PayloadEncoding()) - assert.Equal(t, in.Signature(), out.Signature()) - - return out.(T) -}