From 4876860f5401f7837875954ff31c9d7aa5817854 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 26 Jul 2024 16:28:55 +0200 Subject: [PATCH 01/26] add signature to resolver by parameter --- .golangci.yml | 10 +- Dockerfile | 2 +- README.md | 2 + cmd/driver/main.go | 2 +- go.mod | 4 + go.sum | 12 + pkg/app/configs/driver.go | 1 + pkg/app/handler.go | 11 +- pkg/document/did.go | 20 +- pkg/document/proof.go | 61 ++++ pkg/document/proof_test.go | 137 ++++++++ pkg/services/blockchain/eth/resolver.go | 342 ++++++++++++++++++- pkg/services/blockchain/eth/resolver_test.go | 175 +++++++++- pkg/services/did.go | 72 +++- pkg/services/registry.go | 11 + 15 files changed, 815 insertions(+), 47 deletions(-) create mode 100644 pkg/document/proof.go create mode 100644 pkg/document/proof_test.go diff --git a/.golangci.yml b/.golangci.yml index c547f45..96a41b5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -39,22 +39,16 @@ linters-settings: linters: enable: - bodyclose - - megacheck - revive - govet - unconvert - - megacheck - - structcheck - - gas + - gosec - gocyclo - dupl - misspell - unparam - - varcheck - - deadcode - typecheck - ineffassign - - varcheck - stylecheck - gochecknoinits - exportloopref @@ -66,6 +60,8 @@ linters: - errcheck - gofmt - goimports + - staticcheck + - unused fast: false disable-all: true diff --git a/Dockerfile b/Dockerfile index 80d0165..130d0d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ## ## Build did driver ## -FROM golang:1.18-alpine as base +FROM golang:1.18-alpine AS base WORKDIR /build diff --git a/README.md b/README.md index 726d57d..84f3516 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ Driver for the iden3 DID method amoy: contractAddress: "0xf6..." networkURL: "https://polygon-amoy..." + walletKey: "" ``` + `walletKey` is only needed for the resolver if it's a trusted resolver that includes signature of EIP712 message when requested in the resolution with `signature=EthereumEip712Signature2021`. 2. Build docker container: ```bash docker build -t driver-did-iden3:local diff --git a/cmd/driver/main.go b/cmd/driver/main.go index e697237..216c53b 100644 --- a/cmd/driver/main.go +++ b/cmd/driver/main.go @@ -63,7 +63,7 @@ func initResolvers() *services.ResolverRegistry { for chainName, chainSettings := range rs { for networkName, networkSettings := range chainSettings { prefix := fmt.Sprintf("%s:%s", chainName, networkName) - resolver, err := eth.NewResolver(networkSettings.NetworkURL, networkSettings.ContractAddress) + resolver, err := eth.NewResolver(networkSettings.NetworkURL, networkSettings.ContractAddress, networkSettings.WalletKey) if err != nil { log.Fatalf("failed configure resolver for network '%s': %v", prefix, err) } diff --git a/go.mod b/go.mod index 2aabf24..4f10990 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,8 @@ require ( ) require ( + github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/dchest/blake512 v1.0.0 // indirect github.com/deckarep/golang-set/v2 v2.2.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -55,6 +57,8 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect + github.com/tyler-smith/go-bip32 v1.0.0 + github.com/tyler-smith/go-bip39 v1.1.0 github.com/wealdtech/go-multicodec v1.4.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/sys v0.15.0 // indirect diff --git a/go.sum b/go.sum index e0b8dc9..eb756b6 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,8 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -20,6 +24,7 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= @@ -147,6 +152,7 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -155,7 +161,10 @@ github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+Kd github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE= +github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= github.com/wealdtech/go-ens/v3 v3.5.5 h1:/jq3CDItK0AsFnZtiFJK44JthkAMD5YE3WAJOh4i7lc= github.com/wealdtech/go-ens/v3 v3.5.5/go.mod h1:w0EDKIm0dIQnqEKls6ORat/or+AVfPEdEXVfN71EeEE= @@ -166,11 +175,13 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= @@ -220,5 +231,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= diff --git a/pkg/app/configs/driver.go b/pkg/app/configs/driver.go index 42c1479..e3be040 100644 --- a/pkg/app/configs/driver.go +++ b/pkg/app/configs/driver.go @@ -16,6 +16,7 @@ const defaultPathToResolverSettings = "./resolvers.settings.yaml" type ResolverSettings map[string]map[string]struct { ContractAddress string `yaml:"contractAddress"` NetworkURL string `yaml:"networkURL"` + WalletKey string `yaml:"walletKey"` } // Config structure represent yaml config for did driver. diff --git a/pkg/app/handler.go b/pkg/app/handler.go index cc8b45c..bb34000 100644 --- a/pkg/app/handler.go +++ b/pkg/app/handler.go @@ -23,6 +23,7 @@ func (d *DidDocumentHandler) Get(w http.ResponseWriter, r *http.Request) { opts, err := getResolverOpts( r.URL.Query().Get("state"), r.URL.Query().Get("gist"), + r.URL.Query().Get("signature"), ) if err != nil { log.Println("invalid options query:", err) @@ -96,7 +97,7 @@ func (d *DidDocumentHandler) GetGist(w http.ResponseWriter, r *http.Request) { gistInfo, err := d.DidDocumentService.GetGist(r.Context(), chain, networkid, nil) if errors.Is(err, services.ErrNetworkIsNotSupported) { w.WriteHeader(http.StatusNotFound) - fmt.Fprintf(w, `{"error":"resolver for '%s:%s' network not found"}`, chain, networkid) + log.Printf(`{"error":"resolver for '%s:%s' network not found"}`, chain, networkid) return } else if err != nil { log.Printf("failed get info about latest gist from network '%s:%s': %v\n", chain, networkid, err) @@ -110,7 +111,7 @@ func (d *DidDocumentHandler) GetGist(w http.ResponseWriter, r *http.Request) { } } -func getResolverOpts(state, gistRoot string) (ro services.ResolverOpts, err error) { +func getResolverOpts(state, gistRoot, signature string) (ro services.ResolverOpts, err error) { if state != "" && gistRoot != "" { return ro, errors.New("'state' and 'gist root' cannot be used together") } @@ -128,5 +129,11 @@ func getResolverOpts(state, gistRoot string) (ro services.ResolverOpts, err erro } ro.GistRoot = g.BigInt() } + if signature != "" { + if signature != "EthereumEip712Signature2021" { + return ro, fmt.Errorf("not supported signature type %s", signature) + } + ro.Signature = signature + } return } diff --git a/pkg/document/did.go b/pkg/document/did.go index 715e305..c4154c7 100644 --- a/pkg/document/did.go +++ b/pkg/document/did.go @@ -15,6 +15,7 @@ const ( ErrUnknownNetwork ErrorCode = "unknownNetwork" StateType = "Iden3StateInfo2023" + Iden3ResolutionMetadataType = "Iden3ResolutionMetadata" EcdsaSecp256k1RecoveryMethod2020Type = "EcdsaSecp256k1RecoveryMethod2020" ) @@ -24,6 +25,8 @@ const ( iden3Context = "https://schema.iden3.io/core/jsonld/auth.jsonld" EcdsaSecp256k1RecoveryContext = "https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-2.0.jsonld" defaultContentType = "application/did+ld+json" + iden3ResolutionContext = "https://schema.iden3.io/core/jsonld/resolution.jsonld" + eip712sigContext = "https://w3id.org/security/suites/eip712sig-2021/v1" ) // DidResolution representation of did resolution. @@ -45,6 +48,8 @@ func NewDidResolution() *DidResolution { VerificationMethod: []verifiable.CommonVerificationMethod{}, }, DidResolutionMetadata: &DidResolutionMetadata{ + Context: []string{iden3ResolutionContext}, + Type: Iden3ResolutionMetadataType, ContentType: defaultContentType, Retrieved: time.Now(), }, @@ -52,6 +57,10 @@ func NewDidResolution() *DidResolution { } } +func DidResolutionMetadataSigContext() []string { + return []string{iden3ResolutionContext, eip712sigContext} +} + func NewDidMethodNotSupportedResolution(msg string) *DidResolution { return NewDidErrorResolution(ErrMethodNotSupported, msg) } @@ -81,10 +90,13 @@ func NewDidErrorResolution(errCode ErrorCode, errMsg string) *DidResolution { // DidResolutionMetadata representation of resolution metadata. type DidResolutionMetadata struct { - Error ErrorCode `json:"error,omitempty"` - Message string `json:"message,omitempty"` - ContentType string `json:"contentType,omitempty"` - Retrieved time.Time `json:"retrieved,omitempty"` + Context []string `json:"@context,omitempty"` + Error ErrorCode `json:"error,omitempty"` + Message string `json:"message,omitempty"` + ContentType string `json:"contentType,omitempty"` + Retrieved time.Time `json:"retrieved,omitempty"` + Type string `json:"type,omitempty"` + Proof DidResolutionProofs `json:"proof,omitempty"` } // DidDocumentMetadata metadata of did document. diff --git a/pkg/document/proof.go b/pkg/document/proof.go new file mode 100644 index 0000000..c080932 --- /dev/null +++ b/pkg/document/proof.go @@ -0,0 +1,61 @@ +package document + +import ( + "encoding/json" + "errors" + "time" + + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/iden3/go-schema-processor/v2/verifiable" +) + +type DidResolutionProof interface { + ProofType() verifiable.ProofType +} + +type DidResolutionProofs []DidResolutionProof + +type EthereumEip712SignatureProof2021 struct { + Type verifiable.ProofType `json:"type"` + ProofPursopose string `json:"proofPurpose"` + ProofValue string `json:"proofValue"` + VerificationMethod string `json:"verificationMethod"` + Created time.Time `json:"created"` + Eip712 apitypes.TypedData `json:"eip712"` +} + +// EthereumEip712Signature2021Type is a proof type for EIP172 signature proofs +// nolint:stylecheck // we need to keep the name as it is +const EthereumEip712SignatureProof2021Type verifiable.ProofType = "EthereumEip712Signature2021" + +func (p *EthereumEip712SignatureProof2021) ProofType() verifiable.ProofType { + return p.Type +} + +func (p *EthereumEip712SignatureProof2021) UnmarshalJSON(in []byte) error { + var obj struct { + Type verifiable.ProofType `json:"type"` + ProofPursopose string `json:"proofPurpose"` + ProofValue string `json:"proofValue"` + VerificationMethod string `json:"verificationMethod"` + Created time.Time `json:"created"` + Eip712 json.RawMessage `json:"eip712"` + } + err := json.Unmarshal(in, &obj) + if err != nil { + return err + } + if obj.Type != EthereumEip712SignatureProof2021Type { + return errors.New("invalid proof type") + } + p.Type = obj.Type + err = json.Unmarshal(obj.Eip712, &p.Eip712) + if err != nil { + return err + } + p.VerificationMethod = obj.VerificationMethod + p.ProofPursopose = obj.ProofPursopose + p.ProofValue = obj.ProofValue + p.Created = obj.Created + return nil +} diff --git a/pkg/document/proof_test.go b/pkg/document/proof_test.go new file mode 100644 index 0000000..90f19d1 --- /dev/null +++ b/pkg/document/proof_test.go @@ -0,0 +1,137 @@ +package document + +import ( + "encoding/json" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/stretchr/testify/require" +) + +func TestEthereumEip712SignatureProof2021_JSONUnmarshal(t *testing.T) { + in := `{ + "type": "EthereumEip712Signature2021", + "proofPurpose": "assertionMethod", + "proofValue": "0xd5e5ffe290a258116a0f7acb4c9a5bbfdd842516061c6a794892b6db05fbd14706de7e189d965bead2ffb23e30d2f6b02ecf764e6fe24be788721049b7e331481c", + "verificationMethod": "did:pkh:eip155:1:0x5b18eF56aA61eeAE0E3434e3c3d8AEB19b141fe7#blockchainAccountId", + "created": "2021-09-23T20:21:34Z", + "eip712": { + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "IdentityState": [ + { "name": "from", "type": "address" }, + { "name": "timestamp", "type": "uint256" }, + { "name": "state", "type": "uint256" }, + { "name": "stateCreatedAtTimestamp", "type": "uint256" }, + { "name": "stateReplacedByState", "type": "uint256" }, + { "name": "stateReplacedAtTimestamp", "type": "uint256" }, + { "name": "gistRoot", "type": "uint256" }, + { "name": "gistRootCreatedAtTimestamp", "type": "uint256" }, + { "name": "gistRootReplacedByRoot", "type": "uint256" }, + { "name": "gistRootReplacedAtTimestamp", "type": "uint256" }, + { "name": "identity", "type": "uint256" } + ] + }, + "primaryType": "IdentityState", + "domain": { + "name": "StateInfo", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "from": "0x5b18eF56aA61eeAE0E3434e3c3d8AEB19b141fe7", + "timestamp": "0", + "state": "444", + "stateCreatedAtTimestamp": "0", + "stateReplacedByState": "0", + "stateReplacedAtTimestamp": "0", + "gistRoot": "555", + "gistRootCreatedAtTimestamp": "0", + "gistRootReplacedByRoot": "0", + "gistRootReplacedAtTimestamp": "0", + "identity": "19090607534999372304474213543962416547920895595808567155882840509226423042" + } + } + }` + var proof EthereumEip712SignatureProof2021 + err := json.Unmarshal([]byte(in), &proof) + require.NoError(t, err) + + timeParsed, _ := time.Parse("2006-01-02T15:04:05Z", "2021-09-23T20:21:34Z") + + var apiTypes = apitypes.Types{ + "IdentityState": []apitypes.Type{ + {Name: "from", Type: "address"}, + {Name: "timestamp", Type: "uint256"}, + {Name: "state", Type: "uint256"}, + {Name: "stateCreatedAtTimestamp", Type: "uint256"}, + {Name: "stateReplacedByState", Type: "uint256"}, + {Name: "stateReplacedAtTimestamp", Type: "uint256"}, + {Name: "gistRoot", Type: "uint256"}, + {Name: "gistRootCreatedAtTimestamp", Type: "uint256"}, + {Name: "gistRootReplacedByRoot", Type: "uint256"}, + {Name: "gistRootReplacedAtTimestamp", Type: "uint256"}, + {Name: "identity", Type: "uint256"}, + }, + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, + } + + var primaryType = "IdentityState" + walletAddress := "0x5b18eF56aA61eeAE0E3434e3c3d8AEB19b141fe7" + state := "444" + stateCreatedAtTimestamp := "0" + stateReplacedByState := "0" + stateReplacedAtTimestamp := "0" + gistRootCreatedAtTimestamp := "0" + gistRootReplacedByRoot := "0" + gistRootReplacedAtTimestamp := "0" + timestamp := "0" + gistRoot := "555" + identity := "19090607534999372304474213543962416547920895595808567155882840509226423042" + chainID := 1 + + wantProof := EthereumEip712SignatureProof2021{ + Type: "EthereumEip712Signature2021", + ProofPursopose: "assertionMethod", + ProofValue: "0xd5e5ffe290a258116a0f7acb4c9a5bbfdd842516061c6a794892b6db05fbd14706de7e189d965bead2ffb23e30d2f6b02ecf764e6fe24be788721049b7e331481c", + VerificationMethod: "did:pkh:eip155:1:0x5b18eF56aA61eeAE0E3434e3c3d8AEB19b141fe7#blockchainAccountId", + Created: timeParsed, + Eip712: apitypes.TypedData{ + Types: apiTypes, + PrimaryType: primaryType, + Domain: apitypes.TypedDataDomain{ + Name: "StateInfo", + Version: "1", + ChainId: math.NewHexOrDecimal256(int64(chainID)), + VerifyingContract: "0x0000000000000000000000000000000000000000", + }, + Message: apitypes.TypedDataMessage{ + "from": walletAddress, + "timestamp": timestamp, + "state": state, + "stateCreatedAtTimestamp": stateCreatedAtTimestamp, + "stateReplacedByState": stateReplacedByState, + "stateReplacedAtTimestamp": stateReplacedAtTimestamp, + "gistRoot": gistRoot, + "gistRootCreatedAtTimestamp": gistRootCreatedAtTimestamp, + "gistRootReplacedByRoot": gistRootReplacedByRoot, + "gistRootReplacedAtTimestamp": gistRootReplacedAtTimestamp, + "identity": identity, + }, + }, + } + require.Equal(t, wantProof, proof) +} diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 6210c3c..dd4ba24 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -2,13 +2,22 @@ package eth import ( "context" + "crypto/ecdsa" + "crypto/subtle" + "encoding/hex" "errors" "fmt" "math/big" + "strconv" + "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/signer/core/apitypes" contract "github.com/iden3/contracts-abi/state/go/abi" "github.com/iden3/driver-did-iden3/pkg/services" core "github.com/iden3/go-iden3-core/v2" @@ -30,6 +39,13 @@ type Resolver struct { contractAddress string chainID int + walletKey string +} + +type AuthData struct { + TypedData apitypes.TypedData + Signature string + Address string } var ( @@ -38,8 +54,45 @@ var ( stateNotFoundException = "execution reverted: State does not exist" ) +var IdentityStateAPITypes = apitypes.Types{ + "IdentityState": []apitypes.Type{ + {Name: "from", Type: "address"}, + {Name: "timestamp", Type: "uint256"}, + {Name: "identity", Type: "uint256"}, + {Name: "state", Type: "uint256"}, + {Name: "replacedByState", Type: "uint256"}, + {Name: "createdAtTimestamp", Type: "uint256"}, + {Name: "replacedAtTimestamp", Type: "uint256"}, + }, + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, +} + +var GlobalStateAPITypes = apitypes.Types{ + "GlobalState": []apitypes.Type{ + {Name: "from", Type: "address"}, + {Name: "timestamp", Type: "uint256"}, + {Name: "root", Type: "uint256"}, + {Name: "replacedByRoot", Type: "uint256"}, + {Name: "createdAtTimestamp", Type: "uint256"}, + {Name: "replacedAtTimestamp", Type: "uint256"}, + }, + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, +} + +var TimeStamp = TimeStampFn + // NewResolver create new ethereum resolver. -func NewResolver(url, address string) (*Resolver, error) { +func NewResolver(url, address, walletKey string) (*Resolver, error) { c, err := ethclient.Dial(url) if err != nil { return nil, err @@ -52,6 +105,7 @@ func NewResolver(url, address string) (*Resolver, error) { resolver := &Resolver{ state: sc, contractAddress: address, + walletKey: walletKey, } chainID, err := c.NetworkID(context.Background()) if err != nil { @@ -65,6 +119,27 @@ func (r *Resolver) BlockchainID() string { return fmt.Sprintf("%d:%s", r.chainID, r.contractAddress) } +func (r *Resolver) WalletAddress() (string, error) { + if r.walletKey == "" { + return "", errors.New("wallet key is not set") + } + + privateKey, err := crypto.HexToECDSA(r.walletKey) + if err != nil { + return "", err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return "", errors.New("error casting public key to ECDSA") + } + + walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + return walletAddress.String(), nil +} + func (r *Resolver) ResolveGist( ctx context.Context, opts *services.ResolverOpts, @@ -110,19 +185,31 @@ func (r *Resolver) Resolve( err error ) - userID, err := core.IDFromDID(did) - if err != nil { - return services.IdentityState{}, - fmt.Errorf("invalid did format for did '%s': %v", did, err) - } + if did.IDStrings[2] == "000000000000000000000000000000000000000000" { + if opts.GistRoot == nil { + return services.IdentityState{}, + errors.New("options GistRoot is required for root only did") + } + stateInfo = nil + gistInfo, err = r.resolveGistRootOnly(ctx, opts.GistRoot) + } else { + userID, err := core.IDFromDID(did) + if err != nil { + return services.IdentityState{}, + fmt.Errorf("invalid did format for did '%s': %v", did, err) + } - switch { - case opts.GistRoot != nil: - stateInfo, gistInfo, err = r.resolveStateByGistRoot(ctx, userID, opts.GistRoot) - case opts.State != nil: - stateInfo, err = r.resolveState(ctx, userID, opts.State) - default: - stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) + switch { + case opts.GistRoot != nil: + stateInfo, gistInfo, err = r.resolveStateByGistRoot(ctx, userID, opts.GistRoot) + case opts.State != nil: + stateInfo, err = r.resolveState(ctx, userID, opts.State) + default: + stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) + } + if err != nil && err.Error() != "identity not found" { + return services.IdentityState{}, err + } } identityState := services.IdentityState{} @@ -148,9 +235,226 @@ func (r *Resolver) Resolve( } } + signature := "" + if r.walletKey != "" && opts.Signature != "" { + primaryType := services.IdentityStateType + if stateInfo == nil { + primaryType = services.GlobalStateType + } + signature, err = r.signTypedData(primaryType, did, identityState) + if err != nil { + return services.IdentityState{}, err + } + } + + identityState.Signature = signature + return identityState, err } +func (r *Resolver) VerifyState( + primaryType services.PrimaryType, + identityState services.IdentityState, + did w3c.DID, +) (bool, error) { + privateKey, err := crypto.HexToECDSA(r.walletKey) + if err != nil { + return false, err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return false, errors.New("error casting public key to ECDSA") + } + + walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + typedData, err := r.TypedData(primaryType, did, identityState, walletAddress.String()) + if err != nil { + return false, err + } + + authData := AuthData{TypedData: typedData, Signature: identityState.Signature, Address: walletAddress.String()} + return r.verifyTypedData(authData) +} + +func TimeStampFn() string { + timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10) + return timestamp +} + +func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, identityState services.IdentityState, walletAddress string) (apitypes.TypedData, error) { + identity := "0" + if did.IDStrings[2] != "000000000000000000000000000000000000000000" { + userID, err := core.IDFromDID(did) + if err != nil { + return apitypes.TypedData{}, + fmt.Errorf("invalid did format for did '%s': %v", did, err) + } + identity = userID.BigInt().String() + } + + root := "0" + state := "0" + createdAtTimestamp := "0" + replacedByRoot := "0" + replacedByState := "0" + replacedAtTimestamp := "0" + + if identityState.StateInfo != nil { + state = identityState.StateInfo.State.String() + replacedByState = identityState.StateInfo.ReplacedByState.String() + createdAtTimestamp = identityState.StateInfo.CreatedAtTimestamp.String() + replacedAtTimestamp = identityState.StateInfo.ReplacedAtTimestamp.String() + } + if identityState.GistInfo != nil { + root = identityState.GistInfo.Root.String() + replacedByRoot = identityState.GistInfo.ReplacedByRoot.String() + createdAtTimestamp = identityState.GistInfo.CreatedAtTimestamp.String() + replacedAtTimestamp = identityState.GistInfo.ReplacedAtTimestamp.String() + } + + apiTypes := apitypes.Types{} + message := apitypes.TypedDataMessage{} + primaryTypeString := "" + timestamp := TimeStamp() + + switch primaryType { + case services.IdentityStateType: + primaryTypeString = "IdentityState" + apiTypes = IdentityStateAPITypes + message = apitypes.TypedDataMessage{ + "from": walletAddress, + "timestamp": timestamp, + "identity": identity, + "state": state, + "replacedByState": replacedByState, + "createdAtTimestamp": createdAtTimestamp, + "replacedAtTimestamp": replacedAtTimestamp, + } + case services.GlobalStateType: + primaryTypeString = "GlobalState" + apiTypes = GlobalStateAPITypes + message = apitypes.TypedDataMessage{ + "from": walletAddress, + "timestamp": timestamp, + "root": root, + "replacedByRoot": replacedByRoot, + "createdAtTimestamp": createdAtTimestamp, + "replacedAtTimestamp": replacedAtTimestamp, + } + } + + typedData := apitypes.TypedData{ + Types: apiTypes, + PrimaryType: primaryTypeString, + Domain: apitypes.TypedDataDomain{ + Name: "StateInfo", + Version: "1", + ChainId: math.NewHexOrDecimal256(int64(0)), + VerifyingContract: "0x0000000000000000000000000000000000000000", + }, + Message: message, + } + + return typedData, nil +} + +func (r *Resolver) signTypedData(primaryType services.PrimaryType, did w3c.DID, identityState services.IdentityState) (string, error) { + privateKey, err := crypto.HexToECDSA(r.walletKey) + if err != nil { + return "", err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return "", errors.New("error casting public key to ECDSA") + } + + walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + typedData, err := r.TypedData(primaryType, did, identityState, walletAddress.String()) + if err != nil { + return "", errors.New("error getting typed data for signing") + } + + domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + return "", errors.New("error hashing EIP712Domain for signing") + } + typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return "", errors.New("error hashing PrimaryType message for signing") + } + rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) + dataHash := crypto.Keccak256(rawData) + + signature, err := crypto.Sign(dataHash, privateKey) + if err != nil { + return "", err + } + + if signature[64] < 27 { + signature[64] += 27 + } + + return "0x" + hex.EncodeToString(signature), nil +} + +func (r *Resolver) verifyTypedData(authData AuthData) (bool, error) { + signature, err := hexutil.Decode(authData.Signature) + if err != nil { + return false, fmt.Errorf("decode signature: %w", err) + } + + // EIP-712 typed data marshaling + domainSeparator, err := authData.TypedData.HashStruct("EIP712Domain", authData.TypedData.Domain.Map()) + if err != nil { + return false, fmt.Errorf("eip712domain hash struct: %w", err) + } + typedDataHash, err := authData.TypedData.HashStruct(authData.TypedData.PrimaryType, authData.TypedData.Message) + if err != nil { + return false, fmt.Errorf("primary type hash struct: %w", err) + } + + // add magic string prefix + rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) + sighash := crypto.Keccak256(rawData) + + // update the recovery id + // https://github.com/ethereum/go-ethereum/blob/55599ee95d4151a2502465e0afc7c47bd1acba77/internal/ethapi/api.go#L442 + signature[64] -= 27 + + // get the pubkey used to sign this signature + sigPubkey, err := crypto.Ecrecover(sighash, signature) + if err != nil { + return false, fmt.Errorf("ecrecover: %w", err) + } + + // get the address to confirm it's the same one in the auth token + pubkey, err := crypto.UnmarshalPubkey(sigPubkey) + if err != nil { + return false, fmt.Errorf("unmarshal pub key: %w", err) + } + address := crypto.PubkeyToAddress(*pubkey) + + // verify the signature (not sure if this is actually required after ecrecover) + signatureNoRecoverID := signature[:len(signature)-1] + verified := crypto.VerifySignature(sigPubkey, sighash, signatureNoRecoverID) + if !verified { + return false, errors.New("verification failed") + } + + dataAddress := common.HexToAddress(authData.Address) + if subtle.ConstantTimeCompare(address.Bytes(), dataAddress.Bytes()) == 0 { + return false, errors.New("address mismatch") + } + + return true, nil +} + func (r *Resolver) resolveLatest( ctx context.Context, id core.ID, @@ -172,6 +476,18 @@ func (r *Resolver) resolveLatest( return &stateInfo, &gistInfo, verifyContractState(id, stateInfo) } +func (r *Resolver) resolveGistRootOnly( + ctx context.Context, + gistRoot *big.Int, +) (*contract.IStateGistRootInfo, error) { + gistInfo, err := r.state.GetGISTRootInfo(&bind.CallOpts{Context: ctx}, gistRoot) + if err = notFoundErr(err); err != nil { + return nil, err + } + + return &gistInfo, nil +} + func (r *Resolver) resolveState( ctx context.Context, id core.ID, diff --git a/pkg/services/blockchain/eth/resolver_test.go b/pkg/services/blockchain/eth/resolver_test.go index 89fa004..150c9cf 100644 --- a/pkg/services/blockchain/eth/resolver_test.go +++ b/pkg/services/blockchain/eth/resolver_test.go @@ -6,6 +6,7 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/crypto" "github.com/golang/mock/gomock" contract "github.com/iden3/contracts-abi/state/go/abi" "github.com/iden3/driver-did-iden3/pkg/services" @@ -14,9 +15,11 @@ import ( "github.com/iden3/go-iden3-core/v2/w3c" "github.com/pkg/errors" "github.com/stretchr/testify/require" + "github.com/tyler-smith/go-bip32" + "github.com/tyler-smith/go-bip39" ) -var userDID, _ = w3c.ParseDID("did:polygonid:polygon:mumbai:2qJEaVmT5jBrtgBQ4m7b7bRYzWmvMyDjBZGP24QwvD") +var userDID, _ = w3c.ParseDID("did:polygonid:polygon:amoy:2qY71pSkdCsRetTHbUA4YqG7Hx63Ej2PeiJMzAdJ2V") func TestResolveGist_Success(t *testing.T) { tests := []struct { @@ -201,3 +204,173 @@ func TestNotFoundErr(t *testing.T) { }) } } + +func TestResolveSignature_Success(t *testing.T) { + userEmptyDID, _ := w3c.ParseDID("did:polygonid:polygon:amoy:000000000000000000000000000000000000000000") + + tests := []struct { + name string + opts *services.ResolverOpts + userDID *w3c.DID + contractMock func(c *cm.MockStateContract) + timeStamp func() string + expectedIdentityState services.IdentityState + }{ + { + name: "resolve identity state by gist", + opts: &services.ResolverOpts{ + GistRoot: big.NewInt(1), + Signature: "EthereumEip712Signature2021", + }, + userDID: userDID, + contractMock: func(c *cm.MockStateContract) { + proof := contract.IStateGistProof{ + Root: big.NewInt(4), + Existence: true, + Value: big.NewInt(5), + } + userID, _ := core.IDFromDID(*userDID) + c.EXPECT().GetGISTProofByRoot(gomock.Any(), userID.BigInt(), big.NewInt(1)).Return(proof, nil) + gistInfo := contract.IStateGistRootInfo{Root: big.NewInt(555), CreatedAtTimestamp: big.NewInt(0), ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} + c.EXPECT().GetGISTRootInfo(gomock.Any(), big.NewInt(4)).Return(gistInfo, nil) + stateInfo := contract.IStateStateInfo{Id: userID.BigInt(), State: big.NewInt(444), CreatedAtTimestamp: big.NewInt(0), ReplacedByState: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} + c.EXPECT().GetStateInfoByIdAndState(gomock.Any(), gomock.Any(), big.NewInt(5)).Return(stateInfo, nil) + }, + timeStamp: func() string { + return "0" + }, + expectedIdentityState: services.IdentityState{ + StateInfo: &services.StateInfo{ + ID: *userDID, + State: big.NewInt(444), + CreatedAtTimestamp: big.NewInt(0), + ReplacedByState: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + }, + GistInfo: &services.GistInfo{ + Root: big.NewInt(555), + CreatedAtTimestamp: big.NewInt(0), + ReplacedByRoot: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + }, + Signature: "0x6276946bac246584ed6eaa2d5e43be5147e67cc7aa3b969c82bb9b1670e8de8b7f7410286f25d6bee4330b4bc260286cf8505358ffa29c8e677e4f05d78acf131c", + }, + }, + { + name: "resolve identity state by state", + opts: &services.ResolverOpts{ + State: big.NewInt(1), + Signature: "EthereumEip712Signature2021", + }, + userDID: userDID, + contractMock: func(c *cm.MockStateContract) { + userID, _ := core.IDFromDID(*userDID) + res := contract.IStateStateInfo{Id: userID.BigInt(), State: big.NewInt(555), CreatedAtTimestamp: big.NewInt(0), ReplacedByState: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} + c.EXPECT().GetStateInfoByIdAndState(gomock.Any(), gomock.Any(), big.NewInt(1)).Return(res, nil) + }, + timeStamp: func() string { + return "0" + }, + expectedIdentityState: services.IdentityState{ + StateInfo: &services.StateInfo{ + ID: *userDID, + State: big.NewInt(555), + CreatedAtTimestamp: big.NewInt(0), + ReplacedByState: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + }, + GistInfo: nil, + Signature: "0xdd07cd99ee8aa853c3e942aa5d57bfb844cae3db35fe29e8fc635ff4b2f5377d4b3c65f270474e6c5931b3d77f536233bc56d63172da8dba188f1f6fa51a10cb1c", + }, + }, + { + name: "resolve latest state", + opts: &services.ResolverOpts{ + Signature: "EthereumEip712Signature2021", + }, + userDID: userDID, + contractMock: func(c *cm.MockStateContract) { + userID, _ := core.IDFromDID(*userDID) + latestGist := big.NewInt(100) + c.EXPECT().GetGISTRoot(gomock.Any()).Return(latestGist, nil) + latestGistInfo := contract.IStateGistRootInfo{Root: big.NewInt(400), CreatedAtTimestamp: big.NewInt(0), ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} + c.EXPECT().GetGISTRootInfo(gomock.Any(), latestGist).Return(latestGistInfo, nil) + stateInfo := contract.IStateStateInfo{Id: userID.BigInt(), State: big.NewInt(555), CreatedAtTimestamp: big.NewInt(0), ReplacedByState: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} + c.EXPECT().GetStateInfoById(gomock.Any(), userID.BigInt()).Return(stateInfo, nil) + }, + timeStamp: func() string { + return "0" + }, + expectedIdentityState: services.IdentityState{ + StateInfo: &services.StateInfo{ + ID: *userDID, + State: big.NewInt(555), + CreatedAtTimestamp: big.NewInt(0), + ReplacedByState: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + }, + GistInfo: &services.GistInfo{ + Root: big.NewInt(400), + CreatedAtTimestamp: big.NewInt(0), + ReplacedByRoot: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + }, + Signature: "0xdd07cd99ee8aa853c3e942aa5d57bfb844cae3db35fe29e8fc635ff4b2f5377d4b3c65f270474e6c5931b3d77f536233bc56d63172da8dba188f1f6fa51a10cb1c", + }, + }, + { + name: "resolve only gist", + opts: &services.ResolverOpts{ + GistRoot: big.NewInt(400), + Signature: "EthereumEip712Signature2021", + }, + userDID: userEmptyDID, + contractMock: func(c *cm.MockStateContract) { + latestGist := big.NewInt(400) + latestGistInfo := contract.IStateGistRootInfo{Root: big.NewInt(400), CreatedAtTimestamp: big.NewInt(0), ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} + c.EXPECT().GetGISTRootInfo(gomock.Any(), latestGist).Return(latestGistInfo, nil) + }, + timeStamp: func() string { + return "0" + }, + expectedIdentityState: services.IdentityState{ + StateInfo: nil, + GistInfo: &services.GistInfo{ + Root: big.NewInt(400), + CreatedAtTimestamp: big.NewInt(0), + ReplacedByRoot: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + }, + Signature: "0xe64e080d08b948e5303b49288f1ff599df5b21fd20d7a944026a17e69f860e21662538ec1f8cba2f4a76e7c25d0f5cf506dc16bbc3148158ed81dd899528c69f1c", + }, + }, + } + + mnemonic := "rib satisfy drastic trigger trial exclude raccoon wedding then gaze fire hero" + seed := bip39.NewSeed(mnemonic, "Secret Passphrase bla bla bla") + masterPrivateKey, _ := bip32.NewMasterKey(seed) + ecdaPrivateKey := crypto.ToECDSAUnsafe(masterPrivateKey.Key) + privateKeyHex := fmt.Sprintf("%x", ecdaPrivateKey.D) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + stateContract := cm.NewMockStateContract(ctrl) + tt.contractMock(stateContract) + TimeStamp = tt.timeStamp + resolver := Resolver{state: stateContract, chainID: 1, walletKey: privateKeyHex} + identityState, err := resolver.Resolve(context.Background(), *tt.userDID, tt.opts) + require.NoError(t, err) + require.Equal(t, tt.expectedIdentityState, identityState) + + primaryType := services.IdentityStateType + if tt.expectedIdentityState.StateInfo == nil { + primaryType = services.GlobalStateType + } + + ok, _ := resolver.VerifyState(primaryType, identityState, *tt.userDID) + require.Equal(t, true, ok) + ctrl.Finish() + }) + } +} diff --git a/pkg/services/did.go b/pkg/services/did.go index d1d2e7f..fd86c7e 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -6,6 +6,7 @@ import ( "math/big" "net" "strings" + "time" "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/driver-did-iden3/pkg/services/ens" @@ -25,8 +26,9 @@ type DidDocumentServices struct { } type ResolverOpts struct { - State *big.Int - GistRoot *big.Int + State *big.Int + GistRoot *big.Int + Signature string } func NewDidDocumentServices(resolvers *ResolverRegistry, registry *ens.Registry) *DidDocumentServices { @@ -45,25 +47,35 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op return errResolution, err } - userID, err := core.IDFromDID(*userDID) - errResolution, err = expectedError(err) - if err != nil { - return errResolution, err - } + blockchain := "" + network := "" + userID := core.ID{} + if userDID.IDStrings[2] == "000000000000000000000000000000000000000000" { + blockchain = userDID.IDStrings[0] + network = userDID.IDStrings[1] + } else { + userID, err = core.IDFromDID(*userDID) + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err + } - b, err := core.BlockchainFromID(userID) - errResolution, err = expectedError(err) - if err != nil { - return errResolution, err - } + b, err := core.BlockchainFromID(userID) + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err + } - n, err := core.NetworkIDFromID(userID) - errResolution, err = expectedError(err) - if err != nil { - return errResolution, err + n, err := core.NetworkIDFromID(userID) + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err + } + blockchain = string(b) + network = string(n) } - resolver, err := d.resolvers.GetResolverByNetwork(string(b), string(n)) + resolver, err := d.resolvers.GetResolverByNetwork(blockchain, network) errResolution, err = expectedError(err) if err != nil { return errResolution, err @@ -97,7 +109,7 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op chainIDStateAddress := resolver.BlockchainID() - if err == nil { + if err == nil && userDID.IDStrings[2] != "000000000000000000000000000000000000000000" { didResolution.DidDocument.Context = append(didResolution.DidDocument.Context.([]string), document.EcdsaSecp256k1RecoveryContext) addressString := fmt.Sprintf("%x", addr) blockchainAccountID := fmt.Sprintf("eip155:%s:0x%s", strings.Split(chainIDStateAddress, ":")[0], addressString) @@ -128,6 +140,30 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op }, ) + walletAddress, err := resolver.WalletAddress() + + if err == nil && opts.Signature != "" { + primaryType := IdentityStateType + if userDID.IDStrings[2] == "000000000000000000000000000000000000000000" { + primaryType = GlobalStateType + } + eip712TypedData, err := resolver.TypedData(primaryType, *userDID, identityState, walletAddress) + if err != nil { + return nil, fmt.Errorf("invalid typed data: %v", err) + } + + eip712Proof := &document.EthereumEip712SignatureProof2021{ + Type: document.EthereumEip712SignatureProof2021Type, + ProofPursopose: "assertionMethod", + ProofValue: identityState.Signature, + VerificationMethod: fmt.Sprintf("did:pkh:eip155:0:%s#blockchainAccountId", walletAddress), + Eip712: eip712TypedData, + Created: time.Now(), + } + + didResolution.DidResolutionMetadata.Context = document.DidResolutionMetadataSigContext() + didResolution.DidResolutionMetadata.Proof = append(didResolution.DidResolutionMetadata.Proof, eip712Proof) + } return didResolution, nil } diff --git a/pkg/services/registry.go b/pkg/services/registry.go index 86dc1d0..5e858a2 100644 --- a/pkg/services/registry.go +++ b/pkg/services/registry.go @@ -5,6 +5,7 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/go-merkletree-sql/v2" "github.com/iden3/go-schema-processor/v2/verifiable" @@ -18,9 +19,17 @@ var ( ErrNotFound = errors.New("not found") ) +type PrimaryType int32 + +const ( + IdentityStateType PrimaryType = 0 + GlobalStateType PrimaryType = 1 +) + type IdentityState struct { StateInfo *StateInfo GistInfo *GistInfo + Signature string } type StateInfo struct { @@ -95,6 +104,8 @@ type Resolver interface { Resolve(ctx context.Context, did w3c.DID, opts *ResolverOpts) (IdentityState, error) ResolveGist(ctx context.Context, opts *ResolverOpts) (*GistInfo, error) BlockchainID() string + WalletAddress() (string, error) + TypedData(primaryType PrimaryType, did w3c.DID, identityState IdentityState, walletAddress string) (apitypes.TypedData, error) } type ResolverRegistry map[string]Resolver From 6a3655eebcd6debfbd79bd662446b2a261e869a8 Mon Sep 17 00:00:00 2001 From: ilya-korotya Date: Mon, 12 Aug 2024 17:18:51 +0200 Subject: [PATCH 02/26] add envs variables --- .github/workflows/publis-container-dev.yaml | 12 ++++++------ .github/workflows/push-container.yaml | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publis-container-dev.yaml b/.github/workflows/publis-container-dev.yaml index fb707fe..ae8ace3 100644 --- a/.github/workflows/publis-container-dev.yaml +++ b/.github/workflows/publis-container-dev.yaml @@ -13,10 +13,10 @@ env: jobs: build_driver: env: - STATE_CONTRACT_ADDRESS_MAIN: "0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D" - STATE_CONTRACT_ADDRESS_AMOY: "0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124" - STATE_CONTRACT_ADDRESS_PRIVADO_MAIN: "0x0DDd8701C91d8d1Ba35c9DbA98A45fe5bA8A877E" - STATE_CONTRACT_ADDRESS_PRIVADO_TEST: "0xE5BfD683F1Ca574B5be881b7DbbcFDCE9DDBAb90" + STATE_CONTRACT_ADDRESS_POLYGON_MAIN: "0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D" + STATE_CONTRACT_ADDRESS_POLYGON_AMOY: "0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124" + STATE_CONTRACT_ADDRESS_PRIVADO_MAIN: "0x975556428F077dB5877Ea2474D783D6C69233742" + STATE_CONTRACT_ADDRESS_PRIVADO_TEST: "0x975556428F077dB5877Ea2474D783D6C69233742" runs-on: ubuntu-latest permissions: id-token: write @@ -47,11 +47,11 @@ jobs: cd driver-did-iden3 echo -e "polygon:" > resolvers.settings.yaml echo -e " amoy:" >> resolvers.settings.yaml - echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_AMOY }}" >> resolvers.settings.yaml + echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_POLYGON_AMOY }}" >> resolvers.settings.yaml echo -e " networkURL: ${{ secrets.POLYGON_AMOY_NODE_URL }}" >> resolvers.settings.yaml echo -e " walletKey: ${{ secrets.SIGNER_PRIVATE_KEY }}" >> resolvers.settings.yaml echo -e " main:" >> resolvers.settings.yaml - echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_MAIN }}" >> resolvers.settings.yaml + echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_POLYGON_MAIN }}" >> resolvers.settings.yaml echo -e " networkURL: ${{ secrets.POLYGON_MAIN_NODE_URL }}" >> resolvers.settings.yaml echo -e " walletKey: ${{ secrets.SIGNER_PRIVATE_KEY }}" >> resolvers.settings.yaml echo -e "privado:" >> resolvers.settings.yaml diff --git a/.github/workflows/push-container.yaml b/.github/workflows/push-container.yaml index 5d9e524..3c2e55f 100644 --- a/.github/workflows/push-container.yaml +++ b/.github/workflows/push-container.yaml @@ -11,8 +11,8 @@ on: jobs: build_driver: env: - STATE_CONTRACT_ADDRESS_MAIN: "0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D" - STATE_CONTRACT_ADDRESS_AMOY: "0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124" + STATE_CONTRACT_ADDRESS_POLYGON_MAIN: "0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D" + STATE_CONTRACT_ADDRESS_POLYGON_AMOY: "0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124" STATE_CONTRACT_ADDRESS_PRIVADO: "0x975556428F077dB5877Ea2474D783D6C69233742" runs-on: ubuntu-latest steps: @@ -33,10 +33,10 @@ jobs: cd driver-did-iden3 echo -e "polygon:" > resolvers.settings.yaml echo -e " amoy:" >> resolvers.settings.yaml - echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_AMOY }}" >> resolvers.settings.yaml + echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_POLYGON_AMOY }}" >> resolvers.settings.yaml echo -e " networkURL: ${{ secrets.POLYGON_AMOY_NODE_URL }}" >> resolvers.settings.yaml echo -e " main:" >> resolvers.settings.yaml - echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_MAIN }}" >> resolvers.settings.yaml + echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_POLYGON_MAIN }}" >> resolvers.settings.yaml echo -e " networkURL: ${{ secrets.POLYGON_MAIN_NODE_URL }}" >> resolvers.settings.yaml echo -e "privado:" >> resolvers.settings.yaml echo -e " main:" >> resolvers.settings.yaml From 09f8a7f075bad0a30b037a6862dcfd95c807f7f2 Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh <> Date: Tue, 13 Aug 2024 09:49:51 +0100 Subject: [PATCH 03/26] Hotfix --- pkg/services/blockchain/eth/resolver.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index dd4ba24..72fa524 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -207,9 +207,22 @@ func (r *Resolver) Resolve( default: stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) } - if err != nil && err.Error() != "identity not found" { + + if err != nil && err.Error() != "state not found" { return services.IdentityState{}, err } + + if err != nil { + idGen, err := core.CheckGenesisStateID(userID.BigInt(), opts.State) + if err != nil { + return services.IdentityState{}, err + } + if !idGen { + return services.IdentityState{}, services.ErrNotFound + } + stateInfo = &contract.IStateStateInfo{} + stateInfo.State = opts.State + } } identityState := services.IdentityState{} From 7263bee93efe6c114204f7459b6b0d80d1155382 Mon Sep 17 00:00:00 2001 From: ilya-korotya Date: Tue, 13 Aug 2024 12:18:57 +0200 Subject: [PATCH 04/26] add information about genesis state to did resolution document --- pkg/services/blockchain/eth/resolver.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 72fa524..d74709f 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -207,21 +207,28 @@ func (r *Resolver) Resolve( default: stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) } - - if err != nil && err.Error() != "state not found" { + // err == http.505 + if err != nil && !errors.Is(err, services.ErrNotFound) { return services.IdentityState{}, err } - if err != nil { + if opts.State != nil && errors.Is(err, services.ErrNotFound) { idGen, err := core.CheckGenesisStateID(userID.BigInt(), opts.State) if err != nil { return services.IdentityState{}, err } if !idGen { - return services.IdentityState{}, services.ErrNotFound + return services.IdentityState{}, + fmt.Errorf("identity '%s' is '%v' and state '%s' not genesis", userID.String(), err, opts.State) + } + stateInfo = &contract.IStateStateInfo{ + State: opts.State, + ReplacedByState: big.NewInt(0), + CreatedAtTimestamp: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + CreatedAtBlock: big.NewInt(0), + ReplacedAtBlock: big.NewInt(0), } - stateInfo = &contract.IStateStateInfo{} - stateInfo.State = opts.State } } From 626fb0ae43434969e64ea3fe45a5a58eb806a43d Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 19 Aug 2024 08:03:19 +0200 Subject: [PATCH 05/26] fix review comments --- pkg/app/handler.go | 7 ++-- pkg/document/proof.go | 2 +- pkg/document/proof_test.go | 40 +++++++------------- pkg/services/blockchain/eth/resolver.go | 29 +++++--------- pkg/services/blockchain/eth/resolver_test.go | 9 +++-- 5 files changed, 33 insertions(+), 54 deletions(-) diff --git a/pkg/app/handler.go b/pkg/app/handler.go index bb34000..1807b1b 100644 --- a/pkg/app/handler.go +++ b/pkg/app/handler.go @@ -7,6 +7,7 @@ import ( "net/http" "strings" + "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/driver-did-iden3/pkg/services" core "github.com/iden3/go-iden3-core/v2" "github.com/iden3/go-merkletree-sql/v2" @@ -129,10 +130,10 @@ func getResolverOpts(state, gistRoot, signature string) (ro services.ResolverOpt } ro.GistRoot = g.BigInt() } + if signature != "" && signature != string(document.EthereumEip712SignatureProof2021Type) { + return ro, fmt.Errorf("not supported signature type %s", signature) + } if signature != "" { - if signature != "EthereumEip712Signature2021" { - return ro, fmt.Errorf("not supported signature type %s", signature) - } ro.Signature = signature } return diff --git a/pkg/document/proof.go b/pkg/document/proof.go index c080932..166ccf4 100644 --- a/pkg/document/proof.go +++ b/pkg/document/proof.go @@ -24,7 +24,7 @@ type EthereumEip712SignatureProof2021 struct { Eip712 apitypes.TypedData `json:"eip712"` } -// EthereumEip712Signature2021Type is a proof type for EIP172 signature proofs +// EthereumEip712SignatureProof2021Type is a proof type for EIP172 signature proofs // nolint:stylecheck // we need to keep the name as it is const EthereumEip712SignatureProof2021Type verifiable.ProofType = "EthereumEip712Signature2021" diff --git a/pkg/document/proof_test.go b/pkg/document/proof_test.go index 90f19d1..6db2fea 100644 --- a/pkg/document/proof_test.go +++ b/pkg/document/proof_test.go @@ -89,20 +89,6 @@ func TestEthereumEip712SignatureProof2021_JSONUnmarshal(t *testing.T) { }, } - var primaryType = "IdentityState" - walletAddress := "0x5b18eF56aA61eeAE0E3434e3c3d8AEB19b141fe7" - state := "444" - stateCreatedAtTimestamp := "0" - stateReplacedByState := "0" - stateReplacedAtTimestamp := "0" - gistRootCreatedAtTimestamp := "0" - gistRootReplacedByRoot := "0" - gistRootReplacedAtTimestamp := "0" - timestamp := "0" - gistRoot := "555" - identity := "19090607534999372304474213543962416547920895595808567155882840509226423042" - chainID := 1 - wantProof := EthereumEip712SignatureProof2021{ Type: "EthereumEip712Signature2021", ProofPursopose: "assertionMethod", @@ -111,25 +97,25 @@ func TestEthereumEip712SignatureProof2021_JSONUnmarshal(t *testing.T) { Created: timeParsed, Eip712: apitypes.TypedData{ Types: apiTypes, - PrimaryType: primaryType, + PrimaryType: "IdentityState", Domain: apitypes.TypedDataDomain{ Name: "StateInfo", Version: "1", - ChainId: math.NewHexOrDecimal256(int64(chainID)), + ChainId: math.NewHexOrDecimal256(int64(1)), VerifyingContract: "0x0000000000000000000000000000000000000000", }, Message: apitypes.TypedDataMessage{ - "from": walletAddress, - "timestamp": timestamp, - "state": state, - "stateCreatedAtTimestamp": stateCreatedAtTimestamp, - "stateReplacedByState": stateReplacedByState, - "stateReplacedAtTimestamp": stateReplacedAtTimestamp, - "gistRoot": gistRoot, - "gistRootCreatedAtTimestamp": gistRootCreatedAtTimestamp, - "gistRootReplacedByRoot": gistRootReplacedByRoot, - "gistRootReplacedAtTimestamp": gistRootReplacedAtTimestamp, - "identity": identity, + "from": "0x5b18eF56aA61eeAE0E3434e3c3d8AEB19b141fe7", + "timestamp": "0", + "state": "444", + "stateCreatedAtTimestamp": "0", + "stateReplacedByState": "0", + "stateReplacedAtTimestamp": "0", + "gistRoot": "555", + "gistRootCreatedAtTimestamp": "0", + "gistRootReplacedByRoot": "0", + "gistRootReplacedAtTimestamp": "0", + "identity": "19090607534999372304474213543962416547920895595808567155882840509226423042", }, }, } diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index d74709f..312bc4d 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -190,8 +190,10 @@ func (r *Resolver) Resolve( return services.IdentityState{}, errors.New("options GistRoot is required for root only did") } - stateInfo = nil gistInfo, err = r.resolveGistRootOnly(ctx, opts.GistRoot) + if err != nil { + return services.IdentityState{}, err + } } else { userID, err := core.IDFromDID(did) if err != nil { @@ -277,25 +279,17 @@ func (r *Resolver) VerifyState( identityState services.IdentityState, did w3c.DID, ) (bool, error) { - privateKey, err := crypto.HexToECDSA(r.walletKey) + walletAddress, err := r.WalletAddress() if err != nil { return false, err } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return false, errors.New("error casting public key to ECDSA") - } - - walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) - - typedData, err := r.TypedData(primaryType, did, identityState, walletAddress.String()) + typedData, err := r.TypedData(primaryType, did, identityState, walletAddress) if err != nil { return false, err } - authData := AuthData{TypedData: typedData, Signature: identityState.Signature, Address: walletAddress.String()} + authData := AuthData{TypedData: typedData, Signature: identityState.Signature, Address: walletAddress} return r.verifyTypedData(authData) } @@ -387,15 +381,12 @@ func (r *Resolver) signTypedData(primaryType services.PrimaryType, did w3c.DID, return "", err } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return "", errors.New("error casting public key to ECDSA") + walletAddress, err := r.WalletAddress() + if err != nil { + return "", err } - walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) - - typedData, err := r.TypedData(primaryType, did, identityState, walletAddress.String()) + typedData, err := r.TypedData(primaryType, did, identityState, walletAddress) if err != nil { return "", errors.New("error getting typed data for signing") } diff --git a/pkg/services/blockchain/eth/resolver_test.go b/pkg/services/blockchain/eth/resolver_test.go index 150c9cf..649c499 100644 --- a/pkg/services/blockchain/eth/resolver_test.go +++ b/pkg/services/blockchain/eth/resolver_test.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/golang/mock/gomock" contract "github.com/iden3/contracts-abi/state/go/abi" + "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/driver-did-iden3/pkg/services" cm "github.com/iden3/driver-did-iden3/pkg/services/blockchain/eth/contract/mock" core "github.com/iden3/go-iden3-core/v2" @@ -220,7 +221,7 @@ func TestResolveSignature_Success(t *testing.T) { name: "resolve identity state by gist", opts: &services.ResolverOpts{ GistRoot: big.NewInt(1), - Signature: "EthereumEip712Signature2021", + Signature: string(document.EthereumEip712SignatureProof2021Type), }, userDID: userDID, contractMock: func(c *cm.MockStateContract) { @@ -260,7 +261,7 @@ func TestResolveSignature_Success(t *testing.T) { name: "resolve identity state by state", opts: &services.ResolverOpts{ State: big.NewInt(1), - Signature: "EthereumEip712Signature2021", + Signature: string(document.EthereumEip712SignatureProof2021Type), }, userDID: userDID, contractMock: func(c *cm.MockStateContract) { @@ -286,7 +287,7 @@ func TestResolveSignature_Success(t *testing.T) { { name: "resolve latest state", opts: &services.ResolverOpts{ - Signature: "EthereumEip712Signature2021", + Signature: string(document.EthereumEip712SignatureProof2021Type), }, userDID: userDID, contractMock: func(c *cm.MockStateContract) { @@ -322,7 +323,7 @@ func TestResolveSignature_Success(t *testing.T) { name: "resolve only gist", opts: &services.ResolverOpts{ GistRoot: big.NewInt(400), - Signature: "EthereumEip712Signature2021", + Signature: string(document.EthereumEip712SignatureProof2021Type), }, userDID: userEmptyDID, contractMock: func(c *cm.MockStateContract) { From 8634ce1dda647c2ef36782ac5e429d4ec96451a9 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 19 Aug 2024 08:14:00 +0200 Subject: [PATCH 06/26] fix e2e variable --- .github/workflows/e2e.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 4feb86b..f09c5b4 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -7,7 +7,7 @@ on: jobs: run_e2e: env: - STATE_CONTRACT_ADDRESS_AMOY: "0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124" + STATE_CONTRACT_ADDRESS_POLYGON_AMOY: "0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124" runs-on: ubuntu-20.04 steps: @@ -21,7 +21,7 @@ jobs: run: | echo -e "polygon:" > resolvers.settings.yaml echo -e " amoy:" >> resolvers.settings.yaml - echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_AMOY }}" >> resolvers.settings.yaml + echo -e " contractAddress: ${{ env.STATE_CONTRACT_ADDRESS_POLYGON_AMOY }}" >> resolvers.settings.yaml echo -e " networkURL: ${{ secrets.POLYGON_AMOY_NODE_URL }}" >> resolvers.settings.yaml - name: Docker build driver container From 363f8d0b8f6610f4986d9b0f9f22695326fd6f2f Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh <> Date: Mon, 19 Aug 2024 09:46:17 +0100 Subject: [PATCH 07/26] Http requests with to fix description --- test.http | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test.http diff --git a/test.http b/test.http new file mode 100644 index 0000000..6328647 --- /dev/null +++ b/test.http @@ -0,0 +1,62 @@ +#GET https://resolver-dev.privado.id/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS +# ?signature=EthereumEip712Signature2021 +# &state=e500792b5d2401e3ff2277f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b + +### GET state +GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS + ?signature=EthereumEip712Signature2021 + &state=e500792b5d2401e3ff2277f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b + +### GET state (non-existent) +GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS + ?signature=EthereumEip712Signature2021 + &state=0000000000000000000077f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b +# FIX only empty proofValue for non-existed state. But should return error + +### Get zero gist (zero) +GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS + ?signature=EthereumEip712Signature2021 + &gist=0x0000000000000000000000000000000000000000000000000000000000000000 +# FIX why this signs IdentityState but no GlobalState? + +### Get zero gist, root only did (zero) +GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:000000000000000000000000000000000000000000 + ?signature=EthereumEip712Signature2021 + &gist=0x0000000000000000000000000000000000000000000000000000000000000000 + +### Get non-existed GIST +GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:000000000000000000000000000000000000000000 + ?signature=EthereumEip712Signature2021 + &gist=0x0000000000000000000000000000000000000000000000000000000000000001 +# FIX returns and signs zero global state, but should return error + +### Vova's request, amoy, no id +GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:000000000000000000000000000000000000000000 + ?signature=EthereumEip712Signature2021 + &gist=b048eccccdb175b95aa3fa3453ca870818d1cdb4d765eeea4123712bf59cff0f + +### Vova's request, amoy, no id +GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:000000000000000000000000000000000000000000 + ?signature=EthereumEip712Signature2021 + +### Vova's request, amoy, with id +GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:xHSDc6jvEVcKBUMV9rQcJ98S3SUsBHGyinN948JH3 + ?signature=EthereumEip712Signature2021 + &gist=b048eccccdb175b95aa3fa3453ca870818d1cdb4d765eeea4123712bf59cff0f + +### AMOY: Get zero gist, root only did (zero) +GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:000000000000000000000000000000000000000000 + ?signature=EthereumEip712Signature2021 +## TO FIX Need global in ver method in this case + +### Non-existent gist, should throw +GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS + ?signature=EthereumEip712Signature2021 + &gist=0xe500792b5d2401e3ff2277f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b + +### Latest state +GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS + ?signature=EthereumEip712Signature2021 + +## TO FIX Need to impl true "null" did id + From bf27f860441b54956ad9a256a3ebcd93fe73cd76 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 19 Aug 2024 16:01:46 +0200 Subject: [PATCH 08/26] fix edge cases --- pkg/services/blockchain/eth/resolver.go | 6 +++--- pkg/services/did.go | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 312bc4d..d27462f 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -186,9 +186,9 @@ func (r *Resolver) Resolve( ) if did.IDStrings[2] == "000000000000000000000000000000000000000000" { + // If gist root is not provided, it will try to resolve with gist root 0 if opts.GistRoot == nil { - return services.IdentityState{}, - errors.New("options GistRoot is required for root only did") + opts.GistRoot = big.NewInt(0) } gistInfo, err = r.resolveGistRootOnly(ctx, opts.GistRoot) if err != nil { @@ -221,7 +221,7 @@ func (r *Resolver) Resolve( } if !idGen { return services.IdentityState{}, - fmt.Errorf("identity '%s' is '%v' and state '%s' not genesis", userID.String(), err, opts.State) + fmt.Errorf("identity '%s' state '%s' is not found and is not genesis", userID.String(), opts.State) } stateInfo = &contract.IStateStateInfo{ State: opts.State, diff --git a/pkg/services/did.go b/pkg/services/did.go index fd86c7e..c24b97a 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -92,6 +92,10 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op } } + if err != nil && opts.State != nil { + return document.NewDidNotFoundResolution(err.Error()), nil + } + info, err := identityState.StateInfo.ToDidRepresentation() if err != nil { return nil, fmt.Errorf("invalid resolver response: %v", err) @@ -144,7 +148,7 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op if err == nil && opts.Signature != "" { primaryType := IdentityStateType - if userDID.IDStrings[2] == "000000000000000000000000000000000000000000" { + if identityState.StateInfo == nil { primaryType = GlobalStateType } eip712TypedData, err := resolver.TypedData(primaryType, *userDID, identityState, walletAddress) From 510a47750a2cfcfd617e2d69349716ebc073920f Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 20 Aug 2024 17:24:10 +0200 Subject: [PATCH 09/26] update eip712 messages --- pkg/services/blockchain/eth/resolver.go | 109 +++++++------------ pkg/services/blockchain/eth/resolver_test.go | 34 +----- pkg/services/did.go | 42 +++---- 3 files changed, 58 insertions(+), 127 deletions(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index d27462f..6b32909 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -56,12 +56,9 @@ var ( var IdentityStateAPITypes = apitypes.Types{ "IdentityState": []apitypes.Type{ - {Name: "from", Type: "address"}, {Name: "timestamp", Type: "uint256"}, - {Name: "identity", Type: "uint256"}, + {Name: "userID", Type: "uint256"}, {Name: "state", Type: "uint256"}, - {Name: "replacedByState", Type: "uint256"}, - {Name: "createdAtTimestamp", Type: "uint256"}, {Name: "replacedAtTimestamp", Type: "uint256"}, }, "EIP712Domain": []apitypes.Type{ @@ -74,11 +71,9 @@ var IdentityStateAPITypes = apitypes.Types{ var GlobalStateAPITypes = apitypes.Types{ "GlobalState": []apitypes.Type{ - {Name: "from", Type: "address"}, {Name: "timestamp", Type: "uint256"}, + {Name: "userID", Type: "uint256"}, {Name: "root", Type: "uint256"}, - {Name: "replacedByRoot", Type: "uint256"}, - {Name: "createdAtTimestamp", Type: "uint256"}, {Name: "replacedAtTimestamp", Type: "uint256"}, }, "EIP712Domain": []apitypes.Type{ @@ -185,52 +180,41 @@ func (r *Resolver) Resolve( err error ) - if did.IDStrings[2] == "000000000000000000000000000000000000000000" { - // If gist root is not provided, it will try to resolve with gist root 0 - if opts.GistRoot == nil { - opts.GistRoot = big.NewInt(0) - } - gistInfo, err = r.resolveGistRootOnly(ctx, opts.GistRoot) + userID, err := core.IDFromDID(did) + if err != nil { + return services.IdentityState{}, + fmt.Errorf("invalid did format for did '%s': %v", did, err) + } + + switch { + case opts.GistRoot != nil: + stateInfo, gistInfo, err = r.resolveStateByGistRoot(ctx, userID, opts.GistRoot) + case opts.State != nil: + stateInfo, err = r.resolveState(ctx, userID, opts.State) + default: + stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) + } + // err == http.505 + if err != nil && (!errors.Is(err, services.ErrNotFound) || opts.GistRoot != nil) { + return services.IdentityState{}, err + } + + if opts.State != nil && errors.Is(err, services.ErrNotFound) { + idGen, err := core.CheckGenesisStateID(userID.BigInt(), opts.State) if err != nil { return services.IdentityState{}, err } - } else { - userID, err := core.IDFromDID(did) - if err != nil { + if !idGen { return services.IdentityState{}, - fmt.Errorf("invalid did format for did '%s': %v", did, err) + fmt.Errorf("identity '%s' state '%s' is not found and is not genesis", userID.String(), opts.State) } - - switch { - case opts.GistRoot != nil: - stateInfo, gistInfo, err = r.resolveStateByGistRoot(ctx, userID, opts.GistRoot) - case opts.State != nil: - stateInfo, err = r.resolveState(ctx, userID, opts.State) - default: - stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) - } - // err == http.505 - if err != nil && !errors.Is(err, services.ErrNotFound) { - return services.IdentityState{}, err - } - - if opts.State != nil && errors.Is(err, services.ErrNotFound) { - idGen, err := core.CheckGenesisStateID(userID.BigInt(), opts.State) - if err != nil { - return services.IdentityState{}, err - } - if !idGen { - return services.IdentityState{}, - fmt.Errorf("identity '%s' state '%s' is not found and is not genesis", userID.String(), opts.State) - } - stateInfo = &contract.IStateStateInfo{ - State: opts.State, - ReplacedByState: big.NewInt(0), - CreatedAtTimestamp: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - CreatedAtBlock: big.NewInt(0), - ReplacedAtBlock: big.NewInt(0), - } + stateInfo = &contract.IStateStateInfo{ + State: opts.State, + ReplacedByState: big.NewInt(0), + CreatedAtTimestamp: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + CreatedAtBlock: big.NewInt(0), + ReplacedAtBlock: big.NewInt(0), } } @@ -299,33 +283,23 @@ func TimeStampFn() string { } func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, identityState services.IdentityState, walletAddress string) (apitypes.TypedData, error) { - identity := "0" - if did.IDStrings[2] != "000000000000000000000000000000000000000000" { - userID, err := core.IDFromDID(did) - if err != nil { - return apitypes.TypedData{}, - fmt.Errorf("invalid did format for did '%s': %v", did, err) - } - identity = userID.BigInt().String() + id, err := core.IDFromDID(did) + if err != nil { + return apitypes.TypedData{}, + fmt.Errorf("invalid did format for did '%s': %v", did, err) } + userID := id.BigInt().String() root := "0" state := "0" - createdAtTimestamp := "0" - replacedByRoot := "0" - replacedByState := "0" replacedAtTimestamp := "0" if identityState.StateInfo != nil { state = identityState.StateInfo.State.String() - replacedByState = identityState.StateInfo.ReplacedByState.String() - createdAtTimestamp = identityState.StateInfo.CreatedAtTimestamp.String() replacedAtTimestamp = identityState.StateInfo.ReplacedAtTimestamp.String() } if identityState.GistInfo != nil { root = identityState.GistInfo.Root.String() - replacedByRoot = identityState.GistInfo.ReplacedByRoot.String() - createdAtTimestamp = identityState.GistInfo.CreatedAtTimestamp.String() replacedAtTimestamp = identityState.GistInfo.ReplacedAtTimestamp.String() } @@ -339,23 +313,18 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden primaryTypeString = "IdentityState" apiTypes = IdentityStateAPITypes message = apitypes.TypedDataMessage{ - "from": walletAddress, "timestamp": timestamp, - "identity": identity, + "userID": userID, "state": state, - "replacedByState": replacedByState, - "createdAtTimestamp": createdAtTimestamp, "replacedAtTimestamp": replacedAtTimestamp, } case services.GlobalStateType: primaryTypeString = "GlobalState" apiTypes = GlobalStateAPITypes message = apitypes.TypedDataMessage{ - "from": walletAddress, "timestamp": timestamp, + "userID": userID, "root": root, - "replacedByRoot": replacedByRoot, - "createdAtTimestamp": createdAtTimestamp, "replacedAtTimestamp": replacedAtTimestamp, } } diff --git a/pkg/services/blockchain/eth/resolver_test.go b/pkg/services/blockchain/eth/resolver_test.go index 649c499..46d239f 100644 --- a/pkg/services/blockchain/eth/resolver_test.go +++ b/pkg/services/blockchain/eth/resolver_test.go @@ -207,8 +207,6 @@ func TestNotFoundErr(t *testing.T) { } func TestResolveSignature_Success(t *testing.T) { - userEmptyDID, _ := w3c.ParseDID("did:polygonid:polygon:amoy:000000000000000000000000000000000000000000") - tests := []struct { name string opts *services.ResolverOpts @@ -254,7 +252,7 @@ func TestResolveSignature_Success(t *testing.T) { ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0), }, - Signature: "0x6276946bac246584ed6eaa2d5e43be5147e67cc7aa3b969c82bb9b1670e8de8b7f7410286f25d6bee4330b4bc260286cf8505358ffa29c8e677e4f05d78acf131c", + Signature: "0x4640cdda90e9340e4283e5e5d622137ee624142a53a66b98552eae931dab8eb41f25b4c9eae4771be41e7e1dcac364fc2b98cc6adf02586b158c24c3ca36f8af1c", }, }, { @@ -281,7 +279,7 @@ func TestResolveSignature_Success(t *testing.T) { ReplacedAtTimestamp: big.NewInt(0), }, GistInfo: nil, - Signature: "0xdd07cd99ee8aa853c3e942aa5d57bfb844cae3db35fe29e8fc635ff4b2f5377d4b3c65f270474e6c5931b3d77f536233bc56d63172da8dba188f1f6fa51a10cb1c", + Signature: "0xc373a5a9df5c9227af61724bccaacffb117bf96437d9d7c41aff9be9f7662890716f2254dfc750b3768f2afa45843b53a8139264aa79626f4ad351f9390321841c", }, }, { @@ -316,33 +314,7 @@ func TestResolveSignature_Success(t *testing.T) { ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0), }, - Signature: "0xdd07cd99ee8aa853c3e942aa5d57bfb844cae3db35fe29e8fc635ff4b2f5377d4b3c65f270474e6c5931b3d77f536233bc56d63172da8dba188f1f6fa51a10cb1c", - }, - }, - { - name: "resolve only gist", - opts: &services.ResolverOpts{ - GistRoot: big.NewInt(400), - Signature: string(document.EthereumEip712SignatureProof2021Type), - }, - userDID: userEmptyDID, - contractMock: func(c *cm.MockStateContract) { - latestGist := big.NewInt(400) - latestGistInfo := contract.IStateGistRootInfo{Root: big.NewInt(400), CreatedAtTimestamp: big.NewInt(0), ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} - c.EXPECT().GetGISTRootInfo(gomock.Any(), latestGist).Return(latestGistInfo, nil) - }, - timeStamp: func() string { - return "0" - }, - expectedIdentityState: services.IdentityState{ - StateInfo: nil, - GistInfo: &services.GistInfo{ - Root: big.NewInt(400), - CreatedAtTimestamp: big.NewInt(0), - ReplacedByRoot: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - }, - Signature: "0xe64e080d08b948e5303b49288f1ff599df5b21fd20d7a944026a17e69f860e21662538ec1f8cba2f4a76e7c25d0f5cf506dc16bbc3148158ed81dd899528c69f1c", + Signature: "0xc373a5a9df5c9227af61724bccaacffb117bf96437d9d7c41aff9be9f7662890716f2254dfc750b3768f2afa45843b53a8139264aa79626f4ad351f9390321841c", }, }, } diff --git a/pkg/services/did.go b/pkg/services/did.go index c24b97a..74f2d21 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -47,35 +47,25 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op return errResolution, err } - blockchain := "" - network := "" - userID := core.ID{} - if userDID.IDStrings[2] == "000000000000000000000000000000000000000000" { - blockchain = userDID.IDStrings[0] - network = userDID.IDStrings[1] - } else { - userID, err = core.IDFromDID(*userDID) - errResolution, err = expectedError(err) - if err != nil { - return errResolution, err - } + userID, err := core.IDFromDID(*userDID) + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err + } - b, err := core.BlockchainFromID(userID) - errResolution, err = expectedError(err) - if err != nil { - return errResolution, err - } + b, err := core.BlockchainFromID(userID) + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err + } - n, err := core.NetworkIDFromID(userID) - errResolution, err = expectedError(err) - if err != nil { - return errResolution, err - } - blockchain = string(b) - network = string(n) + n, err := core.NetworkIDFromID(userID) + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err } - resolver, err := d.resolvers.GetResolverByNetwork(blockchain, network) + resolver, err := d.resolvers.GetResolverByNetwork(string(b), string(n)) errResolution, err = expectedError(err) if err != nil { return errResolution, err @@ -113,7 +103,7 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op chainIDStateAddress := resolver.BlockchainID() - if err == nil && userDID.IDStrings[2] != "000000000000000000000000000000000000000000" { + if err == nil { didResolution.DidDocument.Context = append(didResolution.DidDocument.Context.([]string), document.EcdsaSecp256k1RecoveryContext) addressString := fmt.Sprintf("%x", addr) blockchainAccountID := fmt.Sprintf("eip155:%s:0x%s", strings.Split(chainIDStateAddress, ":")[0], addressString) From b8b234dadbbc33f66bf5e4af96e22836fbe606b1 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 20 Aug 2024 17:45:59 +0200 Subject: [PATCH 10/26] fix global state message with gist param --- pkg/services/blockchain/eth/resolver.go | 2 +- pkg/services/blockchain/eth/resolver_test.go | 4 ++-- pkg/services/did.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 6b32909..59c7260 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -244,7 +244,7 @@ func (r *Resolver) Resolve( signature := "" if r.walletKey != "" && opts.Signature != "" { primaryType := services.IdentityStateType - if stateInfo == nil { + if opts.GistRoot != nil { primaryType = services.GlobalStateType } signature, err = r.signTypedData(primaryType, did, identityState) diff --git a/pkg/services/blockchain/eth/resolver_test.go b/pkg/services/blockchain/eth/resolver_test.go index 46d239f..f90f143 100644 --- a/pkg/services/blockchain/eth/resolver_test.go +++ b/pkg/services/blockchain/eth/resolver_test.go @@ -252,7 +252,7 @@ func TestResolveSignature_Success(t *testing.T) { ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0), }, - Signature: "0x4640cdda90e9340e4283e5e5d622137ee624142a53a66b98552eae931dab8eb41f25b4c9eae4771be41e7e1dcac364fc2b98cc6adf02586b158c24c3ca36f8af1c", + Signature: "0xc3dd18cd87c75fe225a569473f822daf66eed38f6e81dfc6766f4c35f1610ad96c546812eb416cd29f30098e5e9e38db78c4887db517f0569762e9f62227154d1b", }, }, { @@ -337,7 +337,7 @@ func TestResolveSignature_Success(t *testing.T) { require.Equal(t, tt.expectedIdentityState, identityState) primaryType := services.IdentityStateType - if tt.expectedIdentityState.StateInfo == nil { + if tt.opts.GistRoot != nil { primaryType = services.GlobalStateType } diff --git a/pkg/services/did.go b/pkg/services/did.go index 74f2d21..c65a01f 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -138,7 +138,7 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op if err == nil && opts.Signature != "" { primaryType := IdentityStateType - if identityState.StateInfo == nil { + if opts.GistRoot != nil { primaryType = GlobalStateType } eip712TypedData, err := resolver.TypedData(primaryType, *userDID, identityState, walletAddress) From 1ffe271f08621f348b8b38126f358137cc8d37c3 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 23 Aug 2024 10:04:43 +0200 Subject: [PATCH 11/26] update id and idType for identity and global messages --- pkg/services/blockchain/eth/resolver.go | 11 ++++++----- pkg/services/blockchain/eth/resolver_test.go | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 59c7260..2e2b5e1 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -57,7 +57,7 @@ var ( var IdentityStateAPITypes = apitypes.Types{ "IdentityState": []apitypes.Type{ {Name: "timestamp", Type: "uint256"}, - {Name: "userID", Type: "uint256"}, + {Name: "id", Type: "uint256"}, {Name: "state", Type: "uint256"}, {Name: "replacedAtTimestamp", Type: "uint256"}, }, @@ -72,7 +72,7 @@ var IdentityStateAPITypes = apitypes.Types{ var GlobalStateAPITypes = apitypes.Types{ "GlobalState": []apitypes.Type{ {Name: "timestamp", Type: "uint256"}, - {Name: "userID", Type: "uint256"}, + {Name: "idType", Type: "bytes2"}, {Name: "root", Type: "uint256"}, {Name: "replacedAtTimestamp", Type: "uint256"}, }, @@ -288,7 +288,8 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden return apitypes.TypedData{}, fmt.Errorf("invalid did format for did '%s': %v", did, err) } - userID := id.BigInt().String() + ID := id.BigInt().String() + idType := fmt.Sprintf("0x%02X%02X", id.Type()[0], id.Type()[1]) root := "0" state := "0" @@ -314,7 +315,7 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden apiTypes = IdentityStateAPITypes message = apitypes.TypedDataMessage{ "timestamp": timestamp, - "userID": userID, + "id": ID, "state": state, "replacedAtTimestamp": replacedAtTimestamp, } @@ -323,7 +324,7 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden apiTypes = GlobalStateAPITypes message = apitypes.TypedDataMessage{ "timestamp": timestamp, - "userID": userID, + "idType": idType, "root": root, "replacedAtTimestamp": replacedAtTimestamp, } diff --git a/pkg/services/blockchain/eth/resolver_test.go b/pkg/services/blockchain/eth/resolver_test.go index f90f143..7b7e56b 100644 --- a/pkg/services/blockchain/eth/resolver_test.go +++ b/pkg/services/blockchain/eth/resolver_test.go @@ -252,7 +252,7 @@ func TestResolveSignature_Success(t *testing.T) { ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0), }, - Signature: "0xc3dd18cd87c75fe225a569473f822daf66eed38f6e81dfc6766f4c35f1610ad96c546812eb416cd29f30098e5e9e38db78c4887db517f0569762e9f62227154d1b", + Signature: "0x388e838580e95a771a10806ea1514ab441e9f598ccca01899dea8541411a631c1d67525c652b1662c51547ea0aad445bc4e9d0fc3d41802221e66b0f534526841b", }, }, { @@ -279,7 +279,7 @@ func TestResolveSignature_Success(t *testing.T) { ReplacedAtTimestamp: big.NewInt(0), }, GistInfo: nil, - Signature: "0xc373a5a9df5c9227af61724bccaacffb117bf96437d9d7c41aff9be9f7662890716f2254dfc750b3768f2afa45843b53a8139264aa79626f4ad351f9390321841c", + Signature: "0x3bf7344312b0ef482974de45c722fbd431316b0bc42bd1050b5cb7bbe53034c51aa885d72c6cd958bdc3b46fc247f38b67c03767d98ba815ae3d8c33aac7398c1c", }, }, { @@ -314,7 +314,7 @@ func TestResolveSignature_Success(t *testing.T) { ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0), }, - Signature: "0xc373a5a9df5c9227af61724bccaacffb117bf96437d9d7c41aff9be9f7662890716f2254dfc750b3768f2afa45843b53a8139264aa79626f4ad351f9390321841c", + Signature: "0x3bf7344312b0ef482974de45c722fbd431316b0bc42bd1050b5cb7bbe53034c51aa885d72c6cd958bdc3b46fc247f38b67c03767d98ba815ae3d8c33aac7398c1c", }, }, } From c67a75a25170cb719f81aedb7751620045af5c09 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 28 Aug 2024 14:06:01 +0200 Subject: [PATCH 12/26] updates from review --- .gitignore | 1 + pkg/document/proof.go | 30 ------------ pkg/services/blockchain/eth/resolver.go | 47 +++++++++++-------- pkg/services/did.go | 2 +- pkg/services/registry.go | 2 +- test.http | 62 ------------------------- 6 files changed, 31 insertions(+), 113 deletions(-) delete mode 100644 test.http diff --git a/.gitignore b/.gitignore index 5815575..bb777aa 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ configs/driver.yaml .env* resolvers.settings.yaml +test.http diff --git a/pkg/document/proof.go b/pkg/document/proof.go index 166ccf4..bf16b87 100644 --- a/pkg/document/proof.go +++ b/pkg/document/proof.go @@ -1,8 +1,6 @@ package document import ( - "encoding/json" - "errors" "time" "github.com/ethereum/go-ethereum/signer/core/apitypes" @@ -31,31 +29,3 @@ const EthereumEip712SignatureProof2021Type verifiable.ProofType = "EthereumEip71 func (p *EthereumEip712SignatureProof2021) ProofType() verifiable.ProofType { return p.Type } - -func (p *EthereumEip712SignatureProof2021) UnmarshalJSON(in []byte) error { - var obj struct { - Type verifiable.ProofType `json:"type"` - ProofPursopose string `json:"proofPurpose"` - ProofValue string `json:"proofValue"` - VerificationMethod string `json:"verificationMethod"` - Created time.Time `json:"created"` - Eip712 json.RawMessage `json:"eip712"` - } - err := json.Unmarshal(in, &obj) - if err != nil { - return err - } - if obj.Type != EthereumEip712SignatureProof2021Type { - return errors.New("invalid proof type") - } - p.Type = obj.Type - err = json.Unmarshal(obj.Eip712, &p.Eip712) - if err != nil { - return err - } - p.VerificationMethod = obj.VerificationMethod - p.ProofPursopose = obj.ProofPursopose - p.ProofValue = obj.ProofValue - p.Created = obj.Created - return nil -} diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 2e2b5e1..a571596 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -48,6 +48,10 @@ type AuthData struct { Address string } +const ( + secp256k1VValue = 27 +) + var ( gistNotFoundException = "execution reverted: Root does not exist" identityNotFoundException = "execution reverted: Identity does not exist" @@ -114,7 +118,7 @@ func (r *Resolver) BlockchainID() string { return fmt.Sprintf("%d:%s", r.chainID, r.contractAddress) } -func (r *Resolver) WalletAddress() (string, error) { +func (r *Resolver) GetWalletAddress() (string, error) { if r.walletKey == "" { return "", errors.New("wallet key is not set") } @@ -242,7 +246,11 @@ func (r *Resolver) Resolve( } signature := "" - if r.walletKey != "" && opts.Signature != "" { + if opts.Signature != "" { + if r.walletKey == "" { + return services.IdentityState{}, + errors.New("no wallet key found for generating signature") + } primaryType := services.IdentityStateType if opts.GistRoot != nil { primaryType = services.GlobalStateType @@ -263,7 +271,7 @@ func (r *Resolver) VerifyState( identityState services.IdentityState, did w3c.DID, ) (bool, error) { - walletAddress, err := r.WalletAddress() + walletAddress, err := r.GetWalletAddress() if err != nil { return false, err } @@ -291,19 +299,6 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden ID := id.BigInt().String() idType := fmt.Sprintf("0x%02X%02X", id.Type()[0], id.Type()[1]) - root := "0" - state := "0" - replacedAtTimestamp := "0" - - if identityState.StateInfo != nil { - state = identityState.StateInfo.State.String() - replacedAtTimestamp = identityState.StateInfo.ReplacedAtTimestamp.String() - } - if identityState.GistInfo != nil { - root = identityState.GistInfo.Root.String() - replacedAtTimestamp = identityState.GistInfo.ReplacedAtTimestamp.String() - } - apiTypes := apitypes.Types{} message := apitypes.TypedDataMessage{} primaryTypeString := "" @@ -311,6 +306,13 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden switch primaryType { case services.IdentityStateType: + state := "0" + replacedAtTimestamp := "0" + + if identityState.StateInfo != nil { + state = identityState.StateInfo.State.String() + replacedAtTimestamp = identityState.StateInfo.ReplacedAtTimestamp.String() + } primaryTypeString = "IdentityState" apiTypes = IdentityStateAPITypes message = apitypes.TypedDataMessage{ @@ -320,6 +322,13 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden "replacedAtTimestamp": replacedAtTimestamp, } case services.GlobalStateType: + root := "0" + replacedAtTimestamp := "0" + + if identityState.GistInfo != nil { + root = identityState.GistInfo.Root.String() + replacedAtTimestamp = identityState.GistInfo.ReplacedAtTimestamp.String() + } primaryTypeString = "GlobalState" apiTypes = GlobalStateAPITypes message = apitypes.TypedDataMessage{ @@ -351,7 +360,7 @@ func (r *Resolver) signTypedData(primaryType services.PrimaryType, did w3c.DID, return "", err } - walletAddress, err := r.WalletAddress() + walletAddress, err := r.GetWalletAddress() if err != nil { return "", err } @@ -377,8 +386,8 @@ func (r *Resolver) signTypedData(primaryType services.PrimaryType, did w3c.DID, return "", err } - if signature[64] < 27 { - signature[64] += 27 + if signature[64] < secp256k1VValue { // Invalid Ethereum signature (V is not 27 or 28) + signature[64] += secp256k1VValue // Transform yellow paper V from 27/28 to 0/1 } return "0x" + hex.EncodeToString(signature), nil diff --git a/pkg/services/did.go b/pkg/services/did.go index c65a01f..6ab7bbb 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -134,7 +134,7 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op }, ) - walletAddress, err := resolver.WalletAddress() + walletAddress, err := resolver.GetWalletAddress() if err == nil && opts.Signature != "" { primaryType := IdentityStateType diff --git a/pkg/services/registry.go b/pkg/services/registry.go index 5e858a2..f3cd834 100644 --- a/pkg/services/registry.go +++ b/pkg/services/registry.go @@ -104,7 +104,7 @@ type Resolver interface { Resolve(ctx context.Context, did w3c.DID, opts *ResolverOpts) (IdentityState, error) ResolveGist(ctx context.Context, opts *ResolverOpts) (*GistInfo, error) BlockchainID() string - WalletAddress() (string, error) + GetWalletAddress() (string, error) TypedData(primaryType PrimaryType, did w3c.DID, identityState IdentityState, walletAddress string) (apitypes.TypedData, error) } diff --git a/test.http b/test.http deleted file mode 100644 index 6328647..0000000 --- a/test.http +++ /dev/null @@ -1,62 +0,0 @@ -#GET https://resolver-dev.privado.id/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS -# ?signature=EthereumEip712Signature2021 -# &state=e500792b5d2401e3ff2277f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b - -### GET state -GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS - ?signature=EthereumEip712Signature2021 - &state=e500792b5d2401e3ff2277f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b - -### GET state (non-existent) -GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS - ?signature=EthereumEip712Signature2021 - &state=0000000000000000000077f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b -# FIX only empty proofValue for non-existed state. But should return error - -### Get zero gist (zero) -GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS - ?signature=EthereumEip712Signature2021 - &gist=0x0000000000000000000000000000000000000000000000000000000000000000 -# FIX why this signs IdentityState but no GlobalState? - -### Get zero gist, root only did (zero) -GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:000000000000000000000000000000000000000000 - ?signature=EthereumEip712Signature2021 - &gist=0x0000000000000000000000000000000000000000000000000000000000000000 - -### Get non-existed GIST -GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:000000000000000000000000000000000000000000 - ?signature=EthereumEip712Signature2021 - &gist=0x0000000000000000000000000000000000000000000000000000000000000001 -# FIX returns and signs zero global state, but should return error - -### Vova's request, amoy, no id -GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:000000000000000000000000000000000000000000 - ?signature=EthereumEip712Signature2021 - &gist=b048eccccdb175b95aa3fa3453ca870818d1cdb4d765eeea4123712bf59cff0f - -### Vova's request, amoy, no id -GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:000000000000000000000000000000000000000000 - ?signature=EthereumEip712Signature2021 - -### Vova's request, amoy, with id -GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:xHSDc6jvEVcKBUMV9rQcJ98S3SUsBHGyinN948JH3 - ?signature=EthereumEip712Signature2021 - &gist=b048eccccdb175b95aa3fa3453ca870818d1cdb4d765eeea4123712bf59cff0f - -### AMOY: Get zero gist, root only did (zero) -GET http://localhost:8080/1.0/identifiers/did:iden3:polygon:amoy:000000000000000000000000000000000000000000 - ?signature=EthereumEip712Signature2021 -## TO FIX Need global in ver method in this case - -### Non-existent gist, should throw -GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS - ?signature=EthereumEip712Signature2021 - &gist=0xe500792b5d2401e3ff2277f26ebc04a3733d6b57ddb94e82612a8dfd18644c2b - -### Latest state -GET http://localhost:8080/1.0/identifiers/did:iden3:privado:test:2SoPJ7UQh3jFPLqf15N3z6rEYhzJ38N8DUemLN2KWS - ?signature=EthereumEip712Signature2021 - -## TO FIX Need to impl true "null" did id - From 8a3726fff3718d3d5529bdac3151369bbc55af5d Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 28 Aug 2024 15:53:00 +0200 Subject: [PATCH 13/26] resolve lint issues --- pkg/services/blockchain/eth/resolver.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index a571596..bad1a1d 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -466,18 +466,6 @@ func (r *Resolver) resolveLatest( return &stateInfo, &gistInfo, verifyContractState(id, stateInfo) } -func (r *Resolver) resolveGistRootOnly( - ctx context.Context, - gistRoot *big.Int, -) (*contract.IStateGistRootInfo, error) { - gistInfo, err := r.state.GetGISTRootInfo(&bind.CallOpts{Context: ctx}, gistRoot) - if err = notFoundErr(err); err != nil { - return nil, err - } - - return &gistInfo, nil -} - func (r *Resolver) resolveState( ctx context.Context, id core.ID, From c58ef57bcab57e451a98f9661545dee8d3879129 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 28 Aug 2024 18:50:14 +0200 Subject: [PATCH 14/26] remove invalid condition --- pkg/services/blockchain/eth/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index bad1a1d..dca9766 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -199,7 +199,7 @@ func (r *Resolver) Resolve( stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) } // err == http.505 - if err != nil && (!errors.Is(err, services.ErrNotFound) || opts.GistRoot != nil) { + if err != nil && (!errors.Is(err, services.ErrNotFound)) { return services.IdentityState{}, err } From 2d148ff086c6fa4d7c738ce718e4f68c10a3fb52 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 29 Aug 2024 10:19:05 +0200 Subject: [PATCH 15/26] remove parentheses --- pkg/services/blockchain/eth/resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index dca9766..343cae3 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -199,7 +199,7 @@ func (r *Resolver) Resolve( stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) } // err == http.505 - if err != nil && (!errors.Is(err, services.ErrNotFound)) { + if err != nil && !errors.Is(err, services.ErrNotFound) { return services.IdentityState{}, err } From af00532280e579b19e930e5881d2965ebf574387 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 29 Aug 2024 13:04:25 +0200 Subject: [PATCH 16/26] update from review comments --- cmd/driver/main.go | 3 +- pkg/services/blockchain/eth/resolver.go | 46 +++++++++++++++++-------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/cmd/driver/main.go b/cmd/driver/main.go index 216c53b..9c5ae4c 100644 --- a/cmd/driver/main.go +++ b/cmd/driver/main.go @@ -63,7 +63,8 @@ func initResolvers() *services.ResolverRegistry { for chainName, chainSettings := range rs { for networkName, networkSettings := range chainSettings { prefix := fmt.Sprintf("%s:%s", chainName, networkName) - resolver, err := eth.NewResolver(networkSettings.NetworkURL, networkSettings.ContractAddress, networkSettings.WalletKey) + resolver, err := eth.NewResolver(networkSettings.NetworkURL, networkSettings.ContractAddress, + eth.WithSigner(networkSettings.WalletKey)) if err != nil { log.Fatalf("failed configure resolver for network '%s': %v", prefix, err) } diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 343cae3..7687d9e 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -48,6 +48,8 @@ type AuthData struct { Address string } +type ResolverOption func(*Resolver) + const ( secp256k1VValue = 27 ) @@ -90,8 +92,17 @@ var GlobalStateAPITypes = apitypes.Types{ var TimeStamp = TimeStampFn +func WithSigner(walletKey string) ResolverOption { + return func(r *Resolver) { + if walletKey != "" { + r.walletKey = walletKey + return + } + } +} + // NewResolver create new ethereum resolver. -func NewResolver(url, address, walletKey string) (*Resolver, error) { +func NewResolver(url string, address string, opts ...ResolverOption) (*Resolver, error) { c, err := ethclient.Dial(url) if err != nil { return nil, err @@ -104,8 +115,11 @@ func NewResolver(url, address, walletKey string) (*Resolver, error) { resolver := &Resolver{ state: sc, contractAddress: address, - walletKey: walletKey, } + for _, opt := range opts { + opt(resolver) + } + chainID, err := c.NetworkID(context.Background()) if err != nil { return nil, err @@ -291,13 +305,21 @@ func TimeStampFn() string { } func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, identityState services.IdentityState, walletAddress string) (apitypes.TypedData, error) { + if primaryType == services.IdentityStateType && identityState.StateInfo == nil { + return apitypes.TypedData{}, + errors.New("identity state info is required for primary type 'IdentityState'") + } + if primaryType == services.GlobalStateType && identityState.GistInfo == nil { + return apitypes.TypedData{}, + errors.New("gist info is required for primary type 'GlobalState'") + } id, err := core.IDFromDID(did) if err != nil { return apitypes.TypedData{}, fmt.Errorf("invalid did format for did '%s': %v", did, err) } ID := id.BigInt().String() - idType := fmt.Sprintf("0x%02X%02X", id.Type()[0], id.Type()[1]) + idType := fmt.Sprintf("0x%X", id.Type()) apiTypes := apitypes.Types{} message := apitypes.TypedDataMessage{} @@ -308,11 +330,8 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden case services.IdentityStateType: state := "0" replacedAtTimestamp := "0" - - if identityState.StateInfo != nil { - state = identityState.StateInfo.State.String() - replacedAtTimestamp = identityState.StateInfo.ReplacedAtTimestamp.String() - } + state = identityState.StateInfo.State.String() + replacedAtTimestamp = identityState.StateInfo.ReplacedAtTimestamp.String() primaryTypeString = "IdentityState" apiTypes = IdentityStateAPITypes message = apitypes.TypedDataMessage{ @@ -324,11 +343,8 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden case services.GlobalStateType: root := "0" replacedAtTimestamp := "0" - - if identityState.GistInfo != nil { - root = identityState.GistInfo.Root.String() - replacedAtTimestamp = identityState.GistInfo.ReplacedAtTimestamp.String() - } + root = identityState.GistInfo.Root.String() + replacedAtTimestamp = identityState.GistInfo.ReplacedAtTimestamp.String() primaryTypeString = "GlobalState" apiTypes = GlobalStateAPITypes message = apitypes.TypedDataMessage{ @@ -346,7 +362,7 @@ func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, iden Name: "StateInfo", Version: "1", ChainId: math.NewHexOrDecimal256(int64(0)), - VerifyingContract: "0x0000000000000000000000000000000000000000", + VerifyingContract: common.Address{}.String(), }, Message: message, } @@ -387,7 +403,7 @@ func (r *Resolver) signTypedData(primaryType services.PrimaryType, did w3c.DID, } if signature[64] < secp256k1VValue { // Invalid Ethereum signature (V is not 27 or 28) - signature[64] += secp256k1VValue // Transform yellow paper V from 27/28 to 0/1 + signature[64] += secp256k1VValue // Transform yellow paper V from 0/1 to 27/28 } return "0x" + hex.EncodeToString(signature), nil From e84025b9b82aba40120a38e2de4e08181fb9847a Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Sep 2024 09:55:16 +0200 Subject: [PATCH 17/26] refactor eip712 signer out from resolver --- .gitignore | 2 +- Dockerfile | 4 +- cmd/driver/main.go | 30 +- pkg/app/configs/driver.go | 29 +- pkg/services/blockchain/eth/resolver.go | 286 +------------------ pkg/services/blockchain/eth/resolver_test.go | 146 ---------- pkg/services/did.go | 152 ++++++++-- pkg/services/registry.go | 10 - pkg/services/signers.go | 46 +++ pkg/services/signers/EIP712Signer.go | 93 ++++++ 10 files changed, 333 insertions(+), 465 deletions(-) create mode 100644 pkg/services/signers.go create mode 100644 pkg/services/signers/EIP712Signer.go diff --git a/.gitignore b/.gitignore index bb777aa..a771297 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,5 @@ # Envs configs/driver.yaml .env* -resolvers.settings.yaml +*.settings.yaml test.http diff --git a/Dockerfile b/Dockerfile index 130d0d0..3406dba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN CGO_ENABLED=0 go build -o ./driver ./cmd/driver/main.go # Build an driver image FROM scratch -COPY ./resolvers.settings.yaml /app/resolvers.settings.yaml +COPY ./*.settings.yaml /app/ COPY --from=base /build/driver /app/driver COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ @@ -28,4 +28,4 @@ ENV HOST=0.0.0.0 ENV PORT=8080 # Command to run -ENTRYPOINT ["/app/driver", "/app/resolvers.settings.yaml"] +ENTRYPOINT ["/app/driver", "/app/resolvers.settings.yaml", "/app/signers.settings.yaml"] diff --git a/cmd/driver/main.go b/cmd/driver/main.go index 9c5ae4c..7a19b9e 100644 --- a/cmd/driver/main.go +++ b/cmd/driver/main.go @@ -13,6 +13,7 @@ import ( "github.com/iden3/driver-did-iden3/pkg/services" "github.com/iden3/driver-did-iden3/pkg/services/blockchain/eth" "github.com/iden3/driver-did-iden3/pkg/services/ens" + "github.com/iden3/driver-did-iden3/pkg/services/signers" ) func main() { @@ -34,7 +35,7 @@ func main() { } mux := app.Handlers{DidDocumentHandler: &app.DidDocumentHandler{ - DidDocumentService: services.NewDidDocumentServices(initResolvers(), r), + DidDocumentService: services.NewDidDocumentServices(initResolvers(), r, services.WithSigners(initEIP712Signers())), }, } @@ -63,8 +64,7 @@ func initResolvers() *services.ResolverRegistry { for chainName, chainSettings := range rs { for networkName, networkSettings := range chainSettings { prefix := fmt.Sprintf("%s:%s", chainName, networkName) - resolver, err := eth.NewResolver(networkSettings.NetworkURL, networkSettings.ContractAddress, - eth.WithSigner(networkSettings.WalletKey)) + resolver, err := eth.NewResolver(networkSettings.NetworkURL, networkSettings.ContractAddress) if err != nil { log.Fatalf("failed configure resolver for network '%s': %v", prefix, err) } @@ -74,3 +74,27 @@ func initResolvers() *services.ResolverRegistry { return resolvers } + +func initEIP712Signers() *services.EIP712SignerRegistry { + var path string + if len(os.Args) > 3 { + path = os.Args[2] + } + rs, err := configs.ParseSignersSettings(path) + if err != nil { + log.Fatal("can't read signers settings:", err) + } + chainSigners := services.NewChainEIP712Signers() + for chainName, chainSettings := range rs { + for networkName, networkSettings := range chainSettings { + prefix := fmt.Sprintf("%s:%s", chainName, networkName) + signer, err := signers.NewEIP712Signer(networkSettings.WalletKey) + if err != nil { + log.Fatalf("failed configure signer for network '%s': %v", prefix, err) + } + chainSigners.Add(prefix, signer) + } + } + + return chainSigners +} diff --git a/pkg/app/configs/driver.go b/pkg/app/configs/driver.go index e3be040..9e0f093 100644 --- a/pkg/app/configs/driver.go +++ b/pkg/app/configs/driver.go @@ -11,12 +11,16 @@ import ( ) const defaultPathToResolverSettings = "./resolvers.settings.yaml" +const defaultPathToSignersSettings = "./signers.settings.yaml" // ResolverSettings represent settings for resolver. type ResolverSettings map[string]map[string]struct { ContractAddress string `yaml:"contractAddress"` NetworkURL string `yaml:"networkURL"` - WalletKey string `yaml:"walletKey"` +} + +type SignersSettings map[string]map[string]struct { + WalletKey string `yaml:"walletKey"` } // Config structure represent yaml config for did driver. @@ -61,3 +65,26 @@ func ParseResolversSettings(path string) (ResolverSettings, error) { return settings, nil } + +// ParseSignersSettings parse yaml file with signers settings. +func ParseSignersSettings(path string) (SignersSettings, error) { + if path == "" { + path = defaultPathToSignersSettings + } + f, err := os.Open(filepath.Clean(path)) + if err != nil { + return nil, err + } + defer func() { + if err := f.Close(); err != nil { + log.Println("failed to close setting file:", err) + } + }() + + settings := SignersSettings{} + if err := yaml.NewDecoder(f).Decode(&settings); err != nil { + return nil, errors.Errorf("invalid yaml file: %v", settings) + } + + return settings, nil +} diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 7687d9e..0622e43 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -2,22 +2,13 @@ package eth import ( "context" - "crypto/ecdsa" - "crypto/subtle" - "encoding/hex" "errors" "fmt" "math/big" - "strconv" - "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/signer/core/apitypes" contract "github.com/iden3/contracts-abi/state/go/abi" "github.com/iden3/driver-did-iden3/pkg/services" core "github.com/iden3/go-iden3-core/v2" @@ -39,70 +30,18 @@ type Resolver struct { contractAddress string chainID int - walletKey string -} - -type AuthData struct { - TypedData apitypes.TypedData - Signature string - Address string } type ResolverOption func(*Resolver) -const ( - secp256k1VValue = 27 -) - var ( gistNotFoundException = "execution reverted: Root does not exist" identityNotFoundException = "execution reverted: Identity does not exist" stateNotFoundException = "execution reverted: State does not exist" ) -var IdentityStateAPITypes = apitypes.Types{ - "IdentityState": []apitypes.Type{ - {Name: "timestamp", Type: "uint256"}, - {Name: "id", Type: "uint256"}, - {Name: "state", Type: "uint256"}, - {Name: "replacedAtTimestamp", Type: "uint256"}, - }, - "EIP712Domain": []apitypes.Type{ - {Name: "name", Type: "string"}, - {Name: "version", Type: "string"}, - {Name: "chainId", Type: "uint256"}, - {Name: "verifyingContract", Type: "address"}, - }, -} - -var GlobalStateAPITypes = apitypes.Types{ - "GlobalState": []apitypes.Type{ - {Name: "timestamp", Type: "uint256"}, - {Name: "idType", Type: "bytes2"}, - {Name: "root", Type: "uint256"}, - {Name: "replacedAtTimestamp", Type: "uint256"}, - }, - "EIP712Domain": []apitypes.Type{ - {Name: "name", Type: "string"}, - {Name: "version", Type: "string"}, - {Name: "chainId", Type: "uint256"}, - {Name: "verifyingContract", Type: "address"}, - }, -} - -var TimeStamp = TimeStampFn - -func WithSigner(walletKey string) ResolverOption { - return func(r *Resolver) { - if walletKey != "" { - r.walletKey = walletKey - return - } - } -} - // NewResolver create new ethereum resolver. -func NewResolver(url string, address string, opts ...ResolverOption) (*Resolver, error) { +func NewResolver(url, address string) (*Resolver, error) { c, err := ethclient.Dial(url) if err != nil { return nil, err @@ -116,9 +55,6 @@ func NewResolver(url string, address string, opts ...ResolverOption) (*Resolver, state: sc, contractAddress: address, } - for _, opt := range opts { - opt(resolver) - } chainID, err := c.NetworkID(context.Background()) if err != nil { @@ -132,27 +68,6 @@ func (r *Resolver) BlockchainID() string { return fmt.Sprintf("%d:%s", r.chainID, r.contractAddress) } -func (r *Resolver) GetWalletAddress() (string, error) { - if r.walletKey == "" { - return "", errors.New("wallet key is not set") - } - - privateKey, err := crypto.HexToECDSA(r.walletKey) - if err != nil { - return "", err - } - - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return "", errors.New("error casting public key to ECDSA") - } - - walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) - - return walletAddress.String(), nil -} - func (r *Resolver) ResolveGist( ctx context.Context, opts *services.ResolverOpts, @@ -259,208 +174,9 @@ func (r *Resolver) Resolve( } } - signature := "" - if opts.Signature != "" { - if r.walletKey == "" { - return services.IdentityState{}, - errors.New("no wallet key found for generating signature") - } - primaryType := services.IdentityStateType - if opts.GistRoot != nil { - primaryType = services.GlobalStateType - } - signature, err = r.signTypedData(primaryType, did, identityState) - if err != nil { - return services.IdentityState{}, err - } - } - - identityState.Signature = signature - return identityState, err } -func (r *Resolver) VerifyState( - primaryType services.PrimaryType, - identityState services.IdentityState, - did w3c.DID, -) (bool, error) { - walletAddress, err := r.GetWalletAddress() - if err != nil { - return false, err - } - - typedData, err := r.TypedData(primaryType, did, identityState, walletAddress) - if err != nil { - return false, err - } - - authData := AuthData{TypedData: typedData, Signature: identityState.Signature, Address: walletAddress} - return r.verifyTypedData(authData) -} - -func TimeStampFn() string { - timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10) - return timestamp -} - -func (r *Resolver) TypedData(primaryType services.PrimaryType, did w3c.DID, identityState services.IdentityState, walletAddress string) (apitypes.TypedData, error) { - if primaryType == services.IdentityStateType && identityState.StateInfo == nil { - return apitypes.TypedData{}, - errors.New("identity state info is required for primary type 'IdentityState'") - } - if primaryType == services.GlobalStateType && identityState.GistInfo == nil { - return apitypes.TypedData{}, - errors.New("gist info is required for primary type 'GlobalState'") - } - id, err := core.IDFromDID(did) - if err != nil { - return apitypes.TypedData{}, - fmt.Errorf("invalid did format for did '%s': %v", did, err) - } - ID := id.BigInt().String() - idType := fmt.Sprintf("0x%X", id.Type()) - - apiTypes := apitypes.Types{} - message := apitypes.TypedDataMessage{} - primaryTypeString := "" - timestamp := TimeStamp() - - switch primaryType { - case services.IdentityStateType: - state := "0" - replacedAtTimestamp := "0" - state = identityState.StateInfo.State.String() - replacedAtTimestamp = identityState.StateInfo.ReplacedAtTimestamp.String() - primaryTypeString = "IdentityState" - apiTypes = IdentityStateAPITypes - message = apitypes.TypedDataMessage{ - "timestamp": timestamp, - "id": ID, - "state": state, - "replacedAtTimestamp": replacedAtTimestamp, - } - case services.GlobalStateType: - root := "0" - replacedAtTimestamp := "0" - root = identityState.GistInfo.Root.String() - replacedAtTimestamp = identityState.GistInfo.ReplacedAtTimestamp.String() - primaryTypeString = "GlobalState" - apiTypes = GlobalStateAPITypes - message = apitypes.TypedDataMessage{ - "timestamp": timestamp, - "idType": idType, - "root": root, - "replacedAtTimestamp": replacedAtTimestamp, - } - } - - typedData := apitypes.TypedData{ - Types: apiTypes, - PrimaryType: primaryTypeString, - Domain: apitypes.TypedDataDomain{ - Name: "StateInfo", - Version: "1", - ChainId: math.NewHexOrDecimal256(int64(0)), - VerifyingContract: common.Address{}.String(), - }, - Message: message, - } - - return typedData, nil -} - -func (r *Resolver) signTypedData(primaryType services.PrimaryType, did w3c.DID, identityState services.IdentityState) (string, error) { - privateKey, err := crypto.HexToECDSA(r.walletKey) - if err != nil { - return "", err - } - - walletAddress, err := r.GetWalletAddress() - if err != nil { - return "", err - } - - typedData, err := r.TypedData(primaryType, did, identityState, walletAddress) - if err != nil { - return "", errors.New("error getting typed data for signing") - } - - domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - return "", errors.New("error hashing EIP712Domain for signing") - } - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - return "", errors.New("error hashing PrimaryType message for signing") - } - rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) - dataHash := crypto.Keccak256(rawData) - - signature, err := crypto.Sign(dataHash, privateKey) - if err != nil { - return "", err - } - - if signature[64] < secp256k1VValue { // Invalid Ethereum signature (V is not 27 or 28) - signature[64] += secp256k1VValue // Transform yellow paper V from 0/1 to 27/28 - } - - return "0x" + hex.EncodeToString(signature), nil -} - -func (r *Resolver) verifyTypedData(authData AuthData) (bool, error) { - signature, err := hexutil.Decode(authData.Signature) - if err != nil { - return false, fmt.Errorf("decode signature: %w", err) - } - - // EIP-712 typed data marshaling - domainSeparator, err := authData.TypedData.HashStruct("EIP712Domain", authData.TypedData.Domain.Map()) - if err != nil { - return false, fmt.Errorf("eip712domain hash struct: %w", err) - } - typedDataHash, err := authData.TypedData.HashStruct(authData.TypedData.PrimaryType, authData.TypedData.Message) - if err != nil { - return false, fmt.Errorf("primary type hash struct: %w", err) - } - - // add magic string prefix - rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) - sighash := crypto.Keccak256(rawData) - - // update the recovery id - // https://github.com/ethereum/go-ethereum/blob/55599ee95d4151a2502465e0afc7c47bd1acba77/internal/ethapi/api.go#L442 - signature[64] -= 27 - - // get the pubkey used to sign this signature - sigPubkey, err := crypto.Ecrecover(sighash, signature) - if err != nil { - return false, fmt.Errorf("ecrecover: %w", err) - } - - // get the address to confirm it's the same one in the auth token - pubkey, err := crypto.UnmarshalPubkey(sigPubkey) - if err != nil { - return false, fmt.Errorf("unmarshal pub key: %w", err) - } - address := crypto.PubkeyToAddress(*pubkey) - - // verify the signature (not sure if this is actually required after ecrecover) - signatureNoRecoverID := signature[:len(signature)-1] - verified := crypto.VerifySignature(sigPubkey, sighash, signatureNoRecoverID) - if !verified { - return false, errors.New("verification failed") - } - - dataAddress := common.HexToAddress(authData.Address) - if subtle.ConstantTimeCompare(address.Bytes(), dataAddress.Bytes()) == 0 { - return false, errors.New("address mismatch") - } - - return true, nil -} - func (r *Resolver) resolveLatest( ctx context.Context, id core.ID, diff --git a/pkg/services/blockchain/eth/resolver_test.go b/pkg/services/blockchain/eth/resolver_test.go index 7b7e56b..d497031 100644 --- a/pkg/services/blockchain/eth/resolver_test.go +++ b/pkg/services/blockchain/eth/resolver_test.go @@ -6,18 +6,14 @@ import ( "math/big" "testing" - "github.com/ethereum/go-ethereum/crypto" "github.com/golang/mock/gomock" contract "github.com/iden3/contracts-abi/state/go/abi" - "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/driver-did-iden3/pkg/services" cm "github.com/iden3/driver-did-iden3/pkg/services/blockchain/eth/contract/mock" core "github.com/iden3/go-iden3-core/v2" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/pkg/errors" "github.com/stretchr/testify/require" - "github.com/tyler-smith/go-bip32" - "github.com/tyler-smith/go-bip39" ) var userDID, _ = w3c.ParseDID("did:polygonid:polygon:amoy:2qY71pSkdCsRetTHbUA4YqG7Hx63Ej2PeiJMzAdJ2V") @@ -205,145 +201,3 @@ func TestNotFoundErr(t *testing.T) { }) } } - -func TestResolveSignature_Success(t *testing.T) { - tests := []struct { - name string - opts *services.ResolverOpts - userDID *w3c.DID - contractMock func(c *cm.MockStateContract) - timeStamp func() string - expectedIdentityState services.IdentityState - }{ - { - name: "resolve identity state by gist", - opts: &services.ResolverOpts{ - GistRoot: big.NewInt(1), - Signature: string(document.EthereumEip712SignatureProof2021Type), - }, - userDID: userDID, - contractMock: func(c *cm.MockStateContract) { - proof := contract.IStateGistProof{ - Root: big.NewInt(4), - Existence: true, - Value: big.NewInt(5), - } - userID, _ := core.IDFromDID(*userDID) - c.EXPECT().GetGISTProofByRoot(gomock.Any(), userID.BigInt(), big.NewInt(1)).Return(proof, nil) - gistInfo := contract.IStateGistRootInfo{Root: big.NewInt(555), CreatedAtTimestamp: big.NewInt(0), ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} - c.EXPECT().GetGISTRootInfo(gomock.Any(), big.NewInt(4)).Return(gistInfo, nil) - stateInfo := contract.IStateStateInfo{Id: userID.BigInt(), State: big.NewInt(444), CreatedAtTimestamp: big.NewInt(0), ReplacedByState: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} - c.EXPECT().GetStateInfoByIdAndState(gomock.Any(), gomock.Any(), big.NewInt(5)).Return(stateInfo, nil) - }, - timeStamp: func() string { - return "0" - }, - expectedIdentityState: services.IdentityState{ - StateInfo: &services.StateInfo{ - ID: *userDID, - State: big.NewInt(444), - CreatedAtTimestamp: big.NewInt(0), - ReplacedByState: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - }, - GistInfo: &services.GistInfo{ - Root: big.NewInt(555), - CreatedAtTimestamp: big.NewInt(0), - ReplacedByRoot: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - }, - Signature: "0x388e838580e95a771a10806ea1514ab441e9f598ccca01899dea8541411a631c1d67525c652b1662c51547ea0aad445bc4e9d0fc3d41802221e66b0f534526841b", - }, - }, - { - name: "resolve identity state by state", - opts: &services.ResolverOpts{ - State: big.NewInt(1), - Signature: string(document.EthereumEip712SignatureProof2021Type), - }, - userDID: userDID, - contractMock: func(c *cm.MockStateContract) { - userID, _ := core.IDFromDID(*userDID) - res := contract.IStateStateInfo{Id: userID.BigInt(), State: big.NewInt(555), CreatedAtTimestamp: big.NewInt(0), ReplacedByState: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} - c.EXPECT().GetStateInfoByIdAndState(gomock.Any(), gomock.Any(), big.NewInt(1)).Return(res, nil) - }, - timeStamp: func() string { - return "0" - }, - expectedIdentityState: services.IdentityState{ - StateInfo: &services.StateInfo{ - ID: *userDID, - State: big.NewInt(555), - CreatedAtTimestamp: big.NewInt(0), - ReplacedByState: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - }, - GistInfo: nil, - Signature: "0x3bf7344312b0ef482974de45c722fbd431316b0bc42bd1050b5cb7bbe53034c51aa885d72c6cd958bdc3b46fc247f38b67c03767d98ba815ae3d8c33aac7398c1c", - }, - }, - { - name: "resolve latest state", - opts: &services.ResolverOpts{ - Signature: string(document.EthereumEip712SignatureProof2021Type), - }, - userDID: userDID, - contractMock: func(c *cm.MockStateContract) { - userID, _ := core.IDFromDID(*userDID) - latestGist := big.NewInt(100) - c.EXPECT().GetGISTRoot(gomock.Any()).Return(latestGist, nil) - latestGistInfo := contract.IStateGistRootInfo{Root: big.NewInt(400), CreatedAtTimestamp: big.NewInt(0), ReplacedByRoot: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} - c.EXPECT().GetGISTRootInfo(gomock.Any(), latestGist).Return(latestGistInfo, nil) - stateInfo := contract.IStateStateInfo{Id: userID.BigInt(), State: big.NewInt(555), CreatedAtTimestamp: big.NewInt(0), ReplacedByState: big.NewInt(0), ReplacedAtTimestamp: big.NewInt(0)} - c.EXPECT().GetStateInfoById(gomock.Any(), userID.BigInt()).Return(stateInfo, nil) - }, - timeStamp: func() string { - return "0" - }, - expectedIdentityState: services.IdentityState{ - StateInfo: &services.StateInfo{ - ID: *userDID, - State: big.NewInt(555), - CreatedAtTimestamp: big.NewInt(0), - ReplacedByState: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - }, - GistInfo: &services.GistInfo{ - Root: big.NewInt(400), - CreatedAtTimestamp: big.NewInt(0), - ReplacedByRoot: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - }, - Signature: "0x3bf7344312b0ef482974de45c722fbd431316b0bc42bd1050b5cb7bbe53034c51aa885d72c6cd958bdc3b46fc247f38b67c03767d98ba815ae3d8c33aac7398c1c", - }, - }, - } - - mnemonic := "rib satisfy drastic trigger trial exclude raccoon wedding then gaze fire hero" - seed := bip39.NewSeed(mnemonic, "Secret Passphrase bla bla bla") - masterPrivateKey, _ := bip32.NewMasterKey(seed) - ecdaPrivateKey := crypto.ToECDSAUnsafe(masterPrivateKey.Key) - privateKeyHex := fmt.Sprintf("%x", ecdaPrivateKey.D) - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctrl := gomock.NewController(t) - stateContract := cm.NewMockStateContract(ctrl) - tt.contractMock(stateContract) - TimeStamp = tt.timeStamp - resolver := Resolver{state: stateContract, chainID: 1, walletKey: privateKeyHex} - identityState, err := resolver.Resolve(context.Background(), *tt.userDID, tt.opts) - require.NoError(t, err) - require.Equal(t, tt.expectedIdentityState, identityState) - - primaryType := services.IdentityStateType - if tt.opts.GistRoot != nil { - primaryType = services.GlobalStateType - } - - ok, _ := resolver.VerifyState(primaryType, identityState, *tt.userDID) - require.Equal(t, true, ok) - ctrl.Finish() - }) - } -} diff --git a/pkg/services/did.go b/pkg/services/did.go index 6ab7bbb..6410e66 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -5,9 +5,13 @@ import ( "fmt" "math/big" "net" + "strconv" "strings" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/driver-did-iden3/pkg/services/ens" core "github.com/iden3/go-iden3-core/v2" @@ -23,6 +27,7 @@ const ( type DidDocumentServices struct { resolvers *ResolverRegistry ens *ens.Registry + signers *EIP712SignerRegistry } type ResolverOpts struct { @@ -31,8 +36,21 @@ type ResolverOpts struct { Signature string } -func NewDidDocumentServices(resolvers *ResolverRegistry, registry *ens.Registry) *DidDocumentServices { - return &DidDocumentServices{resolvers, registry} +type DidDocumentOption func(*DidDocumentServices) + +func WithSigners(signers *EIP712SignerRegistry) DidDocumentOption { + return func(d *DidDocumentServices) { + d.signers = signers + } +} + +func NewDidDocumentServices(resolvers *ResolverRegistry, registry *ens.Registry, opts ...DidDocumentOption) *DidDocumentServices { + didDocumentService := &DidDocumentServices{resolvers, registry, nil} + + for _, opt := range opts { + opt(didDocumentService) + } + return didDocumentService } // GetDidDocument return did document by identifier. @@ -134,25 +152,36 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op }, ) - walletAddress, err := resolver.GetWalletAddress() - - if err == nil && opts.Signature != "" { - primaryType := IdentityStateType - if opts.GistRoot != nil { - primaryType = GlobalStateType + if opts.Signature != "" { + if d.signers == nil { + return nil, errors.New("signers not initialized") } - eip712TypedData, err := resolver.TypedData(primaryType, *userDID, identityState, walletAddress) + signer, err := d.signers.GetEIP712SignerByNetwork(string(b), string(n)) if err != nil { - return nil, fmt.Errorf("invalid typed data: %v", err) + return nil, fmt.Errorf("invalid signer: %v", err) + } + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err } - eip712Proof := &document.EthereumEip712SignatureProof2021{ - Type: document.EthereumEip712SignatureProof2021Type, - ProofPursopose: "assertionMethod", - ProofValue: identityState.Signature, - VerificationMethod: fmt.Sprintf("did:pkh:eip155:0:%s#blockchainAccountId", walletAddress), - Eip712: eip712TypedData, - Created: time.Now(), + var eip712TypedData *apitypes.TypedData + if opts.GistRoot != nil { + typedData, err := getTypedData(GlobalStateType, *userDID, identityState) + if err != nil { + return nil, fmt.Errorf("invalid typed data for global state: %v", err) + } + eip712TypedData = &typedData + } else { + typedData, err := getTypedData(IdentityStateType, *userDID, identityState) + if err != nil { + return nil, fmt.Errorf("invalid typed data for identity state: %v", err) + } + eip712TypedData = &typedData + } + eip712Proof, err := signer.Sign(*eip712TypedData) + if err != nil { + return nil, fmt.Errorf("invalid eip712 typed data: %v", err) } didResolution.DidResolutionMetadata.Context = document.DidResolutionMetadataSigContext() @@ -161,6 +190,95 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op return didResolution, nil } +func getTypedData(typedDataType TypedDataType, did w3c.DID, identityState IdentityState) (apitypes.TypedData, error) { + id, err := core.IDFromDID(did) + if err != nil { + return apitypes.TypedData{}, + fmt.Errorf("invalid did format for did '%s': %v", did, err) + } + + timestamp := timeStamp() + + var apiTypes apitypes.Types + var message apitypes.TypedDataMessage + var primaryType string + + switch typedDataType { + case IdentityStateType: + primaryType = "IdentityState" + apiTypes = apitypes.Types{ + "IdentityState": []apitypes.Type{ + {Name: "timestamp", Type: "uint256"}, + {Name: "id", Type: "uint256"}, + {Name: "state", Type: "uint256"}, + {Name: "replacedAtTimestamp", Type: "uint256"}, + }, + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, + } + ID := id.BigInt().String() + state := identityState.StateInfo.State.String() + replacedAtTimestamp := identityState.StateInfo.ReplacedAtTimestamp.String() + message = apitypes.TypedDataMessage{ + "timestamp": timestamp, + "id": ID, + "state": state, + "replacedAtTimestamp": replacedAtTimestamp, + } + + case GlobalStateType: + primaryType = "GlobalState" + apiTypes = apitypes.Types{ + "GlobalState": []apitypes.Type{ + {Name: "timestamp", Type: "uint256"}, + {Name: "idType", Type: "bytes2"}, + {Name: "root", Type: "uint256"}, + {Name: "replacedAtTimestamp", Type: "uint256"}, + }, + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, + } + idType := fmt.Sprintf("0x%X", id.Type()) + root := identityState.GistInfo.Root.String() + replacedAtTimestamp := identityState.GistInfo.ReplacedAtTimestamp.String() + message = apitypes.TypedDataMessage{ + "timestamp": timestamp, + "idType": idType, + "root": root, + "replacedAtTimestamp": replacedAtTimestamp, + } + default: + return apitypes.TypedData{}, fmt.Errorf("typedDataType %d not defined", typedDataType) + } + + typedData := apitypes.TypedData{ + Types: apiTypes, + PrimaryType: primaryType, + Domain: apitypes.TypedDataDomain{ + Name: "StateInfo", + Version: "1", + ChainId: math.NewHexOrDecimal256(int64(0)), + VerifyingContract: common.Address{}.String(), + }, + Message: message, + } + + return typedData, nil +} + +func timeStamp() string { + timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10) + return timestamp +} + // ResolveDNSDomain return did document by domain via DNS. func (d *DidDocumentServices) ResolveDNSDomain(ctx context.Context, domain string) (*document.DidResolution, error) { domain = fmt.Sprintf("_did.%s", domain) diff --git a/pkg/services/registry.go b/pkg/services/registry.go index f3cd834..c4c688d 100644 --- a/pkg/services/registry.go +++ b/pkg/services/registry.go @@ -5,7 +5,6 @@ import ( "fmt" "math/big" - "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/iden3/go-iden3-core/v2/w3c" "github.com/iden3/go-merkletree-sql/v2" "github.com/iden3/go-schema-processor/v2/verifiable" @@ -19,13 +18,6 @@ var ( ErrNotFound = errors.New("not found") ) -type PrimaryType int32 - -const ( - IdentityStateType PrimaryType = 0 - GlobalStateType PrimaryType = 1 -) - type IdentityState struct { StateInfo *StateInfo GistInfo *GistInfo @@ -104,8 +96,6 @@ type Resolver interface { Resolve(ctx context.Context, did w3c.DID, opts *ResolverOpts) (IdentityState, error) ResolveGist(ctx context.Context, opts *ResolverOpts) (*GistInfo, error) BlockchainID() string - GetWalletAddress() (string, error) - TypedData(primaryType PrimaryType, did w3c.DID, identityState IdentityState, walletAddress string) (apitypes.TypedData, error) } type ResolverRegistry map[string]Resolver diff --git a/pkg/services/signers.go b/pkg/services/signers.go new file mode 100644 index 0000000..813f794 --- /dev/null +++ b/pkg/services/signers.go @@ -0,0 +1,46 @@ +package services + +import ( + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/iden3/driver-did-iden3/pkg/document" +) + +type TypedDataType int32 + +const ( + IdentityStateType TypedDataType = 0 + GlobalStateType TypedDataType = 1 +) + +type EIP712Signer interface { + Sign(typedData apitypes.TypedData) (*document.EthereumEip712SignatureProof2021, error) +} + +type EIP712SignerRegistry map[string]EIP712Signer + +func NewChainEIP712Signers() *EIP712SignerRegistry { + return &EIP712SignerRegistry{} +} + +func (ch *EIP712SignerRegistry) Add(prefix string, signer EIP712Signer) { + (*ch)[prefix] = signer +} + +func (ch *EIP712SignerRegistry) Append(prefix string, signer EIP712Signer) error { + _, ok := (*ch)[prefix] + if ok { + return ErrResolverAlreadyExists + } + (*ch)[prefix] = signer + return nil +} + +func (ch *EIP712SignerRegistry) GetEIP712SignerByNetwork(chain, networkID string) (EIP712Signer, error) { + p := resolverPrefix(chain, networkID) + signer, ok := (*ch)[p] + if !ok { + return nil, ErrNetworkIsNotSupported + } + + return signer, nil +} diff --git a/pkg/services/signers/EIP712Signer.go b/pkg/services/signers/EIP712Signer.go new file mode 100644 index 0000000..4f0b179 --- /dev/null +++ b/pkg/services/signers/EIP712Signer.go @@ -0,0 +1,93 @@ +package signers + +import ( + "crypto/ecdsa" + "encoding/hex" + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/iden3/driver-did-iden3/pkg/document" +) + +const ( + secp256k1VValue = 27 +) + +type EIP712Signer struct { + walletKey string +} + +func NewEIP712Signer(walletKey string) (*EIP712Signer, error) { + globalStateSigner := &EIP712Signer{ + walletKey: walletKey, + } + + return globalStateSigner, nil +} + +func (s *EIP712Signer) getWalletAddress() (string, error) { + if s.walletKey == "" { + return "", errors.New("wallet key is not set") + } + + privateKey, err := crypto.HexToECDSA(s.walletKey) + if err != nil { + return "", err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return "", errors.New("error casting public key to ECDSA") + } + + walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + return walletAddress.String(), nil +} + +func (s *EIP712Signer) Sign(typedData apitypes.TypedData) (*document.EthereumEip712SignatureProof2021, error) { + privateKey, err := crypto.HexToECDSA(s.walletKey) + if err != nil { + return nil, err + } + walletAddress, err := s.getWalletAddress() + if err != nil { + return nil, err + } + domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + return nil, errors.New("error hashing EIP712Domain for signing") + } + typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return nil, errors.New("error hashing PrimaryType message for signing") + } + rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) + dataHash := crypto.Keccak256(rawData) + + signature, err := crypto.Sign(dataHash, privateKey) + if err != nil { + return nil, err + } + + if signature[64] < secp256k1VValue { // Invalid Ethereum signature (V is not 27 or 28) + signature[64] += secp256k1VValue // Transform yellow paper V from 0/1 to 27/28 + } + + messageSignature := "0x" + hex.EncodeToString(signature) + + eip712Proof := &document.EthereumEip712SignatureProof2021{ + Type: document.EthereumEip712SignatureProof2021Type, + ProofPursopose: "assertionMethod", + ProofValue: messageSignature, + VerificationMethod: fmt.Sprintf("did:pkh:eip155:0:%s#blockchainAccountId", walletAddress), + Eip712: typedData, + Created: time.Now(), + } + + return eip712Proof, nil +} From ab0c4780ddf37373ab0b9500f0c40d7ccc5dd7cf Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 16:18:19 +0300 Subject: [PATCH 18/26] add CORS settings --- cmd/driver/main.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cmd/driver/main.go b/cmd/driver/main.go index 7a19b9e..9e38f38 100644 --- a/cmd/driver/main.go +++ b/cmd/driver/main.go @@ -41,7 +41,7 @@ func main() { server := http.Server{ Addr: fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port), - Handler: mux.Routes(), + Handler: addCORSHeaders(mux.Routes()), ReadHeaderTimeout: time.Second, } log.Printf("HTTP server start on '%s:%d'\n", cfg.Server.Host, cfg.Server.Port) @@ -98,3 +98,17 @@ func initEIP712Signers() *services.EIP712SignerRegistry { return chainSigners } + +func addCORSHeaders(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") + + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusOK) + return + } + + next.ServeHTTP(w, r) + }) +} From a4fe29ab88b70a6edc00de01a2d944efacab5d47 Mon Sep 17 00:00:00 2001 From: vmidyllic <74898029+vmidyllic@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:19:21 +0300 Subject: [PATCH 19/26] refactor --- cmd/driver/main.go | 40 ++--- pkg/app/configs/driver.go | 31 +--- pkg/document/proof.go | 2 +- pkg/document/proof_test.go | 2 +- pkg/services/did.go | 138 ++------------- pkg/services/provers.go | 46 +++++ .../provers/EthereumEip712Signature2021.go | 162 ++++++++++++++++++ pkg/services/registry.go | 5 +- pkg/services/signers.go | 46 ----- pkg/services/signers/EIP712Signer.go | 93 ---------- pkg/services/signers/secp256k1.go | 38 ++++ pkg/services/utils/convert.go | 30 ++++ resolvers.settings.yaml | 15 +- 13 files changed, 325 insertions(+), 323 deletions(-) create mode 100644 pkg/services/provers.go create mode 100644 pkg/services/provers/EthereumEip712Signature2021.go delete mode 100644 pkg/services/signers.go delete mode 100644 pkg/services/signers/EIP712Signer.go create mode 100644 pkg/services/signers/secp256k1.go create mode 100644 pkg/services/utils/convert.go diff --git a/cmd/driver/main.go b/cmd/driver/main.go index 7a19b9e..e4063e3 100644 --- a/cmd/driver/main.go +++ b/cmd/driver/main.go @@ -10,10 +10,11 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/iden3/driver-did-iden3/pkg/app" "github.com/iden3/driver-did-iden3/pkg/app/configs" + "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/driver-did-iden3/pkg/services" "github.com/iden3/driver-did-iden3/pkg/services/blockchain/eth" "github.com/iden3/driver-did-iden3/pkg/services/ens" - "github.com/iden3/driver-did-iden3/pkg/services/signers" + "github.com/iden3/driver-did-iden3/pkg/services/provers" ) func main() { @@ -33,10 +34,16 @@ func main() { log.Fatal("can't create registry:", err) } } + var proverRegistry *services.DIDResolutionProverRegistry + if cfg.WalletKey != "" { + proverRegistry, err = initDIDResolutionProverRegistry(*cfg) + if err != nil { + log.Fatal("can't create registry:", err) + } + } mux := app.Handlers{DidDocumentHandler: &app.DidDocumentHandler{ - DidDocumentService: services.NewDidDocumentServices(initResolvers(), r, services.WithSigners(initEIP712Signers())), - }, + DidDocumentService: services.NewDidDocumentServices(initResolvers(), r, services.WithProvers(proverRegistry))}, } server := http.Server{ @@ -75,26 +82,15 @@ func initResolvers() *services.ResolverRegistry { return resolvers } -func initEIP712Signers() *services.EIP712SignerRegistry { - var path string - if len(os.Args) > 3 { - path = os.Args[2] - } - rs, err := configs.ParseSignersSettings(path) +func initDIDResolutionProverRegistry(cfg configs.Config) (*services.DIDResolutionProverRegistry, error) { + + proverRegistry := services.NewDIDResolutionProverRegistry() + + prover, err := provers.NewEIP712Prover(cfg.WalletKey) if err != nil { - log.Fatal("can't read signers settings:", err) - } - chainSigners := services.NewChainEIP712Signers() - for chainName, chainSettings := range rs { - for networkName, networkSettings := range chainSettings { - prefix := fmt.Sprintf("%s:%s", chainName, networkName) - signer, err := signers.NewEIP712Signer(networkSettings.WalletKey) - if err != nil { - log.Fatalf("failed configure signer for network '%s': %v", prefix, err) - } - chainSigners.Add(prefix, signer) - } + return nil, err } + proverRegistry.Add(document.EthereumEip712SignatureProof2021Type, prover) - return chainSigners + return proverRegistry, nil } diff --git a/pkg/app/configs/driver.go b/pkg/app/configs/driver.go index 9e0f093..c0f88d5 100644 --- a/pkg/app/configs/driver.go +++ b/pkg/app/configs/driver.go @@ -11,7 +11,6 @@ import ( ) const defaultPathToResolverSettings = "./resolvers.settings.yaml" -const defaultPathToSignersSettings = "./signers.settings.yaml" // ResolverSettings represent settings for resolver. type ResolverSettings map[string]map[string]struct { @@ -19,13 +18,10 @@ type ResolverSettings map[string]map[string]struct { NetworkURL string `yaml:"networkURL"` } -type SignersSettings map[string]map[string]struct { - WalletKey string `yaml:"walletKey"` -} - // Config structure represent yaml config for did driver. type Config struct { - Server struct { + WalletKey string `envconfig:"WALLET_KEY" default:""` + Server struct { Port int `envconfig:"PORT" default:"8080"` Host string `envconfig:"HOST" default:"localhost"` } @@ -65,26 +61,3 @@ func ParseResolversSettings(path string) (ResolverSettings, error) { return settings, nil } - -// ParseSignersSettings parse yaml file with signers settings. -func ParseSignersSettings(path string) (SignersSettings, error) { - if path == "" { - path = defaultPathToSignersSettings - } - f, err := os.Open(filepath.Clean(path)) - if err != nil { - return nil, err - } - defer func() { - if err := f.Close(); err != nil { - log.Println("failed to close setting file:", err) - } - }() - - settings := SignersSettings{} - if err := yaml.NewDecoder(f).Decode(&settings); err != nil { - return nil, errors.Errorf("invalid yaml file: %v", settings) - } - - return settings, nil -} diff --git a/pkg/document/proof.go b/pkg/document/proof.go index bf16b87..3a28daf 100644 --- a/pkg/document/proof.go +++ b/pkg/document/proof.go @@ -15,7 +15,7 @@ type DidResolutionProofs []DidResolutionProof type EthereumEip712SignatureProof2021 struct { Type verifiable.ProofType `json:"type"` - ProofPursopose string `json:"proofPurpose"` + ProofPurpose string `json:"proofPurpose"` ProofValue string `json:"proofValue"` VerificationMethod string `json:"verificationMethod"` Created time.Time `json:"created"` diff --git a/pkg/document/proof_test.go b/pkg/document/proof_test.go index 6db2fea..ef73a4d 100644 --- a/pkg/document/proof_test.go +++ b/pkg/document/proof_test.go @@ -91,7 +91,7 @@ func TestEthereumEip712SignatureProof2021_JSONUnmarshal(t *testing.T) { wantProof := EthereumEip712SignatureProof2021{ Type: "EthereumEip712Signature2021", - ProofPursopose: "assertionMethod", + ProofPurpose: "assertionMethod", ProofValue: "0xd5e5ffe290a258116a0f7acb4c9a5bbfdd842516061c6a794892b6db05fbd14706de7e189d965bead2ffb23e30d2f6b02ecf764e6fe24be788721049b7e331481c", VerificationMethod: "did:pkh:eip155:1:0x5b18eF56aA61eeAE0E3434e3c3d8AEB19b141fe7#blockchainAccountId", Created: timeParsed, diff --git a/pkg/services/did.go b/pkg/services/did.go index 6410e66..6cf79b3 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -5,13 +5,8 @@ import ( "fmt" "math/big" "net" - "strconv" "strings" - "time" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/iden3/driver-did-iden3/pkg/document" "github.com/iden3/driver-did-iden3/pkg/services/ens" core "github.com/iden3/go-iden3-core/v2" @@ -27,7 +22,7 @@ const ( type DidDocumentServices struct { resolvers *ResolverRegistry ens *ens.Registry - signers *EIP712SignerRegistry + provers *DIDResolutionProverRegistry } type ResolverOpts struct { @@ -38,9 +33,9 @@ type ResolverOpts struct { type DidDocumentOption func(*DidDocumentServices) -func WithSigners(signers *EIP712SignerRegistry) DidDocumentOption { +func WithProvers(provers *DIDResolutionProverRegistry) DidDocumentOption { return func(d *DidDocumentServices) { - d.signers = signers + d.provers = provers } } @@ -151,134 +146,33 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op }, }, ) - + errResolution, err = expectedError(err) + if err != nil { + return errResolution, err + } if opts.Signature != "" { - if d.signers == nil { - return nil, errors.New("signers not initialized") - } - signer, err := d.signers.GetEIP712SignerByNetwork(string(b), string(n)) - if err != nil { - return nil, fmt.Errorf("invalid signer: %v", err) + if d.provers == nil { + return nil, errors.New("provers are not initialized") } - errResolution, err = expectedError(err) + prover, err := d.provers.GetDIDResolutionProverByProofType(verifiable.ProofType(opts.Signature)) if err != nil { - return errResolution, err + return nil, err } - - var eip712TypedData *apitypes.TypedData + stateType := IdentityStateType if opts.GistRoot != nil { - typedData, err := getTypedData(GlobalStateType, *userDID, identityState) - if err != nil { - return nil, fmt.Errorf("invalid typed data for global state: %v", err) - } - eip712TypedData = &typedData - } else { - typedData, err := getTypedData(IdentityStateType, *userDID, identityState) - if err != nil { - return nil, fmt.Errorf("invalid typed data for identity state: %v", err) - } - eip712TypedData = &typedData + stateType = GlobalStateType } - eip712Proof, err := signer.Sign(*eip712TypedData) + didResolutionProof, err := prover.Prove(*userDID, identityState, stateType) if err != nil { - return nil, fmt.Errorf("invalid eip712 typed data: %v", err) + return nil, err } didResolution.DidResolutionMetadata.Context = document.DidResolutionMetadataSigContext() - didResolution.DidResolutionMetadata.Proof = append(didResolution.DidResolutionMetadata.Proof, eip712Proof) + didResolution.DidResolutionMetadata.Proof = append(didResolution.DidResolutionMetadata.Proof, didResolutionProof) } return didResolution, nil } -func getTypedData(typedDataType TypedDataType, did w3c.DID, identityState IdentityState) (apitypes.TypedData, error) { - id, err := core.IDFromDID(did) - if err != nil { - return apitypes.TypedData{}, - fmt.Errorf("invalid did format for did '%s': %v", did, err) - } - - timestamp := timeStamp() - - var apiTypes apitypes.Types - var message apitypes.TypedDataMessage - var primaryType string - - switch typedDataType { - case IdentityStateType: - primaryType = "IdentityState" - apiTypes = apitypes.Types{ - "IdentityState": []apitypes.Type{ - {Name: "timestamp", Type: "uint256"}, - {Name: "id", Type: "uint256"}, - {Name: "state", Type: "uint256"}, - {Name: "replacedAtTimestamp", Type: "uint256"}, - }, - "EIP712Domain": []apitypes.Type{ - {Name: "name", Type: "string"}, - {Name: "version", Type: "string"}, - {Name: "chainId", Type: "uint256"}, - {Name: "verifyingContract", Type: "address"}, - }, - } - ID := id.BigInt().String() - state := identityState.StateInfo.State.String() - replacedAtTimestamp := identityState.StateInfo.ReplacedAtTimestamp.String() - message = apitypes.TypedDataMessage{ - "timestamp": timestamp, - "id": ID, - "state": state, - "replacedAtTimestamp": replacedAtTimestamp, - } - - case GlobalStateType: - primaryType = "GlobalState" - apiTypes = apitypes.Types{ - "GlobalState": []apitypes.Type{ - {Name: "timestamp", Type: "uint256"}, - {Name: "idType", Type: "bytes2"}, - {Name: "root", Type: "uint256"}, - {Name: "replacedAtTimestamp", Type: "uint256"}, - }, - "EIP712Domain": []apitypes.Type{ - {Name: "name", Type: "string"}, - {Name: "version", Type: "string"}, - {Name: "chainId", Type: "uint256"}, - {Name: "verifyingContract", Type: "address"}, - }, - } - idType := fmt.Sprintf("0x%X", id.Type()) - root := identityState.GistInfo.Root.String() - replacedAtTimestamp := identityState.GistInfo.ReplacedAtTimestamp.String() - message = apitypes.TypedDataMessage{ - "timestamp": timestamp, - "idType": idType, - "root": root, - "replacedAtTimestamp": replacedAtTimestamp, - } - default: - return apitypes.TypedData{}, fmt.Errorf("typedDataType %d not defined", typedDataType) - } - - typedData := apitypes.TypedData{ - Types: apiTypes, - PrimaryType: primaryType, - Domain: apitypes.TypedDataDomain{ - Name: "StateInfo", - Version: "1", - ChainId: math.NewHexOrDecimal256(int64(0)), - VerifyingContract: common.Address{}.String(), - }, - Message: message, - } - - return typedData, nil -} - -func timeStamp() string { - timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10) - return timestamp -} - // ResolveDNSDomain return did document by domain via DNS. func (d *DidDocumentServices) ResolveDNSDomain(ctx context.Context, domain string) (*document.DidResolution, error) { domain = fmt.Sprintf("_did.%s", domain) diff --git a/pkg/services/provers.go b/pkg/services/provers.go new file mode 100644 index 0000000..d4440de --- /dev/null +++ b/pkg/services/provers.go @@ -0,0 +1,46 @@ +package services + +import ( + "github.com/iden3/driver-did-iden3/pkg/document" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/go-schema-processor/v2/verifiable" +) + +type StateType int32 + +const ( + IdentityStateType StateType = 0 + GlobalStateType StateType = 1 +) + +type DIDResolutionProver interface { + Prove(did w3c.DID, info IdentityState, dataType StateType) (document.DidResolutionProof, error) +} + +type DIDResolutionProverRegistry map[verifiable.ProofType]DIDResolutionProver + +func NewDIDResolutionProverRegistry() *DIDResolutionProverRegistry { + return &DIDResolutionProverRegistry{} +} + +func (ch *DIDResolutionProverRegistry) Add(proofType verifiable.ProofType, prover DIDResolutionProver) { + (*ch)[proofType] = prover +} + +func (ch *DIDResolutionProverRegistry) Append(proofType verifiable.ProofType, prover DIDResolutionProver) error { + _, ok := (*ch)[proofType] + if ok { + return ErrResolverAlreadyExists + } + (*ch)[proofType] = prover + return nil +} + +func (ch *DIDResolutionProverRegistry) GetDIDResolutionProverByProofType(proofType verifiable.ProofType) (DIDResolutionProver, error) { + signer, ok := (*ch)[proofType] + if !ok { + return nil, ErrProofTypeIsNotSupported + } + + return signer, nil +} diff --git a/pkg/services/provers/EthereumEip712Signature2021.go b/pkg/services/provers/EthereumEip712Signature2021.go new file mode 100644 index 0000000..dd445ce --- /dev/null +++ b/pkg/services/provers/EthereumEip712Signature2021.go @@ -0,0 +1,162 @@ +package provers + +import ( + "encoding/hex" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/iden3/driver-did-iden3/pkg/document" + "github.com/iden3/driver-did-iden3/pkg/services" + "github.com/iden3/driver-did-iden3/pkg/services/signers" + "github.com/iden3/driver-did-iden3/pkg/services/utils" + core "github.com/iden3/go-iden3-core/v2" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/pkg/errors" +) + +type EIP712Prover struct { + signer *signers.Secp256k1Signer + address common.Address +} + +func NewEIP712Prover(walletKey string) (*EIP712Prover, error) { + + s, err := signers.NewSecp256k1Signer(walletKey) // eip712 provers uses ecdsa signer + if err != nil { + return nil, err + } + addr, err := utils.PrivateKeyToAddress(walletKey) + if err != nil { + return nil, err + } + globalStateSigner := &EIP712Prover{ + signer: s, + address: addr, + } + + return globalStateSigner, nil +} + +func (p *EIP712Prover) Prove(did w3c.DID, state services.IdentityState, stateType services.StateType) (document.DidResolutionProof, error) { + + typedData, err := p.getTypedData(did, state, stateType) + if err != nil { + return nil, err + } + domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + return nil, errors.New("error hashing EIP712Domain for signing") + } + typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return nil, errors.New("error hashing PrimaryType message for signing") + } + rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) + dataHash := crypto.Keccak256(rawData) + + signature, err := p.signer.Sign(dataHash) + if err != nil { + return nil, err + } + + messageSignature := "0x" + hex.EncodeToString(signature) + + eip712Proof := &document.EthereumEip712SignatureProof2021{ + Type: document.EthereumEip712SignatureProof2021Type, + ProofPurpose: "assertionMethod", + ProofValue: messageSignature, + VerificationMethod: fmt.Sprintf("did:pkh:eip155:0:%s#blockchainAccountId", p.address), + Eip712: typedData, + Created: time.Now(), + } + + return eip712Proof, nil +} + +func (p *EIP712Prover) getTypedData(did w3c.DID, identityState services.IdentityState, stateType services.StateType) (apitypes.TypedData, error) { + id, err := core.IDFromDID(did) + if err != nil { + return apitypes.TypedData{}, + fmt.Errorf("invalid did format for did '%s': %v", did, err) + } + + timestamp := utils.TimeStamp() + + var apiTypes apitypes.Types + var message apitypes.TypedDataMessage + var primaryType string + + switch stateType { + case services.IdentityStateType: + primaryType = "IdentityState" + apiTypes = apitypes.Types{ + "IdentityState": []apitypes.Type{ + {Name: "timestamp", Type: "uint256"}, + {Name: "id", Type: "uint256"}, + {Name: "state", Type: "uint256"}, + {Name: "replacedAtTimestamp", Type: "uint256"}, + }, + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, + } + ID := id.BigInt().String() + state := identityState.StateInfo.State.String() + replacedAtTimestamp := identityState.StateInfo.ReplacedAtTimestamp.String() + message = apitypes.TypedDataMessage{ + "timestamp": timestamp, + "id": ID, + "state": state, + "replacedAtTimestamp": replacedAtTimestamp, + } + + case services.GlobalStateType: + primaryType = "GlobalState" + apiTypes = apitypes.Types{ + "GlobalState": []apitypes.Type{ + {Name: "timestamp", Type: "uint256"}, + {Name: "idType", Type: "bytes2"}, + {Name: "root", Type: "uint256"}, + {Name: "replacedAtTimestamp", Type: "uint256"}, + }, + "EIP712Domain": []apitypes.Type{ + {Name: "name", Type: "string"}, + {Name: "version", Type: "string"}, + {Name: "chainId", Type: "uint256"}, + {Name: "verifyingContract", Type: "address"}, + }, + } + idType := fmt.Sprintf("0x%X", id.Type()) + root := identityState.GistInfo.Root.String() + replacedAtTimestamp := identityState.GistInfo.ReplacedAtTimestamp.String() + message = apitypes.TypedDataMessage{ + "timestamp": timestamp, + "idType": idType, + "root": root, + "replacedAtTimestamp": replacedAtTimestamp, + } + default: + return apitypes.TypedData{}, fmt.Errorf("type of state info %d is not supportede", stateType) + } + + typedData := apitypes.TypedData{ + Types: apiTypes, + PrimaryType: primaryType, + Domain: apitypes.TypedDataDomain{ + Name: "StateInfo", + Version: "1", + ChainId: math.NewHexOrDecimal256(int64(0)), + VerifyingContract: common.Address{}.String(), + }, + Message: message, + } + + return typedData, nil +} diff --git a/pkg/services/registry.go b/pkg/services/registry.go index c4c688d..80eed33 100644 --- a/pkg/services/registry.go +++ b/pkg/services/registry.go @@ -12,7 +12,9 @@ import ( ) var ( - ErrNetworkIsNotSupported = errors.New("network is not supported") + ErrNetworkIsNotSupported = errors.New("network is not supported") + ErrProofTypeIsNotSupported = errors.New("proof is not supported") + ErrResolverAlreadyExists = errors.New("resolver already exists") ErrNotFound = errors.New("not found") @@ -21,7 +23,6 @@ var ( type IdentityState struct { StateInfo *StateInfo GistInfo *GistInfo - Signature string } type StateInfo struct { diff --git a/pkg/services/signers.go b/pkg/services/signers.go deleted file mode 100644 index 813f794..0000000 --- a/pkg/services/signers.go +++ /dev/null @@ -1,46 +0,0 @@ -package services - -import ( - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/iden3/driver-did-iden3/pkg/document" -) - -type TypedDataType int32 - -const ( - IdentityStateType TypedDataType = 0 - GlobalStateType TypedDataType = 1 -) - -type EIP712Signer interface { - Sign(typedData apitypes.TypedData) (*document.EthereumEip712SignatureProof2021, error) -} - -type EIP712SignerRegistry map[string]EIP712Signer - -func NewChainEIP712Signers() *EIP712SignerRegistry { - return &EIP712SignerRegistry{} -} - -func (ch *EIP712SignerRegistry) Add(prefix string, signer EIP712Signer) { - (*ch)[prefix] = signer -} - -func (ch *EIP712SignerRegistry) Append(prefix string, signer EIP712Signer) error { - _, ok := (*ch)[prefix] - if ok { - return ErrResolverAlreadyExists - } - (*ch)[prefix] = signer - return nil -} - -func (ch *EIP712SignerRegistry) GetEIP712SignerByNetwork(chain, networkID string) (EIP712Signer, error) { - p := resolverPrefix(chain, networkID) - signer, ok := (*ch)[p] - if !ok { - return nil, ErrNetworkIsNotSupported - } - - return signer, nil -} diff --git a/pkg/services/signers/EIP712Signer.go b/pkg/services/signers/EIP712Signer.go deleted file mode 100644 index 4f0b179..0000000 --- a/pkg/services/signers/EIP712Signer.go +++ /dev/null @@ -1,93 +0,0 @@ -package signers - -import ( - "crypto/ecdsa" - "encoding/hex" - "errors" - "fmt" - "time" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/iden3/driver-did-iden3/pkg/document" -) - -const ( - secp256k1VValue = 27 -) - -type EIP712Signer struct { - walletKey string -} - -func NewEIP712Signer(walletKey string) (*EIP712Signer, error) { - globalStateSigner := &EIP712Signer{ - walletKey: walletKey, - } - - return globalStateSigner, nil -} - -func (s *EIP712Signer) getWalletAddress() (string, error) { - if s.walletKey == "" { - return "", errors.New("wallet key is not set") - } - - privateKey, err := crypto.HexToECDSA(s.walletKey) - if err != nil { - return "", err - } - - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return "", errors.New("error casting public key to ECDSA") - } - - walletAddress := crypto.PubkeyToAddress(*publicKeyECDSA) - - return walletAddress.String(), nil -} - -func (s *EIP712Signer) Sign(typedData apitypes.TypedData) (*document.EthereumEip712SignatureProof2021, error) { - privateKey, err := crypto.HexToECDSA(s.walletKey) - if err != nil { - return nil, err - } - walletAddress, err := s.getWalletAddress() - if err != nil { - return nil, err - } - domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - return nil, errors.New("error hashing EIP712Domain for signing") - } - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - return nil, errors.New("error hashing PrimaryType message for signing") - } - rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) - dataHash := crypto.Keccak256(rawData) - - signature, err := crypto.Sign(dataHash, privateKey) - if err != nil { - return nil, err - } - - if signature[64] < secp256k1VValue { // Invalid Ethereum signature (V is not 27 or 28) - signature[64] += secp256k1VValue // Transform yellow paper V from 0/1 to 27/28 - } - - messageSignature := "0x" + hex.EncodeToString(signature) - - eip712Proof := &document.EthereumEip712SignatureProof2021{ - Type: document.EthereumEip712SignatureProof2021Type, - ProofPursopose: "assertionMethod", - ProofValue: messageSignature, - VerificationMethod: fmt.Sprintf("did:pkh:eip155:0:%s#blockchainAccountId", walletAddress), - Eip712: typedData, - Created: time.Now(), - } - - return eip712Proof, nil -} diff --git a/pkg/services/signers/secp256k1.go b/pkg/services/signers/secp256k1.go new file mode 100644 index 0000000..ebf6ef8 --- /dev/null +++ b/pkg/services/signers/secp256k1.go @@ -0,0 +1,38 @@ +package signers + +import ( + "github.com/ethereum/go-ethereum/crypto" +) + +const ( + secp256k1VValue = 27 +) + +type Secp256k1Signer struct { + walletKey string +} + +func NewSecp256k1Signer(walletKey string) (*Secp256k1Signer, error) { + globalStateSigner := &Secp256k1Signer{ + walletKey: walletKey, + } + + return globalStateSigner, nil +} + +func (s *Secp256k1Signer) Sign(payload []byte) ([]byte, error) { + privateKey, err := crypto.HexToECDSA(s.walletKey) + if err != nil { + return nil, err + } + + signature, err := crypto.Sign(payload, privateKey) + if err != nil { + return nil, err + } + + if signature[64] < secp256k1VValue { // Invalid Ethereum signature (V is not 27 or 28) + signature[64] += secp256k1VValue // Transform yellow paper V from 0/1 to 27/28 + } + return signature, nil +} diff --git a/pkg/services/utils/convert.go b/pkg/services/utils/convert.go new file mode 100644 index 0000000..275f21e --- /dev/null +++ b/pkg/services/utils/convert.go @@ -0,0 +1,30 @@ +package utils + +import ( + "crypto/ecdsa" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" +) + +func PrivateKeyToAddress(key string) (common.Address, error) { + privateKey, err := crypto.HexToECDSA(key) + if err != nil { + return common.Address{}, err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return common.Address{}, errors.New("error casting public key to ECDSA") + } + return crypto.PubkeyToAddress(*publicKeyECDSA), nil +} + +func TimeStamp() string { + timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10) + return timestamp +} diff --git a/resolvers.settings.yaml b/resolvers.settings.yaml index 54837f5..935c3b4 100644 --- a/resolvers.settings.yaml +++ b/resolvers.settings.yaml @@ -1,7 +1,8 @@ -iden3: - amoy: - contractAddress: 0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124 - networkURL: https://polygon-amoy.g.alchemy.com/v2/nQxE0N6UyGmmiurh735CVn4hZVLkTu0v - main: - contractAddress: 0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D - networkURL: https://polygon-mainnet.g.alchemy.com/v2/4RsuBJrPQLIInqj6-PH5b4NQa3nIZUxa +resolvers: + iden3: + amoy: + contractAddress: 0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124 + networkURL: https://polygon-amoy.g.alchemy.com/v2/nQxE0N6UyGmmiurh735CVn4hZVLkTu0v + main: + contractAddress: 0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D + networkURL: https://polygon-mainnet.g.alchemy.com/v2/4RsuBJrPQLIInqj6-PH5b4NQa3nIZUxa From aee788e781b2e353795a230e1dcf4232d28af013 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Sep 2024 16:28:35 +0200 Subject: [PATCH 20/26] revert resolvers.settings.yaml --- resolvers.settings.yaml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/resolvers.settings.yaml b/resolvers.settings.yaml index 935c3b4..54837f5 100644 --- a/resolvers.settings.yaml +++ b/resolvers.settings.yaml @@ -1,8 +1,7 @@ -resolvers: - iden3: - amoy: - contractAddress: 0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124 - networkURL: https://polygon-amoy.g.alchemy.com/v2/nQxE0N6UyGmmiurh735CVn4hZVLkTu0v - main: - contractAddress: 0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D - networkURL: https://polygon-mainnet.g.alchemy.com/v2/4RsuBJrPQLIInqj6-PH5b4NQa3nIZUxa +iden3: + amoy: + contractAddress: 0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124 + networkURL: https://polygon-amoy.g.alchemy.com/v2/nQxE0N6UyGmmiurh735CVn4hZVLkTu0v + main: + contractAddress: 0x624ce98D2d27b20b8f8d521723Df8fC4db71D79D + networkURL: https://polygon-mainnet.g.alchemy.com/v2/4RsuBJrPQLIInqj6-PH5b4NQa3nIZUxa From 9001a2cb48b213c34efb77740c758fbaf3b9b6c8 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Sep 2024 17:08:02 +0200 Subject: [PATCH 21/26] fix dockerfile and eth address error --- Dockerfile | 4 ++-- pkg/services/did.go | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3406dba..130d0d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN CGO_ENABLED=0 go build -o ./driver ./cmd/driver/main.go # Build an driver image FROM scratch -COPY ./*.settings.yaml /app/ +COPY ./resolvers.settings.yaml /app/resolvers.settings.yaml COPY --from=base /build/driver /app/driver COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ @@ -28,4 +28,4 @@ ENV HOST=0.0.0.0 ENV PORT=8080 # Command to run -ENTRYPOINT ["/app/driver", "/app/resolvers.settings.yaml", "/app/signers.settings.yaml"] +ENTRYPOINT ["/app/driver", "/app/resolvers.settings.yaml"] diff --git a/pkg/services/did.go b/pkg/services/did.go index 6cf79b3..f6d8d89 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -146,10 +146,7 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op }, }, ) - errResolution, err = expectedError(err) - if err != nil { - return errResolution, err - } + if opts.Signature != "" { if d.provers == nil { return nil, errors.New("provers are not initialized") From db8e2ec2a90342e57a8ad28fef4f52a82c490260 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Sep 2024 17:16:14 +0200 Subject: [PATCH 22/26] update README --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 84f3516..a336e3e 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,7 @@ Driver for the iden3 DID method amoy: contractAddress: "0xf6..." networkURL: "https://polygon-amoy..." - walletKey: "" ``` - `walletKey` is only needed for the resolver if it's a trusted resolver that includes signature of EIP712 message when requested in the resolution with `signature=EthereumEip712Signature2021`. 2. Build docker container: ```bash docker build -t driver-did-iden3:local @@ -19,3 +17,9 @@ Driver for the iden3 DID method ```bash docker run -p 8080:8080 driver-did-iden3:local ``` + + `WALLET_KEY` is only needed for the resolver if it's a trusted resolver that includes signature of EIP712 message when requested in the resolution with `signature=EthereumEip712Signature2021`. + In this case you have to run: + ```bash + docker run -p 8080:8080 -e WALLET_KEY= driver-did-iden3:local + ``` From 6037240a0c68d6df135f0975ea9f66e4ed6a6aec Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 3 Sep 2024 12:10:28 +0200 Subject: [PATCH 23/26] fix checking error not found properly --- pkg/services/did.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/services/did.go b/pkg/services/did.go index f6d8d89..7f35c99 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -93,9 +93,7 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op if !gen { return document.NewDidNotFoundResolution(err.Error()), nil } - } - - if err != nil && opts.State != nil { + } else if err != nil && opts.State != nil { return document.NewDidNotFoundResolution(err.Error()), nil } From 7a05e34e522b4cf4831de4105cb0f5ea55aa7971 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 3 Sep 2024 17:40:17 +0300 Subject: [PATCH 24/26] fix e2e --- .../http_nameservice.postman_collection.json | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/e2e/http_nameservice.postman_collection.json b/tests/e2e/http_nameservice.postman_collection.json index 796cf00..db933d3 100644 --- a/tests/e2e/http_nameservice.postman_collection.json +++ b/tests/e2e/http_nameservice.postman_collection.json @@ -61,12 +61,12 @@ } }, "url": { - "raw": "{{issuer_server}}/v1/identities", + "raw": "{{issuer_server}}/v2/identities", "host": [ "{{issuer_server}}" ], "path": [ - "v1", + "v2", "identities" ] } @@ -127,12 +127,12 @@ } }, "url": { - "raw": "{{issuer_server}}/v1/identities", + "raw": "{{issuer_server}}/v2/identities", "host": [ "{{issuer_server}}" ], "path": [ - "v1", + "v2", "identities" ] } @@ -386,14 +386,15 @@ } }, "url": { - "raw": "{{issuer_server}}/v1/{{issuer_identity}}/claims", + "raw": "{{issuer_server}}/v2/identities/{{issuer_identity}}/credentials", "host": [ "{{issuer_server}}" ], "path": [ - "v1", + "v2", + "identities", "{{issuer_identity}}", - "claims" + "credentials" ] } }, @@ -420,12 +421,13 @@ "method": "POST", "header": [], "url": { - "raw": "{{issuer_server}}/v1/{{issuer_identity}}/state/publish", + "raw": "{{issuer_server}}/v2/identities/{{issuer_identity}}/state/publish", "host": [ "{{issuer_server}}" ], "path": [ - "v1", + "v2", + "identities", "{{issuer_identity}}", "state", "publish" @@ -508,14 +510,15 @@ } }, "url": { - "raw": "{{issuer_server}}/v1/{{issuer_identity}}/claims/{{age_claim_non_merklized_id}}", + "raw": "{{issuer_server}}/v2/identities/{{issuer_identity}}/credentials/{{age_claim_non_merklized_id}}", "host": [ "{{issuer_server}}" ], "path": [ - "v1", + "v2", + "identities", "{{issuer_identity}}", - "claims", + "credentials", "{{age_claim_non_merklized_id}}" ] } From 0806d3177ab33a690e75eda4a0d2655e7bf2be39 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 4 Sep 2024 09:28:11 +0200 Subject: [PATCH 25/26] fix genesis state resolver/prover --- pkg/services/blockchain/eth/resolver.go | 19 ------------------- pkg/services/did.go | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 0622e43..14bafca 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -132,25 +132,6 @@ func (r *Resolver) Resolve( return services.IdentityState{}, err } - if opts.State != nil && errors.Is(err, services.ErrNotFound) { - idGen, err := core.CheckGenesisStateID(userID.BigInt(), opts.State) - if err != nil { - return services.IdentityState{}, err - } - if !idGen { - return services.IdentityState{}, - fmt.Errorf("identity '%s' state '%s' is not found and is not genesis", userID.String(), opts.State) - } - stateInfo = &contract.IStateStateInfo{ - State: opts.State, - ReplacedByState: big.NewInt(0), - CreatedAtTimestamp: big.NewInt(0), - ReplacedAtTimestamp: big.NewInt(0), - CreatedAtBlock: big.NewInt(0), - ReplacedAtBlock: big.NewInt(0), - } - } - identityState := services.IdentityState{} if stateInfo != nil { identityState.StateInfo = &services.StateInfo{ diff --git a/pkg/services/did.go b/pkg/services/did.go index 7f35c99..87c64d7 100644 --- a/pkg/services/did.go +++ b/pkg/services/did.go @@ -157,6 +157,20 @@ func (d *DidDocumentServices) GetDidDocument(ctx context.Context, did string, op if opts.GistRoot != nil { stateType = GlobalStateType } + + if opts.State != nil && identityState.StateInfo == nil { // this case is genesis state + // fill state info for genesis state to be able to prove it + identityState.StateInfo = &StateInfo{ + ID: *userDID, + State: opts.State, + ReplacedByState: big.NewInt(0), + CreatedAtTimestamp: big.NewInt(0), + ReplacedAtTimestamp: big.NewInt(0), + CreatedAtBlock: big.NewInt(0), + ReplacedAtBlock: big.NewInt(0), + } + } + didResolutionProof, err := prover.Prove(*userDID, identityState, stateType) if err != nil { return nil, err From d7e4af16d2a5b88aa5c2b4253231f524da510240 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 4 Sep 2024 12:34:24 +0200 Subject: [PATCH 26/26] remove comment --- .gitignore | 2 +- pkg/services/blockchain/eth/resolver.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a771297..bb777aa 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,5 @@ # Envs configs/driver.yaml .env* -*.settings.yaml +resolvers.settings.yaml test.http diff --git a/pkg/services/blockchain/eth/resolver.go b/pkg/services/blockchain/eth/resolver.go index 14bafca..f530d51 100644 --- a/pkg/services/blockchain/eth/resolver.go +++ b/pkg/services/blockchain/eth/resolver.go @@ -127,7 +127,7 @@ func (r *Resolver) Resolve( default: stateInfo, gistInfo, err = r.resolveLatest(ctx, userID) } - // err == http.505 + if err != nil && !errors.Is(err, services.ErrNotFound) { return services.IdentityState{}, err }