diff --git a/impl/concurrencytest/main.go b/impl/concurrencytest/main.go index 9f3fbc27..c15751e3 100644 --- a/impl/concurrencytest/main.go +++ b/impl/concurrencytest/main.go @@ -114,7 +114,7 @@ func generateDIDPutRequest() (string, []byte, error) { return "", nil, err } - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) if err != nil { return "", nil, err } diff --git a/impl/integrationtest/main.go b/impl/integrationtest/main.go index 18ed29c7..03b45fca 100644 --- a/impl/integrationtest/main.go +++ b/impl/integrationtest/main.go @@ -98,7 +98,7 @@ func generateDIDPutRequest() (string, []byte, error) { return "", nil, err } - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) if err != nil { return "", nil, err } diff --git a/impl/internal/did/client.go b/impl/internal/did/client.go index d945e21b..d0c0ef34 100644 --- a/impl/internal/did/client.go +++ b/impl/internal/did/client.go @@ -8,7 +8,6 @@ import ( "net/url" "time" - "github.com/TBD54566975/ssi-sdk/did" "github.com/anacrolix/dht/v2/bep44" "github.com/miekg/dns" "github.com/pkg/errors" @@ -34,30 +33,30 @@ func NewGatewayClient(gatewayURL string) (*GatewayClient, error) { } // GetDIDDocument gets a DID document, its types, and authoritative gateways, from a did:dht Gateway -func (c *GatewayClient) GetDIDDocument(id string) (*did.Document, []TypeIndex, []AuthoritativeGateway, error) { +func (c *GatewayClient) GetDIDDocument(id string) (*DIDDHTDocument, error) { d := DHT(id) if !d.IsValid() { - return nil, nil, nil, errors.New("invalid did") + return nil, errors.New("invalid did") } suffix, err := d.Suffix() if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to get suffix") + return nil, errors.Wrap(err, "failed to get suffix") } resp, err := http.Get(c.gatewayURL + "/" + suffix) if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to get did document") + return nil, errors.Wrap(err, "failed to get did document") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, nil, nil, errors.Errorf("failed to get did document, status code: %d", resp.StatusCode) + return nil, errors.Errorf("failed to get did document, status code: %d", resp.StatusCode) } body, err := io.ReadAll(resp.Body) if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to read response body") + return nil, errors.Wrap(err, "failed to read response body") } msg := new(dns.Msg) if err = msg.Unpack(body[72:]); err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to unpack records") + return nil, errors.Wrap(err, "failed to unpack records") } return d.FromDNSPacket(msg) } diff --git a/impl/internal/did/client_test.go b/impl/internal/did/client_test.go index cea9c9e7..6c996fca 100644 --- a/impl/internal/did/client_test.go +++ b/impl/internal/did/client_test.go @@ -23,7 +23,7 @@ func TestClient(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) @@ -34,9 +34,9 @@ func TestClient(t *testing.T) { err = client.PutDocument(doc.ID, *bep44Put) assert.NoError(t, err) - gotDID, _, _, err := client.GetDIDDocument(doc.ID) + gotDID, err := client.GetDIDDocument(doc.ID) assert.NoError(t, err) - assert.EqualValues(t, doc, gotDID) + assert.EqualValues(t, *doc, gotDID.Doc) since := time.Since(start) t.Logf("time to put and get: %s", since) @@ -53,33 +53,25 @@ func TestInvalidDIDDocument(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, client) - did, types, gateways, err := client.GetDIDDocument("this is not a valid did") + gotDID, err := client.GetDIDDocument("this is not a valid did") assert.Error(t, err) - assert.Empty(t, did) - assert.Empty(t, types) - assert.Empty(t, gateways) + assert.Empty(t, gotDID) - did, types, gateways, err = client.GetDIDDocument("did:dht:example") + gotDID, err = client.GetDIDDocument("did:dht:example") assert.EqualError(t, err, "invalid did") - assert.Empty(t, did) - assert.Empty(t, types) - assert.Empty(t, gateways) + assert.Empty(t, gotDID) - did, types, gateways, err = client.GetDIDDocument("did:dht:i9xkp8ddcbcg8jwq54ox699wuzxyifsqx4jru45zodqu453ksz6y") + gotDID, err = client.GetDIDDocument("did:dht:i9xkp8ddcbcg8jwq54ox699wuzxyifsqx4jru45zodqu453ksz6y") assert.Error(t, err) // this should error because the gateway URL is invalid - assert.Empty(t, did) - assert.Empty(t, types) - assert.Empty(t, gateways) + assert.Empty(t, gotDID) client, err = NewGatewayClient("https://tbd.website") require.NoError(t, err) require.NotEmpty(t, client) - did, types, gateways, err = client.GetDIDDocument("did:dht:i9xkp8ddcbcg8jwq54ox699wuzxyifsqx4jru45zodqu453ksz6y") + gotDID, err = client.GetDIDDocument("did:dht:i9xkp8ddcbcg8jwq54ox699wuzxyifsqx4jru45zodqu453ksz6y") assert.Error(t, err) // this should error because the gateway URL will return a non-200 - assert.Empty(t, did) - assert.Empty(t, types) - assert.Empty(t, gateways) + assert.Empty(t, gotDID) err = client.PutDocument("did:dht:example", bep44.Put{}) assert.Error(t, err) diff --git a/impl/internal/did/did.go b/impl/internal/did/did.go index 5750cd2b..d8f617ec 100644 --- a/impl/internal/did/did.go +++ b/impl/internal/did/did.go @@ -23,6 +23,11 @@ type ( AuthoritativeGateway string ) +type PreviousDID struct { + PreviousDID DHT `json:"did"` + Signature string `json:"signature"` +} + const ( // Prefix did:dht prefix Prefix = "did:dht" @@ -62,13 +67,26 @@ func (d DHT) Suffix() (string, error) { return "", fmt.Errorf("invalid did:dht prefix: %s", d) } +// IdentityKey returns the ed25519 public key for the DHT identifier https://did-dht.com/#identity-key +func (d DHT) IdentityKey() (ed25519.PublicKey, error) { + suffix, err := d.Suffix() + if err != nil { + return nil, err + } + pk, err := zbase32.DecodeString(suffix) + if err != nil { + return nil, err + } + return pk, nil +} + func (DHT) Method() did.Method { return DHTMethod } +// CreateDIDDHTOpts is a set of options for creating a did:dht identifier +// Note: this does not include additional properties only present in the DNS representation (e.g. gateways, types) type CreateDIDDHTOpts struct { - // AuthoritativeGateways is a list of authoritative gateways for the DID Document - AuthoritativeGateways []string // Controller is the DID Controller, can be a list of DIDs Controller []string // AlsoKnownAs is a list of alternative identifiers for the DID Document @@ -239,7 +257,7 @@ func GetDIDDHTIdentifier(pubKey []byte) string { } // ToDNSPacket converts a DID DHT Document to a DNS packet with an optional list of types to include -func (d DHT) ToDNSPacket(doc did.Document, types []TypeIndex, gateways []AuthoritativeGateway) (*dns.Msg, error) { +func (d DHT) ToDNSPacket(doc did.Document, types []TypeIndex, gateways []AuthoritativeGateway, previousDID *PreviousDID) (*dns.Msg, error) { var records []dns.RR var rootRecord []string keyLookup := make(map[string]string) @@ -249,6 +267,24 @@ func (d DHT) ToDNSPacket(doc did.Document, types []TypeIndex, gateways []Authori return nil, errors.Wrap(err, "failed to get suffix while decoding DNS packet") } + // handle the previous DID if it's present + if previousDID != nil { + // make sure it's valid + if err = ValidatePreviousDIDSignatureValid(d, *previousDID); err != nil { + return nil, err + } + // add it to the record set + records = append(records, &dns.TXT{ + Hdr: dns.RR_Header{ + Name: "_prv._did.", + Rrtype: dns.TypeTXT, + Class: dns.ClassINET, + Ttl: 7200, + }, + Txt: chunkTextRecord(fmt.Sprintf("id=%s;s=%s", previousDID.PreviousDID, previousDID.Signature)), + }) + } + // first append the version to the root record rootRecord = append(rootRecord, fmt.Sprintf("v=%d", Version)) @@ -508,22 +544,32 @@ func parseServiceData(serviceEndpoint any) string { return "" } +// DIDDHTDocument is a DID DHT Document along with additional metadata the DID supports +type DIDDHTDocument struct { + Doc did.Document `json:"did,omitempty"` + Types []TypeIndex `json:"types,omitempty"` + Gateways []AuthoritativeGateway `json:"gateways,omitempty"` + PreviousDID *PreviousDID `json:"previousDid,omitempty"` +} + // FromDNSPacket converts a DNS packet to a DID DHT Document // Returns the DID Document, a list of types, a list of authoritative gateways, and an error -func (d DHT) FromDNSPacket(msg *dns.Msg) (*did.Document, []TypeIndex, []AuthoritativeGateway, error) { +func (d DHT) FromDNSPacket(msg *dns.Msg) (*DIDDHTDocument, error) { doc := did.Document{ ID: d.String(), } suffix, err := d.Suffix() if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to get suffix while decoding DNS packet") + return nil, errors.Wrap(err, "failed to get suffix while decoding DNS packet") } // track the authoritative gateways var gateways []AuthoritativeGateway // track the types var types []TypeIndex + // track the previous DID + var previousDID *PreviousDID keyLookup := make(map[string]string) for _, rr := range msg.Answer { switch record := rr.(type) { @@ -558,23 +604,23 @@ func (d DHT) FromDNSPacket(msg *dns.Msg) (*did.Document, []TypeIndex, []Authorit // Convert keyBase64URL back to PublicKeyJWK pubKeyBytes, err := base64.RawURLEncoding.DecodeString(keyBase64URL) if err != nil { - return nil, nil, nil, err + return nil, err } // as per the spec's guidance DNS representations use compressed keys, so we must unmarshall them as such pubKey, err := crypto.BytesToPubKey(pubKeyBytes, keyType, crypto.ECDSAUnmarshalCompressed) if err != nil { - return nil, nil, nil, err + return nil, err } pubKeyJWK, err := jwx.PublicKeyToPublicKeyJWK(&vmID, pubKey) if err != nil { - return nil, nil, nil, err + return nil, err } // set the algorithm if it's not the default for the key type if alg == "" { defaultAlg := defaultAlgForJWK(*pubKeyJWK) if defaultAlg == "" { - return nil, nil, nil, fmt.Errorf("unable to provide default alg for unsupported key type: %s", keyType) + return nil, fmt.Errorf("unable to provide default alg for unsupported key type: %s", keyType) } pubKeyJWK.ALG = defaultAlg } else { @@ -583,7 +629,7 @@ func (d DHT) FromDNSPacket(msg *dns.Msg) (*did.Document, []TypeIndex, []Authorit // make sure the controller of the identity key matches the DID if vmID == "0" && controller != d.String() { - return nil, nil, nil, fmt.Errorf("controller of identity key must be the DID itself, instead it is: %s", controller) + return nil, fmt.Errorf("controller of identity key must be the DID itself, instead it is: %s", controller) } // if the verification method ID is not set, set it to the thumbprint @@ -592,7 +638,7 @@ func (d DHT) FromDNSPacket(msg *dns.Msg) (*did.Document, []TypeIndex, []Authorit } if vmID != "0" && pubKeyJWK.KID != vmID { - return nil, nil, nil, fmt.Errorf("verification method JWK KID must be set to its thumbprint") + return nil, fmt.Errorf("verification method JWK KID must be set to its thumbprint") } vm := did.VerificationMethod{ @@ -634,23 +680,34 @@ func (d DHT) FromDNSPacket(msg *dns.Msg) (*did.Document, []TypeIndex, []Authorit } else if record.Hdr.Name == "_typ._did." { if record.Txt[0] == "" { - return nil, nil, nil, fmt.Errorf("types record is empty") + return nil, fmt.Errorf("types record is empty") } unchunkedTextRecord := unchunkTextRecord(record.Txt) typesStr := strings.Split(strings.TrimPrefix(unchunkedTextRecord, "id="), ",") for _, t := range typesStr { tInt, err := strconv.Atoi(t) if err != nil { - return nil, nil, nil, err + return nil, err } types = append(types, TypeIndex(tInt)) } } else if record.Hdr.Name == fmt.Sprintf("_did.%s.", suffix) && record.Hdr.Rrtype == dns.TypeNS { if record.Txt[0] == "" { - return nil, nil, nil, fmt.Errorf("gateway record is empty") + return nil, fmt.Errorf("gateway record is empty") } unchunkedTextRecord := unchunkTextRecord(record.Txt) gateways = append(gateways, AuthoritativeGateway(unchunkedTextRecord)) + } else if record.Hdr.Name == "_prv._did." && record.Hdr.Rrtype == dns.TypeTXT { + unchunkedTextRecord := unchunkTextRecord(record.Txt) + data := parseTxtData(unchunkedTextRecord) + previousDID = &PreviousDID{ + PreviousDID: DHT(data["id"]), + Signature: data["s"], + } + // validate previous DID signature + if err = ValidatePreviousDIDSignatureValid(d, *previousDID); err != nil { + return nil, err + } } else if record.Hdr.Name == fmt.Sprintf("_did.%s.", suffix) && record.Hdr.Rrtype == dns.TypeTXT { unchunkedTextRecord := unchunkTextRecord(record.Txt) rootItems := strings.Split(unchunkedTextRecord, ";") @@ -668,7 +725,7 @@ func (d DHT) FromDNSPacket(msg *dns.Msg) (*did.Document, []TypeIndex, []Authorit switch key { case "v": if len(valueItems) != 1 || valueItems[0] != strconv.Itoa(Version) { - return nil, nil, nil, fmt.Errorf("invalid version: %s", values) + return nil, fmt.Errorf("invalid version: %s", values) } seenVersion = true case "auth": @@ -694,13 +751,51 @@ func (d DHT) FromDNSPacket(msg *dns.Msg) (*did.Document, []TypeIndex, []Authorit } } if !seenVersion { - return nil, nil, nil, fmt.Errorf("root record missing version identifier") + return nil, fmt.Errorf("root record missing version identifier") } } } } - return &doc, types, gateways, nil + return &DIDDHTDocument{ + Doc: doc, + Types: types, + Gateways: gateways, + PreviousDID: previousDID, + }, nil +} + +// CreatePreviousDIDRecord creates a PreviousDID record for the given previous DID and current DID +func CreatePreviousDIDRecord(previousDIDPrivateKey ed25519.PrivateKey, previousDID, currentDID DHT) (*PreviousDID, error) { + currentDIDIdentityKey, err := currentDID.IdentityKey() + if err != nil { + return nil, errors.Wrapf(err, "failed to get identity key from currentDID: %s", currentDID) + } + previousDIDSignature := ed25519.Sign(previousDIDPrivateKey, currentDIDIdentityKey) + return &PreviousDID{ + PreviousDID: previousDID, + Signature: base64.RawURLEncoding.EncodeToString(previousDIDSignature), + }, nil +} + +// ValidatePreviousDIDSignatureValid validates the signature of the previous DID over the current DID +func ValidatePreviousDIDSignatureValid(currentDID DHT, previousDID PreviousDID) error { + identityKey, err := currentDID.IdentityKey() + if err != nil { + return errors.Wrapf(err, "failed to get identity key from the current DID: %s", currentDID) + } + previousDIDKey, err := previousDID.PreviousDID.IdentityKey() + if err != nil { + return errors.Wrapf(err, "failed to get identity key from the previous DID: %s", previousDID.PreviousDID) + } + decodedSignature, err := base64.RawURLEncoding.DecodeString(previousDID.Signature) + if err != nil { + return errors.Wrap(err, "failed to decode the previous DID's signature") + } + if ok := ed25519.Verify(previousDIDKey, identityKey, decodedSignature); !ok { + return errors.New("the previous DID signature is invalid") + } + return nil } func parseTxtData(data string) map[string]string { diff --git a/impl/internal/did/did_test.go b/impl/internal/did/did_test.go index fe72b545..cee0203c 100644 --- a/impl/internal/did/did_test.go +++ b/impl/internal/did/did_test.go @@ -3,15 +3,13 @@ package did import ( "crypto/ed25519" "fmt" - "strings" "testing" - "github.com/TBD54566975/ssi-sdk/cryptosuite" - "github.com/goccy/go-json" - "github.com/TBD54566975/ssi-sdk/crypto" "github.com/TBD54566975/ssi-sdk/crypto/jwx" + "github.com/TBD54566975/ssi-sdk/cryptosuite" "github.com/TBD54566975/ssi-sdk/did" + "github.com/goccy/go-json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -126,20 +124,22 @@ func TestToDNSPacket(t *testing.T) { require.NotEmpty(t, doc) didID := DHT(doc.ID) - packet, err := didID.ToDNSPacket(*doc, nil, nil) + packet, err := didID.ToDNSPacket(*doc, nil, nil, nil) require.NoError(t, err) require.NotEmpty(t, packet) - decodedDoc, types, gateways, err := didID.FromDNSPacket(packet) + didDHTDoc, err := didID.FromDNSPacket(packet) require.NoError(t, err) - require.NotEmpty(t, decodedDoc) - require.Empty(t, types) - require.Empty(t, gateways) + require.NotEmpty(t, didDHTDoc) + require.NotEmpty(t, didDHTDoc.Doc) + require.Empty(t, didDHTDoc.Types) + require.Empty(t, didDHTDoc.Gateways) + require.Empty(t, didDHTDoc.PreviousDID) jsonDoc, err := json.Marshal(doc) require.NoError(t, err) - jsonDecodedDoc, err := json.Marshal(decodedDoc) + jsonDecodedDoc, err := json.Marshal(didDHTDoc.Doc) require.NoError(t, err) assert.JSONEq(t, string(jsonDoc), string(jsonDecodedDoc)) @@ -152,19 +152,21 @@ func TestToDNSPacket(t *testing.T) { require.NotEmpty(t, doc) didID := DHT(doc.ID) - packet, err := didID.ToDNSPacket(*doc, []TypeIndex{1, 2, 3}, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}) + packet, err := didID.ToDNSPacket(*doc, []TypeIndex{1, 2, 3}, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}, nil) require.NoError(t, err) require.NotEmpty(t, packet) - decodedDoc, types, gateways, err := didID.FromDNSPacket(packet) + didDHTDoc, err := didID.FromDNSPacket(packet) require.NoError(t, err) - require.NotEmpty(t, decodedDoc) - require.NotEmpty(t, types) - require.Equal(t, types, []TypeIndex{1, 2, 3}) - require.NotEmpty(t, gateways) - require.Equal(t, gateways, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}) + require.NotEmpty(t, didDHTDoc) + require.NotEmpty(t, didDHTDoc.Doc) + require.NotEmpty(t, didDHTDoc.Types) + require.Empty(t, didDHTDoc.PreviousDID) + require.Equal(t, didDHTDoc.Types, []TypeIndex{1, 2, 3}) + require.NotEmpty(t, didDHTDoc.Gateways) + require.Equal(t, didDHTDoc.Gateways, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}) - assert.EqualValues(t, *doc, *decodedDoc) + assert.EqualValues(t, *doc, didDHTDoc.Doc) }) t.Run("doc with multiple keys and services - test to dns packet round trip", func(t *testing.T) { @@ -205,17 +207,19 @@ func TestToDNSPacket(t *testing.T) { require.NotEmpty(t, doc) didID := DHT(doc.ID) - packet, err := didID.ToDNSPacket(*doc, nil, nil) + packet, err := didID.ToDNSPacket(*doc, nil, nil, nil) require.NoError(t, err) require.NotEmpty(t, packet) - decodedDoc, types, gateways, err := didID.FromDNSPacket(packet) + didDHTDoc, err := didID.FromDNSPacket(packet) require.NoError(t, err) - require.NotEmpty(t, decodedDoc) - require.Empty(t, types) - require.Empty(t, gateways) + require.NotEmpty(t, didDHTDoc) + require.NotEmpty(t, didDHTDoc.Doc) + require.Empty(t, didDHTDoc.Types) + require.Empty(t, didDHTDoc.Gateways) + require.Empty(t, didDHTDoc.PreviousDID) - decodedJSON, err := json.Marshal(decodedDoc) + decodedJSON, err := json.Marshal(didDHTDoc.Doc) require.NoError(t, err) docJSON, err := json.Marshal(doc) @@ -225,273 +229,7 @@ func TestToDNSPacket(t *testing.T) { }) } -func TestVectors(t *testing.T) { - type testVectorDNSRecord struct { - Name string `json:"name"` - RecordType string `json:"type"` - TTL string `json:"ttl"` - Record []string `json:"rdata"` - } - - t.Run("test vector 1", func(t *testing.T) { - var pubKeyJWK jwx.PublicKeyJWK - retrieveTestVectorAs(t, vector1PublicKeyJWK1, &pubKeyJWK) - - pubKey, err := pubKeyJWK.ToPublicKey() - require.NoError(t, err) - - doc, err := CreateDIDDHTDID(pubKey.(ed25519.PublicKey), CreateDIDDHTOpts{}) - require.NoError(t, err) - require.NotEmpty(t, doc) - - var expectedDIDDocument did.Document - retrieveTestVectorAs(t, vector1DIDDocument, &expectedDIDDocument) - - docJSON, err := json.Marshal(doc) - require.NoError(t, err) - - expectedDIDDocJSON, err := json.Marshal(expectedDIDDocument) - require.NoError(t, err) - - assert.JSONEq(t, string(expectedDIDDocJSON), string(docJSON)) - - didID := DHT(doc.ID) - packet, err := didID.ToDNSPacket(*doc, nil, nil) - require.NoError(t, err) - require.NotEmpty(t, packet) - - var expectedDNSRecords []testVectorDNSRecord - retrieveTestVectorAs(t, vector1DNSRecords, &expectedDNSRecords) - - // Initialize a map to track matched records - matchedRecords := make(map[int]bool) - for i := range expectedDNSRecords { - matchedRecords[i] = false // Initialize all expected records as unmatched - } - - for _, record := range packet.Answer { - for i, expectedRecord := range expectedDNSRecords { - if record.Header().Name == expectedRecord.Name { - s := record.String() - if strings.Contains(s, expectedRecord.RecordType) && - strings.Contains(s, expectedRecord.TTL) && - strings.Contains(s, strings.Join(expectedRecord.Record, "")) { - matchedRecords[i] = true // Mark as matched - break - } - } - } - } - - // Check if all expected records have been matched - for i, matched := range matchedRecords { - require.True(t, matched, fmt.Sprintf("Expected DNS record %d: %+v not matched", i, expectedDNSRecords[i])) - } - - // Make sure going back to DID Document is consistent - decodedDoc, types, gateways, err := didID.FromDNSPacket(packet) - require.NoError(t, err) - require.NotEmpty(t, decodedDoc) - require.Empty(t, types) - require.Empty(t, gateways) - - decodedDocJSON, err := json.Marshal(decodedDoc) - require.NoError(t, err) - assert.JSONEq(t, string(expectedDIDDocJSON), string(decodedDocJSON)) - }) - - t.Run("test vector 2", func(t *testing.T) { - var pubKeyJWK jwx.PublicKeyJWK - retrieveTestVectorAs(t, vector1PublicKeyJWK1, &pubKeyJWK) - - pubKey, err := pubKeyJWK.ToPublicKey() - require.NoError(t, err) - - var secpJWK jwx.PublicKeyJWK - retrieveTestVectorAs(t, vector2PublicKeyJWK2, &secpJWK) - - doc, err := CreateDIDDHTDID(pubKey.(ed25519.PublicKey), CreateDIDDHTOpts{ - AuthoritativeGateways: []string{ - "gateway1.example-did-dht-gateway.com.", - "gateway2.example-did-dht-gateway.com.", - }, - Controller: []string{"did:example:abcd"}, - AlsoKnownAs: []string{"did:example:efgh", "did:example:ijkl"}, - VerificationMethods: []VerificationMethod{ - { - VerificationMethod: did.VerificationMethod{ - ID: secpJWK.KID, - Type: cryptosuite.JSONWebKeyType, - PublicKeyJWK: &secpJWK, - }, - Purposes: []did.PublicKeyPurpose{did.AssertionMethod, did.CapabilityInvocation}, - }, - }, - Services: []did.Service{ - { - ID: "service-1", - Type: "TestService", - ServiceEndpoint: []string{"https://test-service.com/1", "https://test-service.com/2"}, - }, - }, - }) - require.NoError(t, err) - require.NotEmpty(t, doc) - - var expectedDIDDocument did.Document - retrieveTestVectorAs(t, vector2DIDDocument, &expectedDIDDocument) - - docJSON, err := json.Marshal(doc) - require.NoError(t, err) - - expectedDIDDocJSON, err := json.Marshal(expectedDIDDocument) - require.NoError(t, err) - - assert.JSONEq(t, string(expectedDIDDocJSON), string(docJSON)) - - didID := DHT(doc.ID) - packet, err := didID.ToDNSPacket(*doc, []TypeIndex{1, 2, 3}, - []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com.", "gateway2.example-did-dht-gateway.com."}) - require.NoError(t, err) - require.NotEmpty(t, packet) - - var expectedDNSRecords []testVectorDNSRecord - retrieveTestVectorAs(t, vector2DNSRecords, &expectedDNSRecords) - - // Initialize a map to track matched records - matchedRecords := make(map[int]bool) - for i := range expectedDNSRecords { - matchedRecords[i] = false // Initialize all expected records as unmatched - } - - for _, record := range packet.Answer { - for i, expectedRecord := range expectedDNSRecords { - if record.Header().Name == expectedRecord.Name { - s := record.String() - if strings.Contains(s, expectedRecord.RecordType) && - strings.Contains(s, expectedRecord.TTL) && - strings.Contains(s, strings.Join(expectedRecord.Record, "")) { - matchedRecords[i] = true // Mark as matched - break - } - } - } - } - - // Check if all expected records have been matched - for i, matched := range matchedRecords { - require.True(t, matched, fmt.Sprintf("Expected DNS record %d: %+v not matched", i, expectedDNSRecords[i])) - } - - // Make sure going back to DID Document is consistent - decodedDoc, types, gateways, err := didID.FromDNSPacket(packet) - require.NoError(t, err) - require.NotEmpty(t, decodedDoc) - require.NotEmpty(t, types) - require.Equal(t, types, []TypeIndex{1, 2, 3}) - require.NotEmpty(t, gateways) - require.Equal(t, gateways, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com.", "gateway2.example-did-dht-gateway.com."}) - - decodedDocJSON, err := json.Marshal(decodedDoc) - require.NoError(t, err) - assert.JSONEq(t, string(expectedDIDDocJSON), string(decodedDocJSON)) - }) - - t.Run("test vector 3", func(t *testing.T) { - var pubKeyJWK jwx.PublicKeyJWK - retrieveTestVectorAs(t, vector3PublicKeyJWK1, &pubKeyJWK) - - pubKey, err := pubKeyJWK.ToPublicKey() - require.NoError(t, err) - - var x25519JWK jwx.PublicKeyJWK - retrieveTestVectorAs(t, vector3PublicKeyJWK2, &x25519JWK) - - doc, err := CreateDIDDHTDID(pubKey.(ed25519.PublicKey), CreateDIDDHTOpts{ - VerificationMethods: []VerificationMethod{ - { - VerificationMethod: did.VerificationMethod{ - ID: x25519JWK.KID, - Type: cryptosuite.JSONWebKeyType, - PublicKeyJWK: &x25519JWK, - }, - Purposes: []did.PublicKeyPurpose{did.KeyAgreement}, - }, - }, - Services: []did.Service{ - { - ID: "service-1", - Type: "TestLongService", - ServiceEndpoint: []string{"https://test-lllllllllllllllllllllllllllllllllllooooooooooooooooooooonnnnnnnnnnnnnnnnnnngggggggggggggggggggggggggggggggggggggsssssssssssssssssssssssssseeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrvvvvvvvvvvvvvvvvvvvviiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiccccccccccccccccccccccccccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.com/1"}, - }, - }, - }) - require.NoError(t, err) - require.NotEmpty(t, doc) - - var expectedDIDDocument did.Document - retrieveTestVectorAs(t, vector3DIDDocument, &expectedDIDDocument) - - docJSON, err := json.Marshal(doc) - require.NoError(t, err) - - expectedDIDDocJSON, err := json.Marshal(expectedDIDDocument) - require.NoError(t, err) - - assert.JSONEq(t, string(expectedDIDDocJSON), string(docJSON)) - didID := DHT(doc.ID) - packet, err := didID.ToDNSPacket(*doc, nil, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}) - require.NoError(t, err) - require.NotEmpty(t, packet) - - var expectedDNSRecords []testVectorDNSRecord - retrieveTestVectorAs(t, vector3DNSRecords, &expectedDNSRecords) - - // Initialize a map to track matched records - matchedRecords := make(map[int]bool) - for i := range expectedDNSRecords { - matchedRecords[i] = false // Initialize all expected records as unmatched - } - - for _, record := range packet.Answer { - for i, expectedRecord := range expectedDNSRecords { - if record.Header().Name == expectedRecord.Name { - s := record.String() - if strings.Contains(s, expectedRecord.RecordType) && - strings.Contains(s, expectedRecord.TTL) { - // make sure all parts of the record are contained within s - for _, r := range expectedRecord.Record { - if !strings.Contains(s, r) { - break - } - } - matchedRecords[i] = true // Mark as matched - break - } - } - } - } - - // Check if all expected records have been matched - for i, matched := range matchedRecords { - require.True(t, matched, fmt.Sprintf("Expected DNS record %d: %+v not matched", i, expectedDNSRecords[i])) - } - - // Make sure going back to DID Document is consistent - decodedDoc, types, gateways, err := didID.FromDNSPacket(packet) - require.NoError(t, err) - require.NotEmpty(t, decodedDoc) - require.Empty(t, types) - require.NotEmpty(t, gateways) - require.Equal(t, gateways, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}) - - decodedDocJSON, err := json.Marshal(decodedDoc) - require.NoError(t, err) - assert.JSONEq(t, string(expectedDIDDocJSON), string(decodedDocJSON)) - }) -} - -func TestMisc(t *testing.T) { +func TestDIDDHTFeatures(t *testing.T) { t.Run("DHT.Method()", func(t *testing.T) { privKey, doc, err := GenerateDIDDHT(CreateDIDDHTOpts{}) require.NoError(t, err) @@ -639,6 +377,73 @@ func TestMisc(t *testing.T) { assert.NoError(t, err) assert.NotEmpty(t, doc) }) + + t.Run("prev", func(t *testing.T) { + previousPrivKey, previousDoc, err := GenerateDIDDHT(CreateDIDDHTOpts{}) + require.NoError(t, err) + require.NotEmpty(t, previousPrivKey) + require.NotEmpty(t, previousDoc) + + previousDIDDHT := DHT(previousDoc.ID) + previousDID, err := CreatePreviousDIDRecord(previousPrivKey, previousDIDDHT, "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy") + assert.NoError(t, err) + assert.NotEmpty(t, previousDID) + + println(previousDID.PreviousDID.String()) + println(previousDID.Signature) + }) + + t.Run("Test Previous DID", func(t *testing.T) { + // generate previous DID + previousPrivKey, previousDoc, err := GenerateDIDDHT(CreateDIDDHTOpts{}) + require.NoError(t, err) + require.NotEmpty(t, previousPrivKey) + require.NotEmpty(t, previousDoc) + + // generate new DID + doc, err := CreateDIDDHTDID(pubKey.(ed25519.PublicKey), CreateDIDDHTOpts{}) + assert.NoError(t, err) + assert.NotEmpty(t, doc) + + // set previous DID signature + previousDIDDHT := DHT(previousDoc.ID) + currentDIDDHT := DHT(doc.ID) + previousDID, err := CreatePreviousDIDRecord(previousPrivKey, previousDIDDHT, currentDIDDHT) + assert.NoError(t, err) + assert.NotEmpty(t, previousDID) + assert.NotEmpty(t, previousDID.PreviousDID) + assert.Equal(t, previousDID.PreviousDID.String(), previousDoc.ID) + assert.NotEmpty(t, previousDID.Signature) + + // validate previous DID signature + err = ValidatePreviousDIDSignatureValid(currentDIDDHT, *previousDID) + assert.NoError(t, err) + + // construct the DNS packet with the previous DID entry + dnsMsg, err := currentDIDDHT.ToDNSPacket(*doc, nil, nil, previousDID) + assert.NoError(t, err) + assert.NotEmpty(t, dnsMsg) + + // parse the DNS packet + didDHTDoc, err := currentDIDDHT.FromDNSPacket(dnsMsg) + assert.NoError(t, err) + assert.NotEmpty(t, didDHTDoc) + assert.NotEmpty(t, didDHTDoc.Doc) + assert.NotEmpty(t, didDHTDoc.PreviousDID) + assert.Equal(t, didDHTDoc.PreviousDID.PreviousDID.String(), previousDoc.ID) + assert.NotEmpty(t, didDHTDoc.PreviousDID.Signature) + + // validate previous DID signature with wrong DID + err = ValidatePreviousDIDSignatureValid("did:dht:123456789abcdefghi", *previousDID) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to get identity key from the current DID") + + // validate previous DID signature with wrong signature + previousDID.Signature = "wrong signature" + err = ValidatePreviousDIDSignatureValid(currentDIDDHT, *previousDID) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to decode the previous DID's signature") + }) } func TestCreationFailures(t *testing.T) { diff --git a/impl/internal/did/did_vectors_test.go b/impl/internal/did/did_vectors_test.go new file mode 100644 index 00000000..7f5a22bf --- /dev/null +++ b/impl/internal/did/did_vectors_test.go @@ -0,0 +1,291 @@ +package did + +import ( + "crypto/ed25519" + "fmt" + "strings" + "testing" + + "github.com/TBD54566975/ssi-sdk/crypto/jwx" + "github.com/TBD54566975/ssi-sdk/cryptosuite" + "github.com/TBD54566975/ssi-sdk/did" + "github.com/goccy/go-json" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestVectors from the spec https://did-dht.com/#test-vectors +func TestVectors(t *testing.T) { + type testVectorDNSRecord struct { + Name string `json:"name"` + RecordType string `json:"type"` + TTL string `json:"ttl"` + Record []string `json:"rdata"` + } + + // https://did-dht.com/#vector-1 + t.Run("test vector 1", func(t *testing.T) { + var pubKeyJWK jwx.PublicKeyJWK + retrieveTestVectorAs(t, vector1PublicKeyJWK1, &pubKeyJWK) + + pubKey, err := pubKeyJWK.ToPublicKey() + require.NoError(t, err) + + doc, err := CreateDIDDHTDID(pubKey.(ed25519.PublicKey), CreateDIDDHTOpts{}) + require.NoError(t, err) + require.NotEmpty(t, doc) + + var expectedDIDDocument did.Document + retrieveTestVectorAs(t, vector1DIDDocument, &expectedDIDDocument) + + docJSON, err := json.Marshal(doc) + require.NoError(t, err) + + expectedDIDDocJSON, err := json.Marshal(expectedDIDDocument) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedDIDDocJSON), string(docJSON)) + + didID := DHT(doc.ID) + packet, err := didID.ToDNSPacket(*doc, nil, nil, nil) + require.NoError(t, err) + require.NotEmpty(t, packet) + + var expectedDNSRecords []testVectorDNSRecord + retrieveTestVectorAs(t, vector1DNSRecords, &expectedDNSRecords) + + // Initialize a map to track matched records + matchedRecords := make(map[int]bool) + for i := range expectedDNSRecords { + matchedRecords[i] = false // Initialize all expected records as unmatched + } + + for _, record := range packet.Answer { + for i, expectedRecord := range expectedDNSRecords { + if record.Header().Name == expectedRecord.Name { + s := record.String() + if strings.Contains(s, expectedRecord.RecordType) && + strings.Contains(s, expectedRecord.TTL) && + strings.Contains(s, strings.Join(expectedRecord.Record, "")) { + matchedRecords[i] = true // Mark as matched + break + } + } + } + } + + // Check if all expected records have been matched + for i, matched := range matchedRecords { + require.True(t, matched, fmt.Sprintf("Expected DNS record %d: %+v not matched", i, expectedDNSRecords[i])) + } + + // Make sure going back to DID Document is consistent + didDHTDoc, err := didID.FromDNSPacket(packet) + require.NoError(t, err) + require.NotEmpty(t, didDHTDoc) + require.NotEmpty(t, didDHTDoc.Doc) + require.Empty(t, didDHTDoc.Types) + require.Empty(t, didDHTDoc.Gateways) + + decodedDocJSON, err := json.Marshal(didDHTDoc.Doc) + require.NoError(t, err) + assert.JSONEq(t, string(expectedDIDDocJSON), string(decodedDocJSON)) + }) + + // https://did-dht.com/#vector-2 + t.Run("test vector 2", func(t *testing.T) { + var pubKeyJWK jwx.PublicKeyJWK + retrieveTestVectorAs(t, vector1PublicKeyJWK1, &pubKeyJWK) + + pubKey, err := pubKeyJWK.ToPublicKey() + require.NoError(t, err) + + var secpJWK jwx.PublicKeyJWK + retrieveTestVectorAs(t, vector2PublicKeyJWK2, &secpJWK) + + doc, err := CreateDIDDHTDID(pubKey.(ed25519.PublicKey), CreateDIDDHTOpts{ + Controller: []string{"did:example:abcd"}, + AlsoKnownAs: []string{"did:example:efgh", "did:example:ijkl"}, + VerificationMethods: []VerificationMethod{ + { + VerificationMethod: did.VerificationMethod{ + ID: secpJWK.KID, + Type: cryptosuite.JSONWebKeyType, + PublicKeyJWK: &secpJWK, + }, + Purposes: []did.PublicKeyPurpose{did.AssertionMethod, did.CapabilityInvocation}, + }, + }, + Services: []did.Service{ + { + ID: "service-1", + Type: "TestService", + ServiceEndpoint: []string{"https://test-service.com/1", "https://test-service.com/2"}, + }, + }, + }) + require.NoError(t, err) + require.NotEmpty(t, doc) + + var expectedDIDDocument did.Document + retrieveTestVectorAs(t, vector2DIDDocument, &expectedDIDDocument) + + docJSON, err := json.Marshal(doc) + require.NoError(t, err) + + expectedDIDDocJSON, err := json.Marshal(expectedDIDDocument) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedDIDDocJSON), string(docJSON)) + + didID := DHT(doc.ID) + packet, err := didID.ToDNSPacket(*doc, []TypeIndex{1, 2, 3}, + []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com.", "gateway2.example-did-dht-gateway.com."}, nil) + require.NoError(t, err) + require.NotEmpty(t, packet) + + var expectedDNSRecords []testVectorDNSRecord + retrieveTestVectorAs(t, vector2DNSRecords, &expectedDNSRecords) + + // Initialize a map to track matched records + matchedRecords := make(map[int]bool) + for i := range expectedDNSRecords { + matchedRecords[i] = false // Initialize all expected records as unmatched + } + + for _, record := range packet.Answer { + for i, expectedRecord := range expectedDNSRecords { + if record.Header().Name == expectedRecord.Name { + s := record.String() + if strings.Contains(s, expectedRecord.RecordType) && + strings.Contains(s, expectedRecord.TTL) && + strings.Contains(s, strings.Join(expectedRecord.Record, "")) { + matchedRecords[i] = true // Mark as matched + break + } + } + } + } + + // Check if all expected records have been matched + for i, matched := range matchedRecords { + require.True(t, matched, fmt.Sprintf("Expected DNS record %d: %+v not matched", i, expectedDNSRecords[i])) + } + + // Make sure going back to DID Document is consistent + didDHTDoc, err := didID.FromDNSPacket(packet) + require.NoError(t, err) + require.NotEmpty(t, didDHTDoc) + require.NotEmpty(t, didDHTDoc.Doc) + require.NotEmpty(t, didDHTDoc.Types) + require.Equal(t, didDHTDoc.Types, []TypeIndex{1, 2, 3}) + require.NotEmpty(t, didDHTDoc.Gateways) + require.Equal(t, didDHTDoc.Gateways, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com.", "gateway2.example-did-dht-gateway.com."}) + require.Empty(t, didDHTDoc.PreviousDID) + + decodedDocJSON, err := json.Marshal(didDHTDoc.Doc) + require.NoError(t, err) + assert.JSONEq(t, string(expectedDIDDocJSON), string(decodedDocJSON)) + }) + + // https://did-dht.com/#vector-3 + t.Run("test vector 3", func(t *testing.T) { + var pubKeyJWK jwx.PublicKeyJWK + retrieveTestVectorAs(t, vector3PublicKeyJWK1, &pubKeyJWK) + + pubKey, err := pubKeyJWK.ToPublicKey() + require.NoError(t, err) + + var x25519JWK jwx.PublicKeyJWK + retrieveTestVectorAs(t, vector3PublicKeyJWK2, &x25519JWK) + + doc, err := CreateDIDDHTDID(pubKey.(ed25519.PublicKey), CreateDIDDHTOpts{ + VerificationMethods: []VerificationMethod{ + { + VerificationMethod: did.VerificationMethod{ + ID: x25519JWK.KID, + Type: cryptosuite.JSONWebKeyType, + PublicKeyJWK: &x25519JWK, + }, + Purposes: []did.PublicKeyPurpose{did.KeyAgreement}, + }, + }, + Services: []did.Service{ + { + ID: "service-1", + Type: "TestLongService", + ServiceEndpoint: []string{"https://test-lllllllllllllllllllllllllllllllllllooooooooooooooooooooonnnnnnnnnnnnnnnnnnngggggggggggggggggggggggggggggggggggggsssssssssssssssssssssssssseeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrvvvvvvvvvvvvvvvvvvvviiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiccccccccccccccccccccccccccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.com/1"}, + }, + }, + }) + require.NoError(t, err) + require.NotEmpty(t, doc) + + var expectedDIDDocument did.Document + retrieveTestVectorAs(t, vector3DIDDocument, &expectedDIDDocument) + + docJSON, err := json.Marshal(doc) + require.NoError(t, err) + + expectedDIDDocJSON, err := json.Marshal(expectedDIDDocument) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedDIDDocJSON), string(docJSON)) + didID := DHT(doc.ID) + previousDID := PreviousDID{ + PreviousDID: "did:dht:x3heus3ke8fhgb5pbecday9wtbfynd6m19q4pm6gcf5j356qhjzo", + Signature: "Tt9DRT6J32v7O2lzbfasW63_FfagiMHTHxtaEOD7p85zHE0r_EfiNleyL6BZGyB1P-oQ5p6_7KONaHAjr2K6Bw", + } + packet, err := didID.ToDNSPacket(*doc, nil, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}, &previousDID) + require.NoError(t, err) + require.NotEmpty(t, packet) + + var expectedDNSRecords []testVectorDNSRecord + retrieveTestVectorAs(t, vector3DNSRecords, &expectedDNSRecords) + + // Initialize a map to track matched records + matchedRecords := make(map[int]bool) + for i := range expectedDNSRecords { + matchedRecords[i] = false // Initialize all expected records as unmatched + } + + for _, record := range packet.Answer { + for i, expectedRecord := range expectedDNSRecords { + if record.Header().Name == expectedRecord.Name { + s := record.String() + if strings.Contains(s, expectedRecord.RecordType) && + strings.Contains(s, expectedRecord.TTL) { + // make sure all parts of the record are contained within s + for _, r := range expectedRecord.Record { + if !strings.Contains(s, r) { + break + } + } + matchedRecords[i] = true // Mark as matched + break + } + } + } + } + + // Check if all expected records have been matched + for i, matched := range matchedRecords { + require.True(t, matched, fmt.Sprintf("Expected DNS record %d: %+v not matched", i, expectedDNSRecords[i])) + } + + // Make sure going back to DID Document is consistent + didDHTDoc, err := didID.FromDNSPacket(packet) + require.NoError(t, err) + require.NotEmpty(t, didDHTDoc) + require.NotEmpty(t, didDHTDoc.Doc) + require.Empty(t, didDHTDoc.Types) + require.NotEmpty(t, didDHTDoc.Gateways) + require.Equal(t, didDHTDoc.Gateways, []AuthoritativeGateway{"gateway1.example-did-dht-gateway.com."}) + require.NotEmpty(t, didDHTDoc.PreviousDID) + require.Equal(t, didDHTDoc.PreviousDID.PreviousDID.String(), "did:dht:x3heus3ke8fhgb5pbecday9wtbfynd6m19q4pm6gcf5j356qhjzo") + + decodedDocJSON, err := json.Marshal(didDHTDoc.Doc) + require.NoError(t, err) + assert.JSONEq(t, string(expectedDIDDocJSON), string(decodedDocJSON)) + }) +} diff --git a/impl/internal/did/testdata/vector-3-dns-records.json b/impl/internal/did/testdata/vector-3-dns-records.json index 4f250e9f..cc123891 100644 --- a/impl/internal/did/testdata/vector-3-dns-records.json +++ b/impl/internal/did/testdata/vector-3-dns-records.json @@ -1,4 +1,10 @@ [ + { + "name": "_prv._did.", + "type": "TXT", + "ttl": "7200", + "rdata": ["id=did:dht:x3heus3ke8fhgb5pbecday9wtbfynd6m19q4pm6gcf5j356qhjzo;s=Tt9DRT6J32v7O2lzbfasW63_FfagiMHTHxtaEOD7p85zHE0r_EfiNleyL6BZGyB1P-oQ5p6_7KONaHAjr2K6Bw"] + }, { "name": "_did.sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy.", "type": "NS", diff --git a/impl/pkg/dht/dns_test.go b/impl/pkg/dht/dns_test.go index 431e4a0c..cee05c3f 100644 --- a/impl/pkg/dht/dns_test.go +++ b/impl/pkg/dht/dns_test.go @@ -100,7 +100,7 @@ func TestGetPutDIDDHT(t *testing.T) { require.NotEmpty(t, doc) didID := did.DHT(doc.ID) - didDocPacket, err := didID.ToDNSPacket(*doc, nil, nil) + didDocPacket, err := didID.ToDNSPacket(*doc, nil, nil, nil) require.NoError(t, err) putReq, err := CreateDNSPublishRequest(privKey, *didDocPacket) @@ -119,7 +119,7 @@ func TestGetPutDIDDHT(t *testing.T) { require.NotEmpty(t, gotMsg.Answer) d := did.DHT("did:dht:" + gotID) - gotDoc, _, _, err := d.FromDNSPacket(gotMsg) + gotDoc, err := d.FromDNSPacket(gotMsg) require.NoError(t, err) require.NotEmpty(t, gotDoc) } diff --git a/impl/pkg/dht/record_test.go b/impl/pkg/dht/record_test.go index 4be14fce..4875dd3a 100644 --- a/impl/pkg/dht/record_test.go +++ b/impl/pkg/dht/record_test.go @@ -22,7 +22,7 @@ func TestNewRecord(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) diff --git a/impl/pkg/server/dht_test.go b/impl/pkg/server/dht_test.go index 64e068d3..01470ac1 100644 --- a/impl/pkg/server/dht_test.go +++ b/impl/pkg/server/dht_test.go @@ -184,7 +184,7 @@ func generateDIDPutRequest(t *testing.T) (string, []byte) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) diff --git a/impl/pkg/service/dht_test.go b/impl/pkg/service/dht_test.go index f2affb02..2042677c 100644 --- a/impl/pkg/service/dht_test.go +++ b/impl/pkg/service/dht_test.go @@ -44,7 +44,7 @@ func TestDHTService(t *testing.T) { require.NotEmpty(t, doc) d := did.DHT(doc.ID) - packet, err := d.ToDNSPacket(*doc, nil, nil) + packet, err := d.ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) @@ -71,7 +71,7 @@ func TestDHTService(t *testing.T) { require.NotEmpty(t, doc) d := did.DHT(doc.ID) - packet, err := d.ToDNSPacket(*doc, nil, nil) + packet, err := d.ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) @@ -99,7 +99,7 @@ func TestDHTService(t *testing.T) { require.NotEmpty(t, doc) d := did.DHT(doc.ID) - packet, err := d.ToDNSPacket(*doc, nil, nil) + packet, err := d.ToDNSPacket(*doc, nil, nil, nil) require.NoError(t, err) require.NotEmpty(t, packet) @@ -146,7 +146,7 @@ func TestDHT(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) d := did.DHT(doc.ID) - packet, err := d.ToDNSPacket(*doc, nil, nil) + packet, err := d.ToDNSPacket(*doc, nil, nil, nil) require.NoError(t, err) require.NotEmpty(t, packet) putMsg, err := dht.CreateDNSPublishRequest(sk, *packet) diff --git a/impl/pkg/storage/db/bolt/bolt_test.go b/impl/pkg/storage/db/bolt/bolt_test.go index 7d9ee485..5ce89542 100644 --- a/impl/pkg/storage/db/bolt/bolt_test.go +++ b/impl/pkg/storage/db/bolt/bolt_test.go @@ -118,7 +118,7 @@ func TestReadWrite(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) require.NoError(t, err) require.NotEmpty(t, packet) @@ -163,7 +163,7 @@ func TestDBPagination(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) @@ -184,7 +184,7 @@ func TestDBPagination(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) diff --git a/impl/pkg/storage/db/postgres/postgres_test.go b/impl/pkg/storage/db/postgres/postgres_test.go index 028146d0..ce8f598e 100644 --- a/impl/pkg/storage/db/postgres/postgres_test.go +++ b/impl/pkg/storage/db/postgres/postgres_test.go @@ -3,6 +3,7 @@ package postgres_test import ( "context" "net/url" + "os" "testing" "github.com/stretchr/testify/assert" @@ -15,7 +16,7 @@ import ( ) func getTestDB(t *testing.T) storage.Storage { - uri := "postgres://postgres:a@127.0.0.1:5432/postgres" // os.Getenv("TEST_DB") + uri := os.Getenv("TEST_DB") if uri == "" { t.SkipNow() } @@ -44,7 +45,7 @@ func TestReadWrite(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) require.NoError(t, err) require.NotEmpty(t, packet) @@ -89,7 +90,7 @@ func TestDBPagination(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) @@ -110,7 +111,7 @@ func TestDBPagination(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, doc) - packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil) + packet, err := did.DHT(doc.ID).ToDNSPacket(*doc, nil, nil, nil) assert.NoError(t, err) assert.NotEmpty(t, packet) diff --git a/spec/spec.md b/spec/spec.md index 72149bbd..1a3f0080 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -9,7 +9,7 @@ The DID DHT Method Specification 1.0 **Draft Created:** October 20, 2023 -**Latest Update:** April 22, 2024 +**Last Updated:** April 29, 2024 **Editors:** ~ [Gabe Cohen](https://github.com/decentralgabe) @@ -26,7 +26,8 @@ The DID DHT Method Specification 1.0 ## Abstract -A DID Method [[spec:DID-CORE]] based on [[ref:DNS Resource Records]] and [[ref:Mainline DHT]], identified by the prefix `did:dht`. +A DID Method [[spec:DID-CORE]] based on [[ref:DNS Resource Records]] and [[ref:Mainline DHT]], identified by the prefix +`did:dht`.