feat(test): add more tests for the presets, and compat with iso-ucan

This commit is contained in:
Michael Muré
2025-07-23 10:58:51 +02:00
parent a43c3af4c8
commit 2f22cb9b15
3 changed files with 178 additions and 100 deletions

View File

@@ -1,41 +1,64 @@
package varsig 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 // [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) { func Ed25519(payloadEncoding PayloadEncoding, opts ...Option) (EdDSAVarsig, error) {
return NewEdDSAVarsig(CurveEd25519, HashSha2_512, payloadEncoding, opts...) return NewEdDSAVarsig(CurveEd25519, HashSha2_512, payloadEncoding, opts...)
} }
// Ed448 produces a varsig that describes the associated algorithm defined // Ed448 produces a varsig for EdDSA using Ed448 curve.
// by the [IANA JOSE specification]. // This algorithm is defined in [IANA JOSE specification].
//
// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
func Ed448(payloadEncoding PayloadEncoding, opts ...Option) (EdDSAVarsig, error) { func Ed448(payloadEncoding PayloadEncoding, opts ...Option) (EdDSAVarsig, error) {
return NewEdDSAVarsig(CurveEd448, HashShake_256, payloadEncoding, opts...) return NewEdDSAVarsig(CurveEd448, HashShake_256, payloadEncoding, opts...)
} }
// RS256 produces a varsig that describes the associated algorithm defined // RS256 produces a varsig for RSASSA-PKCS1-v1_5 using SHA-256.
// by the [IANA JOSE specification]. // This algorithm is defined in [IANA JOSE specification].
//
// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
func RS256(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) { func RS256(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) {
return NewRSAVarsig(HashSha2_256, keyLength, payloadEncoding, opts...) return NewRSAVarsig(HashSha2_256, keyLength, payloadEncoding, opts...)
} }
// RS384 produces a varsig that describes the associated algorithm defined // RS384 produces a varsig for RSASSA-PKCS1-v1_5 using SHA-384.
// by the [IANA JOSE specification]. // This algorithm is defined in [IANA JOSE specification].
//
// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
func RS384(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) { func RS384(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) {
return NewRSAVarsig(HashSha2_384, keyLength, payloadEncoding, opts...) return NewRSAVarsig(HashSha2_384, keyLength, payloadEncoding, opts...)
} }
// RS512 produces a varsig that describes the associated algorithm defined // RS512 produces a varsig for RSASSA-PKCS1-v1_5 using SHA-512.
// by the [IANA JOSE specification]. // This algorithm is defined in [IANA JOSE specification].
//
// [IANA JOSE specification]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
func RS512(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) { func RS512(keyLength uint64, payloadEncoding PayloadEncoding, opts ...Option) (RSAVarsig, error) {
return NewRSAVarsig(HashSha2_512, keyLength, payloadEncoding, opts...) 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...)
}

View File

@@ -1,68 +1,149 @@
package varsig_test package varsig_test
import ( import (
"encoding/hex"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/require"
"github.com/ucan-wg/go-varsig" "github.com/ucan-wg/go-varsig"
) )
func TestEd25519(t *testing.T) { func TestRoundTrip(t *testing.T) {
t.Parallel() 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) // from https://github.com/hugomrdias/iso-repo/blob/main/packages/iso-ucan/test/varsig.test.js
mustVarsig(t, in, err) {
out := roundTrip(t, in, "3401ed01ed011371") name: "RS256+RAW",
assertEdDSAEqual(t, in, out) 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) { func must[T any](v T, err error) T {
t.Parallel() if err != nil {
panic(err)
in, err := varsig.Ed448(varsig.PayloadEncodingDAGCBOR) }
mustVarsig(t, in, err) return v
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())
} }

View File

@@ -2,7 +2,6 @@ package varsig_test
import ( import (
"encoding/hex" "encoding/hex"
"errors"
"io" "io"
"testing" "testing"
@@ -157,28 +156,3 @@ func TestDecode(t *testing.T) {
assert.NotNil(t, vs) // varsig is still returned with just "header" 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)
}