From 2b4fe9f922f5a9ea8e997ea0ebde7ca2b3d6fdf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 3 Jul 2025 15:48:27 +0200 Subject: [PATCH] add support for Ed25519VerificationKey2018 --- verifications/ed25519/VerificationKey2018.go | 102 ++++++++++++++++++ .../ed25519/VerificationKey2018_test.go | 27 +++++ verifications/ed25519/VerificationKey2020.go | 8 +- .../ed25519/VerificationKey2020_test.go | 4 +- 4 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 verifications/ed25519/VerificationKey2018.go create mode 100644 verifications/ed25519/VerificationKey2018_test.go diff --git a/verifications/ed25519/VerificationKey2018.go b/verifications/ed25519/VerificationKey2018.go new file mode 100644 index 0000000..e7e9c1c --- /dev/null +++ b/verifications/ed25519/VerificationKey2018.go @@ -0,0 +1,102 @@ +package ed25519vm + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/mr-tron/base58" + + "github.com/INFURA/go-did" + "github.com/INFURA/go-did/crypto/ed25519" +) + +// Specification: https://w3c-ccg.github.io/lds-ed25519-2018/ + +const ( + JsonLdContext2018 = "https://w3id.org/security/suites/ed25519-2018/v1" + Type2018 = "Ed25519VerificationKey2018" +) + +var _ did.VerificationMethodSignature = &VerificationKey2018{} + +type VerificationKey2018 struct { + id string + pubkey ed25519.PublicKey + controller string +} + +func NewVerificationKey2018(id string, pubkey ed25519.PublicKey, controller did.DID) *VerificationKey2018 { + return &VerificationKey2018{ + id: id, + pubkey: pubkey, + controller: controller.String(), + } +} + +func (v VerificationKey2018) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + ID string `json:"id"` + Type string `json:"type"` + Controller string `json:"controller"` + PublicKeyBase58 string `json:"publicKeyBase58"` + }{ + ID: v.ID(), + Type: v.Type(), + Controller: v.Controller(), + PublicKeyBase58: base58.Encode(v.pubkey.ToBytes()), + }) +} + +func (v *VerificationKey2018) UnmarshalJSON(bytes []byte) error { + aux := struct { + ID string `json:"id"` + Type string `json:"type"` + Controller string `json:"controller"` + PublicKeyBase58 string `json:"publicKeyBase58"` + }{} + err := json.Unmarshal(bytes, &aux) + if err != nil { + return err + } + if aux.Type != v.Type() { + return errors.New("invalid type") + } + v.id = aux.ID + if len(v.id) == 0 { + return errors.New("invalid id") + } + pubBytes, err := base58.Decode(aux.PublicKeyBase58) + if err != nil { + return fmt.Errorf("invalid publicKeyBase58: %w", err) + } + v.pubkey, err = ed25519.PublicKeyFromBytes(pubBytes) + if err != nil { + return fmt.Errorf("invalid publicKeyBase58: %w", err) + } + v.controller = aux.Controller + if !did.HasValidDIDSyntax(v.controller) { + return errors.New("invalid controller") + } + return nil +} + +func (v VerificationKey2018) ID() string { + return v.id +} + +func (v VerificationKey2018) Type() string { + return Type2018 +} + +func (v VerificationKey2018) Controller() string { + return v.controller +} + +func (v VerificationKey2018) JsonLdContext() string { + return JsonLdContext2018 +} + +func (v VerificationKey2018) Verify(data []byte, sig []byte) (bool, error) { + return v.pubkey.VerifyBytes(data, sig), nil +} diff --git a/verifications/ed25519/VerificationKey2018_test.go b/verifications/ed25519/VerificationKey2018_test.go new file mode 100644 index 0000000..0ed276d --- /dev/null +++ b/verifications/ed25519/VerificationKey2018_test.go @@ -0,0 +1,27 @@ +package ed25519vm_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + ed25519vm "github.com/INFURA/go-did/verifications/ed25519" +) + +func TestJsonRoundTrip2018(t *testing.T) { + data := `{ + "id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG", + "type": "Ed25519VerificationKey2018", + "controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG", + "publicKeyBase58": "6ASf5EcmmEHTgDJ4X4ZT5vT6iHVJBXPg5AN5YoTCpGWt" + }` + + var vk ed25519vm.VerificationKey2018 + err := json.Unmarshal([]byte(data), &vk) + require.NoError(t, err) + + bytes, err := json.Marshal(vk) + require.NoError(t, err) + require.JSONEq(t, data, string(bytes)) +} diff --git a/verifications/ed25519/VerificationKey2020.go b/verifications/ed25519/VerificationKey2020.go index 7526102..e524e3b 100644 --- a/verifications/ed25519/VerificationKey2020.go +++ b/verifications/ed25519/VerificationKey2020.go @@ -12,8 +12,8 @@ import ( // Specification: https://w3c.github.io/cg-reports/credentials/CG-FINAL-di-eddsa-2020-20220724/ const ( - JsonLdContext = "https://w3id.org/security/suites/ed25519-2020/v1" - Type = "Ed25519VerificationKey2020" + JsonLdContext2020 = "https://w3id.org/security/suites/ed25519-2020/v1" + Type2020 = "Ed25519VerificationKey2020" ) var _ did.VerificationMethodSignature = &VerificationKey2020{} @@ -80,7 +80,7 @@ func (v VerificationKey2020) ID() string { } func (v VerificationKey2020) Type() string { - return Type + return Type2020 } func (v VerificationKey2020) Controller() string { @@ -88,7 +88,7 @@ func (v VerificationKey2020) Controller() string { } func (v VerificationKey2020) JsonLdContext() string { - return JsonLdContext + return JsonLdContext2020 } func (v VerificationKey2020) Verify(data []byte, sig []byte) (bool, error) { diff --git a/verifications/ed25519/VerificationKey2020_test.go b/verifications/ed25519/VerificationKey2020_test.go index af0c2dd..47ba3e9 100644 --- a/verifications/ed25519/VerificationKey2020_test.go +++ b/verifications/ed25519/VerificationKey2020_test.go @@ -13,7 +13,7 @@ import ( "github.com/INFURA/go-did/verifications/ed25519" ) -func TestJsonRoundTrip(t *testing.T) { +func TestJsonRoundTrip2020(t *testing.T) { data := `{ "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", "type": "Ed25519VerificationKey2020", @@ -30,7 +30,7 @@ func TestJsonRoundTrip(t *testing.T) { require.JSONEq(t, data, string(bytes)) } -func TestSignature(t *testing.T) { +func TestSignature2020(t *testing.T) { // test vector from https://datatracker.ietf.org/doc/html/rfc8032#section-7.1 pkHex := "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"