From 2ffdf004acef73d74d6c08a6ba096451e43e36c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 24 Oct 2024 12:03:23 +0200 Subject: [PATCH] test(did): split the test vector reading code in a separate file/package tests are much cleaner and explicit now --- did/key_spec_test.go | 175 +----------------- did/{testdata => testvectors}/bls12381.json | 0 .../ed25519-x25519.json | 0 .../nist-curves.json | 0 did/{testdata => testvectors}/rsa.json | 0 did/{testdata => testvectors}/secp256k1.json | 0 did/testvectors/vectors.go | 163 ++++++++++++++++ did/{testdata => testvectors}/x25519.json | 0 8 files changed, 171 insertions(+), 167 deletions(-) rename did/{testdata => testvectors}/bls12381.json (100%) rename did/{testdata => testvectors}/ed25519-x25519.json (100%) rename did/{testdata => testvectors}/nist-curves.json (100%) rename did/{testdata => testvectors}/rsa.json (100%) rename did/{testdata => testvectors}/secp256k1.json (100%) create mode 100644 did/testvectors/vectors.go rename did/{testdata => testvectors}/x25519.json (100%) diff --git a/did/key_spec_test.go b/did/key_spec_test.go index bcc5098..7d4e4b2 100644 --- a/did/key_spec_test.go +++ b/did/key_spec_test.go @@ -1,26 +1,19 @@ -// go:build jwx_es256k +//go:build jwx_es256k package did_test import ( - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rsa" - "crypto/x509" "encoding/json" - "errors" "os" "path/filepath" "testing" - "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/lestrrat-go/jwx/v2/jwk" "github.com/libp2p/go-libp2p/core/crypto" - "github.com/mr-tron/base58" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/ucan-wg/go-ucan/did" + "github.com/ucan-wg/go-ucan/did/testvectors" ) // TestDidKeyVectors executes tests read from the [test vector files] provided @@ -67,177 +60,25 @@ func TestDidKeyVectors(t *testing.T) { } } -func loadTestVectors(t *testing.T, filename string) vectors { +func loadTestVectors(t *testing.T, filename string) testvectors.Vectors { t.Helper() - data, err := os.ReadFile(filepath.Join("testdata", filename)) + data, err := os.ReadFile(filepath.Join("testvectors", filename)) require.NoError(t, err) - var vs vectors + var vs testvectors.Vectors require.NoError(t, json.Unmarshal(data, &vs)) return vs } -func vectorPubKey(t *testing.T, v vector) crypto.PubKey { +func vectorPubKey(t *testing.T, v testvectors.Vector) crypto.PubKey { t.Helper() - pubKey, err := v.pubKey() + pubKey, err := v.PubKey() require.NoError(t, err) require.NotZero(t, pubKey) return pubKey } - -func vectorType(t *testing.T, v vector) string { - vectorType, err := v.pubKeyType() - require.NoError(t, err) - - return vectorType -} - -type vectors map[string]vector - -// This is pretty gross but the structure allows the repeated verifier, -// PublicKeyJwk and PublicKeyBase58 account for the fact that the test -// files are very inconsistent. -type vector struct { - VerificationKeyPair verifier - VerificationMethod verifier - PublicKeyJwk json.RawMessage - DidDocument json.RawMessage // TODO: if we start producing DID documents, we should test this too -} - -type verifier struct { - ID string - Type string - PublicKeyBase58 string - PublicKeyJwk json.RawMessage -} - -func (v vector) pubKey() (crypto.PubKey, error) { - // If the public key is in base58 - if pubB58 := v.pubKeyBase58(); len(pubB58) > 0 { - pubBytes, err := base58.Decode(pubB58) - if err != nil { - return nil, err - } - - t, err := v.pubKeyType() - if err != nil { - return nil, err - } - - var unmarshaler crypto.PubKeyUnmarshaller - - switch t { - case "Ed25519VerificationKey2018": - unmarshaler = crypto.UnmarshalEd25519PublicKey - case "EcdsaSecp256k1VerificationKey2019": - unmarshaler = crypto.UnmarshalSecp256k1PublicKey - // This is weak as it assumes the P256 curve - that's all the vectors contain (for now) - case "P256Key2021": - unmarshaler = compressedEcdsaPublicKeyUnmarshaler - default: - return nil, errors.New("failed to resolve unmarshaler") - } - - return unmarshaler(pubBytes) - } - - // If the public key is in a JWK - if pubJwk := v.pubKeyJwk(); len(pubJwk) > 0 { - key, err := jwk.ParseKey(pubJwk) - if err != nil { - return nil, err - } - - var a any - - if err := key.Raw(&a); err != nil { - return nil, err - } - - switch a.(type) { - case *ecdsa.PublicKey: - epub := a.(*ecdsa.PublicKey) - - if epub.Curve == secp256k1.S256() { - bytes := append([]byte{0x04}, append(epub.X.Bytes(), epub.Y.Bytes()...)...) - - return crypto.UnmarshalSecp256k1PublicKey(bytes) - } - - asn1, err := x509.MarshalPKIXPublicKey(epub) - if err != nil { - return nil, err - } - - return crypto.UnmarshalECDSAPublicKey(asn1) - case ed25519.PublicKey: - return crypto.UnmarshalEd25519PublicKey(a.(ed25519.PublicKey)) - case *rsa.PublicKey: - asn1, err := x509.MarshalPKIXPublicKey(a.(*rsa.PublicKey)) - if err != nil { - return nil, err - } - - return crypto.UnmarshalRsaPublicKey(asn1) - default: - return nil, errors.New("unsupported key type") - } - } - - // If we don't find a public key at all - return nil, errors.New("vector's public key not found") -} - -func (v vector) pubKeyBase58() string { - if len(v.VerificationKeyPair.PublicKeyBase58) > 0 { - return v.VerificationKeyPair.PublicKeyBase58 - } - - return v.VerificationMethod.PublicKeyBase58 -} - -func (v vector) pubKeyJwk() json.RawMessage { - if len(v.VerificationKeyPair.PublicKeyJwk) > 0 { - return v.VerificationKeyPair.PublicKeyJwk - } - - if len(v.VerificationMethod.PublicKeyJwk) > 0 { - return v.VerificationMethod.PublicKeyJwk - } - - return v.PublicKeyJwk -} - -func (v vector) pubKeyType() (string, error) { - if len(v.VerificationKeyPair.Type) > 0 { - return v.VerificationKeyPair.Type, nil - } - - if len(v.VerificationMethod.Type) > 0 { - return v.VerificationMethod.Type, nil - } - - return "", errors.New("vector's type not found") -} - -func compressedEcdsaPublicKeyUnmarshaler(data []byte) (crypto.PubKey, error) { - x, y := elliptic.UnmarshalCompressed(elliptic.P256(), data) - - ecdsaPublicKey := ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: x, - Y: y, - } - - asn1, err := x509.MarshalPKIXPublicKey(&ecdsaPublicKey) - if err != nil { - return nil, err - } - - return crypto.UnmarshalECDSAPublicKey(asn1) -} diff --git a/did/testdata/bls12381.json b/did/testvectors/bls12381.json similarity index 100% rename from did/testdata/bls12381.json rename to did/testvectors/bls12381.json diff --git a/did/testdata/ed25519-x25519.json b/did/testvectors/ed25519-x25519.json similarity index 100% rename from did/testdata/ed25519-x25519.json rename to did/testvectors/ed25519-x25519.json diff --git a/did/testdata/nist-curves.json b/did/testvectors/nist-curves.json similarity index 100% rename from did/testdata/nist-curves.json rename to did/testvectors/nist-curves.json diff --git a/did/testdata/rsa.json b/did/testvectors/rsa.json similarity index 100% rename from did/testdata/rsa.json rename to did/testvectors/rsa.json diff --git a/did/testdata/secp256k1.json b/did/testvectors/secp256k1.json similarity index 100% rename from did/testdata/secp256k1.json rename to did/testvectors/secp256k1.json diff --git a/did/testvectors/vectors.go b/did/testvectors/vectors.go new file mode 100644 index 0000000..6e3cc2d --- /dev/null +++ b/did/testvectors/vectors.go @@ -0,0 +1,163 @@ +//go:build jwx_es256k + +package testvectors + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rsa" + "crypto/x509" + "encoding/json" + "errors" + + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/mr-tron/base58" +) + +type Vectors map[string]Vector + +// This is pretty gross but the structure allows the repeated Verifier, +// PublicKeyJwk and PublicKeyBase58 account for the fact that the test +// files are very inconsistent. +type Vector struct { + VerificationKeyPair Verifier + VerificationMethod Verifier + PublicKeyJwk json.RawMessage + DidDocument json.RawMessage // TODO: if we start producing DID documents, we should test this too +} + +type Verifier struct { + ID string + Type string + PublicKeyBase58 string + PublicKeyJwk json.RawMessage +} + +func (v Vector) PubKey() (crypto.PubKey, error) { + // If the public key is in base58 + if pubB58 := v.PubKeyBase58(); len(pubB58) > 0 { + pubBytes, err := base58.Decode(pubB58) + if err != nil { + return nil, err + } + + t, err := v.PubKeyType() + if err != nil { + return nil, err + } + + var unmarshaler crypto.PubKeyUnmarshaller + + switch t { + case "Ed25519VerificationKey2018": + unmarshaler = crypto.UnmarshalEd25519PublicKey + case "EcdsaSecp256k1VerificationKey2019": + unmarshaler = crypto.UnmarshalSecp256k1PublicKey + // This is weak as it assumes the P256 curve - that's all the vectors contain (for now) + case "P256Key2021": + unmarshaler = compressedEcdsaPublicKeyUnmarshaler + default: + return nil, errors.New("failed to resolve unmarshaler") + } + + return unmarshaler(pubBytes) + } + + // If the public key is in a JWK + if pubJwk := v.PubKeyJwk(); len(pubJwk) > 0 { + key, err := jwk.ParseKey(pubJwk) + if err != nil { + return nil, err + } + + var a any + + if err := key.Raw(&a); err != nil { + return nil, err + } + + switch a.(type) { + case *ecdsa.PublicKey: + epub := a.(*ecdsa.PublicKey) + + if epub.Curve == secp256k1.S256() { + bytes := append([]byte{0x04}, append(epub.X.Bytes(), epub.Y.Bytes()...)...) + + return crypto.UnmarshalSecp256k1PublicKey(bytes) + } + + asn1, err := x509.MarshalPKIXPublicKey(epub) + if err != nil { + return nil, err + } + + return crypto.UnmarshalECDSAPublicKey(asn1) + case ed25519.PublicKey: + return crypto.UnmarshalEd25519PublicKey(a.(ed25519.PublicKey)) + case *rsa.PublicKey: + asn1, err := x509.MarshalPKIXPublicKey(a.(*rsa.PublicKey)) + if err != nil { + return nil, err + } + + return crypto.UnmarshalRsaPublicKey(asn1) + default: + return nil, errors.New("unsupported key type") + } + } + + // If we don't find a public key at all + return nil, errors.New("vector's public key not found") +} + +func (v Vector) PubKeyBase58() string { + if len(v.VerificationKeyPair.PublicKeyBase58) > 0 { + return v.VerificationKeyPair.PublicKeyBase58 + } + + return v.VerificationMethod.PublicKeyBase58 +} + +func (v Vector) PubKeyJwk() json.RawMessage { + if len(v.VerificationKeyPair.PublicKeyJwk) > 0 { + return v.VerificationKeyPair.PublicKeyJwk + } + + if len(v.VerificationMethod.PublicKeyJwk) > 0 { + return v.VerificationMethod.PublicKeyJwk + } + + return v.PublicKeyJwk +} + +func (v Vector) PubKeyType() (string, error) { + if len(v.VerificationKeyPair.Type) > 0 { + return v.VerificationKeyPair.Type, nil + } + + if len(v.VerificationMethod.Type) > 0 { + return v.VerificationMethod.Type, nil + } + + return "", errors.New("vector's type not found") +} + +func compressedEcdsaPublicKeyUnmarshaler(data []byte) (crypto.PubKey, error) { + x, y := elliptic.UnmarshalCompressed(elliptic.P256(), data) + + ecdsaPublicKey := ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: x, + Y: y, + } + + asn1, err := x509.MarshalPKIXPublicKey(&ecdsaPublicKey) + if err != nil { + return nil, err + } + + return crypto.UnmarshalECDSAPublicKey(asn1) +} diff --git a/did/testdata/x25519.json b/did/testvectors/x25519.json similarity index 100% rename from did/testdata/x25519.json rename to did/testvectors/x25519.json