diff --git a/impl/internal/did/did.go b/impl/internal/did/did.go index 110eddd7..2c0015b6 100644 --- a/impl/internal/did/did.go +++ b/impl/internal/did/did.go @@ -77,9 +77,9 @@ func CreateDIDDHTDID(pubKey ed25519.PublicKey, opts CreateDIDDHTOpts) (*did.Docu // validate opts and build verification methods, key purposes, and services var vms []did.VerificationMethod + var keyAgreement []did.VerificationMethodSet authentication := []did.VerificationMethodSet{"#0"} assertionMethod := []did.VerificationMethodSet{"#0"} - keyAgreement := []did.VerificationMethodSet{"#0"} capabilityInvocation := []did.VerificationMethodSet{"#0"} capabilityDelegation := []did.VerificationMethodSet{"#0"} if len(opts.VerificationMethods) > 0 { @@ -107,27 +107,28 @@ func CreateDIDDHTDID(pubKey ed25519.PublicKey, opts CreateDIDDHTOpts) (*did.Docu vms = append(vms, vm.VerificationMethod) // add purposes + vmID := vm.VerificationMethod.ID[strings.LastIndex(vm.VerificationMethod.ID, "#"):] for _, purpose := range vm.Purposes { switch purpose { case ion.Authentication: - authentication = append(authentication, vm.VerificationMethod.ID) + authentication = append(authentication, vmID) case ion.AssertionMethod: - assertionMethod = append(assertionMethod, vm.VerificationMethod.ID) + assertionMethod = append(assertionMethod, vmID) case ion.KeyAgreement: - keyAgreement = append(keyAgreement, vm.VerificationMethod.ID) + keyAgreement = append(keyAgreement, vmID) case ion.CapabilityInvocation: - capabilityInvocation = append(capabilityInvocation, vm.VerificationMethod.ID) + capabilityInvocation = append(capabilityInvocation, vmID) case ion.CapabilityDelegation: - capabilityDelegation = append(capabilityDelegation, vm.VerificationMethod.ID) + capabilityDelegation = append(capabilityDelegation, vmID) default: - return nil, fmt.Errorf("unknown key purpose: %s:%s", vm.VerificationMethod.ID, purpose) + return nil, fmt.Errorf("unknown key purpose: %s:%s", vmID, purpose) } } } } if len(opts.Services) > 0 { seenIDs := make(map[string]bool) - for _, s := range opts.Services { + for i, s := range opts.Services { if seenIDs[s.ID] { return nil, fmt.Errorf("service id %s is not unique", s.ID) } @@ -136,7 +137,7 @@ func CreateDIDDHTDID(pubKey ed25519.PublicKey, opts CreateDIDDHTOpts) (*did.Docu seenIDs[s.ID] = true // update ID in place - s.ID = id + "#" + s.ID + opts.Services[i].ID = id + "#" + s.ID } } @@ -178,7 +179,10 @@ func (d DHT) ToDNSPacket(doc did.Document) (*dns.Msg, error) { var vmIDs []string for i, vm := range doc.VerificationMethod { recordIdentifier := fmt.Sprintf("k%d", i) - vmID := strings.Split(vm.ID, "#")[1] + vmID := vm.ID + if strings.Contains(vmID, "#") { + vmID = vmID[strings.LastIndex(vm.ID, "#")+1:] + } keyLookup[vm.ID] = recordIdentifier var keyType int @@ -221,7 +225,10 @@ func (d DHT) ToDNSPacket(doc did.Document) (*dns.Msg, error) { var svcIDs []string for i, service := range doc.Services { recordIdentifier := fmt.Sprintf("s%d", i) - sID := strings.Split("#", service.ID)[1] + sID := service.ID + if strings.Contains(sID, "#") { + sID = sID[strings.LastIndex(service.ID, "#")+1:] + } serviceRecord := dns.TXT{ Hdr: dns.RR_Header{ diff --git a/impl/internal/did/did_test.go b/impl/internal/did/did_test.go index bcb49286..b60ed57a 100644 --- a/impl/internal/did/did_test.go +++ b/impl/internal/did/did_test.go @@ -77,6 +77,39 @@ func TestGenerateDIDDHT(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, privKey) require.NotEmpty(t, doc) + + assert.NotEmpty(t, doc.ID) + assert.Empty(t, doc.Context) + assert.NotEmpty(t, doc.VerificationMethod) + assert.NotEmpty(t, doc.Authentication) + assert.NotEmpty(t, doc.AssertionMethod) + assert.Empty(t, doc.KeyAgreement) + assert.NotEmpty(t, doc.CapabilityDelegation) + assert.NotEmpty(t, doc.CapabilityInvocation) + assert.NotEmpty(t, doc.Services) + + assert.Len(t, doc.VerificationMethod, 2) + assert.Len(t, doc.Authentication, 1) + assert.Len(t, doc.AssertionMethod, 2) + assert.Len(t, doc.KeyAgreement, 0) + assert.Len(t, doc.CapabilityDelegation, 1) + assert.Len(t, doc.CapabilityInvocation, 2) + assert.Len(t, doc.Services, 2) + + assert.NotEmpty(t, doc.VerificationMethod[0].ID) + assert.EqualValues(t, doc.ID+"#0", doc.VerificationMethod[0].ID) + assert.NotEmpty(t, doc.VerificationMethod[0].Controller) + assert.Equal(t, doc.ID, doc.VerificationMethod[0].Controller) + assert.NotEmpty(t, doc.VerificationMethod[0].Type) + assert.NotEmpty(t, doc.VerificationMethod[0].PublicKeyJWK) + + assert.Equal(t, doc.Services[0].ID, "did:dht:123456789abcdefghi#vcs") + assert.Equal(t, doc.Services[0].Type, "VerifiableCredentialService") + assert.Equal(t, doc.Services[0].ServiceEndpoint, "https://example.com/vc/") + + assert.Equal(t, doc.Services[1].ID, "did:dht:123456789abcdefghi#hub") + assert.Equal(t, doc.Services[1].Type, "MessagingService") + assert.Equal(t, doc.Services[1].ServiceEndpoint, "https://example.com/hub/") }) } @@ -99,4 +132,53 @@ func TestToDNSPacket(t *testing.T) { assert.EqualValues(t, *doc, *decodedDoc) }) + + t.Run("doc with multiple keys and services - test to dns packet round trip", func(t *testing.T) { + pubKey, _, err := crypto.GenerateSECP256k1Key() + require.NoError(t, err) + pubKeyJWK, err := jwx.PublicKeyToPublicKeyJWK("key1", pubKey) + require.NoError(t, err) + + opts := CreateDIDDHTOpts{ + VerificationMethods: []VerificationMethod{ + { + VerificationMethod: did.VerificationMethod{ + ID: "key1", + Type: "JsonWebKey2020", + Controller: "did:dht:123456789abcdefghi", + PublicKeyJWK: pubKeyJWK, + }, + Purposes: []ion.PublicKeyPurpose{ion.AssertionMethod, ion.CapabilityInvocation}, + }, + }, + Services: []did.Service{ + { + ID: "vcs", + Type: "VerifiableCredentialService", + ServiceEndpoint: "https://example.com/vc/", + }, + { + ID: "hub", + Type: "MessagingService", + ServiceEndpoint: "https://example.com/hub/", + }, + }, + } + privKey, doc, err := GenerateDIDDHT(opts) + require.NoError(t, err) + require.NotEmpty(t, privKey) + require.NotEmpty(t, doc) + + did := DHT(doc.ID) + + packet, err := did.ToDNSPacket(*doc) + require.NoError(t, err) + require.NotEmpty(t, packet) + + decodedDoc, err := did.FromDNSPacket(packet) + require.NoError(t, err) + require.NotEmpty(t, decodedDoc) + + assert.EqualValues(t, *doc, *decodedDoc) + }) }